Sync with latest bnd code for testing purposes

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1354104 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/pom.xml b/bundleplugin/pom.xml
index 8ec7b93..07933a3 100644
--- a/bundleplugin/pom.xml
+++ b/bundleplugin/pom.xml
@@ -86,6 +86,11 @@
 -->
   <dependency>
     <groupId>org.osgi</groupId>
+    <artifactId>org.osgi.core</artifactId>
+    <version>5.0.0</version>
+  </dependency>
+  <dependency>
+    <groupId>org.osgi</groupId>
     <artifactId>org.osgi.compendium</artifactId>
     <version>4.3.0</version>
   </dependency>
diff --git a/bundleplugin/src/main/java/aQute/bnd/annotation/Export.java b/bundleplugin/src/main/java/aQute/bnd/annotation/Export.java
index 72526af..cd48468 100644
--- a/bundleplugin/src/main/java/aQute/bnd/annotation/Export.java
+++ b/bundleplugin/src/main/java/aQute/bnd/annotation/Export.java
@@ -18,13 +18,4 @@
 	Class< ? >[] exclude() default Object.class;
 
 	Class< ? >[] include() default Object.class;
-
-	/**
-	 * Use {@link @Version} annotation instead
-	 * 
-	 * @return
-	 */
-	@Deprecated()
-	String version() default "";
-
 }
diff --git a/bundleplugin/src/main/java/aQute/bnd/annotation/UsePolicy.java b/bundleplugin/src/main/java/aQute/bnd/annotation/UsePolicy.java
deleted file mode 100644
index d4db6d9..0000000
--- a/bundleplugin/src/main/java/aQute/bnd/annotation/UsePolicy.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package aQute.bnd.annotation;
-
-import java.lang.annotation.*;
-
-/**
- * This annotation can be applied to interface where an implementation should be
- * treated as a use policy, not an implementation policy. Many package have
- * interfaces that are very stable and can be maintained backward compatible for
- * implementers during minor changes. For example, in Event Admin, the
- * EventAdmin implementers should follow the minor version, e.g. [1.1,1.2),
- * however, an implementer of EventHandler should not require such a small
- * range. Therefore an interface like EventHandler should use this anotation.
- */
-@Retention(RetentionPolicy.CLASS)
-@Target(ElementType.TYPE)
-@Deprecated
-public @interface UsePolicy {
-	String	RNAME	= "LaQute/bnd/annotation/UsePolicy;";
-
-}
diff --git a/bundleplugin/src/main/java/aQute/bnd/annotation/metatype/Configurable.java b/bundleplugin/src/main/java/aQute/bnd/annotation/metatype/Configurable.java
index 3dcaae5..cd6b831 100644
--- a/bundleplugin/src/main/java/aQute/bnd/annotation/metatype/Configurable.java
+++ b/bundleplugin/src/main/java/aQute/bnd/annotation/metatype/Configurable.java
@@ -97,8 +97,7 @@
 					double b = ((Number) o).doubleValue();
 					if (b == 0)
 						return false;
-					else
-						return true;
+					return true;
 				}
 				return true;
 
@@ -253,8 +252,8 @@
 						result.add(Array.get(o, i));
 					}
 					return result;
