Sync bnd code

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1412393 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/ProjectBuilder.java b/bundleplugin/src/main/java/aQute/bnd/build/ProjectBuilder.java
index bb39983..e551c9c 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/ProjectBuilder.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/ProjectBuilder.java
@@ -116,37 +116,23 @@
 		}
 	}
 
-	protected Jar getBaselineJar(boolean fallback) throws Exception {
+	public Jar getBaselineJar(boolean fallback) throws Exception {
 
 		String baseline = getProperty(Constants.BASELINE);
-		if ((baseline == null || baseline.trim().length() == 0) && !fallback)
+		String baselineRepo = getProperty(Constants.BASELINEREPO);
+		if ((baseline == null || baseline.trim().length() == 0) 
+				&& (baselineRepo == null || baselineRepo.trim().length() == 0) && !fallback)
 			return null;
 
-		trace("baseline %s", baseline);
-
 		File baselineFile = null;
-		if ((baseline == null || baseline.trim().length() == 0) && fallback) {
-
-			String repoName = getProperty(Constants.BASELINEREPO);
-			if (repoName == null) {
-				repoName = getProperty(Constants.RELEASEREPO);
-				if (repoName == null) {
-					return null;
-				}
-			}
-
-			List<RepositoryPlugin> repos = getPlugins(RepositoryPlugin.class);
-			for (RepositoryPlugin repo : repos) {
-				if (repoName.equals(repo.getName())) {
-					SortedSet<Version> versions = repo.versions(getBsn());
-					if (!versions.isEmpty()) {
-						baselineFile = repo.get(getBsn(), versions.last(), null);
-					}
-					break;
-				}
-			}
+		if ((baseline == null || baseline.trim().length() == 0)) {
+			baselineFile = getBaselineFromRepo(fallback);
+			if (baselineFile != null)
+				trace("baseline %s", baselineFile.getName());
 		} else {
 
+			trace("baseline %s", baseline);
+
 			Collection<Container> bundles = project.getBundles(Strategy.LOWEST, baseline);
 			for (Container c : bundles) {
 
@@ -165,6 +151,32 @@
 		return new Jar(baselineFile);
 	}
 
+	private File getBaselineFromRepo(boolean fallback) throws Exception {
+		String repoName = getProperty(Constants.BASELINEREPO);
+		if (repoName == null && !fallback)
+			return null;
+
+		if (repoName == null) {
+			repoName = getProperty(Constants.RELEASEREPO);
+			if (repoName == null) {
+				return null;
+			}
+		}
+
+		List<RepositoryPlugin> repos = getPlugins(RepositoryPlugin.class);
+		for (RepositoryPlugin repo : repos) {
+			if (repoName.equals(repo.getName())) {
+				SortedSet<Version> versions = repo.versions(getBsn());
+				if (!versions.isEmpty()) {
+					return repo.get(getBsn(), versions.last(), null);
+				}
+				break;
+			}
+		}
+		return null;
+
+	}
+
 	/** 
 	 * Gets the baseline Jar. 
 	 * 
diff --git a/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java b/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java
index 84e6234..b79780f 100644
--- a/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java
+++ b/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java
@@ -38,7 +38,7 @@
 																							+ Verifier.VERSIONRANGE
 																									.toString()));
 
-	static Syntax[]							syntaxes				= new Syntax[] {
+	static final Syntax[]					syntaxes				= new Syntax[] {
 			new Syntax(
 					BUNDLE_ACTIVATIONPOLICY,
 					"The " + BUNDLE_ACTIVATIONPOLICY + " header specifies how the framework should activate the bundle once started.",
diff --git a/bundleplugin/src/main/java/aQute/bnd/obr/OBRFragment.java b/bundleplugin/src/main/java/aQute/bnd/obr/OBRFragment.java
new file mode 100644
index 0000000..1af3518
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/obr/OBRFragment.java
@@ -0,0 +1,300 @@
+package aQute.bnd.obr;
+
+import java.io.*;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.*;
+import org.osgi.namespace.service.*;
+import org.osgi.resource.*;
+import org.osgi.service.repository.*;
+
+import aQute.bnd.header.*;
+import aQute.bnd.osgi.*;
+import aQute.bnd.osgi.resource.*;
+import aQute.bnd.version.*;
+import aQute.libg.cryptography.*;
+import aQute.libg.map.*;
+import aQute.service.reporter.*;
+
+public class OBRFragment {
+
+	// The mime-type of an OSGi bundle
+	static final String	MIME_TYPE_OSGI_BUNDLE	= "application/vnd.osgi.bundle";
+static Pattern EE_PATTERN = Pattern.compile("[^.]-([\\d]+(?:\\.[\\d]+(?:\\.[\\d]+(?:\\.)?)?)?)");
+
+	@SuppressWarnings("deprecation")
+	public static Reporter parse(Jar jar, ResourceBuilder resource) throws Exception {
+		Manifest m = jar.getManifest();
+		if (m == null)
+			return null;
+
+		Domain d = Domain.domain(m);
+		d.setTranslation(jar);
+		Entry<String,Attrs> bundleSymbolicName = d.getBundleSymbolicName();
+
+		if (bundleSymbolicName == null)
+			return null;
+
+		boolean singleton = "true".equals(bundleSymbolicName.getValue().get(Constants.SINGLETON_DIRECTIVE + ":"));
+		boolean isFragment = d.get(Constants.FRAGMENT_HOST) != null;
+		Version version = d.getBundleVersion() == null ? Version.emptyVersion : new Version(d.getBundleVersion());
+		
+		CapReqBuilder identity = new CapReqBuilder(IdentityNamespace.IDENTITY_NAMESPACE);
+		identity.addAttribute(IdentityNamespace.IDENTITY_NAMESPACE, bundleSymbolicName.getKey());
+		identity.addAttribute(IdentityNamespace.CAPABILITY_COPYRIGHT_ATTRIBUTE, d.translate(Constants.BUNDLE_COPYRIGHT));
+		identity.addAttribute(IdentityNamespace.CAPABILITY_DESCRIPTION_ATTRIBUTE,
+				d.translate(Constants.BUNDLE_DESCRIPTION));
+		identity.addAttribute(IdentityNamespace.CAPABILITY_DOCUMENTATION_ATTRIBUTE,
+				d.translate(Constants.BUNDLE_DOCURL));
+		identity.addAttribute(IdentityNamespace.CAPABILITY_LICENSE_ATTRIBUTE, d.translate("Bundle-License"));
+		if (singleton)
+			identity.addAttribute(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE, "true");
+		identity.addAttribute(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
+				isFragment ? IdentityNamespace.TYPE_FRAGMENT
+						: IdentityNamespace.TYPE_BUNDLE);
+		identity.addAttribute(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, new Version(d.getBundleVersion()));
+
+		resource.addCapability(identity);
+
+		
+		if ( isFragment ) {
+			
+			//
+			// Fragment-Host
+			//
+
+			Entry<String,Attrs> fragmentHost = d.getFragmentHost();
+			CapReqBuilder fragment = new CapReqBuilder(HostNamespace.HOST_NAMESPACE);
+			String v = fragmentHost.getValue().get("version");
+			if ( v == null)
+				v = "0";
+			Version fragmentVersion = new Version(v);
+			String filter = filter(PackageNamespace.PACKAGE_NAMESPACE, fragmentHost.getKey(), fragmentHost.getValue());
+			fragment.addDirective(HostNamespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString());
+			resource.addRequirement(fragment);
+		} else {
+			
+			//
+			// Bundle-SymbolicName
+			//
+			
+			CapReqBuilder bundle = new CapReqBuilder(BundleNamespace.BUNDLE_NAMESPACE);
+			CapReqBuilder host = new CapReqBuilder(HostNamespace.HOST_NAMESPACE);
+			
+			bundle.addAttribute("version", version);
+			host.addAttribute("version", version);
+			
+			for (Entry<String,String> e : bundleSymbolicName.getValue().entrySet()) {
+				String key = e.getKey();
+				if (key.endsWith(":")) {
+					String directive = key.substring(0, key.length() - 1);
+					if (Constants.FRAGMENT_ATTACHMENT_DIRECTIVE.equalsIgnoreCase(directive)) {
+						if (Constants.FRAGMENT_ATTACHMENT_NEVER.equalsIgnoreCase(e.getValue()))
+							host = null;
+						
+					} else if (!Constants.SINGLETON_DIRECTIVE.equalsIgnoreCase(directive)) {
+						bundle.addDirective(directive, e.getValue());
+					}
+					if ( host != null )
+						host.addDirective(directive, e.getValue());
+					bundle.addDirective(directive, e.getValue());					
+				} else {
+					if ( host != null )
+						host.addAttribute(key, e.getValue());
+					bundle.addAttribute(key, e.getValue());
+				}
+			}
+			if ( host != null)
+				resource.addCapability(host);
+			resource.addCapability(bundle);
+		}		
+		
+		//
+		// Export-Package
+		//
+		
+		Parameters exports = d.getExportPackage();		
+		for (Entry<String,Attrs> entry : exports.entrySet()) {
+			CapReqBuilder exported = new CapReqBuilder(PackageNamespace.PACKAGE_NAMESPACE);
+
+			String pkgName = Processor.removeDuplicateMarker(entry.getKey());
+			exported.addAttribute(PackageNamespace.PACKAGE_NAMESPACE, pkgName);
+
+			String versionStr = entry.getValue().get(Constants.VERSION_ATTRIBUTE);
+			Version v = Version.parseVersion(entry.getValue().get("version"));
+			
+			exported.addAttribute(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE, version);
+
+			for (Entry<String,String> attribEntry : entry.getValue().entrySet()) {
+				String key = attribEntry.getKey();
+				if (key.endsWith(":")) {
+					String directive = key.substring(0, key.length()-1);
+					exported.addDirective(directive, attribEntry.getValue());
+				} else {
+					if ( key.equals("specification-version") || key.equals("version"))
+						exported.addAttribute("version",  Version.parseVersion(attribEntry.getValue()));
+					else
+						exported.addAttribute(key, attribEntry.getValue());
+				}
+			}
+
+			exported.addAttribute(PackageNamespace.CAPABILITY_BUNDLE_SYMBOLICNAME_ATTRIBUTE, bundleSymbolicName.getKey());
+			exported.addAttribute(PackageNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, version);
+
+			resource.addCapability(exported);
+		}
+
+		//
+		// Import-Package
+		//
+		
+		Parameters imports = d.getImportPackage();		
+		for (Entry<String,Attrs> entry : imports.entrySet()) {
+			CapReqBuilder imported = new CapReqBuilder(PackageNamespace.PACKAGE_NAMESPACE);
+			String name = Processor.removeDuplicateMarker(entry.getKey());
+			String filter = filter(PackageNamespace.PACKAGE_NAMESPACE, Processor.removeDuplicateMarker(entry.getKey()), entry.getValue());
+			imported.addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter);			
+			resource.addRequirement(imported);
+		}
+
+		//
+		// Require-Bundle
+		//
+		
+		Parameters requires = d.getRequireBundle();
+		for (Entry<String,Attrs> entry : requires.entrySet()) {
+			CapReqBuilder req = new CapReqBuilder(BundleNamespace.BUNDLE_NAMESPACE);
+			String bsn = Processor.removeDuplicateMarker(entry.getKey());
+			String filter = filter(BundleNamespace.BUNDLE_NAMESPACE, bsn, entry.getValue());
+			req.addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter);			
+			resource.addRequirement(req);
+		}
+
+		//
+		// Bundle-RequiredExecutionEnvironment
+		//
+		
+		Parameters brees = d.getBundleRequiredExecutionEnvironment();
+		Formatter formatter = new Formatter();
+			formatter.format("(|");
+		
+		for ( Entry<String,Attrs> bree : brees.entrySet()	) {
+			String name = Processor.removeDuplicateMarker(bree.getKey());
+			Matcher matcher = EE_PATTERN.matcher(name);
+			if ( matcher.matches()) {
+				name = matcher.group(1);
+				Version v = Version.parseVersion(matcher.group(2));
+				formatter.format("%s", filter(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE, name, MAP.$("version", v.toString())));
+			}
+		}
+		formatter.format(")");
+		
+		CapReqBuilder breeReq = new CapReqBuilder(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE);
+		breeReq.addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, formatter.toString());
+
+		//
+		// Export-Service (deprecated)
+		//
+		
+		for (Entry<String,Attrs> export : d.getParameters(Constants.EXPORT_SERVICE).entrySet()) {
+			CapReqBuilder	exportedService = new CapReqBuilder(ServiceNamespace.SERVICE_NAMESPACE);
+			String service = Processor.removeDuplicateMarker(export.getKey());
+			exportedService.addAttribute(ServiceNamespace.SERVICE_NAMESPACE, service);
+			exportedService.addAttribute(ServiceNamespace.CAPABILITY_OBJECTCLASS_ATTRIBUTE, export.getValue().get("objectclass"));
+			resource.addCapability(exportedService);
+		}
+		
+		//
+		// Import-Service (deprecated)
+		//
+		
+		for (Entry<String,Attrs> imported : d.getParameters(Constants.IMPORT_SERVICE).entrySet()) {
+			CapReqBuilder	importedService = new CapReqBuilder(ServiceNamespace.SERVICE_NAMESPACE);
+			String service = Processor.removeDuplicateMarker(imported.getKey());
+			importedService.addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter(ServiceNamespace.SERVICE_NAMESPACE, service, imported.getValue()));
+			resource.addRequirement(importedService);
+		}
+		
+		//
+		// Provide-Capability
+		//
+		
+		for ( Entry<String,Attrs> rc : d.getProvideCapability().entrySet()) {
+			resource.addCapability( toCapability(rc.getKey(), rc.getValue()));
+		}
+		
+		//
+		// Require-Capability
+		//
+
+		for ( Entry<String,Attrs> rc : d.getRequireCapability().entrySet()) {
+			resource.addCapability( toRequirement(rc.getKey(), rc.getValue()));
+		}
+		
+		
+		return null;
+	}
+
+	private static Capability toRequirement(String key, Attrs value) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	private static Capability toCapability(String key, Attrs value) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public static Reporter parse(File file, ResourceBuilder resource, String base) throws Exception {
+		Jar jar = new Jar(file);
+		try {
+			Reporter reporter = parse(jar, resource);
+			if (!reporter.isOk())
+				return reporter;
+
+			CapReqBuilder content = new CapReqBuilder(ContentNamespace.CONTENT_NAMESPACE);
+			String sha = SHA1.digest(file).asHex();
+			content.addAttribute(ContentNamespace.CONTENT_NAMESPACE, sha);
+			content.addAttribute(ContentNamespace.CAPABILITY_SIZE_ATTRIBUTE, (long) file.length());
+			content.addAttribute(ContentNamespace.CAPABILITY_MIME_ATTRIBUTE, MIME_TYPE_OSGI_BUNDLE);
+
+			if (base != null) {
+				String path = file.getAbsolutePath();
+				if (base.startsWith(path)) {
+					content.addAttribute(ContentNamespace.CAPABILITY_URL_ATTRIBUTE, path.substring(base.length())
+							.replace(File.separatorChar, '/'));
+				} else {
+					reporter.error("Base path %s is not parent of file path: %s", base, file.getAbsolutePath());
+				}
+			}
+
+			resource.addCapability(content);
+			return reporter;
+		}
+		finally {
+			jar.close();
+		}
+	}
+	
+	// TODO finish
+	private static String filter(String ns, String primary, Map<String,String> value) {
+		Formatter f = new Formatter();
+		f.format("(&(%s=%s)", ns, primary);
+		for ( String key : value.keySet()) {
+			if ( key.equals("version") || key.equals("bundle-version")) {
+				VersionRange vr = new VersionRange(value.get(key));
+			} else {
+				f.format("(%s=%s)", key, value.get(key));
+			}
+		}
+		
+		f.format(")");
+		return null;
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/osgi/Analyzer.java b/bundleplugin/src/main/java/aQute/bnd/osgi/Analyzer.java
index fbbf4fa..7548f51 100755
--- a/bundleplugin/src/main/java/aQute/bnd/osgi/Analyzer.java
+++ b/bundleplugin/src/main/java/aQute/bnd/osgi/Analyzer.java
@@ -2008,7 +2008,7 @@
 
 	private static String removeLeadingZeroes(String group) {
 		if (group == null)
-			return null;
+			return "0";
 
 		int n = 0;
 		while (n < group.length() - 1 && group.charAt(n) == '0')
diff --git a/bundleplugin/src/main/java/aQute/bnd/osgi/Domain.java b/bundleplugin/src/main/java/aQute/bnd/osgi/Domain.java
index 1b17aaf..e904aa8 100644
--- a/bundleplugin/src/main/java/aQute/bnd/osgi/Domain.java
+++ b/bundleplugin/src/main/java/aQute/bnd/osgi/Domain.java
@@ -2,6 +2,7 @@
 
 import static aQute.bnd.osgi.Constants.*;
 
+import java.io.*;
 import java.util.*;
 import java.util.Map.Entry;
 import java.util.jar.*;
@@ -17,6 +18,7 @@
  * provides convenient methods to access these properties via semantic methods.
  */
 public abstract class Domain implements Iterable<String> {
+	final Properties	translation	= new Properties();
 
 	public abstract String get(String key);
 
@@ -27,6 +29,23 @@
 		return deflt;
 	}
 
+	public String translate(String key) {
+		return translate(key, null);
+	}
+	
+	
+	public String translate(String key, String deflt) {
+		String value = get(key);
+		if ( value == null)
+			return deflt;
+		
+		if ( value.indexOf('%')>=0) {
+			value = value.trim().substring(1);
+			return translation.getProperty(value,value);
+		}
+		return null;
+	}
+
 	public abstract void set(String key, String value);
 
 	public abstract Iterator<String> iterator();
@@ -147,6 +166,13 @@
 	public Parameters getParameters(String key, String deflt, Reporter reporter) {
 		return new Parameters(get(key, deflt), reporter);
 	}
+	
+	
+	public Parameters getRequireBundle() {
+		return getParameters(Constants.REQUIRE_BUNDLE);
+	}
+
+
 
 	public Parameters getImportPackage() {
 		return getParameters(IMPORT_PACKAGE);
@@ -244,6 +270,13 @@
 		return p.entrySet().iterator().next();
 	}
 
+	public Map.Entry<String,Attrs> getFragmentHost() {
+		Parameters p = getParameters(FRAGMENT_HOST);
+		if (p.isEmpty())
+			return null;
+		return p.entrySet().iterator().next();
+	}
+
 	public void setBundleSymbolicName(String s) {
 		set(BUNDLE_SYMBOLICNAME, s);
 	}
@@ -307,4 +340,37 @@
 		set(CONDITIONAL_PACKAGE, string);
 
 	}
+
+	public void setTranslation(Jar jar) throws Exception {
+
+		Manifest m = jar.getManifest();
+		if (m == null)
+			return;
+
+		String path = m.getMainAttributes().getValue(Constants.BUNDLE_LOCALIZATION);
+		if (path == null)
+			path = org.osgi.framework.Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
+
+		path += ".properties";
+
+		Resource propsResource = jar.getResource(path);
+		if (propsResource != null) {
+			InputStream in = propsResource.openInputStream();
+			try {
+				translation.load(in);
+			}
+			finally {
+				in.close();
+			}
+		}
+	}
+
+	public Parameters getRequireCapability() {
+		return getParameters(Constants.REQUIRE_CAPABILITY);
+	}
+
+	public Parameters getProvideCapability() {
+		return getParameters(Constants.PROVIDE_CAPABILITY);
+	}
+
 }
diff --git a/bundleplugin/src/main/java/aQute/bnd/osgi/Macro.java b/bundleplugin/src/main/java/aQute/bnd/osgi/Macro.java
index be1a4c8..6146a00 100755
--- a/bundleplugin/src/main/java/aQute/bnd/osgi/Macro.java
+++ b/bundleplugin/src/main/java/aQute/bnd/osgi/Macro.java
@@ -220,9 +220,10 @@
 				Method m = target.getClass().getMethod(cname, new Class[] {
 					String[].class
 				});
-				return (String) m.invoke(target, new Object[] {
+				Object result = m.invoke(target, new Object[] {
 					args
 				});
+				return result == null ? null : result.toString();
 			}
 			catch (NoSuchMethodException e) {
 				// Ignore
diff --git a/bundleplugin/src/main/java/aQute/bnd/osgi/Verifier.java b/bundleplugin/src/main/java/aQute/bnd/osgi/Verifier.java
index b5bd72c..77b324d 100755
--- a/bundleplugin/src/main/java/aQute/bnd/osgi/Verifier.java
+++ b/bundleplugin/src/main/java/aQute/bnd/osgi/Verifier.java
@@ -78,7 +78,7 @@
 	public final static String	SYMBOLICNAME_STRING				= "[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*";
 	public final static Pattern	SYMBOLICNAME					= Pattern.compile(SYMBOLICNAME_STRING);
 
-	public final static String	VERSION_STRING					= "[0-9]+(\\.[0-9]+(\\.[0-9]+(\\.[0-9A-Za-z_-]+)?)?)?";
+	public final static String	VERSION_STRING					= "[0-9]{1,9}(\\.[0-9]{1,9}(\\.[0-9]{1,9}(\\.[0-9A-Za-z_-]+)?)?)?";
 	public final static Pattern	VERSION							= Pattern.compile(VERSION_STRING);
 	final static Pattern		FILTEROP						= Pattern.compile("=|<=|>=|~=");
 	public final static Pattern	VERSIONRANGE					= Pattern.compile("((\\(|\\[)"
diff --git a/bundleplugin/src/main/java/aQute/bnd/osgi/packageinfo b/bundleplugin/src/main/java/aQute/bnd/osgi/packageinfo
index 084a0d4..a2afe57 100644
--- a/bundleplugin/src/main/java/aQute/bnd/osgi/packageinfo
+++ b/bundleplugin/src/main/java/aQute/bnd/osgi/packageinfo
@@ -1 +1 @@
-version 2.0.0
+version 2.1.0
diff --git a/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReqBuilder.java b/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReqBuilder.java
index b06f37a..593dd39 100644
--- a/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReqBuilder.java
+++ b/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReqBuilder.java
@@ -18,74 +18,80 @@
 	public CapReqBuilder(String namespace) {
 		this.namespace = namespace;
 	}
-	
+
 	public static CapReqBuilder clone(Capability capability) {
 		CapReqBuilder builder = new CapReqBuilder(capability.getNamespace());
 		builder.addAttributes(capability.getAttributes());
 		builder.addDirectives(capability.getDirectives());
 		return builder;
 	}
-	
+
 	public static CapReqBuilder clone(Requirement requirement) {
 		CapReqBuilder builder = new CapReqBuilder(requirement.getNamespace());
 		builder.addAttributes(requirement.getAttributes());
 		builder.addDirectives(requirement.getDirectives());
 		return builder;
 	}
-	
+
 	public String getNamespace() {
 		return namespace;
 	}
-	
+
 	public CapReqBuilder setResource(Resource resource) {
 		this.resource = resource;
 		return this;
 	}
 
 	public CapReqBuilder addAttribute(String name, Object value) {
-		attributes.put(name, value);
+		if (value != null)
+			attributes.put(name, value);
 		return this;
 	}
-	
-	public CapReqBuilder addAttributes(Map<? extends String, ? extends Object> attributes) {
+
+	public CapReqBuilder addAttributes(Map< ? extends String, ? extends Object> attributes) {
 		this.attributes.putAll(attributes);
 		return this;
 	}
 
 	public CapReqBuilder addDirective(String name, String value) {
-		directives.put(name, value);
+		if (value != null)
+			directives.put(name, value);
 		return this;
 	}
-	
-	public CapReqBuilder addDirectives(Map<? extends String, ? extends String> directives) {
+
+	public CapReqBuilder addDirectives(Map< ? extends String, ? extends String> directives) {
 		this.directives.putAll(directives);
 		return this;
 	}
-	
+
 	public Capability buildCapability() {
 		// TODO check the thrown exception
-		if (resource == null) throw new IllegalStateException("Cannot build Capability with null Resource.");
+		if (resource == null)
+			throw new IllegalStateException("Cannot build Capability with null Resource.");
 		return new CapReq(MODE.Capability, namespace, resource, directives, attributes);
 	}
-	
+
 	public Requirement buildRequirement() {
 		// TODO check the thrown exception
-		if (resource == null) throw new IllegalStateException("Cannot build Requirement with null Resource.");
+		if (resource == null)
+			throw new IllegalStateException("Cannot build Requirement with null Resource.");
 		return new CapReq(MODE.Requirement, namespace, resource, directives, attributes);
 	}
 
 	public Requirement buildSyntheticRequirement() {
 		return new CapReq(MODE.Requirement, namespace, null, directives, attributes);
 	}
-	
+
 	public static final CapReqBuilder createPackageRequirement(String pkgName, String range) {
 		Filter filter;
 		SimpleFilter pkgNameFilter = new SimpleFilter(PackageNamespace.PACKAGE_NAMESPACE, pkgName);
 		if (range != null)
-			filter = new AndFilter().addChild(pkgNameFilter).addChild(new LiteralFilter(Filters.fromVersionRange(range)));
+			filter = new AndFilter().addChild(pkgNameFilter).addChild(
+					new LiteralFilter(Filters.fromVersionRange(range)));
 		else
 			filter = pkgNameFilter;
-		
-		return new CapReqBuilder(PackageNamespace.PACKAGE_NAMESPACE).addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString());
+
+		return new CapReqBuilder(PackageNamespace.PACKAGE_NAMESPACE).addDirective(
+				Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString());
 	}
 }
diff --git a/bundleplugin/src/main/java/aQute/bnd/osgi/resource/packageinfo b/bundleplugin/src/main/java/aQute/bnd/osgi/resource/packageinfo
index a4f1546..b1793a2 100644
--- a/bundleplugin/src/main/java/aQute/bnd/osgi/resource/packageinfo
+++ b/bundleplugin/src/main/java/aQute/bnd/osgi/resource/packageinfo
@@ -1 +1 @@
-version 1.0
\ No newline at end of file
+version 1.1.0
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/version/Version.java b/bundleplugin/src/main/java/aQute/bnd/version/Version.java
index 7ec3ba1..5a9d194 100755
--- a/bundleplugin/src/main/java/aQute/bnd/version/Version.java
+++ b/bundleplugin/src/main/java/aQute/bnd/version/Version.java
@@ -7,7 +7,7 @@
 	final int					minor;
 	final int					micro;
 	final String				qualifier;
-	public final static String	VERSION_STRING	= "(\\d+)(\\.(\\d+)(\\.(\\d+)(\\.([-_\\da-zA-Z]+))?)?)?";
+	public final static String	VERSION_STRING	= "(\\d{1,9})(\\.(\\d{1,9})(\\.(\\d{1,9})(\\.([-_\\da-zA-Z]+))?)?)?";
 	public final static Pattern	VERSION			= Pattern.compile(VERSION_STRING);
 	public final static Version	LOWEST			= new Version();
 	public final static Version	HIGHEST			= new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE,
diff --git a/bundleplugin/src/main/java/aQute/lib/io/IO.java b/bundleplugin/src/main/java/aQute/lib/io/IO.java
index 0cb7e1b..9a118d5 100644
--- a/bundleplugin/src/main/java/aQute/lib/io/IO.java
+++ b/bundleplugin/src/main/java/aQute/lib/io/IO.java
@@ -7,8 +7,8 @@
 import java.util.*;
 
 public class IO {
-	static public File	work	= new File(System.getProperty("user.dir"));
-	static public File	home	= new File(System.getProperty("user.home"));
+	static final public File	work	= new File(System.getProperty("user.dir"));
+	static final public File	home	= new File(System.getProperty("user.home"));
 
 	public static void copy(Reader r, Writer w) throws IOException {
 		try {
diff --git a/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java b/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java
index f8f7afb..d4612ee 100644
--- a/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java
+++ b/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java
@@ -2,7 +2,7 @@
 
 import java.io.Serializable;
 
-public class Pair<A, B> implements Serializable {
+public class Pair<A, B> implements Serializable, Cloneable {
 
 	private static final long	serialVersionUID	= 1L;
 
diff --git a/bundleplugin/src/main/resources/aQute/lib/spring/extract.xsl b/bundleplugin/src/main/resources/aQute/lib/spring/extract.xsl
index 14bd8f4..11726b6 100644
--- a/bundleplugin/src/main/resources/aQute/lib/spring/extract.xsl
+++ b/bundleplugin/src/main/resources/aQute/lib/spring/extract.xsl
@@ -1,6 +1,21 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:jms="http://www.springframework.org/schema/jms" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:osgi-compendium="http://www.springframework.org/schema/osgi-compendium" xmlns:osgi="http://www.springframework.org/schema/osgi" xmlns:tool="http://www.springframework.org/schema/tool" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:webflow-config="http://www.springframework.org/schema/webflow-config" xmlns:blueprint="http://www.eclipse.org/gemini/blueprint/schema/blueprint">
+<xsl:stylesheet version="1.0" 
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:beans="http://www.springframework.org/schema/beans" 
+	xmlns:aop="http://www.springframework.org/schema/aop" 
+	xmlns:context="http://www.springframework.org/schema/context" 
+	xmlns:jee="http://www.springframework.org/schema/jee" 
+	xmlns:jms="http://www.springframework.org/schema/jms" 
+	xmlns:lang="http://www.springframework.org/schema/lang" 
+	xmlns:osgi-compendium="http://www.springframework.org/schema/osgi-compendium" 
+	xmlns:osgi="http://www.springframework.org/schema/osgi" 
+	xmlns:tool="http://www.springframework.org/schema/tool" 
+	xmlns:tx="http://www.springframework.org/schema/tx" 
+	xmlns:util="http://www.springframework.org/schema/util" 
+	xmlns:webflow-config="http://www.springframework.org/schema/webflow-config" 
+	xmlns:gemini-blueprint="http://www.eclipse.org/gemini/blueprint/schema/blueprint"
+	xmlns:blueprint="http://www.osgi.org/xmlns/blueprint/v1.0.0">
 	<xsl:output method="text" />
 
 	<xsl:template match="/">
@@ -20,7 +35,9 @@
 			|	//jee:*/@business-interface
 			|	//lang:*/@script-interfaces
 			|	//osgi:*/@interface
-			|	//blueprint:*/@interface
+			|	//gemini-blueprint:*/@interface
+			|   //blueprint:*/@interface
+			|   //blueprint:*/@class
 			|	//util:list/@list-class
 			|	//util:set/@set-class
 			|	//util:map/@map-class