-				} else
-					return Arrays.asList((Object[]) o);
+				}
+				return Arrays.asList((Object[]) o);
 			}
 
 			if (o instanceof String) {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/Container.java b/bundleplugin/src/main/java/aQute/bnd/build/Container.java
index f726527..394a14a 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/Container.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/Container.java
@@ -106,8 +106,7 @@
 	public boolean equals(Object other) {
 		if (other instanceof Container)
 			return file.equals(((Container) other).file);
-		else
-			return false;
+		return false;
 	}
 
 	public int hashCode() {
@@ -126,8 +125,7 @@
 	public String toString() {
 		if (getError() != null)
 			return "/error/" + getError();
-		else
-			return getFile().getAbsolutePath();
+		return getFile().getAbsolutePath();
 	}
 
 	public Map<String,String> getAttributes() {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/Project.java b/bundleplugin/src/main/java/aQute/bnd/build/Project.java
index b55727a..54c2436 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/Project.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/Project.java
@@ -55,7 +55,7 @@
 	boolean						delayRunDependencies	= false;
 	final ProjectMessages		msgs					= ReporterMessages.base(this, ProjectMessages.class);
 
-	public Project(Workspace workspace, File projectDir, File buildFile) throws Exception {
+	public Project(Workspace workspace, @SuppressWarnings("unused") File projectDir, File buildFile) throws Exception {
 		super(workspace);
 		this.workspace = workspace;
 		setFileMustExist(false);
@@ -121,7 +121,8 @@
 			builder = new ProjectBuilder(parent);
 
 		builder.setBase(getBase());
-
+		builder.setPedantic(isPedantic());
+		builder.setTrace(isTrace());
 		return builder;
 	}
 
@@ -1057,8 +1058,7 @@
 		}
 		if (f.getName().endsWith("lib"))
 			return new Container(this, bsn, range, Container.TYPE.LIBRARY, f, null, attrs);
-		else
-			return new Container(this, bsn, range, Container.TYPE.REPO, f, null, attrs);
+		return new Container(this, bsn, range, Container.TYPE.REPO, f, null, attrs);
 	}
 
 	/**
@@ -1258,7 +1258,7 @@
 		}
 
 		if (isStale()) {
-			trace("Building " + this);
+			trace("building " + this);
 			files = buildLocal(underTest);
 		}
 
@@ -1366,8 +1366,8 @@
 						rdr.close();
 						f.delete();
 						break;
-					} else
-						files.add(ff);
+					}
+					files.add(ff);
 				}
 				return this.files = files.toArray(new File[files.size()]);
 			}
@@ -1377,8 +1377,7 @@
 		}
 		if (buildIfAbsent)
 			return files = buildLocal(false);
-		else
-			return files = null;
+		return files = null;
 	}
 
 	/**
@@ -1428,8 +1427,8 @@
 				}
 				getWorkspace().changedFile(bfs);
 				return files;
-			} else
-				return null;
+			}
+			return null;
 		}
 		finally {
 			builder.close();
@@ -1664,7 +1663,7 @@
 		return jar;
 	}
 
-	public String _project(String args[]) {
+	public String _project(@SuppressWarnings("unused") String args[]) {
 		return getBase().getAbsolutePath();
 	}
 
@@ -1753,7 +1752,7 @@
 	/**
 	 * Run all before command plugins
 	 */
-	void before(Project p, String a) {
+	void before(@SuppressWarnings("unused") Project p, String a) {
 		List<CommandPlugin> testPlugins = getPlugins(CommandPlugin.class);
 		for (CommandPlugin testPlugin : testPlugins) {
 			testPlugin.before(this, a);
@@ -1763,7 +1762,7 @@
 	/**
 	 * Run all after command plugins
 	 */
-	void after(Project p, String a, Throwable t) {
+	void after(@SuppressWarnings("unused") Project p, String a, Throwable t) {
 		List<CommandPlugin> testPlugins = getPlugins(CommandPlugin.class);
 		for (int i = testPlugins.size() - 1; i >= 0; i--) {
 			testPlugins.get(i).after(this, a, t);
@@ -1800,7 +1799,7 @@
 	}
 
 	@SuppressWarnings("unchecked")
-	public void script(String type, String script) throws Exception {
+	public void script(@SuppressWarnings("unused") String type, String script) throws Exception {
 		// TODO check tyiping
 		List<Scripter> scripters = getPlugins(Scripter.class);
 		if (scripters.isEmpty()) {
@@ -1812,7 +1811,7 @@
 		scripters.get(0).eval(x, new StringReader(script));
 	}
 
-	public String _repos(String args[]) throws Exception {
+	public String _repos(@SuppressWarnings("unused") String args[]) throws Exception {
 		List<RepositoryPlugin> repos = getPlugins(RepositoryPlugin.class);
 		List<String> names = new ArrayList<String>();
 		for (RepositoryPlugin rp : repos)
@@ -1906,7 +1905,7 @@
 	 * @return null or the builder for a sub file.
 	 * @throws Exception
 	 */
-	public Container getDeliverable(String bsn, Map<String,String> attrs) throws Exception {
+	public Container getDeliverable(String bsn, @SuppressWarnings("unused") Map<String,String> attrs) throws Exception {
 		Collection< ? extends Builder> builders = getSubBuilders();
 		for (Builder sub : builders) {
 			if (sub.getBsn().equals(bsn))
@@ -2040,19 +2039,18 @@
 
 		if (newVersion.compareTo(oldVersion) == 0) {
 			return;
-		} else {
-			PrintWriter pw = IO.writer(file);
-			pw.println("version " + newVersion);
-			pw.flush();
-			pw.close();
-
-			String path = packageName.replace('.', '/') + "/packageinfo";
-			File binary = IO.getFile(getOutput(), path);
-			binary.getParentFile().mkdirs();
-			IO.copy(file, binary);
-
-			refresh();
 		}
+		PrintWriter pw = IO.writer(file);
+		pw.println("version " + newVersion);
+		pw.flush();
+		pw.close();
+
+		String path = packageName.replace('.', '/') + "/packageinfo";
+		File binary = IO.getFile(getOutput(), path);
+		binary.getParentFile().mkdirs();
+		IO.copy(file, binary);
+
+		refresh();
 	}
 
 	File getPackageInfoFile(String packageName) {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java b/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java
index c74687f..50619c8 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java
@@ -154,7 +154,7 @@
 		}
 	}
 
-	public String _workspace(String args[]) {
+	public String _workspace(@SuppressWarnings("unused") String args[]) {
 		return getBase().getAbsolutePath();
 	}
 
@@ -279,7 +279,6 @@
 					if (in != null)
 						unzip(in, root);
 					else {
-						System.err.println("!!!! Couldn't find embedded-repo.jar in bundle ");
 						error("Couldn't find embedded-repo.jar in bundle ");
 					}
 				}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java b/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
index 9e71975..9a1753c 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
@@ -22,7 +22,7 @@
 		this.workspace = workspace;
 	}
 
-	public File[] get(String bsn, String range) throws Exception {
+	private File[] get(String bsn, String range) throws Exception {
 		Collection<Project> projects = workspace.getAllProjects();
 		SortedMap<Version,File> foundVersion = new TreeMap<Version,File>();
 		for (Project project : projects) {
@@ -45,15 +45,13 @@
 		result = foundVersion.values().toArray(result);
 		if (!"latest".equals(range)) {
 			return result;
-		} else {
-			if (result.length > 0) {
-				return new File[] {
-					result[0]
-				};
-			} else {
-				return new File[0];
-			}
 		}
+		if (result.length > 0) {
+			return new File[] {
+				result[0]
+			};
+		}
+		return new File[0];
 	}
 
 	public File get(String bsn, String range, Strategy strategy, Map<String,String> properties) throws Exception {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java b/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java
new file mode 100644
index 0000000..8dee7e2
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java
@@ -0,0 +1,719 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model;
+
+import java.beans.*;
+import java.io.*;
+import java.util.*;
+import java.util.Map.Entry;
+
+import org.osgi.framework.*;
+
+import aQute.bnd.build.model.clauses.*;
+import aQute.bnd.build.model.conversions.*;
+import aQute.lib.properties.*;
+import aQute.libg.header.*;
+import aQute.libg.tuple.*;
+import aQute.libg.version.Version;
+
+/**
+ * A model for a Bnd file. In the first iteration, use a simple Properties
+ * object; this will need to be enhanced to additionally record formatting, e.g.
+ * line breaks and empty lines, and comments.
+ * 
+ * @author Neil Bartlett
+ */
+public class BndEditModel {
+
+	public static final String										LINE_SEPARATOR				= " \\\n\t";
+	public static final String										LIST_SEPARATOR				= ",\\\n\t";
+
+	protected static final String									ISO_8859_1					= "ISO-8859-1";												//$NON-NLS-1$
+
+	protected static String[]										KNOWN_PROPERTIES			= new String[] {
+			Constants.BUNDLE_SYMBOLICNAME, Constants.BUNDLE_VERSION, Constants.BUNDLE_ACTIVATOR,
+			Constants.EXPORT_PACKAGE, Constants.IMPORT_PACKAGE, aQute.lib.osgi.Constants.PRIVATE_PACKAGE,
+			aQute.lib.osgi.Constants.SOURCES,
+			aQute.lib.osgi.Constants.SERVICE_COMPONENT, aQute.lib.osgi.Constants.CLASSPATH,
+			aQute.lib.osgi.Constants.BUILDPATH, aQute.lib.osgi.Constants.BUILDPACKAGES,
+			aQute.lib.osgi.Constants.RUNBUNDLES, aQute.lib.osgi.Constants.RUNPROPERTIES, aQute.lib.osgi.Constants.SUB,
+			// BndConstants.RUNFRAMEWORK,
+			aQute.lib.osgi.Constants.RUNVM,
+			// BndConstants.RUNVMARGS,
+			// BndConstants.TESTSUITES,
+			aQute.lib.osgi.Constants.TESTCASES, aQute.lib.osgi.Constants.PLUGIN, aQute.lib.osgi.Constants.PLUGINPATH,
+			aQute.lib.osgi.Constants.RUNREPOS,
+																								// BndConstants.RUNREQUIRE,
+																								// BndConstants.RUNEE,
+																								// BndConstants.RESOLVE_MODE
+																								};
+
+	public static final String										BUNDLE_VERSION_MACRO		= "${"
+																										+ Constants.BUNDLE_VERSION
+																										+ "}";
+
+	protected final Map<String,Converter< ? extends Object,String>>	converters					= new HashMap<String,Converter< ? extends Object,String>>();
+	protected final Map<String,Converter<String, ? extends Object>>	formatters					= new HashMap<String,Converter<String, ? extends Object>>();
+	// private final DataModelHelper obrModelHelper = new DataModelHelperImpl();
+
+	private final PropertyChangeSupport								propChangeSupport			= new PropertyChangeSupport(
+																										this);
+	private final Properties										properties					= new Properties();
+
+	private File													bndResource;
+	private boolean													projectFile;
+	private final Map<String,Object>								objectProperties			= new HashMap<String,Object>();
+	private final Map<String,String>								changesToSave				= new HashMap<String,String>();
+
+	// CONVERTERS
+	protected Converter<List<VersionedClause>,String>				buildPathConverter			= new ClauseListConverter<VersionedClause>(
+																										new Converter<VersionedClause,Pair<String,Attrs>>() {
+																											public VersionedClause convert(
+																													Pair<String,Attrs> input)
+																													throws IllegalArgumentException {
+																												return new VersionedClause(
+																														input.getFirst(),
+																														input.getSecond());
+																											}
+																										});
+	protected Converter<List<VersionedClause>,String>				buildPackagesConverter		= new ClauseListConverter<VersionedClause>(
+																										new Converter<VersionedClause,Pair<String,Attrs>>() {
+																											public VersionedClause convert(
+																													Pair<String,Attrs> input)
+																													throws IllegalArgumentException {
+																												return new VersionedClause(
+																														input.getFirst(),
+																														input.getSecond());
+																											}
+																										});
+	protected Converter<List<VersionedClause>,String>				clauseListConverter			= new ClauseListConverter<VersionedClause>(
+																										new VersionedClauseConverter());
+	protected Converter<String,String>								stringConverter				= new NoopConverter<String>();
+	protected Converter<Boolean,String>								includedSourcesConverter	= new Converter<Boolean,String>() {
+																									public Boolean convert(
+																											String string)
+																											throws IllegalArgumentException {
+																										return Boolean
+																												.valueOf(string);
+																									}
+																								};
+	protected Converter<VersionPolicy,String>						versionPolicyConverter		= new Converter<VersionPolicy,String>() {
+																									public VersionPolicy convert(
+																											String string)
+																											throws IllegalArgumentException {
+																										return VersionPolicy
+																												.parse(string);
+																									}
+																								};
+	protected Converter<List<String>,String>						listConverter				= SimpleListConverter
+																										.create();
+	protected Converter<List<HeaderClause>,String>					headerClauseListConverter	= new HeaderClauseListConverter();
+	protected ClauseListConverter<ExportedPackage>					exportPackageConverter		= new ClauseListConverter<ExportedPackage>(
+																										new Converter<ExportedPackage,Pair<String,Attrs>>() {
+																											public ExportedPackage convert(
+																													Pair<String,Attrs> input) {
+																												return new ExportedPackage(
+																														input.getFirst(),
+																														input.getSecond());
+																											}
+																										});
+	protected Converter<List<ServiceComponent>,String>				serviceComponentConverter	= new ClauseListConverter<ServiceComponent>(
+																										new Converter<ServiceComponent,Pair<String,Attrs>>() {
+																											public ServiceComponent convert(
+																													Pair<String,Attrs> input)
+																													throws IllegalArgumentException {
+																												return new ServiceComponent(
+																														input.getFirst(),
+																														input.getSecond());
+																											}
+																										});
+	protected Converter<List<ImportPattern>,String>					importPatternConverter		= new ClauseListConverter<ImportPattern>(
+																										new Converter<ImportPattern,Pair<String,Attrs>>() {
+																											public ImportPattern convert(
+																													Pair<String,Attrs> input)
+																													throws IllegalArgumentException {
+																												return new ImportPattern(
+																														input.getFirst(),
+																														input.getSecond());
+																											}
+																										});
+
+	protected Converter<Map<String,String>,String>					propertiesConverter			= new PropertiesConverter();
+
+	// Converter<List<Requirement>, String> requirementListConverter =
+	// SimpleListConverter.create(new Converter<Requirement, String>() {
+	// public Requirement convert(String input) throws IllegalArgumentException
+	// {
+	// int index = input.indexOf(":");
+	// if (index < 0)
+	// throw new IllegalArgumentException("Invalid format for OBR requirement");
+	//
+	// String name = input.substring(0, index);
+	// String filter = input.substring(index + 1);
+	//
+	// return new Requirement(name, filter);
+	// }
+	// });
+	// Converter<EE, String> eeConverter = new Converter<EE, String>() {
+	// public EE convert(String input) throws IllegalArgumentException {
+	// return EE.parse(input);
+	// }
+	// };
+	//
+	// Converter<ResolveMode, String> resolveModeConverter =
+	// EnumConverter.create(ResolveMode.class, ResolveMode.manual);
+
+	// FORMATTERS
+	protected Converter<String,Object>								defaultFormatter			= new DefaultFormatter();
+	protected Converter<String,String>								newlineEscapeFormatter		= new NewlineEscapedStringFormatter();
+	protected Converter<String,Boolean>								defaultFalseBoolFormatter	= new DefaultBooleanFormatter(
+																										false);
+	protected Converter<String,Collection< ? >>						stringListFormatter			= new CollectionFormatter<Object>(
+																										LIST_SEPARATOR,
+																										(String) null);
+	protected Converter<String,Collection< ? extends HeaderClause>>	headerClauseListFormatter	= new CollectionFormatter<HeaderClause>(
+																										LIST_SEPARATOR,
+																										new HeaderClauseFormatter(),
+																										null);
+	protected Converter<String,Map<String,String>>					propertiesFormatter			= new MapFormatter(
+																										LIST_SEPARATOR,
+																										new PropertiesEntryFormatter(),
+																										null);
+	// Converter<String, Collection<? extends Requirement>>
+	// requirementListFormatter = new
+	// CollectionFormatter<Requirement>(LIST_SEPARATOR, new Converter<String,
+	// Requirement>() {
+	// public String convert(Requirement input) throws IllegalArgumentException
+	// {
+	// return new
+	// StringBuilder().append(input.getName()).append(':').append(input.getFilter()).toString();
+	// }
+	// }, null);
+	// Converter<String, EE> eeFormatter = new Converter<String, EE>() {
+	// public String convert(EE input) throws IllegalArgumentException {
+	// return input != null ? input.getEEName() : null;
+	// }
+	// };
+	Converter<String,Collection< ? extends String>>					runReposFormatter			= new CollectionFormatter<String>(
+																										LIST_SEPARATOR,
+																										aQute.lib.osgi.Constants.EMPTY_HEADER);
+
+	// Converter<String, ResolveMode> resolveModeFormatter =
+	// EnumFormatter.create(ResolveMode.class, ResolveMode.manual);
+
+	@SuppressWarnings("deprecation")
+	public BndEditModel() {
+		// register converters
+		converters.put(aQute.lib.osgi.Constants.BUILDPATH, buildPathConverter);
+		converters.put(aQute.lib.osgi.Constants.BUILDPACKAGES, buildPackagesConverter);
+		converters.put(aQute.lib.osgi.Constants.RUNBUNDLES, clauseListConverter);
+		converters.put(Constants.BUNDLE_SYMBOLICNAME, stringConverter);
+		converters.put(Constants.BUNDLE_VERSION, stringConverter);
+		converters.put(Constants.BUNDLE_ACTIVATOR, stringConverter);
+		converters.put(aQute.lib.osgi.Constants.OUTPUT, stringConverter);
+		converters.put(aQute.lib.osgi.Constants.SOURCES, includedSourcesConverter);
+		converters.put(aQute.lib.osgi.Constants.PRIVATE_PACKAGE, listConverter);
+		converters.put(aQute.lib.osgi.Constants.CLASSPATH, listConverter);
+		converters.put(Constants.EXPORT_PACKAGE, exportPackageConverter);
+		converters.put(aQute.lib.osgi.Constants.SERVICE_COMPONENT, serviceComponentConverter);
+		converters.put(Constants.IMPORT_PACKAGE, importPatternConverter);
+		// converters.put(BndConstants.RUNFRAMEWORK, stringConverter);
+		converters.put(aQute.lib.osgi.Constants.SUB, listConverter);
+		converters.put(aQute.lib.osgi.Constants.RUNPROPERTIES, propertiesConverter);
+		converters.put(aQute.lib.osgi.Constants.RUNVM, stringConverter);
+		// converters.put(BndConstants.RUNVMARGS, stringConverter);
+		converters.put(aQute.lib.osgi.Constants.TESTSUITES, listConverter);
+		converters.put(aQute.lib.osgi.Constants.TESTCASES, listConverter);
+		converters.put(aQute.lib.osgi.Constants.PLUGIN, headerClauseListConverter);
+		// converters.put(BndConstants.RUNREQUIRE, requirementListConverter);
+		// converters.put(BndConstants.RUNEE, new NoopConverter<String>());
+		// converters.put(BndConstants.RUNREPOS, listConverter);
+		// converters.put(BndConstants.RESOLVE_MODE, resolveModeConverter);
+
+		formatters.put(aQute.lib.osgi.Constants.BUILDPATH, headerClauseListFormatter);
+		formatters.put(aQute.lib.osgi.Constants.BUILDPACKAGES, headerClauseListFormatter);
+		formatters.put(aQute.lib.osgi.Constants.RUNBUNDLES, headerClauseListFormatter);
+		formatters.put(Constants.BUNDLE_SYMBOLICNAME, newlineEscapeFormatter);
+		formatters.put(Constants.BUNDLE_VERSION, newlineEscapeFormatter);
+		formatters.put(Constants.BUNDLE_ACTIVATOR, newlineEscapeFormatter);
+		formatters.put(aQute.lib.osgi.Constants.OUTPUT, newlineEscapeFormatter);
+		formatters.put(aQute.lib.osgi.Constants.SOURCES, defaultFalseBoolFormatter);
+		formatters.put(aQute.lib.osgi.Constants.PRIVATE_PACKAGE, stringListFormatter);
+		formatters.put(aQute.lib.osgi.Constants.CLASSPATH, stringListFormatter);
+		formatters.put(Constants.EXPORT_PACKAGE, headerClauseListFormatter);
+		formatters.put(aQute.lib.osgi.Constants.SERVICE_COMPONENT, headerClauseListFormatter);
+		formatters.put(Constants.IMPORT_PACKAGE, headerClauseListFormatter);
+		// formatters.put(BndConstants.RUNFRAMEWORK, newlineEscapeFormatter);
+		formatters.put(aQute.lib.osgi.Constants.SUB, stringListFormatter);
+		formatters.put(aQute.lib.osgi.Constants.RUNPROPERTIES, propertiesFormatter);
+		formatters.put(aQute.lib.osgi.Constants.RUNVM, newlineEscapeFormatter);
+		// formatters.put(BndConstants.RUNVMARGS, newlineEscapeFormatter);
+		// formatters.put(BndConstants.TESTSUITES, stringListFormatter);
+		formatters.put(aQute.lib.osgi.Constants.TESTCASES, stringListFormatter);
+		formatters.put(aQute.lib.osgi.Constants.PLUGIN, headerClauseListFormatter);
+		// formatters.put(BndConstants.RUNREQUIRE, requirementListFormatter);
+		// formatters.put(BndConstants.RUNEE, new NoopConverter<String>());
+		// formatters.put(BndConstants.RUNREPOS, runReposFormatter);
+		// formatters.put(BndConstants.RESOLVE_MODE, resolveModeFormatter);
+	}
+
+	public void loadFrom(IDocument document) throws IOException {
+		InputStream inputStream = new ByteArrayInputStream(document.get().getBytes(ISO_8859_1));
+		loadFrom(inputStream);
+	}
+
+	public void loadFrom(File file) throws IOException {
+		loadFrom(new BufferedInputStream(new FileInputStream(file)));
+	}
+
+	public void loadFrom(InputStream inputStream) throws IOException {
+		try {
+			// Clear and load
+			properties.clear();
+			properties.load(inputStream);
+			objectProperties.clear();
+			changesToSave.clear();
+
+			// Fire property changes on all known property names
+			for (String prop : KNOWN_PROPERTIES) {
+				// null values for old and new forced the change to be fired
+				propChangeSupport.firePropertyChange(prop, null, null);
+			}
+		}
+		finally {
+			inputStream.close();
+		}
+
+	}
+
+	public void saveChangesTo(IDocument document) {
+		for (Iterator<Entry<String,String>> iter = changesToSave.entrySet().iterator(); iter.hasNext();) {
+			Entry<String,String> entry = iter.next();
+			iter.remove();
+
+			String propertyName = entry.getKey();
+			String stringValue = entry.getValue();
+
+			updateDocument(document, propertyName, stringValue);
+		}
+	}
+
+	protected static IRegion findEntry(IDocument document, String name) throws Exception {
+		PropertiesLineReader reader = new PropertiesLineReader(document);
+		LineType type = reader.next();
+		while (type != LineType.eof) {
+			if (type == LineType.entry) {
+				String key = reader.key();
+				if (name.equals(key))
+					return reader.region();
+			}
+			type = reader.next();
+		}
+		return null;
+	}
+
+	protected static void updateDocument(IDocument document, String name, String value) {
+		String newEntry;
+		if (value != null) {
+			StringBuilder buffer = new StringBuilder();
+			buffer.append(name).append(": ").append(value);
+			newEntry = buffer.toString();
+		} else {
+			newEntry = "";
+		}
+
+		try {
+			IRegion region = findEntry(document, name);
+			if (region != null) {
+				// Replace an existing entry
+				int offset = region.getOffset();
+				int length = region.getLength();
+
+				// If the replacement is empty, remove one extra character to
+				// the right, i.e. the following newline,
+				// unless this would take us past the end of the document
+				if (newEntry.length() == 0 && offset + length + 1 < document.getLength()) {
+					length++;
+				}
+				document.replace(offset, length, newEntry);
+			} else if (newEntry.length() > 0) {
+				// This is a new entry, put it at the end of the file
+
+				// Does the last line of the document have a newline? If not,
+				// we need to add one.
+				if (document.getLength() > 0 && document.getChar(document.getLength() - 1) != '\n')
+					newEntry = "\n" + newEntry;
+				document.replace(document.getLength(), 0, newEntry);
+			}
+		}
+		catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	public List<String> getAllPropertyNames() {
+		List<String> result = new ArrayList<String>(properties.size());
+
+		Enumeration<String> names = (Enumeration<String>) properties.propertyNames();
+
+		while (names.hasMoreElements()) {
+			result.add(names.nextElement());
+		}
+		return result;
+	}
+
+	public Object genericGet(String propertyName) {
+		Converter< ? extends Object,String> converter = converters.get(propertyName);
+		if (converter == null)
+			converter = new NoopConverter<String>();
+		return doGetObject(propertyName, converter);
+	}
+
+	public void genericSet(String propertyName, Object value) {
+		Object oldValue = genericGet(propertyName);
+		Converter<String,Object> formatter = (Converter<String,Object>) formatters.get(propertyName);
+		if (formatter == null)
+			formatter = new DefaultFormatter();
+		doSetObject(propertyName, oldValue, value, formatter);
+	}
+
+	public String getBundleSymbolicName() {
+		return doGetObject(Constants.BUNDLE_SYMBOLICNAME, stringConverter);
+	}
+
+	public void setBundleSymbolicName(String bundleSymbolicName) {
+		doSetObject(Constants.BUNDLE_SYMBOLICNAME, getBundleSymbolicName(), bundleSymbolicName, newlineEscapeFormatter);
+	}
+
+	public String getBundleVersionString() {
+		return doGetObject(Constants.BUNDLE_VERSION, stringConverter);
+	}
+
+	public void setBundleVersion(String bundleVersion) {
+		doSetObject(Constants.BUNDLE_VERSION, getBundleVersionString(), bundleVersion, newlineEscapeFormatter);
+	}
+
+	public String getBundleActivator() {
+		return doGetObject(Constants.BUNDLE_ACTIVATOR, stringConverter);
+	}
+
+	public void setBundleActivator(String bundleActivator) {
+		doSetObject(Constants.BUNDLE_ACTIVATOR, getBundleActivator(), bundleActivator, newlineEscapeFormatter);
+	}
+
+	public String getOutputFile() {
+		return doGetObject(aQute.lib.osgi.Constants.OUTPUT, stringConverter);
+	}
+
+	public void setOutputFile(String name) {
+		doSetObject(aQute.lib.osgi.Constants.OUTPUT, getOutputFile(), name, newlineEscapeFormatter);
+	}
+
+	public boolean isIncludeSources() {
+		return doGetObject(aQute.lib.osgi.Constants.SOURCES, includedSourcesConverter);
+	}
+
+	public void setIncludeSources(boolean includeSources) {
+		boolean oldValue = isIncludeSources();
+		doSetObject(aQute.lib.osgi.Constants.SOURCES, oldValue, includeSources, defaultFalseBoolFormatter);
+	}
+
+	public List<String> getPrivatePackages() {
+		return doGetObject(aQute.lib.osgi.Constants.PRIVATE_PACKAGE, listConverter);
+	}
+
+	public void setPrivatePackages(List< ? extends String> packages) {
+		List<String> oldPackages = getPrivatePackages();
+		doSetObject(aQute.lib.osgi.Constants.PRIVATE_PACKAGE, oldPackages, packages, stringListFormatter);
+	}
+
+	public List<ExportedPackage> getSystemPackages() {
+		return doGetObject(aQute.lib.osgi.Constants.RUNSYSTEMPACKAGES, exportPackageConverter);
+	}
+
+	public void setSystemPackages(List< ? extends ExportedPackage> packages) {
+		List<ExportedPackage> oldPackages = getSystemPackages();
+		doSetObject(aQute.lib.osgi.Constants.RUNSYSTEMPACKAGES, oldPackages, packages, headerClauseListFormatter);
+	}
+
+	public List<String> getClassPath() {
+		return doGetObject(aQute.lib.osgi.Constants.CLASSPATH, listConverter);
+	}
+
+	public void addPrivatePackage(String packageName) {
+		List<String> packages = getPrivatePackages();
+		if (packages == null)
+			packages = new ArrayList<String>();
+		else
+			packages = new ArrayList<String>(packages);
+		packages.add(packageName);
+		setPrivatePackages(packages);
+	}
+
+	public void setClassPath(List< ? extends String> classPath) {
+		List<String> oldClassPath = getClassPath();
+		doSetObject(aQute.lib.osgi.Constants.CLASSPATH, oldClassPath, classPath, stringListFormatter);
+	}
+
+	public List<ExportedPackage> getExportedPackages() {
+		return doGetObject(Constants.EXPORT_PACKAGE, exportPackageConverter);
+	}
+
+	public void setExportedPackages(List< ? extends ExportedPackage> exports) {
+		boolean referencesBundleVersion = false;
+
+		if (exports != null) {
+			for (ExportedPackage pkg : exports) {
+				String versionString = pkg.getVersionString();
+				if (versionString != null && versionString.indexOf(BUNDLE_VERSION_MACRO) > -1) {
+					referencesBundleVersion = true;
+				}
+			}
+		}
+		List<ExportedPackage> oldValue = getExportedPackages();
+		doSetObject(Constants.EXPORT_PACKAGE, oldValue, exports, headerClauseListFormatter);
+
+		if (referencesBundleVersion && getBundleVersionString() == null) {
+			setBundleVersion(new Version(0, 0, 0).toString());
+		}
+	}
+
+	public void addExportedPackage(ExportedPackage export) {
+		List<ExportedPackage> exports = getExportedPackages();
+		exports = (exports == null) ? new ArrayList<ExportedPackage>() : new ArrayList<ExportedPackage>(exports);
+		exports.add(export);
+		setExportedPackages(exports);
+	}
+
+	public List<String> getDSAnnotationPatterns() {
+		return doGetObject(aQute.lib.osgi.Constants.DSANNOTATIONS, listConverter);
+	}
+
+	public void setDSAnnotationPatterns(List< ? extends String> patterns) {
+		List<String> oldValue = getDSAnnotationPatterns();
+		doSetObject(aQute.lib.osgi.Constants.DSANNOTATIONS, oldValue, patterns, stringListFormatter);
+	}
+
+	public List<ServiceComponent> getServiceComponents() {
+		return doGetObject(aQute.lib.osgi.Constants.SERVICE_COMPONENT, serviceComponentConverter);
+	}
+
+	public void setServiceComponents(List< ? extends ServiceComponent> components) {
+		List<ServiceComponent> oldValue = getServiceComponents();
+		doSetObject(aQute.lib.osgi.Constants.SERVICE_COMPONENT, oldValue, components, headerClauseListFormatter);
+	}
+
+	public List<ImportPattern> getImportPatterns() {
+		return doGetObject(Constants.IMPORT_PACKAGE, importPatternConverter);
+	}
+
+	public void setImportPatterns(List< ? extends ImportPattern> patterns) {
+		List<ImportPattern> oldValue = getImportPatterns();
+		doSetObject(Constants.IMPORT_PACKAGE, oldValue, patterns, headerClauseListFormatter);
+	}
+
+	public List<VersionedClause> getBuildPath() {
+		return doGetObject(aQute.lib.osgi.Constants.BUILDPATH, buildPathConverter);
+	}
+
+	public void setBuildPath(List< ? extends VersionedClause> paths) {
+		List<VersionedClause> oldValue = getBuildPath();
+		doSetObject(aQute.lib.osgi.Constants.BUILDPATH, oldValue, paths, headerClauseListFormatter);
+	}
+
+	public List<VersionedClause> getBuildPackages() {
+		return doGetObject(aQute.lib.osgi.Constants.BUILDPACKAGES, buildPackagesConverter);
+	}
+
+	public void setBuildPackages(List< ? extends VersionedClause> paths) {
+		List<VersionedClause> oldValue = getBuildPackages();
+		doSetObject(aQute.lib.osgi.Constants.BUILDPACKAGES, oldValue, paths, headerClauseListFormatter);
+	}
+
+	public List<VersionedClause> getRunBundles() {
+		return doGetObject(aQute.lib.osgi.Constants.RUNBUNDLES, clauseListConverter);
+	}
+
+	public void setRunBundles(List< ? extends VersionedClause> paths) {
+		List<VersionedClause> oldValue = getBuildPath();
+		doSetObject(aQute.lib.osgi.Constants.RUNBUNDLES, oldValue, paths, headerClauseListFormatter);
+	}
+
+	public boolean isIncludedPackage(String packageName) {
+		final Collection<String> privatePackages = getPrivatePackages();
+		if (privatePackages != null) {
+			if (privatePackages.contains(packageName))
+				return true;
+		}
+		final Collection<ExportedPackage> exportedPackages = getExportedPackages();
+		if (exportedPackages != null) {
+			for (ExportedPackage pkg : exportedPackages) {
+				if (packageName.equals(pkg.getName())) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	public List<String> getSubBndFiles() {
+		return doGetObject(aQute.lib.osgi.Constants.SUB, listConverter);
+	}
+
+	public void setSubBndFiles(List<String> subBndFiles) {
+		List<String> oldValue = getSubBndFiles();
+		doSetObject(aQute.lib.osgi.Constants.SUB, oldValue, subBndFiles, stringListFormatter);
+	}
+
+	public Map<String,String> getRunProperties() {
+		return doGetObject(aQute.lib.osgi.Constants.RUNPROPERTIES, propertiesConverter);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see bndtools.editor.model.IBndModel#setRunProperties(java.util.Map)
+	 */
+	public void setRunProperties(Map<String,String> props) {
+		Map<String,String> old = getRunProperties();
+		doSetObject(aQute.lib.osgi.Constants.RUNPROPERTIES, old, props, propertiesFormatter);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see bndtools.editor.model.IBndModel#getRunVMArgs()
+	 */
+	public String getRunVMArgs() {
+		return doGetObject(aQute.lib.osgi.Constants.RUNVM, stringConverter);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see bndtools.editor.model.IBndModel#setRunVMArgs(java.lang.String)
+	 */
+	public void setRunVMArgs(String args) {
+		String old = getRunVMArgs();
+		doSetObject(aQute.lib.osgi.Constants.RUNVM, old, args, newlineEscapeFormatter);
+	}
+
+	@SuppressWarnings("deprecation")
+	public List<String> getTestSuites() {
+		List<String> testCases = doGetObject(aQute.lib.osgi.Constants.TESTCASES, listConverter);
+		testCases = testCases != null ? testCases : Collections.<String> emptyList();
+
+		List<String> testSuites = doGetObject(aQute.lib.osgi.Constants.TESTSUITES, listConverter);
+		testSuites = testSuites != null ? testSuites : Collections.<String> emptyList();
+
+		List<String> result = new ArrayList<String>(testCases.size() + testSuites.size());
+		result.addAll(testCases);
+		result.addAll(testSuites);
+		return result;
+	}
+
+	@SuppressWarnings("deprecation")
+	public void setTestSuites(List<String> suites) {
+		List<String> old = getTestSuites();
+		doSetObject(aQute.lib.osgi.Constants.TESTCASES, old, suites, stringListFormatter);
+		doSetObject(aQute.lib.osgi.Constants.TESTSUITES, null, null, stringListFormatter);
+	}
+
+	public List<HeaderClause> getPlugins() {
+		return doGetObject(aQute.lib.osgi.Constants.PLUGIN, headerClauseListConverter);
+	}
+
+	public void setPlugins(List<HeaderClause> plugins) {
+		List<HeaderClause> old = getPlugins();
+		doSetObject(aQute.lib.osgi.Constants.PLUGIN, old, plugins, headerClauseListFormatter);
+	}
+
+	public List<String> getPluginPath() {
+		return doGetObject(aQute.lib.osgi.Constants.PLUGINPATH, listConverter);
+	}
+
+	public void setPluginPath(List<String> pluginPath) {
+		List<String> old = getPluginPath();
+		doSetObject(aQute.lib.osgi.Constants.PLUGINPATH, old, pluginPath, stringListFormatter);
+	}
+	
+    public List<String> getRunRepos() {
+        return doGetObject(aQute.lib.osgi.Constants.RUNREPOS, listConverter);
+    }
+
+    public void setRunRepos(List<String> repos) {
+        List<String> old = getRunRepos();
+        doSetObject(aQute.lib.osgi.Constants.RUNREPOS, old, repos, runReposFormatter);
+    }
+    
+    public String getRunFramework() {
+        return doGetObject(aQute.lib.osgi.Constants.RUNFRAMEWORK, stringConverter);
+    }
+
+    public void setRunFramework(String clause) {
+        String oldValue = getRunFramework();
+        doSetObject(aQute.lib.osgi.Constants.RUNFRAMEWORK, oldValue, clause, newlineEscapeFormatter);
+    }
+
+
+	protected <R> R doGetObject(String name, Converter< ? extends R, ? super String> converter) {
+		R result;
+		if (objectProperties.containsKey(name)) {
+			R temp = (R) objectProperties.get(name);
+			result = temp;
+		} else if (changesToSave.containsKey(name)) {
+			result = converter.convert(changesToSave.get(name));
+			objectProperties.put(name, result);
+		} else if (properties.containsKey(name)) {
+			result = converter.convert(properties.getProperty(name));
+			objectProperties.put(name, result);
+		} else {
+			result = null;
+		}
+		return result;
+	}
+
+	protected <T> void doSetObject(String name, T oldValue, T newValue, Converter<String, ? super T> formatter) {
+		objectProperties.put(name, newValue);
+		changesToSave.put(name, formatter.convert(newValue));
+		propChangeSupport.firePropertyChange(name, oldValue, newValue);
+	}
+
+	public void setProjectFile(boolean projectFile) {
+		this.projectFile = projectFile;
+	}
+
+	public boolean isProjectFile() {
+		return this.projectFile;
+	}
+
+	public void addPropertyChangeListener(PropertyChangeListener listener) {
+		propChangeSupport.addPropertyChangeListener(listener);
+	}
+
+	public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+		propChangeSupport.addPropertyChangeListener(propertyName, listener);
+	}
+
+	public void removePropertyChangeListener(PropertyChangeListener listener) {
+		propChangeSupport.removePropertyChangeListener(listener);
+	}
+
+	public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+		propChangeSupport.removePropertyChangeListener(propertyName, listener);
+	}
+
+	public void setBndResource(File bndResource) {
+		this.bndResource = bndResource;
+	}
+
+	public File getBndResource() {
+		return bndResource;
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/LowerVersionMatchType.java b/bundleplugin/src/main/java/aQute/bnd/build/model/LowerVersionMatchType.java
new file mode 100644
index 0000000..dcdf969
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/LowerVersionMatchType.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model;
+
+public enum LowerVersionMatchType {
+	Exact("${@}"), Micro("${version;===;${@}}"), Minor("${version;==;${@}}"), Major("${version;=;${@}}");
+
+	private final String	representation;
+
+	private LowerVersionMatchType(String representation) {
+		this.representation = representation;
+	}
+
+	public String getRepresentation() {
+		return representation;
+	}
+
+	public static LowerVersionMatchType parse(String string) throws IllegalArgumentException {
+		for (LowerVersionMatchType type : LowerVersionMatchType.class.getEnumConstants()) {
+			if (type.getRepresentation().equals(string)) {
+				return type;
+			}
+		}
+		throw new IllegalArgumentException("Failed to parse version match type.");
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/UpperVersionMatchType.java b/bundleplugin/src/main/java/aQute/bnd/build/model/UpperVersionMatchType.java
new file mode 100644
index 0000000..91d97ca
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/UpperVersionMatchType.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model;
+
+public enum UpperVersionMatchType {
+	Exact("${@}"), NextMicro("${version;==+;${@}}"), NextMinor("${version;=+;${@}}"), NextMajor("${version;+;${@}}");
+
+	private final String	representation;
+
+	private UpperVersionMatchType(String representation) {
+		this.representation = representation;
+	}
+
+	public String getRepresentation() {
+		return representation;
+	}
+
+	public static UpperVersionMatchType parse(String string) throws IllegalArgumentException {
+		for (UpperVersionMatchType type : UpperVersionMatchType.class.getEnumConstants()) {
+			if (type.getRepresentation().equals(string)) {
+				return type;
+			}
+		}
+		throw new IllegalArgumentException("Failed to parse version match type.");
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/VersionPolicy.java b/bundleplugin/src/main/java/aQute/bnd/build/model/VersionPolicy.java
new file mode 100644
index 0000000..3367c9f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/VersionPolicy.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model;
+
+public class VersionPolicy {
+	private final LowerVersionMatchType	lowerMatch;
+	private final UpperVersionMatchType	upperMatch;
+	private final boolean				upperInclusive;
+
+	public VersionPolicy(LowerVersionMatchType lowerMatch, UpperVersionMatchType upperMatch, boolean upperInclusive) {
+		assert lowerMatch != null;
+		this.lowerMatch = lowerMatch;
+		this.upperMatch = upperMatch;
+		this.upperInclusive = upperInclusive;
+	}
+
+	static VersionPolicy parse(String string) throws IllegalArgumentException {
+		String lowerSegment;
+		String upperSegment;
+		boolean upperInclusive;
+
+		if (string.charAt(0) == '[') {
+			int commaIndex = string.indexOf(',');
+			if (commaIndex < 0)
+				throw new IllegalArgumentException("Failed to parse version policy.");
+			lowerSegment = string.substring(1, commaIndex);
+
+			char lastChar = string.charAt(string.length() - 1);
+			if (lastChar == ')')
+				upperInclusive = false;
+			else if (lastChar == ']')
+				upperInclusive = true;
+			else
+				throw new IllegalArgumentException("Failed to parse version policy.");
+
+			upperSegment = string.substring(commaIndex + 1, string.length() - 1);
+		} else {
+			lowerSegment = string;
+			upperSegment = null;
+			upperInclusive = true;
+		}
+
+		LowerVersionMatchType lower = LowerVersionMatchType.parse(lowerSegment);
+		UpperVersionMatchType upper = upperSegment != null ? UpperVersionMatchType.parse(upperSegment) : null;
+
+		return new VersionPolicy(lower, upper, upperInclusive);
+	}
+
+	public LowerVersionMatchType getLowerMatch() {
+		return lowerMatch;
+	}
+
+	public UpperVersionMatchType getUpperMatch() {
+		return upperMatch;
+	}
+
+	public boolean isUpperInclusive() {
+		return upperInclusive;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder buffer = new StringBuilder();
+
+		if (upperMatch != null) {
+			buffer.append('[');
+			buffer.append(lowerMatch.getRepresentation());
+			buffer.append(',');
+			buffer.append(upperMatch.getRepresentation());
+			buffer.append(upperInclusive ? ']' : ')');
+		} else {
+			buffer.append(lowerMatch.getRepresentation());
+		}
+
+		return buffer.toString();
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ComponentSvcReference.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ComponentSvcReference.java
new file mode 100644
index 0000000..7c6700e
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ComponentSvcReference.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model.clauses;
+
+public class ComponentSvcReference implements Cloneable {
+
+	private String	name;
+	private String	bind;
+	private String	unbind;
+	private String	serviceClass;
+	private boolean	optional;
+	private boolean	multiple;
+	private boolean	dynamic;
+	private String	targetFilter;
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getBind() {
+		return bind;
+	}
+
+	public void setBind(String bind) {
+		this.bind = bind;
+	}
+
+	public String getUnbind() {
+		return unbind;
+	}
+
+	public void setUnbind(String unbind) {
+		this.unbind = unbind;
+	}
+
+	public String getServiceClass() {
+		return serviceClass;
+	}
+
+	public void setServiceClass(String serviceClass) {
+		this.serviceClass = serviceClass;
+	}
+
+	public boolean isOptional() {
+		return optional;
+	}
+
+	public void setOptional(boolean optional) {
+		this.optional = optional;
+	}
+
+	public boolean isMultiple() {
+		return multiple;
+	}
+
+	public void setMultiple(boolean multiple) {
+		this.multiple = multiple;
+	}
+
+	public boolean isDynamic() {
+		return dynamic;
+	}
+
+	public void setDynamic(boolean dynamic) {
+		this.dynamic = dynamic;
+	}
+
+	public String getTargetFilter() {
+		return targetFilter;
+	}
+
+	public void setTargetFilter(String targetFilter) {
+		this.targetFilter = targetFilter;
+	}
+
+	@Override
+	public ComponentSvcReference clone() {
+		ComponentSvcReference copy = new ComponentSvcReference();
+		copy.name = this.name;
+		copy.serviceClass = this.serviceClass;
+		copy.bind = this.bind;
+		copy.unbind = this.unbind;
+		copy.optional = this.optional;
+		copy.multiple = this.multiple;
+		copy.dynamic = this.dynamic;
+		copy.targetFilter = this.targetFilter;
+		return copy;
+	}
+
+	public void copyFrom(ComponentSvcReference other) {
+		this.name = other.name;
+		this.serviceClass = other.serviceClass;
+		this.bind = other.bind;
+		this.unbind = other.unbind;
+		this.optional = other.optional;
+		this.multiple = other.multiple;
+		this.dynamic = other.dynamic;
+		this.targetFilter = other.targetFilter;
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ExportedPackage.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ExportedPackage.java
new file mode 100644
index 0000000..0e9be04
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ExportedPackage.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model.clauses;
+
+import org.osgi.framework.Constants;
+
+import aQute.libg.header.Attrs;
+
+public class ExportedPackage extends HeaderClause {
+
+	public ExportedPackage(String packageName, Attrs attribs) {
+		super(packageName, attribs);
+	}
+
+	@Override
+	protected boolean newlinesBetweenAttributes() {
+		return false;
+	}
+
+	public void setVersionString(String version) {
+		attribs.put(Constants.VERSION_ATTRIBUTE, version);
+	}
+
+	public String getVersionString() {
+		return attribs.get(Constants.VERSION_ATTRIBUTE);
+	}
+
+	public boolean isProvided() {
+		return Boolean.valueOf(attribs.get(aQute.lib.osgi.Constants.PROVIDE_DIRECTIVE));
+	}
+
+	public void setProvided(boolean provided) {
+		if (provided)
+			attribs.put(aQute.lib.osgi.Constants.PROVIDE_DIRECTIVE, Boolean.toString(true));
+		else
+			attribs.remove(aQute.lib.osgi.Constants.PROVIDE_DIRECTIVE);
+	}
+
+	@Override
+	public ExportedPackage clone() {
+		return new ExportedPackage(this.name, new Attrs(this.attribs));
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/HeaderClause.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/HeaderClause.java
new file mode 100644
index 0000000..b0391a6
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/HeaderClause.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model.clauses;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import aQute.libg.header.Attrs;
+
+public class HeaderClause implements Cloneable, Comparable<HeaderClause> {
+
+	private static final String	INTERNAL_LIST_SEPARATOR				= ";";
+	private static final String	INTERNAL_LIST_SEPARATOR_NEWLINES	= INTERNAL_LIST_SEPARATOR + "\\\n\t\t";
+
+	protected String			name;
+	protected Attrs				attribs;
+
+	public HeaderClause(String name, Attrs attribs) {
+		assert name != null;
+		assert attribs != null;
+
+		this.name = name;
+		this.attribs = attribs;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public Attrs getAttribs() {
+		return attribs;
+	}
+
+	public List<String> getListAttrib(String attrib) {
+		String string = attribs.get(attrib);
+		if (string == null)
+			return null;
+
+		List<String> result = new ArrayList<String>();
+		StringTokenizer tokenizer = new StringTokenizer(string, ",");
+		while (tokenizer.hasMoreTokens()) {
+			result.add(tokenizer.nextToken().trim());
+		}
+
+		return result;
+	}
+
+	public void setListAttrib(String attrib, Collection< ? extends String> value) {
+		if (value == null || value.isEmpty())
+			attribs.remove(attrib);
+		else {
+			StringBuilder buffer = new StringBuilder();
+			boolean first = true;
+			for (String string : value) {
+				if (!first)
+					buffer.append(',');
+				buffer.append(string);
+				first = false;
+			}
+			attribs.put(attrib, buffer.toString());
+		}
+	}
+
+	public void formatTo(StringBuilder buffer) {
+		formatTo(buffer, null);
+	}
+
+	public void formatTo(StringBuilder buffer, Comparator<Entry<String,String>> sorter) {
+		String separator = newlinesBetweenAttributes() ? INTERNAL_LIST_SEPARATOR_NEWLINES : INTERNAL_LIST_SEPARATOR;
+		buffer.append(name);
+		if (attribs != null) {
+			Set<Entry<String,String>> set;
+			if (sorter != null) {
+				set = new TreeSet<Map.Entry<String,String>>(sorter);
+				set.addAll(attribs.entrySet());
+			} else {
+				set = attribs.entrySet();
+			}
+
+			for (Iterator<Entry<String,String>> iter = set.iterator(); iter.hasNext();) {
+				Entry<String,String> entry = iter.next();
+				String name = entry.getKey();
+				String value = entry.getValue();
+
+				if (value != null && value.length() > 0) {
+					buffer.append(separator);
+
+					// If the value contains any comma or equals, then quote the
+					// whole thing
+					if (value.indexOf(',') > -1 || value.indexOf('=') > -1)
+						value = "'" + value + "'";
+
+					buffer.append(name).append('=').append(value);
+				}
+			}
+		}
+	}
+
+	protected boolean newlinesBetweenAttributes() {
+		return false;
+	}
+
+	@Override
+	public HeaderClause clone() {
+		try {
+			HeaderClause clone = (HeaderClause) super.clone();
+			clone.name = this.name;
+			clone.attribs = new Attrs(this.attribs);
+			return clone;
+		}
+		catch (CloneNotSupportedException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public int compareTo(HeaderClause other) {
+		return this.name.compareTo(other.name);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((attribs == null) ? 0 : attribs.hashCode());
+		result = prime * result + ((name == null) ? 0 : name.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		HeaderClause other = (HeaderClause) obj;
+		if (attribs == null) {
+			if (other.attribs != null)
+				return false;
+		} else if (!attribs.isEqual(other.attribs))
+			return false;
+		if (name == null) {
+			if (other.name != null)
+				return false;
+		} else if (!name.equals(other.name))
+			return false;
+		return true;
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ImportPattern.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ImportPattern.java
new file mode 100644
index 0000000..5e0854c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ImportPattern.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model.clauses;
+
+import org.osgi.framework.Constants;
+
+import aQute.libg.header.Attrs;
+
+public class ImportPattern extends VersionedClause implements Cloneable {
+
+	public ImportPattern(String pattern, Attrs attributes) {
+		super(pattern, attributes);
+	}
+
+	public boolean isOptional() {
+		String resolution = attribs.get(aQute.lib.osgi.Constants.RESOLUTION_DIRECTIVE);
+		return Constants.RESOLUTION_OPTIONAL.equals(resolution);
+	}
+
+	public void setOptional(boolean optional) {
+		if (optional)
+			attribs.put(aQute.lib.osgi.Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_OPTIONAL);
+		else
+			attribs.remove(aQute.lib.osgi.Constants.RESOLUTION_DIRECTIVE);
+	}
+
+	@Override
+	public ImportPattern clone() {
+		return new ImportPattern(this.name, new Attrs(this.attribs));
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ServiceComponent.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ServiceComponent.java
new file mode 100644
index 0000000..69f8610
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ServiceComponent.java
@@ -0,0 +1,266 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model.clauses;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import aQute.libg.header.Attrs;
+
+public class ServiceComponent extends HeaderClause implements Cloneable {
+
+	// v1.0.0 attributes
+	// public final static String COMPONENT_NAME = "name:";
+	public final static String		COMPONENT_FACTORY				= "factory:";
+	public final static String		COMPONENT_SERVICEFACTORY		= "servicefactory:";
+	public final static String		COMPONENT_IMMEDIATE				= "immediate:";
+	public final static String		COMPONENT_ENABLED				= "enabled:";
+
+	public final static String		COMPONENT_DYNAMIC				= "dynamic:";
+	public final static String		COMPONENT_MULTIPLE				= "multiple:";
+	public final static String		COMPONENT_PROVIDE				= "provide:";
+	public final static String		COMPONENT_OPTIONAL				= "optional:";
+	public final static String		COMPONENT_PROPERTIES			= "properties:";
+	// public final static String COMPONENT_IMPLEMENTATION = "implementation:";
+
+	// v1.1.0 attributes
+	public final static String		COMPONENT_VERSION				= "version:";
+	public final static String		COMPONENT_CONFIGURATION_POLICY	= "configuration-policy:";
+	public final static String		COMPONENT_MODIFIED				= "modified:";
+	public final static String		COMPONENT_ACTIVATE				= "activate:";
+	public final static String		COMPONENT_DEACTIVATE			= "deactivate:";
+
+	private final static Pattern	REFERENCE_PATTERN				= Pattern.compile("([^(]+)(\\(.+\\))?");
+
+	public ServiceComponent(String name, Attrs attribs) {
+		super(name, attribs);
+	}
+
+	public boolean isPath() {
+		return name.indexOf('/') >= 0 || name.endsWith(".xml");
+	}
+
+	private Set<String> getStringSet(String attrib) {
+		List<String> list = getListAttrib(attrib);
+		return list != null ? new HashSet<String>(list) : new HashSet<String>();
+	}
+
+	public void setPropertiesMap(Map<String,String> properties) {
+		List<String> strings = new ArrayList<String>(properties.size());
+		for (Entry<String,String> entry : properties.entrySet()) {
+			String line = new StringBuilder().append(entry.getKey()).append("=").append(entry.getValue()).toString();
+			strings.add(line);
+		}
+		setListAttrib(COMPONENT_PROPERTIES, strings);
+	}
+
+	public Map<String,String> getPropertiesMap() {
+		Map<String,String> result = new LinkedHashMap<String,String>();
+
+		List<String> list = getListAttrib(COMPONENT_PROPERTIES);
+		if (list != null) {
+			for (String entryStr : list) {
+				String name;
+				String value;
+
+				int index = entryStr.lastIndexOf('=');
+				if (index == -1) {
+					name = entryStr;
+					value = null;
+				} else {
+					name = entryStr.substring(0, index);
+					value = entryStr.substring(index + 1);
+				}
+
+				result.put(name, value);
+			}
+		}
+
+		return result;
+	}
+
+	public void setSvcRefs(List< ? extends ComponentSvcReference> refs) {
+		// First remove all existing references, i.e. non-directives
+		for (Iterator<String> iter = attribs.keySet().iterator(); iter.hasNext();) {
+			String name = iter.next();
+			if (!name.endsWith(":")) {
+				iter.remove();
+			}
+		}
+
+		// Add in the references
+		Set<String> dynamic = new HashSet<String>();
+		Set<String> optional = new HashSet<String>();
+		Set<String> multiple = new HashSet<String>();
+		for (ComponentSvcReference ref : refs) {
+			// Build the reference name with bind and unbind
+			String expandedRefName = ref.getName();
+			if (ref.getBind() != null) {
+				expandedRefName += "/" + ref.getBind();
+				if (ref.getUnbind() != null) {
+					expandedRefName += "/" + ref.getUnbind();
+				}
+			}
+
+			// Start building the map value
+			StringBuilder buffer = new StringBuilder();
+			buffer.append(ref.getServiceClass());
+
+			// Add the target filter
+			if (ref.getTargetFilter() != null) {
+				buffer.append('(').append(ref.getTargetFilter()).append(')');
+			}
+
+			// Work out the cardinality suffix (i.e. *, +, ? org ~).
+			// Adding to the dynamic/multiple/optional lists for non-standard
+			// cases
+			String cardinalitySuffix;
+			if (ref.isDynamic()) {
+				if (ref.isOptional()) {
+					if (ref.isMultiple()) // 0..n dynamic
+						cardinalitySuffix = "*";
+					else
+						// 0..1 dynamic
+						cardinalitySuffix = "?";
+				} else {
+					if (ref.isMultiple()) // 1..n dynamic
+						cardinalitySuffix = "+";
+					else { // 1..1 dynamic, not a normal combination
+						cardinalitySuffix = null;
+						dynamic.add(ref.getName());
+					}
+				}
+			} else {
+				if (ref.isOptional()) {
+					if (ref.isMultiple()) { // 0..n static, not a normal
+											// combination
+						cardinalitySuffix = null;
+						optional.add(ref.getName());
+						multiple.add(ref.getName());
+					} else { // 0..1 static
+						cardinalitySuffix = "~";
+					}
+				} else {
+					if (ref.isMultiple()) { // 1..n static, not a normal
+											// combination
+						multiple.add(ref.getName());
+						cardinalitySuffix = null;
+					} else { // 1..1 static
+						cardinalitySuffix = null;
+					}
+				}
+			}
+
+			if (cardinalitySuffix != null)
+				buffer.append(cardinalitySuffix);
+
+			// Write to the map
+			attribs.put(expandedRefName, buffer.toString());
+		}
+		setListAttrib(COMPONENT_OPTIONAL, optional);
+		setListAttrib(COMPONENT_MULTIPLE, multiple);
+		setListAttrib(COMPONENT_DYNAMIC, dynamic);
+	}
+
+	public List<ComponentSvcReference> getSvcRefs() {
+		List<ComponentSvcReference> result = new ArrayList<ComponentSvcReference>();
+
+		Set<String> dynamicSet = getStringSet(COMPONENT_DYNAMIC);
+		Set<String> optionalSet = getStringSet(COMPONENT_OPTIONAL);
+		Set<String> multipleSet = getStringSet(COMPONENT_MULTIPLE);
+
+		for (Entry<String,String> entry : attribs.entrySet()) {
+			String referenceName = entry.getKey();
+
+			// Skip directives
+			if (referenceName.endsWith(":"))//$NON-NLS-1$
+				continue;
+
+			ComponentSvcReference svcRef = new ComponentSvcReference();
+
+			String bind = null;
+			String unbind = null;
+
+			if (referenceName.indexOf('/') >= 0) {
+				String parts[] = referenceName.split("/");
+				referenceName = parts[0];
+				bind = parts[1];
+				if (parts.length > 2)
+					unbind = parts[2];
+				/*
+				 * else if (bind.startsWith("add")) unbind =
+				 * bind.replaceAll("add(.+)", "remove$1"); else unbind = "un" +
+				 * bind; } else if
+				 * (Character.isLowerCase(referenceName.charAt(0))) { bind =
+				 * "set" + Character.toUpperCase(referenceName.charAt(0)) +
+				 * referenceName.substring(1); unbind = "un" + bind;
+				 */
+			}
+			svcRef.setName(referenceName);
+			svcRef.setBind(bind);
+			svcRef.setUnbind(unbind);
+
+			String interfaceName = entry.getValue();
+			if (interfaceName == null || interfaceName.length() == 0) {
+				continue;
+			}
+			svcRef.setServiceClass(interfaceName);
+
+			// Check the cardinality by looking at the last
+			// character of the value
+			char c = interfaceName.charAt(interfaceName.length() - 1);
+			if ("?+*~".indexOf(c) >= 0) {
+				if (c == '?' || c == '*' || c == '~')
+					optionalSet.add(referenceName);
+				if (c == '+' || c == '*')
+					multipleSet.add(referenceName);
+				if (c == '+' || c == '*' || c == '?')
+					dynamicSet.add(referenceName);
+				interfaceName = interfaceName.substring(0, interfaceName.length() - 1);
+			}
+			svcRef.setOptional(optionalSet.contains(referenceName));
+			svcRef.setMultiple(multipleSet.contains(referenceName));
+			svcRef.setDynamic(dynamicSet.contains(referenceName));
+
+			// Parse the target from the interface name
+			// The target is a filter.
+			String target = null;
+			Matcher m = REFERENCE_PATTERN.matcher(interfaceName);
+			if (m.matches()) {
+				interfaceName = m.group(1);
+				target = m.group(2);
+			}
+			svcRef.setTargetFilter(target);
+
+			result.add(svcRef);
+		}
+
+		return result;
+	}
+
+	@Override
+	public ServiceComponent clone() {
+		return new ServiceComponent(this.name, new Attrs(this.attribs));
+	}
+
+	@Override
+	protected boolean newlinesBetweenAttributes() {
+		return true;
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/VersionedClause.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/VersionedClause.java
new file mode 100644
index 0000000..a6e79a9
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/VersionedClause.java
@@ -0,0 +1,27 @@
+package aQute.bnd.build.model.clauses;
+
+import org.osgi.framework.Constants;
+
+import aQute.libg.header.Attrs;
+
+public class VersionedClause extends HeaderClause implements Cloneable {
+	public VersionedClause(String name, Attrs attribs) {
+		super(name, attribs);
+	}
+
+	public String getVersionRange() {
+		return attribs.get(Constants.VERSION_ATTRIBUTE);
+	}
+
+	public void setVersionRange(String versionRangeString) {
+		attribs.put(Constants.VERSION_ATTRIBUTE, versionRangeString);
+	}
+
+	@Override
+	public VersionedClause clone() {
+		VersionedClause clone = (VersionedClause) super.clone();
+		clone.name = this.name;
+		clone.attribs = new Attrs(this.attribs);
+		return clone;
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/packageinfo b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/packageinfo
new file mode 100644
index 0000000..55af8e5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/packageinfo
@@ -0,0 +1 @@
+version 2
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/ClauseListConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/ClauseListConverter.java
new file mode 100644
index 0000000..130b60b
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/ClauseListConverter.java
@@ -0,0 +1,31 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+import aQute.lib.osgi.*;
+import aQute.libg.header.*;
+import aQute.libg.tuple.*;
+
+public class ClauseListConverter<R> implements Converter<List<R>,String> {
+
+	private final Converter< ? extends R, ? super Pair<String,Attrs>>	itemConverter;
+
+	public ClauseListConverter(Converter< ? extends R, ? super Pair<String,Attrs>> itemConverter) {
+		this.itemConverter = itemConverter;
+	}
+
+	public List<R> convert(String input) throws IllegalArgumentException {
+		List<R> result = new ArrayList<R>();
+
+		Parameters header = new Parameters(input);
+		for (Entry<String,Attrs> entry : header.entrySet()) {
+			String key = Processor.removeDuplicateMarker(entry.getKey());
+			Pair<String,Attrs> pair = Pair.newInstance(key, entry.getValue());
+			result.add(itemConverter.convert(pair));
+		}
+
+		return result;
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/CollectionFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/CollectionFormatter.java
new file mode 100644
index 0000000..d9f345a
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/CollectionFormatter.java
@@ -0,0 +1,47 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.*;
+
+public class CollectionFormatter<T> implements Converter<String,Collection< ? extends T>> {
+
+	private final String						separator;
+	private final Converter<String, ? super T>	itemFormatter;
+	private final String						emptyOutput;
+
+	public CollectionFormatter(String separator) {
+		this(separator, (String) null);
+	}
+
+	public CollectionFormatter(String separator, String emptyOutput) {
+		this(separator, new DefaultFormatter(), emptyOutput);
+	}
+
+	public CollectionFormatter(String separator, Converter<String, ? super T> itemFormatter) {
+		this(separator, itemFormatter, null);
+	}
+
+	public CollectionFormatter(String separator, Converter<String, ? super T> itemFormatter, String emptyOutput) {
+		this.separator = separator;
+		this.itemFormatter = itemFormatter;
+		this.emptyOutput = emptyOutput;
+	}
+
+	public String convert(Collection< ? extends T> input) throws IllegalArgumentException {
+		String result = null;
+		if (input != null) {
+			if (input.isEmpty()) {
+				result = emptyOutput;
+			} else {
+				StringBuilder buffer = new StringBuilder();
+				for (Iterator< ? extends T> iter = input.iterator(); iter.hasNext();) {
+					T item = iter.next();
+					buffer.append(itemFormatter.convert(item));
+					if (iter.hasNext())
+						buffer.append(separator);
+				}
+				result = buffer.toString();
+			}
+		}
+		return result;
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/Converter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/Converter.java
new file mode 100644
index 0000000..8513f52
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/Converter.java
@@ -0,0 +1,5 @@
+package aQute.bnd.build.model.conversions;
+
+public interface Converter<R, T> {
+	R convert(T input) throws IllegalArgumentException;
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultBooleanFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultBooleanFormatter.java
new file mode 100644
index 0000000..f188569
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultBooleanFormatter.java
@@ -0,0 +1,26 @@
+package aQute.bnd.build.model.conversions;
+
+/**
+ * Formatter for booleans with a default value; if the input value matches the
+ * default then it is formatted to <code>null</code>.
+ * 
+ * @author Neil Bartlett
+ */
+public class DefaultBooleanFormatter implements Converter<String,Boolean> {
+
+	private final boolean	defaultValue;
+
+	public DefaultBooleanFormatter(boolean defaultValue) {
+		this.defaultValue = defaultValue;
+	}
+
+	public String convert(Boolean input) throws IllegalArgumentException {
+		String result = null;
+
+		if (input != null && input.booleanValue() != defaultValue)
+			result = input.toString();
+
+		return result;
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultFormatter.java
new file mode 100644
index 0000000..a552c0d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultFormatter.java
@@ -0,0 +1,9 @@
+package aQute.bnd.build.model.conversions;
+
+public class DefaultFormatter implements Converter<String,Object> {
+
+	public String convert(Object input) throws IllegalArgumentException {
+		return input == null ? null : input.toString();
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumConverter.java
new file mode 100644
index 0000000..4c94d3b
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumConverter.java
@@ -0,0 +1,27 @@
+package aQute.bnd.build.model.conversions;
+
+public class EnumConverter<E extends Enum<E>> implements Converter<E,String> {
+
+	private final Class<E>	enumType;
+	private final E			defaultValue;
+
+	public static <E extends Enum<E>> EnumConverter<E> create(Class<E> enumType) {
+		return new EnumConverter<E>(enumType, null);
+	}
+
+	public static <E extends Enum<E>> EnumConverter<E> create(Class<E> enumType, E defaultValue) {
+		return new EnumConverter<E>(enumType, defaultValue);
+	}
+
+	private EnumConverter(Class<E> enumType, E defaultValue) {
+		this.enumType = enumType;
+		this.defaultValue = defaultValue;
+	}
+
+	public E convert(String input) throws IllegalArgumentException {
+		if (input == null)
+			return defaultValue;
+		return Enum.valueOf(enumType, input);
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumFormatter.java
new file mode 100644
index 0000000..8500a25
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumFormatter.java
@@ -0,0 +1,53 @@
+package aQute.bnd.build.model.conversions;
+
+/**
+ * Formats an enum type. Outputs {@code null} when the value of the enum is
+ * equal to a default value.
+ * 
+ * @param <E>
+ * @author Neil Bartlett
+ */
+public class EnumFormatter<E extends Enum<E>> implements Converter<String,E> {
+
+	private final E			defaultValue;
+
+	/**
+	 * Construct a new formatter with no default value, i.e. any non-null value
+	 * of the enum will print that value.
+	 * 
+	 * @param enumType
+	 *            The enum type.
+	 * @return
+	 */
+	public static <E extends Enum<E>> EnumFormatter<E> create(Class<E> enumType) {
+		return new EnumFormatter<E>(null);
+	}
+
+	/**
+	 * Construct a new formatter with the specified default value.
+	 * 
+	 * @param enumType
+	 *            The enum type.
+	 * @param defaultValue
+	 *            The default value, which will never be output.
+	 * @return
+	 */
+	public static <E extends Enum<E>> EnumFormatter<E> create(Class<E> enumType, E defaultValue) {
+		return new EnumFormatter<E>(defaultValue);
+	}
+
+	private EnumFormatter(E defaultValue) {
+		this.defaultValue = defaultValue;
+	}
+
+	public String convert(E input) throws IllegalArgumentException {
+		String result;
+		if (input == defaultValue || input == null)
+			result = null;
+		else {
+			result = input.toString();
+		}
+		return result;
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseConverter.java
new file mode 100644
index 0000000..dfb3a5d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseConverter.java
@@ -0,0 +1,13 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.clauses.*;
+import aQute.libg.header.*;
+import aQute.libg.tuple.*;
+
+public class HeaderClauseConverter implements Converter<HeaderClause,Pair<String,Attrs>> {
+
+	public HeaderClause convert(Pair<String,Attrs> input) throws IllegalArgumentException {
+		return new HeaderClause(input.getFirst(), input.getSecond());
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseFormatter.java
new file mode 100644
index 0000000..f4161f5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseFormatter.java
@@ -0,0 +1,11 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.clauses.*;
+
+public class HeaderClauseFormatter implements Converter<String,HeaderClause> {
+	public String convert(HeaderClause input) throws IllegalArgumentException {
+		StringBuilder buffer = new StringBuilder();
+		input.formatTo(buffer);
+		return buffer.toString();
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseListConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseListConverter.java
new file mode 100644
index 0000000..65d8471
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseListConverter.java
@@ -0,0 +1,11 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.clauses.*;
+
+public class HeaderClauseListConverter extends ClauseListConverter<HeaderClause> {
+
+	public HeaderClauseListConverter() {
+		super(new HeaderClauseConverter());
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/MapFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/MapFormatter.java
new file mode 100644
index 0000000..03d6f71
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/MapFormatter.java
@@ -0,0 +1,18 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class MapFormatter implements Converter<String,Map<String,String>> {
+
+	private CollectionFormatter<Entry<String,String>>	entrySetFormatter;
+
+	public MapFormatter(String listSeparator, Converter<String, ? super Entry<String,String>> entryFormatter,
+			String emptyOutput) {
+		entrySetFormatter = new CollectionFormatter<Entry<String,String>>(listSeparator, entryFormatter, emptyOutput);
+	}
+
+	public String convert(Map<String,String> input) throws IllegalArgumentException {
+		return entrySetFormatter.convert(input.entrySet());
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NewlineEscapedStringFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NewlineEscapedStringFormatter.java
new file mode 100644
index 0000000..790cda0
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NewlineEscapedStringFormatter.java
@@ -0,0 +1,33 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.*;
+
+public class NewlineEscapedStringFormatter implements Converter<String,String> {
+
+	public String convert(String input) throws IllegalArgumentException {
+		if (input == null)
+			return null;
+
+		// Shortcut the result for the majority of cases where there is no
+		// newline
+		if (input.indexOf('\n') == -1)
+			return input;
+
+		// Build a new string with newlines escaped
+		StringBuilder result = new StringBuilder();
+		int position = 0;
+		while (position < input.length()) {
+			int newlineIndex = input.indexOf('\n', position);
+			if (newlineIndex == -1) {
+				result.append(input.substring(position));
+				break;
+			}
+			result.append(input.substring(position, newlineIndex));
+			result.append(BndEditModel.LINE_SEPARATOR);
+			position = newlineIndex + 1;
+		}
+
+		return result.toString();
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NoopConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NoopConverter.java
new file mode 100644
index 0000000..7a9f39d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NoopConverter.java
@@ -0,0 +1,7 @@
+package aQute.bnd.build.model.conversions;
+
+public class NoopConverter<T> implements Converter<T,T> {
+	public T convert(T input) throws IllegalArgumentException {
+		return input;
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesConverter.java
new file mode 100644
index 0000000..a1ecf5f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesConverter.java
@@ -0,0 +1,13 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.*;
+
+import aQute.libg.header.*;
+
+public class PropertiesConverter implements Converter<Map<String,String>,String> {
+
+	public Map<String,String> convert(String input) throws IllegalArgumentException {
+		return OSGiHeader.parseProperties(input);
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesEntryFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesEntryFormatter.java
new file mode 100644
index 0000000..b487190
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesEntryFormatter.java
@@ -0,0 +1,26 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.Map.Entry;
+
+public class PropertiesEntryFormatter implements Converter<String,Entry<String,String>> {
+	public String convert(Entry<String,String> entry) {
+		StringBuilder buffer = new StringBuilder();
+
+		String name = entry.getKey();
+		buffer.append(name).append('=');
+
+		String value = entry.getValue();
+		if (value != null && value.length() > 0) {
+			int quotableIndex = value.indexOf(',');
+			if (quotableIndex == -1)
+				quotableIndex = value.indexOf('=');
+
+			if (quotableIndex >= 0) {
+				buffer.append('\'').append(value).append('\'');
+			} else {
+				buffer.append(value);
+			}
+		}
+		return buffer.toString();
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/SimpleListConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/SimpleListConverter.java
new file mode 100644
index 0000000..2291be3
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/SimpleListConverter.java
@@ -0,0 +1,41 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.*;
+
+import aQute.lib.osgi.*;
+import aQute.libg.qtokens.*;
+
+public class SimpleListConverter<R> implements Converter<List<R>,String> {
+
+	private Converter< ? extends R, ? super String>	itemConverter;
+
+	public static <R> Converter<List<R>,String> create(Converter<R, ? super String> itemConverter) {
+		return new SimpleListConverter<R>(itemConverter);
+	}
+
+	public static Converter<List<String>,String> create() {
+		return new SimpleListConverter<String>(new NoopConverter<String>());
+	}
+
+	private SimpleListConverter(Converter< ? extends R, ? super String> itemConverter) {
+		this.itemConverter = itemConverter;
+	}
+
+	public List<R> convert(String input) throws IllegalArgumentException {
+		List<R> result = new ArrayList<R>();
+
+		if (Constants.EMPTY_HEADER.equalsIgnoreCase(input.trim()))
+			return result;
+
+		QuotedTokenizer qt = new QuotedTokenizer(input, ",");
+		String token = qt.nextToken();
+
+		while (token != null) {
+			result.add(itemConverter.convert(token.trim()));
+			token = qt.nextToken();
+		}
+
+		return result;
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/StringEntryConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/StringEntryConverter.java
new file mode 100644
index 0000000..c857ddc
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/StringEntryConverter.java
@@ -0,0 +1,11 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.Map.Entry;
+
+public class StringEntryConverter implements Converter<String,Entry<String, ? >> {
+
+	public String convert(Entry<String, ? > input) throws IllegalArgumentException {
+		return input.getKey();
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/VersionedClauseConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/VersionedClauseConverter.java
new file mode 100644
index 0000000..5aead1c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/VersionedClauseConverter.java
@@ -0,0 +1,11 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.clauses.*;
+import aQute.libg.header.*;
+import aQute.libg.tuple.*;
+
+public class VersionedClauseConverter implements Converter<VersionedClause,Pair<String,Attrs>> {
+	public VersionedClause convert(Pair<String,Attrs> input) throws IllegalArgumentException {
+		return new VersionedClause(input.getFirst(), input.getSecond());
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/packageinfo b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/packageinfo
new file mode 100644
index 0000000..55af8e5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/packageinfo
@@ -0,0 +1 @@
+version 2
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/packageinfo b/bundleplugin/src/main/java/aQute/bnd/build/model/packageinfo
new file mode 100644
index 0000000..55af8e5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/packageinfo
@@ -0,0 +1 @@
+version 2
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/packageinfo b/bundleplugin/src/main/java/aQute/bnd/build/packageinfo
index 5035fd2..0ff7674 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/packageinfo
+++ b/bundleplugin/src/main/java/aQute/bnd/build/packageinfo
@@ -1 +1 @@
-version 1.44.0
+version 1.45.0
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/GenericType.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/GenericType.java
index d16affe..ce286f2 100644
--- a/bundleplugin/src/main/java/aQute/bnd/compatibility/GenericType.java
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/GenericType.java
@@ -1,7 +1,7 @@
 package aQute.bnd.compatibility;
 
 public class GenericType {
-	public GenericType(Class<Object> class1) {
+	public GenericType(@SuppressWarnings("unused") Class<Object> class1) {
 		// TODO Auto-generated constructor stub
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/SignatureGenerator.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/SignatureGenerator.java
deleted file mode 100644
index 6567ef8..0000000
--- a/bundleplugin/src/main/java/aQute/bnd/compatibility/SignatureGenerator.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package aQute.bnd.compatibility;
-
-public class SignatureGenerator {
-	// enum ACCESS {
-	// // PUBLIC("+"), PROTECTED("|"), PACKAGE_PRIVATE(""), PRIVATE("-");
-	// // final String repr;
-	// //
-	// // ACCESS(String s) {
-	// // repr = s;
-	// // }
-	// //
-	// // public String toString() {
-	// // return repr;
-	// // }
-	// }
-	//
-	// public static void main(String args[]) throws Exception {
-	// final PrintStream out = System.err;
-	//
-	// Clazz c = new Clazz("x", new FileResource(new File(
-	// "src/aQute/bnd/compatibility/SignatureGenerator.class")));
-	// c.parseClassFileWithCollector(new ClassDataCollector() {
-	// public void classBegin(int access, String name) {
-	// out.print(name);
-	// out.println(access(access));
-	// }
-	//
-	// private ACCESS access(int access) {
-	// if (Modifier.isPublic(access))
-	// return ACCESS.PUBLIC;
-	//
-	// throw new IllegalArgumentException();
-	// }
-	//
-	// public void extendsClass(String name) {
-	// }
-	//
-	// public void implementsInterfaces(String name[]) {
-	// }
-	//
-	// public void addReference(String token) {
-	// }
-	//
-	// public void annotation(Annotation annotation) {
-	// }
-	//
-	// public void parameter(int p) {
-	// }
-	//
-	// public void method(Clazz.MethodDef defined) {
-	// if (defined.isConstructor())
-	// constructor(defined.access, defined.descriptor);
-	// else
-	// method(defined.access, defined.name, defined.descriptor);
-	// }
-	//
-	// public void field(Clazz.FieldDef defined) {
-	// field(defined.access, defined.name, defined.descriptor);
-	// }
-	//
-	// public void reference(Clazz.MethodDef referenced) {
-	// }
-	//
-	// public void reference(Clazz.FieldDef referenced) {
-	// }
-	//
-	// public void classEnd() {
-	// }
-	//
-	// @Deprecated// Will really be removed!
-	// public void field(int access, String name, String descriptor) {
-	// }
-	//
-	// @Deprecated// Will really be removed!
-	// public void constructor(int access, String descriptor) {
-	// }
-	//
-	// @Deprecated// Will really be removed!
-	// public void method(int access, String name, String descriptor) {
-	// }
-	//
-	// /**
-	// * The EnclosingMethod attribute
-	// *
-	// * @param cName
-	// * The name of the enclosing class, never null. Name is
-	// * with slashes.
-	// * @param mName
-	// * The name of the enclosing method in the class with
-	// * cName or null
-	// * @param mDescriptor
-	// * The descriptor of this type
-	// */
-	// public void enclosingMethod(String cName, String mName, String
-	// mDescriptor) {
-	//
-	// }
-	//
-	// /**
-	// * The InnerClass attribute
-	// *
-	// * @param innerClass
-	// * The name of the inner class (with slashes). Can be
-	// * null.
-	// * @param outerClass
-	// * The name of the outer class (with slashes) Can be
-	// * null.
-	// * @param innerName
-	// * The name inside the outer class, can be null.
-	// * @param modifiers
-	// * The access flags
-	// */
-	// public void innerClass(String innerClass, String outerClass, String
-	// innerName,
-	// int innerClassAccessFlags) {
-	// }
-	//
-	// public void signature(String signature) {
-	// }
-	//
-	// public void constant(Object object) {
-	// }
-	//
-	// });
-	// }
-}
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java
index 18b89a9..f387048 100644
--- a/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java
@@ -530,11 +530,9 @@
 	private String assign(Map<String,String> map, String name) {
 		if (map.containsKey(name))
 			return map.get(name);
-		else {
-			int n = map.size();
-			map.put(name, "_" + n);
-			return "_" + n;
-		}
+		int n = map.size();
+		map.put(name, "_" + n);
+		return "_" + n;
 	}
 
 	private boolean isInstance(Class< ? > type, String string) {
diff --git a/bundleplugin/src/main/java/aQute/bnd/component/ReferenceDef.java b/bundleplugin/src/main/java/aQute/bnd/component/ReferenceDef.java
index 7dfc8b0..a626e52 100644
--- a/bundleplugin/src/main/java/aQute/bnd/component/ReferenceDef.java
+++ b/bundleplugin/src/main/java/aQute/bnd/component/ReferenceDef.java
@@ -85,8 +85,7 @@
 		int n = a.compareTo(b);
 		if (n >= 0)
 			return a;
-		else
-			return b;
+		return b;
 	}
 
 	public String toString() {
diff --git a/bundleplugin/src/main/java/aQute/bnd/differ/DiffImpl.java b/bundleplugin/src/main/java/aQute/bnd/differ/DiffImpl.java
index ea6cd50..57c7da5 100644
--- a/bundleplugin/src/main/java/aQute/bnd/differ/DiffImpl.java
+++ b/bundleplugin/src/main/java/aQute/bnd/differ/DiffImpl.java
@@ -200,10 +200,10 @@
 		if (getDelta() == other.getDelta()) {
 			if (getType() == other.getType()) {
 				return getName().compareTo(other.getName());
-			} else
-				return getType().compareTo(other.getType());
-		} else
-			return getDelta().compareTo(other.getDelta());
+			}
+			return getType().compareTo(other.getType());
+		}
+		return getDelta().compareTo(other.getDelta());
 	}
 
 	public Diff get(String name) {
diff --git a/bundleplugin/src/main/java/aQute/bnd/differ/Element.java b/bundleplugin/src/main/java/aQute/bnd/differ/Element.java
index 683c7a8..3d27f62 100644
--- a/bundleplugin/src/main/java/aQute/bnd/differ/Element.java
+++ b/bundleplugin/src/main/java/aQute/bnd/differ/Element.java
@@ -97,8 +97,7 @@
 	public int compareTo(Tree other) {
 		if (type == other.getType())
 			return name.compareTo(other.getName());
-		else
-			return type.compareTo(other.getType());
+		return type.compareTo(other.getType());
 	}
 
 	public boolean equals(Object other) {
@@ -125,7 +124,7 @@
 	}
 
 	public Diff diff(Tree older) {
-		return new DiffImpl(this, (Element) older);
+		return new DiffImpl(this, older);
 	}
 
 	public Element get(String name) {
diff --git a/bundleplugin/src/main/java/aQute/bnd/differ/JavaElement.java b/bundleplugin/src/main/java/aQute/bnd/differ/JavaElement.java
index c5bdb4b..fd3515a 100644
--- a/bundleplugin/src/main/java/aQute/bnd/differ/JavaElement.java
+++ b/bundleplugin/src/main/java/aQute/bnd/differ/JavaElement.java
@@ -297,12 +297,11 @@
 					Clazz c = analyzer.findClass(name);
 					if (c == null) {
 						return "Cannot load " + name;
-					} else {
-						Element s = classElement(c);
-						for (Element child : s.children) {
-							if (INHERITED.contains(child.type) && !child.name.startsWith("<")) {
-								members.add(child);
-							}
+					}
+					Element s = classElement(c);
+					for (Element child : s.children) {
+						if (INHERITED.contains(child.type) && !child.name.startsWith("<")) {
+							members.add(child);
 						}
 					}
 				}
@@ -629,7 +628,7 @@
 		elements.addAll(set);
 	}
 
-	private static void access(Collection<Element> children, int access, boolean deprecated) {
+	private static void access(Collection<Element> children, int access, @SuppressWarnings("unused") boolean deprecated) {
 		if (!isPublic(access))
 			children.add(PROTECTED);
 		if (isAbstract(access))
diff --git a/bundleplugin/src/main/java/aQute/bnd/differ/packageinfo b/bundleplugin/src/main/java/aQute/bnd/differ/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/differ/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/help/Errors.java b/bundleplugin/src/main/java/aQute/bnd/help/Errors.java
deleted file mode 100644
index 1d950ad..0000000
--- a/bundleplugin/src/main/java/aQute/bnd/help/Errors.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package aQute.bnd.help;
-
-public interface Errors {
-
-}
diff --git a/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java b/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java
index 7c7b160..e1a6c53 100644
--- a/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java
+++ b/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java
@@ -38,7 +38,6 @@
 																							+ Verifier.VERSIONRANGE
 																									.toString()));
 
-	@SuppressWarnings("deprecation")
 	static Syntax[]							syntaxes				= new Syntax[] {
 			new Syntax(
 					BUNDLE_ACTIVATIONPOLICY,
@@ -287,8 +286,6 @@
 			new Syntax(NOUSES, "Do not calculate the uses: directive on exports", "-nouses=true", "true,false",
 					Verifier.TRUEORFALSEPATTERN),
 
-			new Syntax(NOPE, "Deprecated, use -nobundles. ", "-nope=true", "true,false", Verifier.TRUEORFALSEPATTERN),
-
 			new Syntax(PEDANTIC, "Warn about things that are not really wrong but still not right", "-nope=true",
 					"true,false", Verifier.TRUEORFALSEPATTERN),
 
@@ -331,10 +328,7 @@
 			new Syntax(
 					RUNVM,
 					"Additional arguments for the VM invokation. Keys that start with a - are added as options, otherwise they are treated as -D properties for the VM",
-					"-runvm=-Xmax=30", null, null),
-			new Syntax(VERSIONPOLICY, "Provides a version policy to imports that are calculated from exports",
-					"-versionpolicy = \"[${version;==;${@}},${version;+;${@}})\"", null, null)
-
+					"-runvm=-Xmax=30", null, null)
 																	};
 
 	public final static Map<String,Syntax>	HELP					= new HashMap<String,Syntax>();
diff --git a/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java b/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java
index 29e5353..3ef7076 100644
--- a/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java
+++ b/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java
@@ -56,8 +56,8 @@
 				}
 			}
 			return new JarResource(jar);
-		} else
-			return null;
+		}
+		return null;
 	}
 
 }
diff --git a/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java b/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java
index 74d2921..9a6897e 100644
--- a/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java
+++ b/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java
@@ -20,23 +20,19 @@
 			if (content == null)
 				throw new IllegalArgumentException("No 'from' or 'content' field in copy " + argumentsOnMake);
 			return new EmbeddedResource(content.getBytes("UTF-8"), 0);
-		} else {
-
-			File f = builder.getFile(from);
-			if (f.isFile())
-				return new FileResource(f);
-			else {
-				try {
-					URL url = new URL(from);
-					return new URLResource(url);
-				}
-				catch (MalformedURLException mfue) {
-					// We ignore this
-				}
-				throw new IllegalArgumentException("Copy source does not exist " + from + " for destination "
-						+ destination);
-			}
 		}
+		File f = builder.getFile(from);
+		if (f.isFile())
+			return new FileResource(f);
+		try {
+			URL url = new URL(from);
+			return new URLResource(url);
+		}
+		catch (MalformedURLException mfue) {
+			// We ignore this
+		}
+		throw new IllegalArgumentException("Copy source does not exist " + from + " for destination "
+				+ destination);
 	}
 
 }
diff --git a/bundleplugin/src/main/java/aQute/bnd/make/component/ServiceComponent.java b/bundleplugin/src/main/java/aQute/bnd/make/component/ServiceComponent.java
index 653c48d..273ccbe 100644
--- a/bundleplugin/src/main/java/aQute/bnd/make/component/ServiceComponent.java
+++ b/bundleplugin/src/main/java/aQute/bnd/make/component/ServiceComponent.java
@@ -169,11 +169,11 @@
 			Collection<Clazz> not = analyzer.getClasses("", QUERY.NAMED.toString(), name //
 					);
 
-			if (not.isEmpty())
+			if (not.isEmpty()) {
 				if ("*".equals(name))
 					return not;
-				else
-					error("Specified %s but could not find any class matching this pattern", name);
+				error("Specified %s but could not find any class matching this pattern", name);
+			}
 
 			for (Clazz c : not) {
 				if (c.getFormat().hasAnnotations())
@@ -432,7 +432,7 @@
 		 * @param pw
 		 * @param provides
 		 */
-		void provide(PrintWriter pw, String provides, boolean servicefactory, String impl) {
+		void provide(PrintWriter pw, String provides, boolean servicefactory, @SuppressWarnings("unused") String impl) {
 			if (provides != null) {
 				if (!servicefactory)
 					pw.println("  <service>");
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDependencyGraph.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDependencyGraph.java
index d34f2b2..bbc45b8 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDependencyGraph.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDependencyGraph.java
@@ -14,7 +14,7 @@
 	final List<Artifact>				dependencies	= new ArrayList<Artifact>();
 	final List<URL>						repositories	= new ArrayList<URL>();
 	final XPath							xpath			= xpathFactory.newXPath();
-	final Map<URL,Artifact>				cache			= new HashMap<URL,Artifact>();
+	final Map<URI,Artifact>				cache			= new HashMap<URI,Artifact>();
 	Artifact							root;
 
 	enum Scope {
@@ -89,12 +89,11 @@
 			String path = getPath(repository.toString(), groupId, artifactId, version);
 
 			try {
-				URL url = new URL(path + ".pom");
+				URI url = new URL(path + ".pom").toURI();
 				if (cache.containsKey(url)) {
 					return cache.get(url);
-				} else {
-					return new Artifact(url);
 				}
+				return new Artifact(url.toURL());
 			}
 			catch (Exception e) {
 				System.err.println("Failed to get " + artifactId + " from " + repository);
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java
index ec5e2b7..b7a1c12 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java
@@ -138,7 +138,7 @@
 		}
 	}
 
-	private void optional(Command command, String key, String value) {
+	private void optional(Command command, @SuppressWarnings("unused") String key, String value) {
 		if (value == null)
 			return;
 
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
index 0209f5c..29de79d 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
@@ -173,7 +173,7 @@
 		}
 	}
 
-	private void optional(Command command, String key, String value) {
+	private void optional(Command command, @SuppressWarnings("unused") String key, String value) {
 		if (value == null)
 			return;
 
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java
index 05bb5eb..00e7863 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java
@@ -25,7 +25,7 @@
 		return false;
 	}
 
-	public File[] get(String bsn, String version) throws Exception {
+	private File[] get(String bsn, String version) throws Exception {
 		VersionRange range = new VersionRange("0");
 		if (version != null)
 			range = new VersionRange(version);
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java b/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java
index d1c0472..17bf92f 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java
@@ -16,16 +16,6 @@
 	Registry	registry;
 	Maven		maven;
 
-	public File[] get(String bsn, String range) throws Exception {
-		File f = get(bsn, range, Strategy.HIGHEST, null);
-		if (f == null)
-			return null;
-
-		return new File[] {
-			f
-		};
-	}
-
 	public File get(String bsn, String version, Strategy strategy, Map<String,String> properties) throws Exception {
 		String groupId = null;
 
diff --git a/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java b/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java
index 0155c24..816dd1b 100644
--- a/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java
+++ b/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java
@@ -130,7 +130,7 @@
 		return false;
 	}
 
-	public File[] get(String bsn, String range) throws Exception {
+	private File[] get(String bsn, String range) throws Exception {
 		VersionRange r = new VersionRange(range);
 		Map<String,String> instances = index.get(bsn);
 		if (instances == null)
diff --git a/bundleplugin/src/main/java/aQute/bnd/service/BndListener.java b/bundleplugin/src/main/java/aQute/bnd/service/BndListener.java
index 6335697..f697308 100644
--- a/bundleplugin/src/main/java/aQute/bnd/service/BndListener.java
+++ b/bundleplugin/src/main/java/aQute/bnd/service/BndListener.java
@@ -8,7 +8,7 @@
 public class BndListener {
 	final AtomicInteger	inside	= new AtomicInteger();
 
-	public void changed(File file) {}
+	public void changed(@SuppressWarnings("unused") File file) {}
 
 	public void begin() {
 		inside.incrementAndGet();
@@ -22,7 +22,7 @@
 		return inside.get() != 0;
 	}
 
-	public void signal(Reporter reporter) {
+	public void signal(@SuppressWarnings("unused") Reporter reporter) {
 
 	}
 }
diff --git a/bundleplugin/src/main/java/aQute/bnd/service/OBRIndexProvider.java b/bundleplugin/src/main/java/aQute/bnd/service/OBRIndexProvider.java
deleted file mode 100644
index 9ba5b04..0000000
--- a/bundleplugin/src/main/java/aQute/bnd/service/OBRIndexProvider.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package aQute.bnd.service;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-@Deprecated
-public interface OBRIndexProvider {
-	Collection<URL> getOBRIndexes() throws IOException;
-	Set<OBRResolutionMode> getSupportedModes();
-}
diff --git a/bundleplugin/src/main/java/aQute/bnd/service/OBRResolutionMode.java b/bundleplugin/src/main/java/aQute/bnd/service/OBRResolutionMode.java
deleted file mode 100644
index 78f9801..0000000
--- a/bundleplugin/src/main/java/aQute/bnd/service/OBRResolutionMode.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package aQute.bnd.service;
-
-@Deprecated
-public enum OBRResolutionMode {
-	build, runtime
-}
diff --git a/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java b/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java
index c190847..accda4f 100644
--- a/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java
+++ b/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java
@@ -19,22 +19,6 @@
 	 * @param range
 	 *            Version range for this bundle,"latest" if you only want the
 	 *            latest, or null when you want all.
-	 * @return A list of URLs sorted on version, lowest version is at index 0.
-	 *         null is returned when no files with the given bsn ould be found.
-	 * @throws Exception
-	 *             when anything goes wrong
-	 */
-	@Deprecated
-	File[] get(String bsn, String range) throws Exception;
-
-	/**
-	 * Return a URL to a matching version of the given bundle.
-	 * 
-	 * @param bsn
-	 *            Bundle-SymbolicName of the searched bundle
-	 * @param range
-	 *            Version range for this bundle,"latest" if you only want the
-	 *            latest, or null when you want all.
 	 * @param strategy
 	 *            Get the highest or the lowest
 	 * @return A list of URLs sorted on version, lowest version is at index 0.
diff --git a/bundleplugin/src/main/java/aQute/bnd/service/diff/packageinfo b/bundleplugin/src/main/java/aQute/bnd/service/diff/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/service/diff/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/settings/Settings.java b/bundleplugin/src/main/java/aQute/bnd/settings/Settings.java
index 6b7ff90..a52c613 100644
--- a/bundleplugin/src/main/java/aQute/bnd/settings/Settings.java
+++ b/bundleplugin/src/main/java/aQute/bnd/settings/Settings.java
@@ -38,8 +38,8 @@
 
 	private void generate() throws NoSuchAlgorithmException {
 		Pair< ? extends PrivateKey, ? extends RSAPublicKey> pair = RSA.generate();
-		prefs.put(KEY_PRIVATE, Crypto.toString(pair.a));
-		prefs.put(KEY_PUBLIC, Crypto.toString(pair.b));
+		prefs.put(KEY_PRIVATE, Crypto.toString(pair.getFirst()));
+		prefs.put(KEY_PUBLIC, Crypto.toString(pair.getSecond()));
 		prefs.putBoolean(KEY_SET, true);
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java b/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java
index 7914df3..600c9a8 100644
--- a/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java
+++ b/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java
@@ -131,10 +131,10 @@
 		for (Map.Entry<String,Resource> entry : jar.getResources().entrySet()) {
 			String name = entry.getKey();
 			if (!METAINFDIR.matcher(name).matches()) {
-				out.write("\r\n".getBytes());
-				out.write("Name: ".getBytes());
+				out.write("\r\n".getBytes("UTF-8"));
+				out.write("Name: ".getBytes("UTF-8"));
 				out.write(name.getBytes("UTF-8"));
-				out.write("\r\n".getBytes());
+				out.write("\r\n".getBytes("UTF-8"));
 
 				digest(algorithms, entry.getValue());
 				for (int a = 0; a < algorithms.length; a++) {
diff --git a/bundleplugin/src/main/java/aQute/configurable/Configurable.java b/bundleplugin/src/main/java/aQute/configurable/Configurable.java
index 1c461e6..72902e2 100644
--- a/bundleplugin/src/main/java/aQute/configurable/Configurable.java
+++ b/bundleplugin/src/main/java/aQute/configurable/Configurable.java
@@ -203,7 +203,6 @@
 								"Unknown interface for a collection, no concrete class found: " + resultType);
 				}
 
-				@SuppressWarnings("unchecked")
 				Collection<Object> result = (Collection<Object>) resultType.newInstance();
 				Type componentType = pType.getActualTypeArguments()[0];
 
@@ -225,7 +224,6 @@
 						throw new IllegalArgumentException(
 								"Unknown interface for a collection, no concrete class found: " + resultType);
 				}
-				@SuppressWarnings("unchecked")
 				Map<Object,Object> result = (Map<Object,Object>) resultType.newInstance();
 				Type keyType = pType.getActualTypeArguments()[0];
 				Type valueType = pType.getActualTypeArguments()[1];
diff --git a/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java b/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
index cd98036..4d0f5b9 100644
--- a/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
+++ b/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
@@ -1,7 +1,6 @@
 package aQute.lib.deployer;
 
 import java.io.*;
-import java.net.*;
 import java.util.*;
 import java.util.jar.*;
 import java.util.regex.*;
@@ -79,8 +78,8 @@
 		if (dirty) {
 			dirty = false;
 			return true;
-		} else
-			return false;
+		}
+		return false;
 	}
 
 	@Override
@@ -102,48 +101,4 @@
 		}
 		return result;
 	}
-
-	@Override
-	public File[] get(String bsn, String versionRange) throws MalformedURLException {
-		// If the version is set to project, we assume it is not
-		// for us. A project repo will then get it.
-		if (versionRange != null && versionRange.equals("project"))
-			return null;
-
-		//
-		// The version range we are looking for can
-		// be null (for all) or a version range.
-		//
-		VersionRange range;
-		if (versionRange == null || versionRange.equals("latest")) {
-			range = new VersionRange("0");
-		} else
-			range = new VersionRange(versionRange);
-
-		//
-		// Iterator over all the versions for this BSN.
-		// Create a sorted map over the version as key
-		// and the file as URL as value. Only versions
-		// that match the desired range are included in
-		// this list.
-		//
-		File instances[] = getRoot().listFiles();
-		SortedMap<Version,File> versions = new TreeMap<Version,File>();
-		for (int i = 0; i < instances.length; i++) {
-			Matcher m = REPO_FILE.matcher(instances[i].getName());
-			if (m.matches() && m.group(1).equals(bsn)) {
-				String versionString = m.group(2);
-				Version version;
-				if (versionString.equals("latest"))
-					version = new Version(Integer.MAX_VALUE);
-				else
-					version = new Version(versionString);
-
-				if (range.includes(version))
-					versions.put(version, instances[i]);
-			}
-		}
-		return versions.values().toArray(new File[versions.size()]);
-	}
-
 }
diff --git a/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java b/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
index cfc69f3..4be5ec8 100644
--- a/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
+++ b/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
@@ -56,7 +56,7 @@
 	 * Get a list of URLs to bundles that are constrained by the bsn and
 	 * versionRange.
 	 */
-	public File[] get(String bsn, String versionRange) throws Exception {
+	private File[] get(String bsn, String versionRange) throws Exception {
 		init();
 
 		// If the version is set to project, we assume it is not
@@ -259,8 +259,8 @@
 		if (dirty) {
 			dirty = false;
 			return true;
-		} else
-			return false;
+		}
+		return false;
 	}
 
 	public String getName() {
@@ -276,8 +276,7 @@
 		File version = new File(bsns, bsn + "-" + v.getMajor() + "." + v.getMinor() + "." + v.getMicro() + ".jar");
 		if (version.exists())
 			return new Jar(version);
-		else
-			return null;
+		return null;
 	}
 
 	public File get(String bsn, String version, Strategy strategy, Map<String,String> properties) throws Exception {
@@ -295,11 +294,9 @@
 			File file = IO.getFile(root, bsn + "/" + bsn + "-" + version + ".jar");
 			if (file.isFile())
 				return file;
-			else {
-				file = IO.getFile(root, bsn + "/" + bsn + "-" + version + ".lib");
-				if (file.isFile())
-					return file;
-			}
+			file = IO.getFile(root, bsn + "/" + bsn + "-" + version + ".lib");
+			if (file.isFile())
+				return file;
 			return null;
 
 		}
diff --git a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
index a5185e4..c36727f 100644
--- a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
+++ b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
@@ -308,7 +308,7 @@
 	 * Provide a help text.
 	 */
 
-	public void help(Formatter f, Object target, String cmd, Class< ? extends Options> specification) {
+	public void help(Formatter f, @SuppressWarnings("unused") Object target, String cmd, Class< ? extends Options> specification) {
 		Description descr = specification.getAnnotation(Description.class);
 		Arguments patterns = specification.getAnnotation(Arguments.class);
 		Map<String,Method> options = getOptions(specification);
diff --git a/bundleplugin/src/main/java/aQute/lib/json/ArrayHandler.java b/bundleplugin/src/main/java/aQute/lib/json/ArrayHandler.java
index 1eb83ac..7e3b35e 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/ArrayHandler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/ArrayHandler.java
@@ -7,7 +7,7 @@
 public class ArrayHandler extends Handler {
 	Type	componentType;
 
-	ArrayHandler(Class< ? > rawClass, Type componentType) {
+	ArrayHandler(@SuppressWarnings("unused") Class< ? > rawClass, Type componentType) {
 		this.componentType = componentType;
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/json/CharacterHandler.java b/bundleplugin/src/main/java/aQute/lib/json/CharacterHandler.java
index 3d00cdc..23605bf 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/CharacterHandler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/CharacterHandler.java
@@ -8,7 +8,7 @@
 	@Override
 	void encode(Encoder app, Object object, Map<Object,Type> visited) throws Exception {
 		Character c = (Character) object;
-		int v = (int) c.charValue();
+		int v = c.charValue();
 		app.append(v + "");
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/json/CollectionHandler.java b/bundleplugin/src/main/java/aQute/lib/json/CollectionHandler.java
index 0ebfc62..aacbb0a 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/CollectionHandler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/CollectionHandler.java
@@ -48,7 +48,6 @@
 		app.append("]");
 	}
 
-	@SuppressWarnings("unchecked")
 	@Override
 	Object decodeArray(Decoder r) throws Exception {
 		Collection<Object> c = (Collection<Object>) rawClass.newInstance();
diff --git a/bundleplugin/src/main/java/aQute/lib/json/Decoder.java b/bundleplugin/src/main/java/aQute/lib/json/Decoder.java
index 703331d..09b6a7f 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/Decoder.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/Decoder.java
@@ -67,7 +67,6 @@
 		return digest.digest();
 	}
 
-	@SuppressWarnings("unchecked")
 	public <T> T get(Class<T> clazz) throws Exception {
 		return (T) codec.decode(clazz, this);
 	}
@@ -80,7 +79,6 @@
 		return codec.decode(null, this);
 	}
 
-	@SuppressWarnings("unchecked")
 	public <T> T get(TypeReference<T> ref) throws Exception {
 		return (T) codec.decode(ref.getType(), this);
 	}
diff --git a/bundleplugin/src/main/java/aQute/lib/json/Handler.java b/bundleplugin/src/main/java/aQute/lib/json/Handler.java
index f002f50..c5e8b17 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/Handler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/Handler.java
@@ -7,23 +7,23 @@
 abstract class Handler {
 	abstract void encode(Encoder app, Object object, Map<Object,Type> visited) throws IOException, Exception;
 
-	Object decodeObject(Decoder isr) throws Exception {
+	Object decodeObject(@SuppressWarnings("unused") Decoder isr) throws Exception {
 		throw new UnsupportedOperationException("Cannot be mapped to object " + this);
 	}
 
-	Object decodeArray(Decoder isr) throws Exception {
+	Object decodeArray(@SuppressWarnings("unused") Decoder isr) throws Exception {
 		throw new UnsupportedOperationException("Cannot be mapped to array " + this);
 	}
 
-	Object decode(String s) throws Exception {
+	Object decode(@SuppressWarnings("unused") String s) throws Exception {
 		throw new UnsupportedOperationException("Cannot be mapped to string " + this);
 	}
 
-	Object decode(Number s) throws Exception {
+	Object decode(@SuppressWarnings("unused") Number s) throws Exception {
 		throw new UnsupportedOperationException("Cannot be mapped to number " + this);
 	}
 
-	Object decode(boolean s) {
+	Object decode(@SuppressWarnings("unused") boolean s) {
 		throw new UnsupportedOperationException("Cannot be mapped to boolean " + this);
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java b/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java
index 4a12c02..349737a 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java
@@ -49,7 +49,6 @@
 		app.append("}");
 	}
 
-	@SuppressWarnings("unchecked")
 	@Override
 	Object decodeObject(Decoder r) throws Exception {
 		assert r.current() == '{';
diff --git a/bundleplugin/src/main/java/aQute/lib/json/ObjectHandler.java b/bundleplugin/src/main/java/aQute/lib/json/ObjectHandler.java
index 50aaac0..9b297b7 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/ObjectHandler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/ObjectHandler.java
@@ -11,7 +11,7 @@
 	final Object	defaults[];
 	final Field		extra;
 
-	ObjectHandler(JSONCodec codec, Class< ? > c) throws Exception {
+	ObjectHandler(@SuppressWarnings("unused") JSONCodec codec, Class< ? > c) throws Exception {
 		rawClass = c;
 		fields = c.getFields();
 
@@ -74,7 +74,6 @@
 		app.append("}");
 	}
 
-	@SuppressWarnings("unchecked")
 	@Override
 	Object decodeObject(Decoder r) throws Exception {
 		assert r.current() == '{';
diff --git a/bundleplugin/src/main/java/aQute/lib/json/StringHandler.java b/bundleplugin/src/main/java/aQute/lib/json/StringHandler.java
index 8ebfee2..0971826 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/StringHandler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/StringHandler.java
@@ -103,7 +103,7 @@
 	 * @return
 	 * @throws Exception
 	 */
-	private Object collect(Decoder isr, char close) throws Exception {
+	private Object collect(Decoder isr, @SuppressWarnings("unused") char close) throws Exception {
 		boolean instring = false;
 		int level = 1;
 		StringBuilder sb = new StringBuilder();
diff --git a/bundleplugin/src/main/java/aQute/lib/justif/Justif.java b/bundleplugin/src/main/java/aQute/lib/justif/Justif.java
index 841d009..26319e8 100644
--- a/bundleplugin/src/main/java/aQute/lib/justif/Justif.java
+++ b/bundleplugin/src/main/java/aQute/lib/justif/Justif.java
@@ -5,7 +5,7 @@
 public class Justif {
 	int[]	tabs;
 
-	public Justif(int width, int... tabs) {
+	public Justif(@SuppressWarnings("unused") int width, int... tabs) {
 		this.tabs = tabs;
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Analyzer.java b/bundleplugin/src/main/java/aQute/lib/osgi/Analyzer.java
index 3c0c9b2..4f26d94 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Analyzer.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Analyzer.java
@@ -47,7 +47,7 @@
 
 public class Analyzer extends Processor {
 	private final SortedSet<Clazz.JAVA>				ees						= new TreeSet<Clazz.JAVA>();
-	static Manifest									bndInfo;
+	static Properties								bndInfo;
 
 	// Bundle parameters
 	private Jar										dot;
@@ -139,7 +139,7 @@
 				for (String dir : current.getDirectories().keySet()) {
 					PackageRef packageRef = getPackageRef(dir);
 					Resource resource = current.getResource(dir + "/packageinfo");
-					setPackageInfo(packageRef, resource, classpathExports);
+					getExportVersionsFromPackageInfo(packageRef, resource, classpathExports);
 				}
 			}
 
@@ -149,6 +149,7 @@
 			if (s != null) {
 				activator = getTypeRefFromFQN(s);
 				referTo(activator);
+				trace("activator %s %s", s, activator);
 			}
 
 			// Execute any plugins
@@ -560,7 +561,7 @@
 		return value.trim();
 	}
 
-	public String _bsn(String args[]) {
+	public String _bsn(@SuppressWarnings("unused") String args[]) {
 		return getBsn();
 	}
 
@@ -643,11 +644,11 @@
 	 * @return version or unknown.
 	 */
 	public String getBndVersion() {
-		return getBndInfo("Bundle-Version", "<unknown>");
+		return getBndInfo("version", "<unknown>");
 	}
 
 	public long getBndLastModified() {
-		String time = getBndInfo("Bnd-LastModified", "0");
+		String time = getBndInfo("lastmodified", "0");
 		try {
 			return Long.parseLong(time);
 		}
@@ -660,13 +661,25 @@
 	public String getBndInfo(String key, String defaultValue) {
 		if (bndInfo == null) {
 			try {
-				bndInfo = new Manifest(getClass().getResourceAsStream("META-INF/MANIFEST.MF"));
+				Properties bndInfoLocal = new Properties();
+				URL url = Analyzer.class.getResource("bnd.info");
+				if (url != null) {
+					InputStream in = url.openStream();
+					try {
+						bndInfoLocal.load(in);
+					}
+					finally {
+						in.close();
+					}
+				}
+				bndInfo = bndInfoLocal;
 			}
 			catch (Exception e) {
+				e.printStackTrace();
 				return defaultValue;
 			}
 		}
-		String value = bndInfo.getMainAttributes().getValue(key);
+		String value = bndInfo.getProperty(key);
 		if (value == null)
 			return defaultValue;
 		return value;
@@ -1345,38 +1358,43 @@
 	 * @param value
 	 * @throws Exception
 	 */
-	void setPackageInfo(PackageRef packageRef, Resource r, Packages classpathExports) throws Exception {
+	void getExportVersionsFromPackageInfo(PackageRef packageRef, Resource r, Packages classpathExports) throws Exception {
 		if (r == null)
 			return;
 
 		Properties p = new Properties();
-		InputStream in = r.openInputStream();
 		try {
-			p.load(in);
-		}
-		finally {
-			in.close();
-		}
-		Attrs map = classpathExports.get(packageRef);
-		if (map == null) {
-			classpathExports.put(packageRef, map = new Attrs());
-		}
-		for (Enumeration<String> t = (Enumeration<String>) p.propertyNames(); t.hasMoreElements();) {
-			String key = t.nextElement();
-			String value = map.get(key);
-			if (value == null) {
-				value = p.getProperty(key);
-
-				// Messy, to allow directives we need to
-				// allow the value to start with a ':' since we cannot
-				// encode this in a property name
-
-				if (value.startsWith(":")) {
-					key = key + ":";
-					value = value.substring(1);
-				}
-				map.put(key, value);
+			InputStream in = r.openInputStream();
+			try {
+				p.load(in);
 			}
+			finally {
+				in.close();
+			}
+			Attrs map = classpathExports.get(packageRef);
+			if (map == null) {
+				classpathExports.put(packageRef, map = new Attrs());
+			}
+			for (Enumeration<String> t = (Enumeration<String>) p.propertyNames(); t.hasMoreElements();) {
+				String key = t.nextElement();
+				String value = map.get(key);
+				if (value == null) {
+					value = p.getProperty(key);
+
+					// Messy, to allow directives we need to
+					// allow the value to start with a ':' since we cannot
+					// encode this in a property name
+
+					if (value.startsWith(":")) {
+						key = key + ":";
+						value = value.substring(1);
+					}
+					map.put(key, value);
+				}
+			}
+		}
+		catch (Exception e) {
+			msgs.NoSuchFile_(r);
 		}
 	}
 
@@ -1628,7 +1646,7 @@
 						// to overlap
 						if (!packageRef.isMetaData()) {
 							Resource pinfo = jar.getResource(prefix + packageRef.getPath() + "/packageinfo");
-							setPackageInfo(packageRef, pinfo, classpathExports);
+							getExportVersionsFromPackageInfo(packageRef, pinfo, classpathExports);
 						}
 					}
 				}
@@ -1677,7 +1695,7 @@
 							contained.put(packageRef);
 							if (!packageRef.isMetaData()) {
 								Resource pinfo = jar.getResource(prefix + packageRef.getPath() + "/packageinfo");
-								setPackageInfo(packageRef, pinfo, classpathExports);
+								getExportVersionsFromPackageInfo(packageRef, pinfo, classpathExports);
 							}
 						}
 						if (info != null)
@@ -1903,39 +1921,12 @@
 	final static String	DEFAULT_PROVIDER_POLICY	= "${range;[==,=+)}";
 	final static String	DEFAULT_CONSUMER_POLICY	= "${range;[==,+)}";
 
-	@SuppressWarnings("deprecation")
 	public String getVersionPolicy(boolean implemented) {
 		if (implemented) {
-			String s = getProperty(PROVIDER_POLICY);
-			if (s != null)
-				return s;
-
-			s = getProperty(VERSIONPOLICY_IMPL);
-			if (s != null)
-				return s;
-
-			return getProperty(VERSIONPOLICY, DEFAULT_PROVIDER_POLICY);
+			return getProperty(PROVIDER_POLICY, DEFAULT_PROVIDER_POLICY);
 		}
-		String s = getProperty(CONSUMER_POLICY);
-		if (s != null)
-			return s;
 
-		s = getProperty(VERSIONPOLICY_USES);
-		if (s != null)
-			return s;
-
-		return getProperty(VERSIONPOLICY, DEFAULT_CONSUMER_POLICY);
-
-		// String vp = implemented ? getProperty(VERSIONPOLICY_IMPL) :
-		// getProperty(VERSIONPOLICY_USES);
-		//
-		// if (vp != null)
-		// return vp;
-		//
-		// if (implemented)
-		// return getProperty(VERSIONPOLICY_IMPL, "{$range;[==,=+}");
-		// else
-		// return getProperty(VERSIONPOLICY, "${range;[==,+)}");
+		return getProperty(CONSUMER_POLICY, DEFAULT_CONSUMER_POLICY);
 	}
 
 	/**
@@ -2303,7 +2294,7 @@
 		return ees.first();
 	}
 
-	public String _ee(String args[]) {
+	public String _ee(@SuppressWarnings("unused") String args[]) {
 		return getLowestEE().getEE();
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
index b44118a..f49d6e3 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
@@ -247,7 +247,7 @@
 		changedFile(f);
 	}
 
-	protected void changedFile(File f) {}
+	protected void changedFile(@SuppressWarnings("unused") File f) {}
 
 	/**
 	 * Sign the jar file. -sign : <alias> [ ';' 'password:=' <password> ] [ ';'
@@ -256,7 +256,7 @@
 	 * @return
 	 */
 
-	void sign(Jar jar) throws Exception {
+	void sign(@SuppressWarnings("unused") Jar jar) throws Exception {
 		String signing = getProperty("-sign");
 		if (signing == null)
 			return;
@@ -283,6 +283,7 @@
 		Parameters conditionals = getParameters(CONDITIONAL_PACKAGE);
 		if (conditionals.isEmpty())
 			return null;
+		trace("do Conditional Package %s", conditionals);
 		Instructions instructions = new Instructions(conditionals);
 
 		Collection<PackageRef> referred = instructions.select(getReferred().keySet(), false);
@@ -294,7 +295,10 @@
 			for (Jar cpe : getClasspath()) {
 				Map<String,Resource> map = cpe.getDirectories().get(pref.getPath());
 				if (map != null) {
-					jar.addDirectory(map, false);
+					copy(jar, cpe, pref.getPath(), false);
+// Now use copy so that bnd.info is processed, next line should be 
+// removed in the future TODO
+//					jar.addDirectory(map, false);
 					break;
 				}
 			}
@@ -429,7 +433,7 @@
 		return sourcePath;
 	}
 
-	private void doVerify(Jar dot) throws Exception {
+	private void doVerify(@SuppressWarnings("unused") Jar dot) throws Exception {
 		Verifier verifier = new Verifier(this);
 		// Give the verifier the benefit of our analysis
 		// prevents parsing the files twice
@@ -608,8 +612,18 @@
 	 * @param overwriteResource
 	 */
 	private void copy(Jar dest, Jar srce, String path, boolean overwrite) {
+		trace("copy d=" + dest + " s=" + srce +" p="+ path);
 		dest.copy(srce, path, overwrite);
-
+		
+		// bnd.info sources must be preprocessed
+		String bndInfoPath = path + "/bnd.info";
+		Resource r = dest.getResource(bndInfoPath);
+		if ( r != null && !(r instanceof PreprocessResource)) {
+			trace("preprocessing bnd.info");
+			PreprocessResource pp = new PreprocessResource(this, r);
+			dest.putResource(bndInfoPath, pp);
+		}
+		
 		if (hasSources()) {
 			String srcPath = "OSGI-OPT/src/" + path;
 			Map<String,Resource> srcContents = srce.getDirectories().get(srcPath);
@@ -926,7 +940,7 @@
 		}
 	}
 
-	private void noSuchFile(Jar jar, String clause, Map<String,String> extra, String source, String destinationPath)
+	private void noSuchFile(Jar jar, @SuppressWarnings("unused") String clause, Map<String,String> extra, String source, String destinationPath)
 			throws Exception {
 		Jar src = getJarFromName(source, "Include-Resource " + source);
 		if (src != null) {
@@ -1398,7 +1412,7 @@
 	 * @throws Exception
 	 */
 
-	public void doDiff(Jar dot) throws Exception {
+	public void doDiff(@SuppressWarnings("unused") Jar dot) throws Exception {
 		Parameters diffs = parseHeader(getProperty("-diff"));
 		if (diffs.isEmpty())
 			return;
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/ClassDataCollector.java b/bundleplugin/src/main/java/aQute/lib/osgi/ClassDataCollector.java
index 7c3d759..04a8406 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/ClassDataCollector.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/ClassDataCollector.java
@@ -3,30 +3,30 @@
 import aQute.lib.osgi.Descriptors.TypeRef;
 
 public class ClassDataCollector {
-	public void classBegin(int access, TypeRef name) {}
+	public void classBegin(@SuppressWarnings("unused") int access, @SuppressWarnings("unused") TypeRef name) {}
 
 	public boolean classStart(int access, TypeRef className) {
 		classBegin(access, className);
 		return true;
 	}
 
-	public void extendsClass(TypeRef zuper) throws Exception {}
+	public void extendsClass(@SuppressWarnings("unused") TypeRef zuper) throws Exception {}
 
-	public void implementsInterfaces(TypeRef[] interfaces) throws Exception {}
+	public void implementsInterfaces(@SuppressWarnings("unused") TypeRef[] interfaces) throws Exception {}
 
-	public void addReference(TypeRef ref) {}
+	public void addReference(@SuppressWarnings("unused") TypeRef ref) {}
 
-	public void annotation(Annotation annotation) {}
+	public void annotation(@SuppressWarnings("unused") Annotation annotation) {}
 
-	public void parameter(int p) {}
+	public void parameter(@SuppressWarnings("unused") int p) {}
 
-	public void method(Clazz.MethodDef defined) {}
+	public void method(@SuppressWarnings("unused") Clazz.MethodDef defined) {}
 
-	public void field(Clazz.FieldDef defined) {}
+	public void field(@SuppressWarnings("unused") Clazz.FieldDef defined) {}
 
-	public void reference(Clazz.MethodDef referenced) {}
+	public void reference(@SuppressWarnings("unused") Clazz.MethodDef referenced) {}
 
-	public void reference(Clazz.FieldDef referenced) {}
+	public void reference(@SuppressWarnings("unused") Clazz.FieldDef referenced) {}
 
 	public void classEnd() throws Exception {}
 
@@ -61,21 +61,24 @@
 	 *            The access flags
 	 * @throws Exception
 	 */
-	public void innerClass(TypeRef innerClass, TypeRef outerClass, String innerName, int innerClassAccessFlags)
+	public void innerClass(TypeRef innerClass, TypeRef outerClass, String innerName, @SuppressWarnings("unused") int innerClassAccessFlags)
 			throws Exception {}
 
-	public void signature(String signature) {}
+	public void signature(@SuppressWarnings("unused") String signature) {}
 
-	public void constant(Object object) {}
+	public void constant(@SuppressWarnings("unused") Object object) {}
 
 	public void memberEnd() {}
 
-	public void version(int minor, int major) {
+	public void version(@SuppressWarnings("unused") int minor, @SuppressWarnings("unused") int major) {
 		// TODO Auto-generated method stub
 
 	}
 
-	public void referenceMethod(int access, TypeRef className, String method, String descriptor) {
+	public void referenceMethod(@SuppressWarnings("unused")
+	int access, @SuppressWarnings("unused")
+	TypeRef className, @SuppressWarnings("unused")
+	String method, @SuppressWarnings("unused") String descriptor) {
 		// TODO Auto-generated method stub
 
 	}
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java b/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java
index d54b084..87ce8a5 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java
@@ -69,7 +69,7 @@
 			return major >= J2SE5.major;
 		}
 
-		public static JAVA getJava(int major, int minor) {
+		public static JAVA getJava(int major, @SuppressWarnings("unused") int minor) {
 			for (JAVA j : JAVA.values()) {
 				if (j.major == major)
 					return j;
@@ -629,7 +629,7 @@
 			pool[poolIndex] = intPool[poolIndex];
 	}
 
-	protected void pool(Object[] pool, int[] intPool) {}
+	protected void pool(@SuppressWarnings("unused") Object[] pool, @SuppressWarnings("unused") int[] intPool) {}
 
 	/**
 	 * @param in
@@ -1127,8 +1127,7 @@
 		}
 		if (collect)
 			return new Annotation(name, elements, member, policy);
-		else
-			return null;
+		return null;
 	}
 
 	private Object doElementValue(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect)
@@ -1572,8 +1571,7 @@
 	public int getAccess() {
 		if (innerAccess == -1)
 			return accessx;
-		else
-			return innerAccess;
+		return innerAccess;
 	}
 
 	public TypeRef getClassName() {
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java b/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java
index 0fa43fe..eefc1e2 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java
@@ -48,7 +48,7 @@
 				cmd.var("PATH", path);
 				domain.trace("PATH: %s", path);
 			}
-			OutputStreamWriter osw = new OutputStreamWriter(out);
+			OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8");
 			int result = cmd.execute(command, stdout, errors);
 			osw.append(stdout);
 			osw.flush();
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java b/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
index df0bbf0..7454087 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
@@ -52,6 +52,11 @@
 	String							CREATED_BY									= "Created-By";
 	String							TOOL										= "Tool";
 	String							TESTCASES									= "Test-Cases";
+        /**
+         * @deprecated Use {@link Constants#TESTCASES}.
+         */
+        @Deprecated
+        String                                                  TESTSUITES                                                                      = "Test-Suites";
 	String							SIGNATURE_TEST								= "-signaturetest";
 
 	String							headers[]									= {
@@ -90,8 +95,6 @@
 	String							NOEXTRAHEADERS								= "-noextraheaders";
 	String							NOMANIFEST									= "-nomanifest";
 	String							NOUSES										= "-nouses";
-	@Deprecated
-	String							NOPE										= "-nope";
 	String							NOBUNDLES									= "-nobundles";
 	String							PEDANTIC									= "-pedantic";
 	String							PLUGIN										= "-plugin";
@@ -106,6 +109,7 @@
 	String							RUNPROPERTIES								= "-runproperties";
 	String							RUNSYSTEMPACKAGES							= "-runsystempackages";
 	String							RUNBUNDLES									= "-runbundles";
+	String							RUNREPOS									= "-runrepos";
 	String							RUNPATH										= "-runpath";
 	String							RUNSTORAGE									= "-runstorage";
 	String							RUNBUILDS									= "-runbuilds";
@@ -126,14 +130,8 @@
 	String							TESTCONTINUOUS								= "-testcontinuous";
 	String							UNDERTEST									= "-undertest";
 	String							VERBOSE										= "-verbose";
-	@Deprecated
-	String							VERSIONPOLICY_IMPL							= "-versionpolicy-impl";
-	@Deprecated
-	String							VERSIONPOLICY_USES							= "-versionpolicy-uses";
 	String							PROVIDER_POLICY								= "-provider-policy";
 	String							CONSUMER_POLICY								= "-consumer-policy";
-	@Deprecated
-	String							VERSIONPOLICY								= "-versionpolicy";
 	String							WAB											= "-wab";
 	String							WABLIB										= "-wablib";
 	String							REQUIRE_BND									= "-require-bnd";
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java b/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java
index c92de90..bfb80bb 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java
@@ -88,8 +88,8 @@
 							sb.append("(\\..*)?");
 							wildcards = true;
 							break loop;
-						} else
-							sb.append("\\.");
+						}
+						sb.append("\\.");
 
 						break;
 					case '*' :
@@ -130,8 +130,7 @@
 
 		if (literal)
 			return match.equals(value);
-		else
-			return getMatcher(value).matches();
+		return getMatcher(value).matches();
 	}
 
 	public boolean isNegated() {
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Instructions.java b/bundleplugin/src/main/java/aQute/lib/osgi/Instructions.java
index 679c374..6ac56dd 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Instructions.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Instructions.java
@@ -48,7 +48,7 @@
 		if (map == null)
 			return false;
 
-		return map.containsKey((Instruction) name);
+		return map.containsKey(name);
 	}
 
 	public boolean containsValue(Attrs value) {
@@ -64,7 +64,7 @@
 		if (map == null)
 			return false;
 
-		return map.containsValue((Attrs) value);
+		return map.containsValue(value);
 	}
 
 	public Set<java.util.Map.Entry<Instruction,Attrs>> entrySet() {
@@ -80,7 +80,7 @@
 		if (map == null)
 			return null;
 
-		return map.get((Instruction) key);
+		return map.get(key);
 	}
 
 	public Attrs get(Instruction key) {
@@ -109,11 +109,11 @@
 	}
 
 	public void putAll(Map< ? extends Instruction, ? extends Attrs> map) {
-		if (this.map == null)
+		if (this.map == null) {
 			if (map.isEmpty())
 				return;
-			else
-				this.map = new LinkedHashMap<Instruction,Attrs>();
+			this.map = new LinkedHashMap<Instruction,Attrs>();
+		}
 		this.map.putAll(map);
 	}
 
@@ -123,7 +123,7 @@
 		if (map == null)
 			return null;
 
-		return map.remove((Instruction) var0);
+		return map.remove(var0);
 	}
 
 	public Attrs remove(Instruction var0) {
@@ -212,8 +212,7 @@
 			if (i.matches(value)) {
 				if (i.isNegated())
 					return false; // we deny this one explicitly
-				else
-					return true; // we allow it explicitly
+				return true; // we allow it explicitly
 			}
 		}
 		return false;
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java b/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
index 032a7d1..76a4b0d 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
@@ -78,11 +78,9 @@
 		String name = f.getName();
 		if (name.equals("bin") || name.equals("src"))
 			return f.getParentFile().getName();
-		else {
-			if (name.endsWith(".jar"))
-				name = name.substring(0, name.length() - 4);
-			return name;
-		}
+		if (name.endsWith(".jar"))
+			name = name.substring(0, name.length() - 4);
+		return name;
 	}
 
 	public Jar(String string, InputStream resourceAsStream) throws IOException {
@@ -455,7 +453,7 @@
 
 			for (Map.Entry< ? , ? > entry : org.getAttributes(name).entrySet()) {
 				String nice = clean((String) entry.getValue());
-				attrs.put((Attributes.Name) entry.getKey(), nice);
+				attrs.put(entry.getKey(), nice);
 			}
 		}
 		return result;
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java b/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java
index 36c16dd..3e109d6 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java
@@ -190,8 +190,7 @@
 			Processor parent = source.start.getParent();
 			if (parent != null)
 				return parent.getProperty(varname);
-			else
-				return null;
+			return null;
 		}
 
 		Processor rover = domain;
@@ -262,11 +261,11 @@
 		return Processor.join(set, ",");
 	}
 
-	public String _pathseparator(String args[]) {
+	public String _pathseparator(@SuppressWarnings("unused") String args[]) {
 		return File.pathSeparator;
 	}
 
-	public String _separator(String args[]) {
+	public String _separator(@SuppressWarnings("unused") String args[]) {
 		return File.separator;
 	}
 
@@ -331,11 +330,10 @@
 
 		if (args.length > 3)
 			return args[3];
-		else
-			return "";
+		return "";
 	}
 
-	public String _now(String args[]) {
+	public String _now(@SuppressWarnings("unused") String args[]) {
 		return new Date().toString();
 	}
 
@@ -476,19 +474,18 @@
 		if (args.length < 2) {
 			domain.warning("Need at least one file name for ${dir;...}");
 			return null;
-		} else {
-			String del = "";
-			StringBuilder sb = new StringBuilder();
-			for (int i = 1; i < args.length; i++) {
-				File f = domain.getFile(args[i]);
-				if (f.exists() && f.getParentFile().exists()) {
-					sb.append(del);
-					sb.append(f.getParentFile().getAbsolutePath());
-					del = ",";
-				}
-			}
-			return sb.toString();
 		}
+		String del = "";
+		StringBuilder sb = new StringBuilder();
+		for (int i = 1; i < args.length; i++) {
+			File f = domain.getFile(args[i]);
+			if (f.exists() && f.getParentFile().exists()) {
+				sb.append(del);
+				sb.append(f.getParentFile().getAbsolutePath());
+				del = ",";
+			}
+		}
+		return sb.toString();
 
 	}
 
@@ -496,19 +493,18 @@
 		if (args.length < 2) {
 			domain.warning("Need at least one file name for ${basename;...}");
 			return null;
-		} else {
-			String del = "";
-			StringBuilder sb = new StringBuilder();
-			for (int i = 1; i < args.length; i++) {
-				File f = domain.getFile(args[i]);
-				if (f.exists() && f.getParentFile().exists()) {
-					sb.append(del);
-					sb.append(f.getName());
-					del = ",";
-				}
-			}
-			return sb.toString();
 		}
+		String del = "";
+		StringBuilder sb = new StringBuilder();
+		for (int i = 1; i < args.length; i++) {
+			File f = domain.getFile(args[i]);
+			if (f.exists() && f.getParentFile().exists()) {
+				sb.append(del);
+				sb.append(f.getName());
+				del = ",";
+			}
+		}
+		return sb.toString();
 
 	}
 
@@ -516,14 +512,13 @@
 		if (args.length < 2) {
 			domain.warning("Need at least one file name for ${isfile;...}");
 			return null;
-		} else {
-			boolean isfile = true;
-			for (int i = 1; i < args.length; i++) {
-				File f = new File(args[i]).getAbsoluteFile();
-				isfile &= f.isFile();
-			}
-			return isfile ? "true" : "false";
 		}
+		boolean isfile = true;
+		for (int i = 1; i < args.length; i++) {
+			File f = new File(args[i]).getAbsoluteFile();
+			isfile &= f.isFile();
+		}
+		return isfile ? "true" : "false";
 
 	}
 
@@ -531,14 +526,13 @@
 		if (args.length < 2) {
 			domain.warning("Need at least one file name for ${isdir;...}");
 			return null;
-		} else {
-			boolean isdir = true;
-			for (int i = 1; i < args.length; i++) {
-				File f = new File(args[i]).getAbsoluteFile();
-				isdir &= f.isDirectory();
-			}
-			return isdir ? "true" : "false";
 		}
+		boolean isdir = true;
+		for (int i = 1; i < args.length; i++) {
+			File f = new File(args[i]).getAbsoluteFile();
+			isdir &= f.isDirectory();
+		}
+		return isdir ? "true" : "false";
 
 	}
 
@@ -611,7 +605,7 @@
 		return Processor.join(result, ",");
 	}
 
-	public String _currenttime(String args[]) {
+	public String _currenttime(@SuppressWarnings("unused") String args[]) {
 		return Long.toString(System.currentTimeMillis());
 	}
 
@@ -849,7 +843,7 @@
 		}
 	}
 
-	public static void verifyCommand(String args[], String help, Pattern[] patterns, int low, int high) {
+	public static void verifyCommand(String args[], @SuppressWarnings("unused") String help, Pattern[] patterns, int low, int high) {
 		String message = "";
 		if (args.length > high) {
 			message = "too many arguments";
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Packages.java b/bundleplugin/src/main/java/aQute/lib/osgi/Packages.java
index fae4597..64e4a4e 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Packages.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Packages.java
@@ -35,7 +35,7 @@
 		if (map == null)
 			return false;
 
-		return map.containsKey((PackageRef) name);
+		return map.containsKey(name);
 	}
 
 	public boolean containsValue(Attrs value) {
@@ -51,7 +51,7 @@
 		if (map == null)
 			return false;
 
-		return map.containsValue((Attrs) value);
+		return map.containsValue(value);
 	}
 
 	public Set<java.util.Map.Entry<PackageRef,Attrs>> entrySet() {
@@ -67,7 +67,7 @@
 		if (map == null)
 			return null;
 
-		return map.get((PackageRef) key);
+		return map.get(key);
 	}
 
 	public Attrs get(PackageRef key) {
@@ -106,11 +106,11 @@
 	}
 
 	public void putAll(Map< ? extends PackageRef, ? extends Attrs> map) {
-		if (this.map == null)
+		if (this.map == null) {
 			if (map.isEmpty())
 				return;
-			else
-				this.map = new LinkedHashMap<PackageRef,Attrs>();
+			this.map = new LinkedHashMap<PackageRef,Attrs>();
+		}
 		this.map.putAll(map);
 	}
 
@@ -127,7 +127,7 @@
 		if (map == null)
 			return null;
 
-		return map.remove((PackageRef) var0);
+		return map.remove(var0);
 	}
 
 	public Attrs remove(PackageRef var0) {
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java b/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
index d7179d6..608e9a0 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
@@ -58,9 +58,9 @@
 		properties = new Properties(parent);
 	}
 
-	public Processor(Processor parent) {
-		this(parent.properties);
-		this.parent = parent;
+	public Processor(Processor child) {
+		this(child.properties);
+		this.parent = child;
 	}
 
 	public void setParent(Processor processor) {
@@ -77,8 +77,7 @@
 	public Processor getTop() {
 		if (parent == null)
 			return this;
-		else
-			return parent.getTop();
+		return parent.getTop();
 	}
 
 	public void getInfo(Reporter processor, String prefix) {
@@ -111,8 +110,7 @@
 		Processor p = current.get();
 		if (p == null)
 			return this;
-		else
-			return p;
+		return p;
 	}
 
 	public void warning(String string, Object... args) {
@@ -338,7 +336,7 @@
 				key = removeDuplicateMarker(key);
 
 				try {
-					Class< ? > c = (Class< ? >) loader.loadClass(key);
+					Class< ? > c = loader.loadClass(key);
 					Object plugin = c.newInstance();
 					customize(plugin, entry.getValue());
 					list.add(plugin);
@@ -453,7 +451,7 @@
 		toBeClosed.clear();
 	}
 
-	public String _basedir(String args[]) {
+	public String _basedir(@SuppressWarnings("unused") String args[]) {
 		if (base == null)
 			throw new IllegalArgumentException("No base dir set");
 
@@ -859,7 +857,7 @@
 		return printClauses(exports, false);
 	}
 
-	public static String printClauses(Map< ? , ? extends Map< ? , ? >> exports, boolean checkMultipleVersions)
+	public static String printClauses(Map< ? , ? extends Map< ? , ? >> exports, @SuppressWarnings("unused") boolean checkMultipleVersions)
 			throws IOException {
 		StringBuilder sb = new StringBuilder();
 		String del = "";
@@ -919,8 +917,7 @@
 	public Macro getReplacer() {
 		if (replacer == null)
 			return replacer = new Macro(this, getMacroDomains());
-		else
-			return replacer;
+		return replacer;
 	}
 
 	/**
@@ -961,7 +958,7 @@
 		return result;
 	}
 
-	public boolean updateModified(long time, String reason) {
+	public boolean updateModified(long time, @SuppressWarnings("unused") String reason) {
 		if (time > lastModified) {
 			lastModified = time;
 			return true;
@@ -1127,8 +1124,7 @@
 	public String normalize(String f) {
 		if (f.startsWith(base.getAbsolutePath() + "/"))
 			return f.substring(base.getAbsolutePath().length() + 1);
-		else
-			return f;
+		return f;
 	}
 
 	public String normalize(File f) {
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Verifier.java b/bundleplugin/src/main/java/aQute/lib/osgi/Verifier.java
index 27026ae..2759049 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Verifier.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Verifier.java
@@ -42,7 +42,7 @@
 		String	name;
 		int		target;
 
-		EE(String name, int source, int target) {
+		EE(String name, @SuppressWarnings("unused") int source, int target) {
 			this.name = name;
 			this.target = target;
 		}
@@ -488,7 +488,7 @@
 		}
 	}
 
-	private void verifyType(Attrs.Type type, String string) {
+	private void verifyType(@SuppressWarnings("unused") Attrs.Type type, @SuppressWarnings("unused") String string) {
 
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java b/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
index 2244fd2..cb511e7 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
@@ -50,7 +50,7 @@
 	 * @throws IOException
 	 */
 
-	public EclipseClasspath(Reporter reporter, File workspace, File project, int options) throws Exception {
+	public EclipseClasspath(Reporter reporter, File workspace, File project, @SuppressWarnings("unused") int options) throws Exception {
 		this.project = project.getCanonicalFile();
 		this.workspace = workspace.getCanonicalFile();
 		this.reporter = reporter;
@@ -184,8 +184,8 @@
 				File b = new File(base);
 				File f = new File(b, remainder.replace('/', File.separatorChar));
 				return f;
-			} else
-				reporter.error("Can't find replacement variable for: " + path);
+			}
+			reporter.error("Can't find replacement variable for: " + path);
 		} else
 			reporter.error("Cant split variable path: " + path);
 		return null;
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReq.java b/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReq.java
new file mode 100644
index 0000000..3f07825
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReq.java
@@ -0,0 +1,97 @@
+package aQute.lib.osgi.resource;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+class CapReq implements Capability, Requirement {
+	
+	static enum MODE { Capability, Requirement }
+	
+	private final MODE mode;
+	private final String	namespace;
+	private final Resource	resource;
+	private final Map<String,String>	directives;
+	private final Map<String,Object>	attributes;
+
+	CapReq(MODE mode, String namespace, Resource resource, Map<String, String> directives, Map<String, Object> attributes) {
+		this.mode = mode;
+		this.namespace = namespace;
+		this.resource = resource;
+		this.directives = new HashMap<String,String>(directives);
+		this.attributes = new HashMap<String,Object>(attributes);
+	}
+
+	public String getNamespace() {
+		return namespace;
+	}
+
+	public Map<String,String> getDirectives() {
+		return Collections.unmodifiableMap(directives);
+	}
+
+	public Map<String,Object> getAttributes() {
+		return Collections.unmodifiableMap(attributes);
+	}
+
+	public Resource getResource() {
+		return resource;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
+		result = prime * result + ((directives == null) ? 0 : directives.hashCode());
+		result = prime * result + ((mode == null) ? 0 : mode.hashCode());
+		result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
+		result = prime * result + ((resource == null) ? 0 : resource.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		CapReq other = (CapReq) obj;
+		if (attributes == null) {
+			if (other.attributes != null)
+				return false;
+		} else if (!attributes.equals(other.attributes))
+			return false;
+		if (directives == null) {
+			if (other.directives != null)
+				return false;
+		} else if (!directives.equals(other.directives))
+			return false;
+		if (mode != other.mode)
+			return false;
+		if (namespace == null) {
+			if (other.namespace != null)
+				return false;
+		} else if (!namespace.equals(other.namespace))
+			return false;
+		if (resource == null) {
+			if (other.resource != null)
+				return false;
+		} else if (!resource.equals(other.resource))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return mode + " [namespace=" + namespace + ", resource=" + System.identityHashCode(resource) + ", directives="
+				+ directives + ", attributes=" + attributes + "]";
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReqBuilder.java b/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReqBuilder.java
new file mode 100644
index 0000000..654c7de
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReqBuilder.java
@@ -0,0 +1,74 @@
+package aQute.lib.osgi.resource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+import aQute.lib.osgi.resource.CapReq.MODE;
+import aQute.libg.filters.AndFilter;
+import aQute.libg.filters.Filter;
+import aQute.libg.filters.SimpleFilter;
+import aQute.libg.version.VersionRange;
+
+public class CapReqBuilder {
+
+	private final String				namespace;
+	private Resource					resource;
+	private final Map<String,Object>	attributes	= new HashMap<String,Object>();
+	private final Map<String,String>	directives	= new HashMap<String,String>();
+
+	public CapReqBuilder(String namespace) {
+		this.namespace = namespace;
+	}
+	
+	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);
+		return this;
+	}
+
+	public CapReqBuilder addDirective(String name, String value) {
+		directives.put(name, value);
+		return this;
+	}
+	
+	public Capability buildCapability() {
+		// TODO check the thrown exception
+		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.");
+		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, VersionRange range) {
+		Filter filter;
+		SimpleFilter pkgNameFilter = new SimpleFilter(PackageNamespace.PACKAGE_NAMESPACE, pkgName);
+		if (range != null)
+			filter = new AndFilter().addChild(pkgNameFilter).addChild(Filters.fromVersionRange(range));
+		else
+			filter = pkgNameFilter;
+		
+		return new CapReqBuilder(PackageNamespace.PACKAGE_NAMESPACE).addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString());
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/resource/Filters.java b/bundleplugin/src/main/java/aQute/lib/osgi/resource/Filters.java
new file mode 100644
index 0000000..20af48d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/resource/Filters.java
@@ -0,0 +1,42 @@
+package aQute.lib.osgi.resource;
+
+import aQute.libg.filters.AndFilter;
+import aQute.libg.filters.Filter;
+import aQute.libg.filters.NotFilter;
+import aQute.libg.filters.Operator;
+import aQute.libg.filters.SimpleFilter;
+import aQute.libg.version.VersionRange;
+
+public class Filters {
+	
+	public static Filter fromVersionRange(VersionRange range) {
+		return fromVersionRange(range, "version");
+	}
+	
+	public static Filter fromVersionRange(VersionRange range, @SuppressWarnings("unused") String versionAttr) {
+		if (range == null)
+			return null;
+		
+		Filter left;
+		if (range.includeLow())
+			left = new SimpleFilter("version", Operator.GreaterThanOrEqual, range.getLow().toString());
+		else
+			left = new NotFilter(new SimpleFilter("version", Operator.LessThanOrEqual, range.getLow().toString()));
+		
+		Filter right;
+		if (!range.isRange())
+			right = null;
+		else if (range.includeHigh())
+			right = new SimpleFilter("version", Operator.LessThanOrEqual, range.getHigh().toString());
+		else
+			right = new NotFilter(new SimpleFilter("version", Operator.GreaterThanOrEqual, range.getHigh().toString()));
+		
+		Filter result;
+		if (right != null)
+			result = new AndFilter().addChild(left).addChild(right);
+		else
+			result = left;
+		
+		return result;
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceBuilder.java b/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceBuilder.java
new file mode 100644
index 0000000..cd9b982
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceBuilder.java
@@ -0,0 +1,48 @@
+package aQute.lib.osgi.resource;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class ResourceBuilder {
+
+	private final ResourceImpl		resource		= new ResourceImpl();
+	private final List<Capability>	capabilities	= new LinkedList<Capability>();
+	private final List<Requirement>	requirements	= new LinkedList<Requirement>();
+
+	private boolean					built			= false;
+
+	public ResourceBuilder addCapability(CapReqBuilder builder) {
+		if (built)
+			throw new IllegalStateException("Resource already built");
+
+		Capability cap = builder.setResource(resource).buildCapability();
+		capabilities.add(cap);
+
+		return this;
+	}
+
+	public ResourceBuilder addRequirement(CapReqBuilder builder) {
+		if (built)
+			throw new IllegalStateException("Resource already built");
+
+		Requirement req = builder.setResource(resource).buildRequirement();
+		requirements.add(req);
+
+		return this;
+	}
+
+	public Resource build() {
+		if (built)
+			throw new IllegalStateException("Resource already built");
+		built = true;
+
+		resource.setCapabilities(capabilities);
+		resource.setRequirements(requirements);
+		return resource;
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceImpl.java b/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceImpl.java
new file mode 100644
index 0000000..3fa6218
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceImpl.java
@@ -0,0 +1,67 @@
+package aQute.lib.osgi.resource;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+class ResourceImpl implements Resource {
+
+	private List<Capability>				allCapabilities;
+	private Map<String,List<Capability>>	capabilityMap;
+
+	private List<Requirement>				allRequirements;
+	private Map<String,List<Requirement>>	requirementMap;
+
+	void setCapabilities(List<Capability> capabilities) {
+		allCapabilities = capabilities;
+
+		capabilityMap = new HashMap<String,List<Capability>>();
+		for (Capability capability : capabilities) {
+			List<Capability> list = capabilityMap.get(capability.getNamespace());
+			if (list == null) {
+				list = new LinkedList<Capability>();
+				capabilityMap.put(capability.getNamespace(), list);
+			}
+			list.add(capability);
+		}
+	}
+
+	public List<Capability> getCapabilities(String namespace) {
+		return namespace == null ? allCapabilities : capabilityMap.get(namespace);
+	}
+
+	void setRequirements(List<Requirement> requirements) {
+		allRequirements = requirements;
+
+		requirementMap = new HashMap<String,List<Requirement>>();
+		for (Requirement requirement : requirements) {
+			List<Requirement> list = requirementMap.get(requirement.getNamespace());
+			if (list == null) {
+				list = new LinkedList<Requirement>();
+				requirementMap.put(requirement.getNamespace(), list);
+			}
+			list.add(requirement);
+		}
+	}
+
+	public List<Requirement> getRequirements(String namespace) {
+		return namespace == null ? allRequirements : requirementMap.get(namespace);
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("ResourceImpl [caps=");
+		builder.append(allCapabilities);
+		builder.append(", reqs=");
+		builder.append(allRequirements);
+		builder.append("]");
+		return builder.toString();
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/resource/packageinfo b/bundleplugin/src/main/java/aQute/lib/osgi/resource/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/resource/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/BadLocationException.java b/bundleplugin/src/main/java/aQute/lib/properties/BadLocationException.java
new file mode 100644
index 0000000..2d9207d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/BadLocationException.java
@@ -0,0 +1,14 @@
+package aQute.lib.properties;
+
+public class BadLocationException extends Exception {
+
+	private static final long	serialVersionUID	= 1L;
+
+	public BadLocationException() {
+		super();
+	}
+
+	public BadLocationException(String var0) {
+		super(var0);
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java b/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java
new file mode 100644
index 0000000..77bb9f6
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java
@@ -0,0 +1,147 @@
+package aQute.lib.properties;
+
+/**
+ * Copy-on-write <code>ITextStore</code> wrapper.
+ * <p>
+ * This implementation uses an unmodifiable text store for the initial content.
+ * Upon first modification attempt, the unmodifiable store is replaced with a
+ * modifiable instance which must be supplied in the constructor.
+ * </p>
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * 
+ * @since 3.2
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class CopyOnWriteTextStore implements ITextStore {
+
+	/**
+	 * An unmodifiable String based text store. It is not possible to modify the
+	 * content other than using {@link #set}. Trying to {@link #replace} a text
+	 * range will throw an <code>UnsupportedOperationException</code>.
+	 */
+	private static class StringTextStore implements ITextStore {
+
+		/** Represents the content of this text store. */
+		private String	fText	= "";	//$NON-NLS-1$
+
+		/**
+		 * Create an empty text store.
+		 */
+		private StringTextStore() {
+			super();
+		}
+
+		/**
+		 * Create a text store with initial content.
+		 * 
+		 * @param text
+		 *            the initial content
+		 */
+		private StringTextStore(String text) {
+			super();
+			set(text);
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.ITextStore#get(int)
+		 */
+		public char get(int offset) {
+			return fText.charAt(offset);
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.ITextStore#get(int, int)
+		 */
+		public String get(int offset, int length) {
+			return fText.substring(offset, offset + length);
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.ITextStore#getLength()
+		 */
+		public int getLength() {
+			return fText.length();
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.ITextStore#replace(int, int,
+		 * java.lang.String)
+		 */
+		public void replace(int offset, int length, String text) {
+			// modification not supported
+			throw new UnsupportedOperationException();
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+		 */
+		public void set(String text) {
+			fText = text != null ? text : ""; //$NON-NLS-1$
+		}
+
+	}
+
+	/** The underlying "real" text store */
+	protected ITextStore		fTextStore	= new StringTextStore();
+
+	/** A modifiable <code>ITextStore</code> instance */
+	private final ITextStore	fModifiableTextStore;
+
+	/**
+	 * Creates an empty text store. The given text store will be used upon first
+	 * modification attempt.
+	 * 
+	 * @param modifiableTextStore
+	 *            a modifiable <code>ITextStore</code> instance, may not be
+	 *            <code>null</code>
+	 */
+	public CopyOnWriteTextStore(ITextStore modifiableTextStore) {
+		fTextStore = new StringTextStore();
+		fModifiableTextStore = modifiableTextStore;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#get(int)
+	 */
+	public char get(int offset) {
+		return fTextStore.get(offset);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#get(int, int)
+	 */
+	public String get(int offset, int length) {
+		return fTextStore.get(offset, length);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#getLength()
+	 */
+	public int getLength() {
+		return fTextStore.getLength();
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#replace(int, int,
+	 * java.lang.String)
+	 */
+	public void replace(int offset, int length, String text) {
+		if (fTextStore != fModifiableTextStore) {
+			String content = fTextStore.get(0, fTextStore.getLength());
+			fTextStore = fModifiableTextStore;
+			fTextStore.set(content);
+		}
+		fTextStore.replace(offset, length, text);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+	 */
+	public void set(String text) {
+		fTextStore = new StringTextStore(text);
+		fModifiableTextStore.set(""); //$NON-NLS-1$
+	}
+
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/Document.java b/bundleplugin/src/main/java/aQute/lib/properties/Document.java
new file mode 100644
index 0000000..4df75a6
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/Document.java
@@ -0,0 +1,59 @@
+package aQute.lib.properties;
+
+public class Document implements IDocument {
+
+	public final static String[]	DELIMITERS	= {
+			"\r", "\n", "\r\n"
+												};
+
+	private LineTracker				lineTracker	= new LineTracker();
+	private ITextStore				textStore	= new CopyOnWriteTextStore(new GapTextStore());
+
+	public Document(String text) {
+		setText(text);
+	}
+
+	public int getNumberOfLines() {
+		return lineTracker.getNumberOfLines();
+	}
+
+	public IRegion getLineInformation(int line) throws BadLocationException {
+		return lineTracker.getLineInformation(line);
+	}
+
+	public String get(int offset, int length) throws BadLocationException {
+		return textStore.get(offset, length);
+	}
+
+	public String getLineDelimiter(int line) throws BadLocationException {
+		return lineTracker.getLineDelimiter(line);
+	}
+
+	public int getLength() {
+		return textStore.getLength();
+	}
+
+	public void replace(int offset, int length, String text) throws BadLocationException {
+		textStore.replace(offset, length, text);
+		lineTracker.set(get());
+	}
+
+	public char getChar(int pos) {
+		return textStore.get(pos);
+	}
+
+	public void setText(String text) {
+		textStore.set(text);
+		lineTracker.set(text);
+	}
+
+	public String get() {
+		return textStore.get(0, textStore.getLength());
+	}
+
+	protected static class DelimiterInfo {
+		public int		delimiterIndex;
+		public int		delimiterLength;
+		public String	delimiter;
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/GapTextStore.java b/bundleplugin/src/main/java/aQute/lib/properties/GapTextStore.java
new file mode 100644
index 0000000..7c4a043
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/GapTextStore.java
@@ -0,0 +1,419 @@
+package aQute.lib.properties;
+
+/**
+ * Implements a gap managing text store. The gap text store relies on the
+ * assumption that consecutive changes to a document are co-located. The start
+ * of the gap is always moved to the location of the last change.
+ * <p>
+ * <strong>Performance:</strong> Typing-style changes perform in constant time
+ * unless re-allocation becomes necessary. Generally, a change that does not
+ * cause re-allocation will cause at most one
+ * {@linkplain System#arraycopy(Object, int, Object, int, int) arraycopy}
+ * operation of a length of about <var>d</var>, where <var>d</var> is the
+ * distance from the previous change. Let <var>a(x)</var> be the algorithmic
+ * performance of an <code>arraycopy</code> operation of the length
+ * <var>x</var>, then such a change then performs in <i>O(a(x))</i>,
+ * {@linkplain #get(int, int) get(int, <var>length</var>)} performs in
+ * <i>O(a(length))</i>, {@link #get(int)} in <i>O(1)</i>.
+ * <p>
+ * How frequently the array needs re-allocation is controlled by the constructor
+ * parameters.
+ * </p>
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * 
+ * @see CopyOnWriteTextStore for a copy-on-write text store wrapper
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class GapTextStore implements ITextStore {
+	/**
+	 * The minimum gap size allocated when re-allocation occurs.
+	 * 
+	 * @since 3.3
+	 */
+	private final int	fMinGapSize;
+	/**
+	 * The maximum gap size allocated when re-allocation occurs.
+	 * 
+	 * @since 3.3
+	 */
+	private final int	fMaxGapSize;
+	/**
+	 * The multiplier to compute the array size from the content length
+	 * (1&nbsp;&lt;=&nbsp;fSizeMultiplier&nbsp;&lt;=&nbsp;2).
+	 * 
+	 * @since 3.3
+	 */
+	private final float	fSizeMultiplier;
+
+	/** The store's content */
+	private char[]		fContent	= new char[0];
+	/** Starting index of the gap */
+	private int			fGapStart	= 0;
+	/** End index of the gap */
+	private int			fGapEnd		= 0;
+	/**
+	 * The current high water mark. If a change would cause the gap to grow
+	 * larger than this, the array is re-allocated.
+	 * 
+	 * @since 3.3
+	 */
+	private int			fThreshold	= 0;
+
+	/**
+	 * Creates a new empty text store using the specified low and high
+	 * watermarks.
+	 * 
+	 * @param lowWatermark
+	 *            unused - at the lower bound, the array is only resized when
+	 *            the content does not fit
+	 * @param highWatermark
+	 *            if the gap is ever larger than this, it will automatically be
+	 *            shrunken (&gt;=&nbsp;0)
+	 * @deprecated use {@link GapTextStore#GapTextStore(int, int, float)}
+	 *             instead
+	 */
+	public GapTextStore(int lowWatermark, int highWatermark) {
+		/*
+		 * Legacy constructor. The API contract states that highWatermark is the
+		 * upper bound for the gap size. Albeit this contract was not previously
+		 * adhered to, it is now: The allocated gap size is fixed at half the
+		 * highWatermark. Since the threshold is always twice the allocated gap
+		 * size, the gap will never grow larger than highWatermark. Previously,
+		 * the gap size was initialized to highWatermark, causing re-allocation
+		 * if the content length shrunk right after allocation. The fixed gap
+		 * size is now only half of the previous value, circumventing that
+		 * problem (there was no API contract specifying the initial gap size).
+		 * The previous implementation did not allow the gap size to become
+		 * smaller than lowWatermark, which doesn't make any sense: that area of
+		 * the gap was simply never ever used.
+		 */
+		this(highWatermark / 2, highWatermark / 2, 0f);
+	}
+
+	/**
+	 * Equivalent to {@linkplain GapTextStore#GapTextStore(int, int, float) new
+	 * GapTextStore(256, 4096, 0.1f)}.
+	 * 
+	 * @since 3.3
+	 */
+	public GapTextStore() {
+		this(256, 4096, 0.1f);
+	}
+
+	/**
+	 * Creates an empty text store that uses re-allocation thresholds relative
+	 * to the content length. Re-allocation is controlled by the
+	 * <em>gap factor</em>, which is the quotient of the gap size and the array
+	 * size. Re-allocation occurs if a change causes the gap factor to go
+	 * outside <code>[0,&nbsp;maxGapFactor]</code>. When re-allocation occurs,
+	 * the array is sized such that the gap factor is
+	 * <code>0.5 * maxGapFactor</code>. The gap size computed in this manner is
+	 * bounded by the <code>minSize</code> and <code>maxSize</code> parameters.
+	 * <p>
+	 * A <code>maxGapFactor</code> of <code>0</code> creates a text store that
+	 * never has a gap at all (if <code>minSize</code> is 0); a
+	 * <code>maxGapFactor</code> of <code>1</code> creates a text store that
+	 * doubles its size with every re-allocation and that never shrinks.
+	 * </p>
+	 * <p>
+	 * The <code>minSize</code> and <code>maxSize</code> parameters are absolute
+	 * bounds to the allocated gap size. Use <code>minSize</code> to avoid
+	 * frequent re-allocation for small documents. Use <code>maxSize</code> to
+	 * avoid a huge gap being allocated for large documents.
+	 * </p>
+	 * 
+	 * @param minSize
+	 *            the minimum gap size to allocate (&gt;=&nbsp;0; use 0 for no
+	 *            minimum)
+	 * @param maxSize
+	 *            the maximum gap size to allocate (&gt;=&nbsp;minSize; use
+	 *            {@link Integer#MAX_VALUE} for no maximum)
+	 * @param maxGapFactor
+	 *            is the maximum fraction of the array that is occupied by the
+	 *            gap (
+	 *            <code>0&nbsp;&lt;=&nbsp;maxGapFactor&nbsp;&lt;=&nbsp;1</code>)
+	 * @since 3.3
+	 */
+	public GapTextStore(int minSize, int maxSize, float maxGapFactor) {
+		fMinGapSize = minSize;
+		fMaxGapSize = maxSize;
+		fSizeMultiplier = 1 / (1 - maxGapFactor / 2);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#get(int)
+	 */
+	public final char get(int offset) {
+		if (offset < fGapStart)
+			return fContent[offset];
+
+		return fContent[offset + gapSize()];
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#get(int, int)
+	 */
+	public final String get(int offset, int length) {
+		if (fGapStart <= offset)
+			return new String(fContent, offset + gapSize(), length);
+
+		final int end = offset + length;
+
+		if (end <= fGapStart)
+			return new String(fContent, offset, length);
+
+		StringBuffer buf = new StringBuffer(length);
+		buf.append(fContent, offset, fGapStart - offset);
+		buf.append(fContent, fGapEnd, end - fGapStart);
+		return buf.toString();
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#getLength()
+	 */
+	public final int getLength() {
+		return fContent.length - gapSize();
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+	 */
+	public final void set(String text) {
+		/*
+		 * Moves the gap to the end of the content. There is no sensible
+		 * prediction of where the next change will occur, but at least the next
+		 * change will not trigger re-allocation. This is especially important
+		 * when using the GapTextStore within a CopyOnWriteTextStore, where the
+		 * GTS is only initialized right before a modification.
+		 */
+		replace(0, getLength(), text);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#replace(int, int,
+	 * java.lang.String)
+	 */
+	public final void replace(int offset, int length, String text) {
+		if (text == null) {
+			adjustGap(offset, length, 0);
+		} else {
+			int textLength = text.length();
+			adjustGap(offset, length, textLength);
+			if (textLength != 0)
+				text.getChars(0, textLength, fContent, offset);
+		}
+	}
+
+	/**
+	 * Moves the gap to <code>offset + add</code>, moving any content after
+	 * <code>offset + remove</code> behind the gap. The gap size is kept between
+	 * 0 and {@link #fThreshold}, leading to re-allocation if needed. The
+	 * content between <code>offset</code> and <code>offset + add</code> is
+	 * undefined after this operation.
+	 * 
+	 * @param offset
+	 *            the offset at which a change happens
+	 * @param remove
+	 *            the number of character which are removed or overwritten at
+	 *            <code>offset</code>
+	 * @param add
+	 *            the number of character which are inserted or overwriting at
+	 *            <code>offset</code>
+	 */
+	private void adjustGap(int offset, int remove, int add) {
+		final int oldGapSize = gapSize();
+		final int newGapSize = oldGapSize - add + remove;
+		final boolean reuseArray = 0 <= newGapSize && newGapSize <= fThreshold;
+
+		final int newGapStart = offset + add;
+		final int newGapEnd;
+
+		if (reuseArray)
+			newGapEnd = moveGap(offset, remove, oldGapSize, newGapSize, newGapStart);
+		else
+			newGapEnd = reallocate(offset, remove, oldGapSize, newGapSize, newGapStart);
+
+		fGapStart = newGapStart;
+		fGapEnd = newGapEnd;
+	}
+
+	/**
+	 * Moves the gap to <code>newGapStart</code>.
+	 * 
+	 * @param offset
+	 *            the change offset
+	 * @param remove
+	 *            the number of removed / overwritten characters
+	 * @param oldGapSize
+	 *            the old gap size
+	 * @param newGapSize
+	 *            the gap size after the change
+	 * @param newGapStart
+	 *            the offset in the array to move the gap to
+	 * @return the new gap end
+	 * @since 3.3
+	 */
+	private int moveGap(int offset, int remove, int oldGapSize, int newGapSize, int newGapStart) {
+		/*
+		 * No re-allocation necessary. The area between the change offset and
+		 * gap can be copied in at most one operation. Don't copy parts that
+		 * will be overwritten anyway.
+		 */
+		final int newGapEnd = newGapStart + newGapSize;
+		if (offset < fGapStart) {
+			int afterRemove = offset + remove;
+			if (afterRemove < fGapStart) {
+				final int betweenSize = fGapStart - afterRemove;
+				arrayCopy(afterRemove, fContent, newGapEnd, betweenSize);
+			}
+			// otherwise, only the gap gets enlarged
+		} else {
+			final int offsetShifted = offset + oldGapSize;
+			final int betweenSize = offsetShifted - fGapEnd; // in the typing
+																// case,
+																// betweenSize
+																// is 0
+			arrayCopy(fGapEnd, fContent, fGapStart, betweenSize);
+		}
+		return newGapEnd;
+	}
+
+	/**
+	 * Reallocates a new array and copies the data from the previous one.
+	 * 
+	 * @param offset
+	 *            the change offset
+	 * @param remove
+	 *            the number of removed / overwritten characters
+	 * @param oldGapSize
+	 *            the old gap size
+	 * @param newGapSize
+	 *            the gap size after the change if no re-allocation would occur
+	 *            (can be negative)
+	 * @param newGapStart
+	 *            the offset in the array to move the gap to
+	 * @return the new gap end
+	 * @since 3.3
+	 */
+	private int reallocate(int offset, int remove, final int oldGapSize, int newGapSize, final int newGapStart) {
+		// the new content length (without any gap)
+		final int newLength = fContent.length - newGapSize;
+		// the new array size based on the gap factor
+		int newArraySize = (int) (newLength * fSizeMultiplier);
+		newGapSize = newArraySize - newLength;
+
+		// bound the gap size within min/max
+		if (newGapSize < fMinGapSize) {
+			newGapSize = fMinGapSize;
+			newArraySize = newLength + newGapSize;
+		} else if (newGapSize > fMaxGapSize) {
+			newGapSize = fMaxGapSize;
+			newArraySize = newLength + newGapSize;
+		}
+
+		// the upper threshold is always twice the gapsize
+		fThreshold = newGapSize * 2;
+		final char[] newContent = allocate(newArraySize);
+		final int newGapEnd = newGapStart + newGapSize;
+
+		/*
+		 * Re-allocation: The old content can be copied in at most 3 operations
+		 * to the newly allocated array. Either one of change offset and the gap
+		 * may come first. - unchanged area before the change offset / gap -
+		 * area between the change offset and the gap (either one may be first)
+		 * - rest area after the change offset / after the gap
+		 */
+		if (offset < fGapStart) {
+			// change comes before gap
+			arrayCopy(0, newContent, 0, offset);
+			int afterRemove = offset + remove;
+			if (afterRemove < fGapStart) {
+				// removal is completely before the gap
+				final int betweenSize = fGapStart - afterRemove;
+				arrayCopy(afterRemove, newContent, newGapEnd, betweenSize);
+				final int restSize = fContent.length - fGapEnd;
+				arrayCopy(fGapEnd, newContent, newGapEnd + betweenSize, restSize);
+			} else {
+				// removal encompasses the gap
+				afterRemove += oldGapSize;
+				final int restSize = fContent.length - afterRemove;
+				arrayCopy(afterRemove, newContent, newGapEnd, restSize);
+			}
+		} else {
+			// gap comes before change
+			arrayCopy(0, newContent, 0, fGapStart);
+			final int offsetShifted = offset + oldGapSize;
+			final int betweenSize = offsetShifted - fGapEnd;
+			arrayCopy(fGapEnd, newContent, fGapStart, betweenSize);
+			final int afterRemove = offsetShifted + remove;
+			final int restSize = fContent.length - afterRemove;
+			arrayCopy(afterRemove, newContent, newGapEnd, restSize);
+		}
+
+		fContent = newContent;
+		return newGapEnd;
+	}
+
+	/**
+	 * Allocates a new <code>char[size]</code>.
+	 * 
+	 * @param size
+	 *            the length of the new array.
+	 * @return a newly allocated char array
+	 * @since 3.3
+	 */
+	private char[] allocate(int size) {
+		return new char[size];
+	}
+
+	/*
+	 * Executes System.arraycopy if length != 0. A length < 0 cannot happen ->
+	 * don't hide coding errors by checking for negative lengths.
+	 * @since 3.3
+	 */
+	private void arrayCopy(int srcPos, char[] dest, int destPos, int length) {
+		if (length != 0)
+			System.arraycopy(fContent, srcPos, dest, destPos, length);
+	}
+
+	/**
+	 * Returns the gap size.
+	 * 
+	 * @return the gap size
+	 * @since 3.3
+	 */
+	private int gapSize() {
+		return fGapEnd - fGapStart;
+	}
+
+	/**
+	 * Returns a copy of the content of this text store. For internal use only.
+	 * 
+	 * @return a copy of the content of this text store
+	 */
+	protected String getContentAsString() {
+		return new String(fContent);
+	}
+
+	/**
+	 * Returns the start index of the gap managed by this text store. For
+	 * internal use only.
+	 * 
+	 * @return the start index of the gap managed by this text store
+	 */
+	protected int getGapStartIndex() {
+		return fGapStart;
+	}
+
+	/**
+	 * Returns the end index of the gap managed by this text store. For internal
+	 * use only.
+	 * 
+	 * @return the end index of the gap managed by this text store
+	 */
+	protected int getGapEndIndex() {
+		return fGapEnd;
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/IDocument.java b/bundleplugin/src/main/java/aQute/lib/properties/IDocument.java
new file mode 100644
index 0000000..aba541f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/IDocument.java
@@ -0,0 +1,21 @@
+package aQute.lib.properties;
+
+public interface IDocument {
+
+	int getNumberOfLines();
+
+	IRegion getLineInformation(int lineNum) throws BadLocationException;
+
+	String get();
+
+	String get(int offset, int length) throws BadLocationException;
+
+	String getLineDelimiter(int line) throws BadLocationException;
+
+	int getLength();
+
+	void replace(int offset, int length, String data) throws BadLocationException;
+
+	char getChar(int offset) throws BadLocationException;
+
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/IRegion.java b/bundleplugin/src/main/java/aQute/lib/properties/IRegion.java
new file mode 100644
index 0000000..70230e7
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/IRegion.java
@@ -0,0 +1,8 @@
+package aQute.lib.properties;
+
+public interface IRegion {
+
+	int getLength();
+
+	int getOffset();
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/ITextStore.java b/bundleplugin/src/main/java/aQute/lib/properties/ITextStore.java
new file mode 100644
index 0000000..f4cf2c7
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/ITextStore.java
@@ -0,0 +1,65 @@
+package aQute.lib.properties;
+
+/**
+ * Interface for storing and managing text.
+ * <p>
+ * Provides access to the stored text and allows to manipulate it.
+ * </p>
+ * <p>
+ * Clients may implement this interface or use
+ * {@link org.eclipse.jface.text.GapTextStore} or
+ * {@link org.eclipse.jface.text.CopyOnWriteTextStore}.
+ * </p>
+ */
+public interface ITextStore {
+
+	/**
+	 * Returns the character at the specified offset.
+	 * 
+	 * @param offset
+	 *            the offset in this text store
+	 * @return the character at this offset
+	 */
+	char get(int offset);
+
+	/**
+	 * Returns the text of the specified character range.
+	 * 
+	 * @param offset
+	 *            the offset of the range
+	 * @param length
+	 *            the length of the range
+	 * @return the text of the range
+	 */
+	String get(int offset, int length);
+
+	/**
+	 * Returns number of characters stored in this text store.
+	 * 
+	 * @return the number of characters stored in this text store
+	 */
+	int getLength();
+
+	/**
+	 * Replaces the specified character range with the given text.
+	 * <code>replace(getLength(), 0, "some text")</code> is a valid call and
+	 * appends text to the end of the text store.
+	 * 
+	 * @param offset
+	 *            the offset of the range to be replaced
+	 * @param length
+	 *            the number of characters to be replaced
+	 * @param text
+	 *            the substitution text
+	 */
+	void replace(int offset, int length, String text);
+
+	/**
+	 * Replace the content of the text store with the given text. Convenience
+	 * method for <code>replace(0, getLength(), text</code>.
+	 * 
+	 * @param text
+	 *            the new content of the text store
+	 */
+	void set(String text);
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/Line.java b/bundleplugin/src/main/java/aQute/lib/properties/Line.java
new file mode 100644
index 0000000..8d07ce1
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/Line.java
@@ -0,0 +1,60 @@
+package aQute.lib.properties;
+
+/**
+ * Describes a line as a particular number of characters beginning at a
+ * particular offset, consisting of a particular number of characters, and being
+ * closed with a particular line delimiter.
+ */
+final class Line implements IRegion {
+
+	/** The offset of the line */
+	public int			offset;
+	/** The length of the line */
+	public int			length;
+	/** The delimiter of this line */
+	public final String	delimiter;
+
+	/**
+	 * Creates a new Line.
+	 * 
+	 * @param offset
+	 *            the offset of the line
+	 * @param end
+	 *            the last including character offset of the line
+	 * @param delimiter
+	 *            the line's delimiter
+	 */
+	public Line(int offset, int end, String delimiter) {
+		this.offset = offset;
+		this.length = (end - offset) + 1;
+		this.delimiter = delimiter;
+	}
+
+	/**
+	 * Creates a new Line.
+	 * 
+	 * @param offset
+	 *            the offset of the line
+	 * @param length
+	 *            the length of the line
+	 */
+	public Line(int offset, int length) {
+		this.offset = offset;
+		this.length = length;
+		this.delimiter = null;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.IRegion#getOffset()
+	 */
+	public int getOffset() {
+		return offset;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.IRegion#getLength()
+	 */
+	public int getLength() {
+		return length;
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/LineTracker.java b/bundleplugin/src/main/java/aQute/lib/properties/LineTracker.java
new file mode 100644
index 0000000..a3c3adf
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/LineTracker.java
@@ -0,0 +1,382 @@
+package aQute.lib.properties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import aQute.lib.properties.Document.DelimiterInfo;
+
+public class LineTracker {
+
+	/** The line information */
+	private final List<Line>	fLines	= new ArrayList<Line>();
+	/** The length of the tracked text */
+	private int					fTextLength;
+
+	/**
+	 * Creates a new line tracker.
+	 */
+	protected LineTracker() {}
+
+	/**
+	 * Binary search for the line at a given offset.
+	 * 
+	 * @param offset
+	 *            the offset whose line should be found
+	 * @return the line of the offset
+	 */
+	private int findLine(int offset) {
+
+		if (fLines.size() == 0)
+			return -1;
+
+		int left = 0;
+		int right = fLines.size() - 1;
+		int mid = 0;
+		Line line = null;
+
+		while (left < right) {
+
+			mid = (left + right) / 2;
+
+			line = fLines.get(mid);
+			if (offset < line.offset) {
+				if (left == mid)
+					right = left;
+				else
+					right = mid - 1;
+			} else if (offset > line.offset) {
+				if (right == mid)
+					left = right;
+				else
+					left = mid + 1;
+			} else if (offset == line.offset) {
+				left = right = mid;
+			}
+		}
+
+		line = fLines.get(left);
+		if (line.offset > offset)
+			--left;
+		return left;
+	}
+
+	/**
+	 * Returns the number of lines covered by the specified text range.
+	 * 
+	 * @param startLine
+	 *            the line where the text range starts
+	 * @param offset
+	 *            the start offset of the text range
+	 * @param length
+	 *            the length of the text range
+	 * @return the number of lines covered by this text range
+	 * @exception BadLocationException
+	 *                if range is undefined in this tracker
+	 */
+	private int getNumberOfLines(int startLine, int offset, int length) throws BadLocationException {
+
+		if (length == 0)
+			return 1;
+
+		int target = offset + length;
+
+		Line l = fLines.get(startLine);
+
+		if (l.delimiter == null)
+			return 1;
+
+		if (l.offset + l.length > target)
+			return 1;
+
+		if (l.offset + l.length == target)
+			return 2;
+
+		return getLineNumberOfOffset(target) - startLine + 1;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineLength(int)
+	 */
+	public final int getLineLength(int line) throws BadLocationException {
+		int lines = fLines.size();
+
+		if (line < 0 || line > lines)
+			throw new BadLocationException();
+
+		if (lines == 0 || lines == line)
+			return 0;
+
+		Line l = fLines.get(line);
+		return l.length;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineNumberOfOffset(int)
+	 */
+	public final int getLineNumberOfOffset(int position) throws BadLocationException {
+		if (position < 0 || position > fTextLength)
+			throw new BadLocationException();
+
+		if (position == fTextLength) {
+
+			int lastLine = fLines.size() - 1;
+			if (lastLine < 0)
+				return 0;
+
+			Line l = fLines.get(lastLine);
+			return (l.delimiter != null ? lastLine + 1 : lastLine);
+		}
+
+		return findLine(position);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineInformationOfOffset(int)
+	 */
+	public final IRegion getLineInformationOfOffset(int position) throws BadLocationException {
+		if (position > fTextLength)
+			throw new BadLocationException();
+
+		if (position == fTextLength) {
+			int size = fLines.size();
+			if (size == 0)
+				return new Region(0, 0);
+			Line l = fLines.get(size - 1);
+			return (l.delimiter != null ? new Line(fTextLength, 0) : new Line(fTextLength - l.length, l.length));
+		}
+
+		return getLineInformation(findLine(position));
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineInformation(int)
+	 */
+	public final IRegion getLineInformation(int line) throws BadLocationException {
+		int lines = fLines.size();
+
+		if (line < 0 || line > lines)
+			throw new BadLocationException();
+
+		if (lines == 0)
+			return new Line(0, 0);
+
+		if (line == lines) {
+			Line l = fLines.get(line - 1);
+			return new Line(l.offset + l.length, 0);
+		}
+
+		Line l = fLines.get(line);
+		return (l.delimiter != null ? new Line(l.offset, l.length - l.delimiter.length()) : l);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineOffset(int)
+	 */
+	public final int getLineOffset(int line) throws BadLocationException {
+		int lines = fLines.size();
+
+		if (line < 0 || line > lines)
+			throw new BadLocationException();
+
+		if (lines == 0)
+			return 0;
+
+		if (line == lines) {
+			Line l = fLines.get(line - 1);
+			if (l.delimiter != null)
+				return l.offset + l.length;
+			throw new BadLocationException();
+		}
+
+		Line l = fLines.get(line);
+		return l.offset;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getNumberOfLines()
+	 */
+	public final int getNumberOfLines() {
+		int lines = fLines.size();
+
+		if (lines == 0)
+			return 1;
+
+		Line l = fLines.get(lines - 1);
+		return (l.delimiter != null ? lines + 1 : lines);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getNumberOfLines(int, int)
+	 */
+	public final int getNumberOfLines(int position, int length) throws BadLocationException {
+
+		if (position < 0 || position + length > fTextLength)
+			throw new BadLocationException();
+
+		if (length == 0) // optimization
+			return 1;
+
+		return getNumberOfLines(getLineNumberOfOffset(position), position, length);
+	}
+
+	/*
+	 * @see
+	 * org.eclipse.jface.text.ILineTracker#computeNumberOfLines(java.lang.String
+	 * )
+	 */
+	public final int computeNumberOfLines(String text) {
+		int count = 0;
+		int start = 0;
+		DelimiterInfo delimiterInfo = nextDelimiterInfo(text, start);
+		while (delimiterInfo != null && delimiterInfo.delimiterIndex > -1) {
+			++count;
+			start = delimiterInfo.delimiterIndex + delimiterInfo.delimiterLength;
+			delimiterInfo = nextDelimiterInfo(text, start);
+		}
+		return count;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineDelimiter(int)
+	 */
+	public final String getLineDelimiter(int line) throws BadLocationException {
+		int lines = fLines.size();
+
+		if (line < 0 || line > lines)
+			throw new BadLocationException();
+
+		if (lines == 0)
+			return null;
+
+		if (line == lines)
+			return null;
+
+		Line l = fLines.get(line);
+		return l.delimiter;
+	}
+
+	/**
+	 * Returns the information about the first delimiter found in the given text
+	 * starting at the given offset.
+	 * 
+	 * @param text
+	 *            the text to be searched
+	 * @param offset
+	 *            the offset in the given text
+	 * @return the information of the first found delimiter or <code>null</code>
+	 */
+	protected DelimiterInfo nextDelimiterInfo(String text, int offset) {
+
+		char ch;
+		int length = text.length();
+		for (int i = offset; i < length; i++) {
+
+			ch = text.charAt(i);
+			if (ch == '\r') {
+
+				if (i + 1 < length) {
+					if (text.charAt(i + 1) == '\n') {
+						DelimiterInfo fDelimiterInfo = new DelimiterInfo();
+						fDelimiterInfo.delimiter = Document.DELIMITERS[2];
+						fDelimiterInfo.delimiterIndex = i;
+						fDelimiterInfo.delimiterLength = 2;
+						return fDelimiterInfo;
+					}
+				}
+				DelimiterInfo fDelimiterInfo = new DelimiterInfo();
+				fDelimiterInfo.delimiter = Document.DELIMITERS[0];
+				fDelimiterInfo.delimiterIndex = i;
+				fDelimiterInfo.delimiterLength = 1;
+				return fDelimiterInfo;
+
+			} else if (ch == '\n') {
+				DelimiterInfo fDelimiterInfo = new DelimiterInfo();
+				fDelimiterInfo.delimiter = Document.DELIMITERS[1];
+				fDelimiterInfo.delimiterIndex = i;
+				fDelimiterInfo.delimiterLength = 1;
+				return fDelimiterInfo;
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * Creates the line structure for the given text. Newly created lines are
+	 * inserted into the line structure starting at the given position. Returns
+	 * the number of newly created lines.
+	 * 
+	 * @param text
+	 *            the text for which to create a line structure
+	 * @param insertPosition
+	 *            the position at which the newly created lines are inserted
+	 *            into the tracker's line structure
+	 * @param offset
+	 *            the offset of all newly created lines
+	 * @return the number of newly created lines
+	 */
+	private int createLines(String text, int insertPosition, int offset) {
+
+		int count = 0;
+		int start = 0;
+		DelimiterInfo delimiterInfo = nextDelimiterInfo(text, 0);
+
+		while (delimiterInfo != null && delimiterInfo.delimiterIndex > -1) {
+
+			int index = delimiterInfo.delimiterIndex + (delimiterInfo.delimiterLength - 1);
+
+			if (insertPosition + count >= fLines.size())
+				fLines.add(new Line(offset + start, offset + index, delimiterInfo.delimiter));
+			else
+				fLines.add(insertPosition + count, new Line(offset + start, offset + index, delimiterInfo.delimiter));
+
+			++count;
+			start = index + 1;
+			delimiterInfo = nextDelimiterInfo(text, start);
+		}
+
+		if (start < text.length()) {
+			if (insertPosition + count < fLines.size()) {
+				// there is a line below the current
+				Line l = fLines.get(insertPosition + count);
+				int delta = text.length() - start;
+				l.offset -= delta;
+				l.length += delta;
+			} else {
+				fLines.add(new Line(offset + start, offset + text.length() - 1, null));
+				++count;
+			}
+		}
+
+		return count;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#replace(int, int,
+	 * java.lang.String)
+	 */
+	public final void replace(@SuppressWarnings("unused") int position, @SuppressWarnings("unused") int length, @SuppressWarnings("unused") String text) throws BadLocationException {
+		throw new UnsupportedOperationException();
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#set(java.lang.String)
+	 */
+	public final void set(String text) {
+		fLines.clear();
+		if (text != null) {
+			fTextLength = text.length();
+			createLines(text, 0, 0);
+		}
+	}
+
+	/**
+	 * Returns the internal data structure, a {@link List} of {@link Line}s.
+	 * Used only by {@link TreeLineTracker#TreeLineTracker(ListLineTracker)}.
+	 * 
+	 * @return the internal list of lines.
+	 */
+	final List<Line> getLines() {
+		return fLines;
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/LineType.java b/bundleplugin/src/main/java/aQute/lib/properties/LineType.java
new file mode 100644
index 0000000..69eda5f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/LineType.java
@@ -0,0 +1,5 @@
+package aQute.lib.properties;
+
+public enum LineType {
+	blank, comment, entry, eof
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/PropertiesLineReader.java b/bundleplugin/src/main/java/aQute/lib/properties/PropertiesLineReader.java
new file mode 100644
index 0000000..c3ea14b
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/PropertiesLineReader.java
@@ -0,0 +1,125 @@
+package aQute.lib.properties;
+
+import static aQute.lib.properties.LineType.*;
+
+public class PropertiesLineReader {
+
+	private final IDocument	document;
+	private final int		lineCount;
+
+	private int				lineNum		= 0;
+
+	private IRegion			lastRegion	= null;
+	private String			lastKey		= null;
+	private String			lastValue	= null;
+
+	public PropertiesLineReader(IDocument document) {
+		this.document = document;
+		this.lineCount = document.getNumberOfLines();
+	}
+
+	public PropertiesLineReader(String data) {
+		this(new Document(data));
+	}
+
+	public LineType next() throws Exception {
+		int index = 0;
+		char[] chars = null;
+
+		StringBuilder keyData = new StringBuilder();
+		StringBuilder valueData = new StringBuilder();
+		StringBuilder currentBuffer = keyData;
+
+		boolean started = false;
+
+		mainLoop: while (true) {
+			if (chars == null)
+				chars = grabLine(false);
+			if (chars == null)
+				return eof;
+
+			if (index >= chars.length)
+				break;
+
+			char c = chars[index];
+			if (c == '\\') {
+				index++;
+				if (index == chars.length) {
+					chars = grabLine(true);
+					index = 0;
+					if (chars == null || chars.length == 0)
+						break; // The last line ended with a backslash
+				}
+				currentBuffer.append(chars[index]);
+				index++;
+				continue mainLoop;
+			}
+
+			if (c == '=' || c == ':')
+				currentBuffer = valueData;
+
+			if (!started && (c == '#' || c == '!'))
+				return comment;
+
+			if (Character.isWhitespace(c)) {
+				if (started) {
+					// whitespace ends the key
+					currentBuffer = valueData;
+				}
+			} else {
+				started = true;
+				currentBuffer.append(c);
+			}
+
+			index++;
+		}
+
+		if (!started)
+			return blank;
+
+		lastKey = keyData.toString();
+		return entry;
+	}
+
+	private char[] grabLine(boolean continued) throws BadLocationException {
+		if (lineNum >= lineCount) {
+			lastRegion = null;
+			return null;
+		}
+
+		IRegion lineInfo = document.getLineInformation(lineNum);
+		char[] chars = document.get(lineInfo.getOffset(), lineInfo.getLength()).toCharArray();
+
+		if (continued) {
+			int length = lastRegion.getLength();
+			length += document.getLineDelimiter(lineNum - 1).length();
+			length += lineInfo.getLength();
+			lastRegion = new Region(lastRegion.getOffset(), length);
+		} else {
+			lastRegion = lineInfo;
+		}
+
+		lineNum++;
+		return chars;
+	}
+
+	public IRegion region() {
+		if (lastRegion == null)
+			throw new IllegalStateException("Last region not available: either before start or after end of document.");
+		return lastRegion;
+	}
+
+	public String key() {
+		if (lastKey == null)
+			throw new IllegalStateException(
+					"Last key not available: either before state or after end of document, or last line type was not 'entry'.");
+		return lastKey;
+	}
+
+	public String value() {
+		if (lastValue == null)
+			throw new IllegalStateException(
+					"Last value not available: either before state or after end of document, or last line type was not 'entry'.");
+		return lastValue;
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/Region.java b/bundleplugin/src/main/java/aQute/lib/properties/Region.java
new file mode 100644
index 0000000..abf567c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/Region.java
@@ -0,0 +1,20 @@
+package aQute.lib.properties;
+
+public class Region implements IRegion {
+
+	private final int	length;
+	private final int	offset;
+
+	public Region(int length, int offset) {
+		this.length = length;
+		this.offset = offset;
+	}
+
+	public int getLength() {
+		return length;
+	}
+
+	public int getOffset() {
+		return offset;
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/packageinfo b/bundleplugin/src/main/java/aQute/lib/properties/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/lib/spring/XMLTypeProcessor.java b/bundleplugin/src/main/java/aQute/lib/spring/XMLTypeProcessor.java
index 64abf3c..dc17eb1 100644
--- a/bundleplugin/src/main/java/aQute/lib/spring/XMLTypeProcessor.java
+++ b/bundleplugin/src/main/java/aQute/lib/spring/XMLTypeProcessor.java
@@ -16,7 +16,7 @@
 		return false;
 	}
 
-	protected List<XMLType> getTypes(Analyzer analyzer) throws Exception {
+	protected List<XMLType> getTypes(@SuppressWarnings("unused") Analyzer analyzer) throws Exception {
 		return new ArrayList<XMLType>();
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/libg/cafs/CAFS.java b/bundleplugin/src/main/java/aQute/libg/cafs/CAFS.java
index db2a175..b97babc 100644
--- a/bundleplugin/src/main/java/aQute/libg/cafs/CAFS.java
+++ b/bundleplugin/src/main/java/aQute/libg/cafs/CAFS.java
@@ -23,8 +23,8 @@
  * underlying idea in Git.
  */
 public class CAFS implements Closeable, Iterable<SHA1> {
-	final static byte[]	CAFS			= "CAFS".getBytes();
-	final static byte[]	CAFE			= "CAFE".getBytes();
+	final static byte[]	CAFS;
+	final static byte[]	CAFE;
 	final static String	INDEXFILE		= "index.idx";
 	final static String	STOREFILE		= "store.cafs";
 	final static String	ALGORITHM		= "SHA-1";
@@ -42,6 +42,15 @@
 	RandomAccessFile	store;
 	FileChannel			channel;
 
+	static {
+		try {
+		CAFS = "CAFS".getBytes("UTF-8");
+		CAFE = "CAFE".getBytes("UTF-8");
+		} catch (Throwable e) {
+			throw new ExceptionInInitializerError(e);
+		}
+	}
+
 	/**
 	 * Constructor for a Content Addressable File Store
 	 * 
diff --git a/bundleplugin/src/main/java/aQute/libg/command/Command.java b/bundleplugin/src/main/java/aQute/libg/command/Command.java
index 3feb702..6bb24c3 100644
--- a/bundleplugin/src/main/java/aQute/libg/command/Command.java
+++ b/bundleplugin/src/main/java/aQute/libg/command/Command.java
@@ -4,7 +4,9 @@
 import java.util.*;
 import java.util.Map.Entry;
 import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
 
+import aQute.lib.io.*;
 import aQute.libg.reporter.*;
 
 public class Command {
@@ -35,7 +37,7 @@
 		return execute(in, stdout, stderr);
 	}
 
-	public int execute(InputStream in, Appendable stdout, Appendable stderr) throws Exception {
+	public int execute(final InputStream in, Appendable stdout, Appendable stderr) throws Exception {
 		if (reporter != null) {
 			reporter.trace("executing cmd: %s", arguments);
 		}
@@ -61,7 +63,8 @@
 		Thread hook = new Thread(r, arguments.toString());
 		Runtime.getRuntime().addShutdownHook(hook);
 		TimerTask timer = null;
-		OutputStream stdin = process.getOutputStream();
+		final OutputStream stdin = process.getOutputStream();
+		Thread rdInThread = null;
 
 		if (timeout != 0) {
 			timer = new TimerTask() {
@@ -73,6 +76,7 @@
 			Command.timer.schedule(timer, timeout);
 		}
 
+		final AtomicBoolean finished = new AtomicBoolean(false);
 		InputStream out = process.getInputStream();
 		try {
 			InputStream err = process.getErrorStream();
@@ -82,25 +86,45 @@
 				Collector cerr = new Collector(err, stderr);
 				cerr.start();
 
-				try {
-					int c = in.read();
-					while (c >= 0) {
-						stdin.write(c);
-						if (c == '\n')
-							stdin.flush();
-						c = in.read();
+				if (in != null) {
+					if (in == System.in) {
+						rdInThread = new Thread("Read Input Thread") {
+							public void run() {
+								try {
+									while (!finished.get()) {
+										int n = in.available();
+										if (n == 0) {
+											sleep(100);
+										} else {
+											int c = in.read();
+											if (c < 0) {
+												stdin.close();
+												return;
+											}
+											stdin.write(c);
+											if (c == '\n')
+												stdin.flush();
+										}
+									}
+								}
+								catch (InterruptedIOException e) {
+									// Ignore here
+								}
+								catch (Exception e) {
+									// Who cares?
+								}
+								finally {
+									IO.close(stdin);
+								}
+							}
+						};
+						rdInThread.setDaemon(true);
+						rdInThread.start();
+					} else {
+						IO.copy(in, stdin);
+						stdin.close();
 					}
 				}
-				catch (InterruptedIOException e) {
-					// Ignore here
-				}
-				catch (Exception e) {
-					// Who cares?
-				}
-				finally {
-					stdin.close();
-				}
-
 				if (reporter != null)
 					reporter.trace("exited process");
 
@@ -121,6 +145,13 @@
 		}
 
 		byte exitValue = (byte) process.waitFor();
+		finished.set(true);
+		if (rdInThread != null) {
+			if (in != null)
+				IO.close(in);
+			rdInThread.interrupt();
+		}
+
 		if (reporter != null)
 			reporter.trace("cmd %s executed with result=%d, result: %s/%s, timedout=%s", arguments, exitValue, stdout,
 					stderr, timedout);
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/Crypto.java b/bundleplugin/src/main/java/aQute/libg/cryptography/Crypto.java
index 22eac45..3d27188 100644
--- a/bundleplugin/src/main/java/aQute/libg/cryptography/Crypto.java
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/Crypto.java
@@ -16,7 +16,6 @@
 	 * @return
 	 * @throws Exception
 	 */
-	@SuppressWarnings("unchecked")
 	public static <T> T fromString(String spec, Class<T> c) throws Exception {
 		if (PrivateKey.class.isAssignableFrom(c)) {
 			Matcher m = RSA_PRIVATE.matcher(spec);
diff --git a/bundleplugin/src/main/java/aQute/libg/filters/AndFilter.java b/bundleplugin/src/main/java/aQute/libg/filters/AndFilter.java
new file mode 100644
index 0000000..2561d77
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filters/AndFilter.java
@@ -0,0 +1,30 @@
+package aQute.libg.filters;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public final class AndFilter extends Filter {
+	
+	private final List<Filter> children = new LinkedList<Filter>();
+	
+	public AndFilter addChild(Filter child) {
+		if (child instanceof AndFilter)
+			children.addAll(((AndFilter) child).children);
+		else
+			children.add(child);
+		return this;
+	}
+
+	@Override
+	public void append(StringBuilder builder) {
+		if (children.isEmpty())
+			return;
+		
+		builder.append("(&");
+		for (Filter child : children) {
+			child.append(builder);
+		}
+		builder.append(")");
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/filters/Filter.java b/bundleplugin/src/main/java/aQute/libg/filters/Filter.java
new file mode 100644
index 0000000..e0a4555
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filters/Filter.java
@@ -0,0 +1,14 @@
+package aQute.libg.filters;
+
+public abstract class Filter {
+
+	public abstract void append(StringBuilder builder);
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		append(builder);
+		return builder.toString();
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/filters/NotFilter.java b/bundleplugin/src/main/java/aQute/libg/filters/NotFilter.java
new file mode 100644
index 0000000..0f1061c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filters/NotFilter.java
@@ -0,0 +1,19 @@
+package aQute.libg.filters;
+
+public final class NotFilter extends Filter {
+
+	private final Filter	child;
+
+	public NotFilter(Filter child) {
+		this.child = child;
+	}
+
+	@Override
+	public void append(StringBuilder builder) {
+		builder.append("(!");
+		child.append(builder);
+		builder.append(")");
+	}
+	
+	
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/filters/Operator.java b/bundleplugin/src/main/java/aQute/libg/filters/Operator.java
new file mode 100644
index 0000000..b17aaff
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filters/Operator.java
@@ -0,0 +1,17 @@
+package aQute.libg.filters;
+
+public enum Operator {
+
+	Equals("="), LessThanOrEqual("<="), GreaterThanOrEqual(">="), ApproxEqual("~=");
+
+	private final String	symbol;
+
+	Operator(String symbol) {
+		this.symbol = symbol;
+	}
+
+	public String getSymbol() {
+		return symbol;
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/filters/OrFilter.java b/bundleplugin/src/main/java/aQute/libg/filters/OrFilter.java
new file mode 100644
index 0000000..b3332bd
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filters/OrFilter.java
@@ -0,0 +1,30 @@
+package aQute.libg.filters;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public final class OrFilter extends Filter {
+	
+	private final List<Filter> children = new LinkedList<Filter>();
+	
+	public OrFilter addChild(Filter child) {
+		if (child instanceof OrFilter)
+			children.addAll(((OrFilter) child).children);
+		else
+			children.add(child);
+		return this;
+	}
+
+	@Override
+	public void append(StringBuilder builder) {
+		if (children.isEmpty())
+			return;
+		
+		builder.append("(|");
+		for (Filter child : children) {
+			child.append(builder);
+		}
+		builder.append(")");
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/filters/SimpleFilter.java b/bundleplugin/src/main/java/aQute/libg/filters/SimpleFilter.java
new file mode 100644
index 0000000..f0dea65
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filters/SimpleFilter.java
@@ -0,0 +1,33 @@
+package aQute.libg.filters;
+
+public final class SimpleFilter extends Filter {
+
+	private final String	name;
+	private final Operator	operator;
+	private final String	value;
+
+	/**
+	 * Construct a simple filter with the default "equals" operator, i.e.
+	 * {@code (name=value)}.
+	 */
+	public SimpleFilter(String name, String value) {
+		this(name, Operator.Equals, value);
+	}
+
+	/**
+	 * Construct a simple filter with any of the comparison operators.
+	 */
+	public SimpleFilter(String name, Operator operator, String value) {
+		this.name = name;
+		this.operator = operator;
+		this.value = value;
+	}
+
+	@Override
+	public void append(StringBuilder builder) {
+		builder.append('(');
+		builder.append(name).append(operator.getSymbol()).append(value);
+		builder.append(')');
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/filters/packageinfo b/bundleplugin/src/main/java/aQute/libg/filters/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filters/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/libg/reporter/Messages.java b/bundleplugin/src/main/java/aQute/libg/reporter/Messages.java
index 504c10a..033df00 100644
--- a/bundleplugin/src/main/java/aQute/libg/reporter/Messages.java
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/Messages.java
@@ -16,14 +16,13 @@
 
 package aQute.libg.reporter;
 
-import java.io.*;
 
 public interface Messages {
 	static public class ERROR {}
 
 	static public class WARNING {}
 
-	ERROR NoSuchFile_(File f);
+	ERROR NoSuchFile_(Object r);
 
 	ERROR Unexpected_Error_(String context, Exception e);
 
diff --git a/bundleplugin/src/main/java/aQute/libg/reporter/ReporterMessages.java b/bundleplugin/src/main/java/aQute/libg/reporter/ReporterMessages.java
index 90da59b..eaf363c 100644
--- a/bundleplugin/src/main/java/aQute/libg/reporter/ReporterMessages.java
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/ReporterMessages.java
@@ -24,7 +24,6 @@
 
 public class ReporterMessages {
 
-	@SuppressWarnings("unchecked")
 	public static <T> T base(final Reporter reporter, Class<T> messages) {
 		return (T) Proxy.newProxyInstance(messages.getClassLoader(), new Class[] {
 			messages
diff --git a/bundleplugin/src/main/java/aQute/libg/tuple/ComparablePair.java b/bundleplugin/src/main/java/aQute/libg/tuple/ComparablePair.java
new file mode 100644
index 0000000..36f0242
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/tuple/ComparablePair.java
@@ -0,0 +1,14 @@
+package aQute.libg.tuple;
+
+public class ComparablePair<A extends Comparable<A>, B> extends Pair<A,B> implements Comparable<Pair<A, ? >> {
+
+	private static final long	serialVersionUID	= 1L;
+
+	public ComparablePair(A first, B second) {
+		super(first, second);
+	}
+
+	public int compareTo(Pair<A, ? > o) {
+		return getFirst().compareTo(o.getFirst());
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java b/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java
index d86d6c8..f8f7afb 100644
--- a/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java
+++ b/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java
@@ -1,11 +1,71 @@
 package aQute.libg.tuple;
 
-public class Pair<A, B> {
-	final public A	a;
-	final public B	b;
+import java.io.Serializable;
 
-	public Pair(A a, B b) {
-		this.a = a;
-		this.b = b;
+public class Pair<A, B> implements Serializable {
+
+	private static final long	serialVersionUID	= 1L;
+
+	private final A				first;
+	private final B				second;
+
+	public Pair(A first, B second) {
+		assert first != null && second != null : "both parameters must be non-null";
+		this.first = first;
+		this.second = second;
 	}
-}
+
+	public static <A, B> Pair<A,B> newInstance(A first, B second) {
+		return new Pair<A,B>(first, second);
+	}
+
+	public A getFirst() {
+		return first;
+	}
+
+	public B getSecond() {
+		return second;
+	}
+
+	@Override
+	public String toString() {
+		return "Pair [" + first + ", " + second + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((first == null) ? 0 : first.hashCode());
+		result = prime * result + ((second == null) ? 0 : second.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		@SuppressWarnings("unchecked")
+		Pair<A,B> other = (Pair<A,B>) obj;
+		if (first == null) {
+			if (other.first != null)
+				return false;
+		} else if (!first.equals(other.first))
+			return false;
+		if (second == null) {
+			if (other.second != null)
+				return false;
+		} else if (!second.equals(other.second))
+			return false;
+		return true;
+	}
+
+	@Override
+	public Pair<A,B> clone() {
+		return new Pair<A,B>(first, second);
+	}
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/libg/xslt/Transform.java b/bundleplugin/src/main/java/aQute/libg/xslt/Transform.java
index 4d5835b..a505c4b 100644
--- a/bundleplugin/src/main/java/aQute/libg/xslt/Transform.java
+++ b/bundleplugin/src/main/java/aQute/libg/xslt/Transform.java
@@ -11,20 +11,20 @@
 public class Transform {
 	static TransformerFactory	transformerFactory	= TransformerFactory.newInstance();
 
-	static Map<URL,Templates>	cache				= new ConcurrentHashMap<URL,Templates>();
+	static Map<URI,Templates>	cache				= new ConcurrentHashMap<URI,Templates>();
 
 	public static void transform(TransformerFactory transformerFactory, URL xslt, InputStream in, OutputStream out)
 			throws Exception {
 		if (xslt == null)
 			throw new IllegalArgumentException("No source template specified");
 
-		Templates templates = cache.get(xslt);
+		Templates templates = cache.get(xslt.toURI());
 		if (templates == null) {
 			InputStream xsltIn = xslt.openStream();
 			try {
 				templates = transformerFactory.newTemplates(new StreamSource(xsltIn));
 
-				cache.put(xslt, templates);
+				cache.put(xslt.toURI(), templates);
 			}
 			finally {
 				in.close();
diff --git a/bundleplugin/src/main/resources/aQute/lib/osgi/bnd.info b/bundleplugin/src/main/resources/aQute/lib/osgi/bnd.info
index b1706a5..ee80a6b 100644
--- a/bundleplugin/src/main/resources/aQute/lib/osgi/bnd.info
+++ b/bundleplugin/src/main/resources/aQute/lib/osgi/bnd.info
@@ -1,2 +1,2 @@
-modified=${currenttime}
-version=${Bundle-Version}
+version = ${Bundle-Version}
+lastmodified = ${now}