Another bnd code refresh

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1360983 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java b/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java
index 4450c39..d11fbc6 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java
@@ -5,13 +5,15 @@
 import java.util.*;
 import java.util.Map.Entry;
 
+import org.osgi.resource.Requirement;
+
 import aQute.bnd.build.model.clauses.*;
 import aQute.bnd.build.model.conversions.*;
-import aQute.lib.osgi.*;
+import aQute.lib.osgi.Constants;
 import aQute.lib.properties.*;
 import aQute.libg.header.*;
 import aQute.libg.tuple.*;
-import aQute.libg.version.*;
+import aQute.libg.version.Version;
 
 /**
  * A model for a Bnd file. In the first iteration, use a simple Properties
@@ -34,16 +36,12 @@
 			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.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
-																								};
+			aQute.lib.osgi.Constants.RUNREPOS, aQute.lib.osgi.Constants.RUNREQUIRES, aQute.lib.osgi.Constants.RUNEE};
 
 	public static final String										BUNDLE_VERSION_MACRO		= "${"
 																										+ Constants.BUNDLE_VERSION
@@ -136,27 +134,10 @@
 																										});
 
 	protected Converter<Map<String,String>,String>					propertiesConverter			= new PropertiesConverter();
+	
+	protected Converter<List<Requirement>,String>					requirementListConverter	= new RequirementListConverter();
+	protected Converter<EE,String>									eeConverter					= new EEConverter();
 
-	// 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);
 
@@ -176,21 +157,13 @@
 																										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;
-	// }
-	// };
+	
+	protected Converter<String,Collection< ? extends Requirement>>	requirementListFormatter	= new CollectionFormatter<Requirement>(
+																										LIST_SEPARATOR,
+																										new RequirementFormatter(),
+																										null);
+
+	protected Converter<String,EE>									eeFormatter					= new EEFormatter();
 	Converter<String,Collection< ? extends String>>					runReposFormatter			= new CollectionFormatter<String>(
 																										LIST_SEPARATOR,
 																										aQute.lib.osgi.Constants.EMPTY_HEADER);
@@ -214,7 +187,7 @@
 		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.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);
@@ -222,9 +195,9 @@
 		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(aQute.lib.osgi.Constants.RUNREQUIRES, requirementListConverter);
+		converters.put(aQute.lib.osgi.Constants.RUNEE, eeConverter);
+		converters.put(aQute.lib.osgi.Constants.RUNREPOS, listConverter);
 		// converters.put(BndConstants.RESOLVE_MODE, resolveModeConverter);
 
 		formatters.put(aQute.lib.osgi.Constants.BUILDPATH, headerClauseListFormatter);
@@ -240,7 +213,7 @@
 		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.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);
@@ -248,9 +221,9 @@
 		// 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(aQute.lib.osgi.Constants.RUNREQUIRES, requirementListFormatter);
+		formatters.put(aQute.lib.osgi.Constants.RUNEE, eeFormatter);
+		formatters.put(aQute.lib.osgi.Constants.RUNREPOS, runReposFormatter);
 		// formatters.put(BndConstants.RESOLVE_MODE, resolveModeFormatter);
 	}
 
@@ -472,7 +445,7 @@
 		doSetObject(Constants.EXPORT_PACKAGE, oldValue, exports, headerClauseListFormatter);
 
 		if (referencesBundleVersion && getBundleVersionString() == null) {
-			setBundleVersion(new Version(0, 0, 0).toString());
+			setBundleVersion(Version.emptyVersion.toString());
 		}
 	}
 
@@ -645,10 +618,29 @@
         return doGetObject(aQute.lib.osgi.Constants.RUNFRAMEWORK, stringConverter);
     }
 
+    public EE getEE() {
+        return doGetObject(aQute.lib.osgi.Constants.RUNEE, eeConverter);
+    }
+
+    public void setEE(EE ee) {
+        EE old = getEE();
+        doSetObject(aQute.lib.osgi.Constants.RUNEE, old, ee, eeFormatter);
+    }
+
+    
     public void setRunFramework(String clause) {
         String oldValue = getRunFramework();
         doSetObject(aQute.lib.osgi.Constants.RUNFRAMEWORK, oldValue, clause, newlineEscapeFormatter);
     }
+    
+    public List<Requirement> getRunRequires() {
+    	return doGetObject(aQute.lib.osgi.Constants.RUNREQUIRES, requirementListConverter);
+    }
+    
+    public void setRunRequires(List<Requirement> requires) {
+    	List<Requirement> oldValue = getRunRequires();
+    	doSetObject(aQute.lib.osgi.Constants.RUNREQUIRES, oldValue, requires, requirementListFormatter);
+    }
 
 
 	protected <R> R doGetObject(String name, Converter< ? extends R, ? super String> converter) {
@@ -705,4 +697,4 @@
 	public File getBndResource() {
 		return bndResource;
 	}
-}
\ No newline at end of file
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/EE.java b/bundleplugin/src/main/java/aQute/bnd/build/model/EE.java
new file mode 100644
index 0000000..e7188a9
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/EE.java
@@ -0,0 +1,40 @@
+package aQute.bnd.build.model;
+
+public enum EE {
+
+    OSGI_Minimum_1_0("OSGi/Minimum-1.0"), OSGI_Minimum_1_1("OSGi/Minimum-1.1", OSGI_Minimum_1_0), OSGI_Minimum_1_2("OSGi/Minimum-1.2", OSGI_Minimum_1_0, OSGI_Minimum_1_1),
+
+    JRE_1_1("JRE-1.1"), J2SE_1_2("J2SE-1.2", JRE_1_1), J2SE_1_3("J2SE-1.3", JRE_1_1, J2SE_1_2, OSGI_Minimum_1_0, OSGI_Minimum_1_1), J2SE_1_4("J2SE-1.4", JRE_1_1, J2SE_1_2, J2SE_1_3, OSGI_Minimum_1_0, OSGI_Minimum_1_1, OSGI_Minimum_1_2), J2SE_1_5(
+            "J2SE-1.5", JRE_1_1, J2SE_1_2, J2SE_1_3, J2SE_1_4, OSGI_Minimum_1_0, OSGI_Minimum_1_1, OSGI_Minimum_1_2),
+
+    JavaSE_1_6("JavaSE-1.6", JRE_1_1, J2SE_1_2, J2SE_1_3, J2SE_1_4, J2SE_1_5, OSGI_Minimum_1_0, OSGI_Minimum_1_1, OSGI_Minimum_1_2), JavaSE_1_7("JavaSE-1.7", JRE_1_1, J2SE_1_2, J2SE_1_3, J2SE_1_4, J2SE_1_5, JavaSE_1_6, OSGI_Minimum_1_0,
+            OSGI_Minimum_1_1, OSGI_Minimum_1_2), JavaSE_1_8("JavaSE-1.8", JRE_1_1, J2SE_1_2, J2SE_1_3, J2SE_1_4, J2SE_1_5, JavaSE_1_6, JavaSE_1_7, OSGI_Minimum_1_0, OSGI_Minimum_1_1, OSGI_Minimum_1_2), JavaSE_1_9("JavaSE-1.9", JRE_1_1,
+            J2SE_1_2, J2SE_1_3, J2SE_1_4, J2SE_1_5, JavaSE_1_6, JavaSE_1_7, JavaSE_1_8, OSGI_Minimum_1_0, OSGI_Minimum_1_1, OSGI_Minimum_1_2);
+
+    private final String eeName;
+    private final EE[] compatible;
+
+    EE(String name, EE... compatible) {
+        eeName = name;
+        this.compatible = compatible;
+    }
+
+    public String getEEName() {
+        return eeName;
+    }
+
+    /**
+     * @return An array of EEs that this EE implicitly offers, through backwards compatibility.
+     */
+    public EE[] getCompatible() {
+        return compatible != null ? compatible : new EE[0];
+    }
+
+    public static EE parse(String str) {
+        for (EE ee : values()) {
+            if (ee.eeName.equals(str))
+                return ee;
+        }
+        throw new IllegalArgumentException("Unrecognised execution environment name: " + str);
+    }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EEConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EEConverter.java
new file mode 100644
index 0000000..de6d2ea
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EEConverter.java
@@ -0,0 +1,11 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.EE;
+
+public class EEConverter implements Converter<EE,String> {
+
+	public EE convert(String input) throws IllegalArgumentException {
+		return EE.parse(input);
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EEFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EEFormatter.java
new file mode 100644
index 0000000..1853685
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EEFormatter.java
@@ -0,0 +1,9 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.EE;
+
+public final class EEFormatter implements Converter<String,EE> {
+	public String convert(EE input) throws IllegalArgumentException {
+		return input != null ? input.getEEName() : null;
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/RequirementFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/RequirementFormatter.java
new file mode 100644
index 0000000..b64f1bd
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/RequirementFormatter.java
@@ -0,0 +1,25 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.Map.Entry;
+
+import org.osgi.resource.Requirement;
+
+public class RequirementFormatter implements Converter<String,Requirement> {
+
+	public String convert(Requirement req) throws IllegalArgumentException {
+		StringBuilder builder = new StringBuilder();
+		
+		builder.append(req.getNamespace());
+		
+		for (Entry<String,String> directive : req.getDirectives().entrySet()) {
+			builder.append(';').append(directive.getKey()).append(":=").append(directive.getValue());
+		}
+		
+		for (Entry<String,Object> attribute : req.getAttributes().entrySet()) {
+			builder.append(';').append(attribute.getKey()).append("=").append(attribute.getValue());
+		}
+		
+		return builder.toString();
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/RequirementListConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/RequirementListConverter.java
new file mode 100644
index 0000000..6249c0f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/RequirementListConverter.java
@@ -0,0 +1,32 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.Map.Entry;
+
+import org.osgi.resource.Requirement;
+
+import aQute.lib.osgi.resource.CapReqBuilder;
+import aQute.libg.header.Attrs;
+import aQute.libg.tuple.Pair;
+
+public class RequirementListConverter extends ClauseListConverter<Requirement> {
+
+	public RequirementListConverter() {
+		super(new Converter<Requirement,Pair<String,Attrs>>() {
+			public Requirement convert(Pair<String,Attrs> input) throws IllegalArgumentException {
+				String namespace = input.getFirst();
+				CapReqBuilder builder = new CapReqBuilder(namespace);
+				for (Entry<String,String> entry : input.getSecond().entrySet()) {
+					String key = entry.getKey();
+					if (key.endsWith(":")) {
+						key = key.substring(0, key.length() - 1);
+						builder.addDirective(key, entry.getValue());
+					} else {
+						builder.addAttribute(key, entry.getValue());
+					}
+				}
+				return builder.buildSyntheticRequirement();
+			}
+		});
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/differ/Baseline.java b/bundleplugin/src/main/java/aQute/bnd/differ/Baseline.java
index fd27e41..4319b6b 100644
--- a/bundleplugin/src/main/java/aQute/bnd/differ/Baseline.java
+++ b/bundleplugin/src/main/java/aQute/bnd/differ/Baseline.java
@@ -34,6 +34,12 @@
 	final Differ	differ;
 	final Reporter	bnd;
 	Diff			diff;
+	Set<Info>		infos;
+	String 			bsn;
+	Version			newerVersion;
+	Version			olderVersion;
+	Version			suggestedVersion;
+	String			releaseRepository;
 
 	public Baseline(Reporter bnd, Differ differ) throws IOException {
 		this.differ = differ;
@@ -66,8 +72,14 @@
 			throws Exception {
 		diff = n.diff(o);
 		Diff apiDiff = diff.get("<api>");
-		Set<Info> infos = Create.set();
+		infos = Create.set();
 
+		bsn = getBsn(n);
+
+		newerVersion = getVersion(n);
+		olderVersion = getVersion(o);
+		
+		Delta highestDelta = Delta.MICRO;
 		for (Diff pdiff : apiDiff.getChildren()) {
 			if (pdiff.getType() != Type.PACKAGE) // Just packages
 				continue;
@@ -91,7 +103,7 @@
 			if (pdiff.getDelta() == Delta.UNCHANGED) {
 				info.suggestedVersion = info.olderVersion;
 				if (!info.newerVersion.equals(info.olderVersion)) {
-					info.warning += "No difference but versions are equal";
+					info.warning += "No difference but versions are not equal";
 				}
 			} else if (pdiff.getDelta() == Delta.REMOVED) {
 				info.suggestedVersion = null;
@@ -135,6 +147,13 @@
 					}
 				}
 			}
+			if (pdiff.getDelta().compareTo(highestDelta) > 0) {
+				highestDelta = pdiff.getDelta();
+			}
+		}
+		suggestedVersion = bumpBundle(highestDelta, olderVersion, 1, 0);
+		if (suggestedVersion.getMajor() == 0) {
+			suggestedVersion = Version.ONE;
 		}
 		return infos;
 	}
@@ -148,6 +167,40 @@
 		return diff;
 	}
 
+	public Set<Info> getPackageInfos() {
+		if (infos == null)
+			return Collections.emptySet();
+		return infos;
+	}
+
+	public String getBsn() {
+		return bsn;
+	}
+
+	public Version getSuggestedVersion() {
+		return suggestedVersion;
+	}
+
+	public void setSuggestedVersion(Version suggestedVersion) {
+		this.suggestedVersion = suggestedVersion;
+	}
+
+	public Version getNewerVersion() {
+		return newerVersion;
+	}
+
+	public Version getOlderVersion() {
+		return olderVersion;
+	}
+
+	public String getReleaseRepository() {
+		return releaseRepository;
+	}
+
+	public void setReleaseRepository(String releaseRepository) {
+		this.releaseRepository = releaseRepository;
+	}
+
 	private Version bump(Delta delta, Version last, int offset, int base) {
 		switch (delta) {
 			case UNCHANGED :
@@ -177,5 +230,43 @@
 
 		return OSGiHeader.parseHeader(m.getMainAttributes().getValue(Constants.EXPORT_PACKAGE));
 	}
+	
+	private Version getVersion(Tree top) {
+		Tree manifest = top.get("<manifest>");
+		if (manifest == null) {
+			return Version.emptyVersion;
+		}
+		for (Tree tree : manifest.getChildren()) {
+			if (tree.getName().startsWith(Constants.BUNDLE_VERSION)) {
+				return Version.parseVersion(tree.getName().substring(15));
+			}
+		}
+		return Version.emptyVersion;
+	}
 
+	private String getBsn(Tree top) {
+		Tree manifest = top.get("<manifest>");
+		if (manifest == null) {
+			return "";
+		}
+		for (Tree tree : manifest.getChildren()) {
+			if (tree.getName().startsWith(Constants.BUNDLE_SYMBOLICNAME) && tree.getChildren().length > 0) {
+				return tree.getChildren()[0].getName();
+			}
+		}
+		return "";
+	}
+
+	private Version bumpBundle(Delta delta, Version last, int offset, int base) {
+		switch (delta) {
+			case MINOR :
+				return new Version(last.getMajor(), last.getMinor() + offset, base);
+			case MAJOR :
+				return new Version(last.getMajor() + 1, base, base);
+			case ADDED :
+				return new Version(last.getMajor(), last.getMinor() + offset, base);
+			default :
+				return new Version(last.getMajor(), last.getMinor(), last.getMicro() + offset);
+		}
+	}
 }
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenCommand.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenCommand.java
index edd212b..3717062 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/MavenCommand.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenCommand.java
@@ -77,9 +77,9 @@
 	}
 
 	private void help() {
-		System.err.println("Usage:%n");
+		System.err.printf("Usage:%n");
 		System.err
-				.println("  maven %n" //
+				.printf("  maven %n" //
 						+ "  [-temp <dir>]            use as temp directory%n" //
 						+ "  settings                 show maven settings%n" //
 						+ "  bundle                   turn a bundle into a maven bundle%n" //
@@ -93,7 +93,7 @@
 						+ "    [-developer <email>]   developer email%n" //
 						+ "    [-nodelete]            do not delete temp files%n" //
 						+ "    [-passphrase <gpgp passphrase>] signer password%n"//
-						+ "        <file|url> ");
+						+ "        <file|url>%n");
 	}
 
 	/**
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
index da4eff0..8f80562 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
@@ -30,7 +30,7 @@
 	 */
 	void run(String args[], int i) throws Exception {
 		if (i >= args.length) {
-			System.err.println("Usage:%n");
+			System.err.printf("Usage:%n");
 			System.err
 					.println("  deploy [-url repo] [-passphrase passphrase] [-homedir homedir] [-keyname keyname] bundle ...");
 			System.err.println("  settings");
diff --git a/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java b/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java
index 600c9a8..f885d4d 100644
--- a/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java
+++ b/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java
@@ -141,7 +141,7 @@
 					if (algorithms[a] != null) {
 						byte[] digest = algorithms[a].digest();
 						String header = digestNames[a] + "-Digest: " + new Base64(digest) + "\r\n";
-						out.write(header.getBytes());
+						out.write(header.getBytes("UTF-8"));
 					}
 				}
 			}
diff --git a/bundleplugin/src/main/java/aQute/lib/data/Data.java b/bundleplugin/src/main/java/aQute/lib/data/Data.java
index f44d1a7..9d318b8 100644
--- a/bundleplugin/src/main/java/aQute/lib/data/Data.java
+++ b/bundleplugin/src/main/java/aQute/lib/data/Data.java
@@ -18,7 +18,7 @@
 			Object value = f.get(o);
 			if (value == null) {
 				if (allowNull == null)
-					formatter.format("Value for %s must not be null\n", f.getName());
+					formatter.format("Value for %s must not be null%n", f.getName());
 			} else {
 
 				if (patternValidator != null) {
@@ -27,10 +27,10 @@
 					if (!m.matches()) {
 						String reason = patternValidator.reason();
 						if (reason.length() == 0)
-							formatter.format("Value for %s=%s does not match pattern %s\n", f.getName(), value,
+							formatter.format("Value for %s=%s does not match pattern %s%n", f.getName(), value,
 									patternValidator.value());
 						else
-							formatter.format("Value for %s=%s %s\n", f.getName(), value, reason);
+							formatter.format("Value for %s=%s %s%n", f.getName(), value, reason);
 					}
 				}
 
@@ -40,7 +40,7 @@
 							o = Double.parseDouble((String) o);
 						}
 						catch (Exception e) {
-							formatter.format("Value for %s=%s %s\n", f.getName(), value, "Not a number");
+							formatter.format("Value for %s=%s %s%n", f.getName(), value, "Not a number");
 						}
 					}
 
@@ -48,12 +48,12 @@
 						Number n = (Number) o;
 						long number = n.longValue();
 						if (number >= numericValidator.min() && number < numericValidator.max()) {
-							formatter.format("Value for %s=%s not in valid range (%s,%s]\n", f.getName(), value,
+							formatter.format("Value for %s=%s not in valid range (%s,%s]%n", f.getName(), value,
 									numericValidator.min(), numericValidator.max());
 						}
 					}
 					catch (ClassCastException e) {
-						formatter.format("Value for %s=%s [%s,%s) is not a number\n", f.getName(), value,
+						formatter.format("Value for %s=%s [%s,%s) is not a number%n", f.getName(), value,
 								numericValidator.min(), numericValidator.max());
 					}
 				}
@@ -74,7 +74,7 @@
 		for (Field f : fields) {
 			String name = f.getName();
 			name = Character.toUpperCase(name.charAt(0)) + name.substring(1);
-			formatter.format("%-40s %s\n", name, f.get(data));
+			formatter.format("%-40s %s%n", name, f.get(data));
 		}
 	}
 }
diff --git a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
index e1b7560..db81599 100644
--- a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
+++ b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
@@ -316,11 +316,11 @@
 
 		String description = descr == null ? "" : descr.value();
 
-		f.format("NAME\n  %s - %s\n\n", cmd, description);
-		f.format("SYNOPSIS\n   %s [options] ", cmd);
+		f.format("NAME%n  %s - %s%n%n", cmd, description);
+		f.format("SYNOPSIS%n   %s [options] ", cmd);
 
 		if (patterns == null)
-			f.format(" ...\n\n");
+			f.format(" ...%n%n");
 		else {
 			String del = " ";
 			for (String pattern : patterns.arg()) {
@@ -330,10 +330,10 @@
 					f.format("%s<%s>", del, pattern);
 				del = " ";
 			}
-			f.format("\n\n");
+			f.format("%n%n");
 		}
 
-		f.format("OPTIONS\n");
+		f.format("OPTIONS%n");
 		for (Entry<String,Method> entry : options.entrySet()) {
 			String optionName = entry.getKey();
 			Method m = entry.getValue();
@@ -344,14 +344,14 @@
 
 			String methodDescription = cfg != null ? cfg.description() : (d == null ? "" : d.value());
 
-			f.format("   %s -%s, --%s %s%s - %s\n", required ? " " : "[", //
+			f.format("   %s -%s, --%s %s%s - %s%n", required ? " " : "[", //
 					optionName.charAt(0), //
 					optionName, //
 					getTypeDescriptor(m.getGenericReturnType()), //
 					required ? " " : "]",//
 					methodDescription);
 		}
-		f.format("\n");
+		f.format("%n");
 	}
 
 	static Pattern	LAST_PART	= Pattern.compile(".*[\\$\\.]([^\\$\\.]+)");
@@ -370,7 +370,7 @@
 		// TODO get help from the class
 		Description descr = target.getClass().getAnnotation(Description.class);
 		if (descr != null) {
-			f.format("%s\n\n", descr.value());
+			f.format("%s%n%n", descr.value());
 		}
 		f.format("Available commands: ");
 
@@ -379,7 +379,7 @@
 			f.format("%s%s", del, name);
 			del = ", ";
 		}
-		f.format("\n");
+		f.format("%n");
 
 	}
 
@@ -390,7 +390,7 @@
 
 		Method m = getCommands(target).get(cmd);
 		if (m == null)
-			f.format("No such command: %s\n", cmd);
+			f.format("No such command: %s%n", cmd);
 		else {
 			Class< ? extends Options> options = (Class< ? extends Options>) m.getParameterTypes()[0];
 			help(f, target, cmd, options);
diff --git a/bundleplugin/src/main/java/aQute/lib/index/Index.java b/bundleplugin/src/main/java/aQute/lib/index/Index.java
index 1781798..02bb37c 100644
--- a/bundleplugin/src/main/java/aQute/lib/index/Index.java
+++ b/bundleplugin/src/main/java/aQute/lib/index/Index.java
@@ -254,7 +254,7 @@
 
 		public void toString(StringBuilder sb, String indent) throws IOException {
 			for (int i = 0; i < n; i++) {
-				sb.append(String.format("%s %02d:%02d %20s %s %d\n", indent, number, i, hex(k(i), 0, 4), leaf ? "=="
+				sb.append(String.format("%s %02d:%02d %20s %s %d%n", indent, number, i, hex(k(i), 0, 4), leaf ? "=="
 						: "->", c(i)));
 				if (!leaf) {
 					long c = c(i);
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
index b31be5c..152f326 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
@@ -15,6 +15,7 @@
 import aQute.bnd.make.metatype.*;
 import aQute.bnd.maven.*;
 import aQute.bnd.service.*;
+import aQute.bnd.service.RepositoryPlugin.Strategy;
 import aQute.bnd.service.diff.*;
 import aQute.lib.collections.*;
 import aQute.lib.osgi.Descriptors.PackageRef;
@@ -1504,23 +1505,17 @@
 
 		System.err.printf("baseline %s%n", diffs);
 
+		Jar other = getBaselineJar();
+		if (other == null) {
+			return;
+		}
 		Baseline baseline = new Baseline(this, differ);
-
-		for (Entry<String,Attrs> entry : diffs.entrySet()) {
-			String path = entry.getKey();
-			File file = getFile(path);
-			if (!file.isFile()) {
-				error("Diffing against %s that is not a file", file);
-				continue;
-			}
-			Jar other = new Jar(file);
-			Set<Info> infos = baseline.baseline(dot, other, null);
-			for (Info info : infos) {
-				if (info.mismatch) {
-					error("%s %-50s %-10s %-10s %-10s %-10s %-10s\n", info.mismatch ? '*' : ' ', info.packageName,
-							info.packageDiff.getDelta(), info.newerVersion, info.olderVersion, info.suggestedVersion,
-							info.suggestedIfProviders == null ? "-" : info.suggestedIfProviders);
-				}
+		Set<Info> infos = baseline.baseline(dot, other, null);
+		for (Info info : infos) {
+			if (info.mismatch) {
+				error("%s %-50s %-10s %-10s %-10s %-10s %-10s\n", info.mismatch ? '*' : ' ', info.packageName,
+						info.packageDiff.getDelta(), info.newerVersion, info.olderVersion, info.suggestedVersion,
+						info.suggestedIfProviders == null ? "-" : info.suggestedIfProviders);
 			}
 		}
 	}
@@ -1531,4 +1526,50 @@
 		}
 	}
 
+	public Jar getBaselineJar() throws Exception {
+
+		List<RepositoryPlugin> repos = getPlugins(RepositoryPlugin.class);
+
+		Parameters diffs = parseHeader(getProperty("-baseline"));
+		File baselineFile = null;
+		if (diffs.isEmpty()) {
+			String repoName = getProperty("-baseline-repo");
+			if (repoName == null) {
+				return null;
+			}
+			for (RepositoryPlugin repo : repos) {
+				if (repoName.equals(repo.getName())) {
+					baselineFile = repo.get(getBsn(), null, Strategy.HIGHEST, null);
+					break;
+				}
+			}
+		} else {
+
+			String bsn = null;
+			String version = null;
+			for (Entry<String,Attrs> entry : diffs.entrySet()) {
+				bsn = entry.getKey();
+				if ("@".equals(bsn)) {
+					bsn = getBsn();
+				}
+				version = entry.getValue().get(Constants.VERSION_ATTRIBUTE);
+				break;
+			}
+	
+			for (RepositoryPlugin repo : repos) {
+				if (version == null) {
+					baselineFile = repo.get(bsn, null, Strategy.HIGHEST, null);
+				} else {
+					baselineFile = repo.get(bsn, version, Strategy.EXACT, null);
+				}
+				if (baselineFile != null) {
+					break;
+				}
+			}
+		}
+		if (baselineFile == null) {
+			return new Jar(".");
+		}
+		return new Jar(baselineFile);
+	}
 }
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java b/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
index 7454087..767e516 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
@@ -110,6 +110,16 @@
 	String							RUNSYSTEMPACKAGES							= "-runsystempackages";
 	String							RUNBUNDLES									= "-runbundles";
 	String							RUNREPOS									= "-runrepos";
+
+	/**
+	 * @deprecated This is for support of the legacy OBR requirement format, use {@link #RUNREQUIRES} for new format.
+	 */
+	@Deprecated
+	String							RUNREQUIRE									= "-runrequire";
+	
+	String							RUNREQUIRES									= "-runrequires";
+	
+	String							RUNEE										= "-runee";
 	String							RUNPATH										= "-runpath";
 	String							RUNSTORAGE									= "-runstorage";
 	String							RUNBUILDS									= "-runbuilds";
@@ -250,7 +260,6 @@
 	Charset							DEFAULT_CHARSET								= Charset.forName("UTF8");
 	String							VERSION_FILTER								= "version";
 	String							PROVIDER_TYPE_DIRECTIVE						= "x-provider-type:";
-
 	/**
 	 * Component constants
 	 */
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java b/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java
index 3e109d6..8730b69 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java
@@ -854,7 +854,7 @@
 				if (patterns[i] != null) {
 					Matcher m = patterns[i].matcher(args[i]);
 					if (!m.matches())
-						message += String.format("Argument %s (%s) does not match %s\n", i, args[i],
+						message += String.format("Argument %s (%s) does not match %s%n", i, args[i],
 								patterns[i].pattern());
 				}
 			}
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java b/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
index c6b5dd9..ef4013f 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
@@ -1254,15 +1254,15 @@
 
 	protected void report(Appendable out) throws IOException {
 		if (errors.size() > 0) {
-			out.append("-----------------\nErrors\n");
+			out.append(String.format("-----------------%nErrors%n"));
 			for (int i = 0; i < errors.size(); i++) {
-				out.append(String.format("%03d: %s\n", i, errors.get(i)));
+				out.append(String.format("%03d: %s%n", i, errors.get(i)));
 			}
 		}
 		if (warnings.size() > 0) {
-			out.append(String.format("-----------------\nWarnings\n"));
+			out.append(String.format("-----------------%nWarnings%n"));
 			for (int i = 0; i < warnings.size(); i++) {
-				out.append(String.format("%03d: %s\n", i, warnings.get(i)));
+				out.append(String.format("%03d: %s%n", i, warnings.get(i)));
 			}
 		}
 	}
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReq.java b/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReq.java
index 3f07825..c175060 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReq.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReq.java
@@ -5,6 +5,7 @@
 import java.util.Map;
 
 import org.osgi.resource.Capability;
+import org.osgi.resource.Namespace;
 import org.osgi.resource.Requirement;
 import org.osgi.resource.Resource;
 
@@ -90,8 +91,18 @@
 
 	@Override
 	public String toString() {
-		return mode + " [namespace=" + namespace + ", resource=" + System.identityHashCode(resource) + ", directives="
-				+ directives + ", attributes=" + attributes + "]";
+		StringBuilder builder = new StringBuilder();
+		if (mode == MODE.Capability) {
+			Object value = attributes.get(namespace);
+			builder.append(namespace).append('=').append(value);
+		} else {
+			String filter = directives.get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
+			builder.append(filter);
+			if (Namespace.RESOLUTION_OPTIONAL.equals(directives.get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE))) {
+				builder.append("%OPT");
+			}
+		}
+		return builder.toString();
 	}
 
 }
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReqBuilder.java b/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReqBuilder.java
index 654c7de..1e259c9 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReqBuilder.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/resource/CapReqBuilder.java
@@ -26,6 +26,20 @@
 		this.namespace = namespace;
 	}
 	
+	public static CapReqBuilder clone(Capability capability) {
+		CapReqBuilder builder = new CapReqBuilder(capability.getNamespace());
+		builder.addAttributes(capability.getAttributes());
+		builder.addDirectives(capability.getDirectives());
+		return builder;
+	}
+	
+	public static CapReqBuilder clone(Requirement requirement) {
+		CapReqBuilder builder = new CapReqBuilder(requirement.getNamespace());
+		builder.addAttributes(requirement.getAttributes());
+		builder.addDirectives(requirement.getDirectives());
+		return builder;
+	}
+	
 	public String getNamespace() {
 		return namespace;
 	}
@@ -39,12 +53,22 @@
 		attributes.put(name, value);
 		return this;
 	}
+	
+	public CapReqBuilder addAttributes(Map<? extends String, ? extends Object> attributes) {
+		this.attributes.putAll(attributes);
+		return this;
+	}
 
 	public CapReqBuilder addDirective(String name, String value) {
 		directives.put(name, value);
 		return this;
 	}
 	
+	public CapReqBuilder addDirectives(Map<? extends String, ? extends String> directives) {
+		this.directives.putAll(directives);
+		return this;
+	}
+	
 	public Capability buildCapability() {
 		// TODO check the thrown exception
 		if (resource == null) throw new IllegalStateException("Cannot build Capability with null Resource.");
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceBuilder.java b/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceBuilder.java
index cd9b982..9e9cde3 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceBuilder.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceBuilder.java
@@ -15,6 +15,11 @@
 
 	private boolean					built			= false;
 
+	public ResourceBuilder addCapability(Capability capability) {
+		CapReqBuilder builder = CapReqBuilder.clone(capability);
+		return addCapability(builder);
+	}
+	
 	public ResourceBuilder addCapability(CapReqBuilder builder) {
 		if (built)
 			throw new IllegalStateException("Resource already built");
@@ -24,7 +29,12 @@
 
 		return this;
 	}
-
+	
+	public ResourceBuilder addRequirement(Requirement requirement) {
+		CapReqBuilder builder = CapReqBuilder.clone(requirement);
+		return addRequirement(builder);
+	}
+	
 	public ResourceBuilder addRequirement(CapReqBuilder builder) {
 		if (built)
 			throw new IllegalStateException("Resource already built");
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceImpl.java b/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceImpl.java
index 3fa6218..7305148 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceImpl.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/resource/ResourceImpl.java
@@ -5,6 +5,7 @@
 import java.util.List;
 import java.util.Map;
 
+import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.resource.Capability;
 import org.osgi.resource.Requirement;
 import org.osgi.resource.Resource;
@@ -55,12 +56,22 @@
 
 	@Override
 	public String toString() {
-		StringBuilder builder = new StringBuilder();
-		builder.append("ResourceImpl [caps=");
-		builder.append(allCapabilities);
-		builder.append(", reqs=");
-		builder.append(allRequirements);
-		builder.append("]");
+		final StringBuilder builder = new StringBuilder();
+		List<Capability> identities = getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+		if (identities != null && identities.size() == 1) {
+			Capability idCap = identities.get(0);
+			Object id = idCap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE);
+			Object version = idCap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+			
+			builder.append(id).append(" ver=").append(version);
+		} else {
+			// Generic toString
+			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/properties/Region.java b/bundleplugin/src/main/java/aQute/lib/properties/Region.java
index abf567c..5ece58e 100644
--- a/bundleplugin/src/main/java/aQute/lib/properties/Region.java
+++ b/bundleplugin/src/main/java/aQute/lib/properties/Region.java
@@ -2,19 +2,21 @@
 
 public class Region implements IRegion {
 
-	private final int	length;
 	private final int	offset;
+	private final int	length;
 
-	public Region(int length, int offset) {
-		this.length = length;
+	public Region(int offset, int length) {
 		this.offset = offset;
+		this.length = length;
+	}
+
+	public int getOffset() {
+		return offset;
+
 	}
 
 	public int getLength() {
 		return length;
 	}
 
-	public int getOffset() {
-		return offset;
-	}
 }
diff --git a/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java b/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java
index 1f29467..86740ca 100755
--- a/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java
+++ b/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java
@@ -47,9 +47,9 @@
 	}
 
 	final String		path;
-	final static String	NUM_COLUMN	= "%-30s %d\n";
-	final static String	HEX_COLUMN	= "%-30s %x\n";
-	final static String	STR_COLUMN	= "%-30s %s\n";
+	final static String	NUM_COLUMN	= "%-30s %d%n";
+	final static String	HEX_COLUMN	= "%-30s %x%n";
+	final static String	STR_COLUMN	= "%-30s %s%n";
 
 	PrintStream			ps			= System.err;
 	Object[]			pool;
@@ -90,13 +90,13 @@
 
 			switch (tag) {
 				case 0 :
-					ps.printf("%30d tag (0)\n", poolIndex);
+					ps.printf("%30d tag (0)%n", poolIndex);
 					break process;
 
 				case 1 :
 					String name = in.readUTF();
 					pool[poolIndex] = name;
-					ps.printf("%30d tag(1) utf8 '%s'\n", poolIndex, name);
+					ps.printf("%30d tag(1) utf8 '%s'%n", poolIndex, name);
 					break;
 
 				case 2 :
@@ -105,13 +105,13 @@
 				case 3 :
 					int i = in.readInt();
 					pool[poolIndex] = Integer.valueOf(i);
-					ps.printf("%30d tag(3) int %s\n", poolIndex, i);
+					ps.printf("%30d tag(3) int %s%n", poolIndex, i);
 					break;
 
 				case 4 :
 					float f = in.readFloat();
 					pool[poolIndex] = new Float(f);
-					ps.printf("%30d tag(4) float %s\n", poolIndex, f);
+					ps.printf("%30d tag(4) float %s%n", poolIndex, f);
 					break;
 
 				// For some insane optimization reason are
@@ -120,48 +120,48 @@
 				case 5 :
 					long l = in.readLong();
 					pool[poolIndex] = Long.valueOf(l);
-					ps.printf("%30d tag(5) long %s\n", poolIndex, l);
+					ps.printf("%30d tag(5) long %s%n", poolIndex, l);
 					poolIndex++;
 					break;
 
 				case 6 :
 					double d = in.readDouble();
 					pool[poolIndex] = new Double(d);
-					ps.printf("%30d tag(6) double %s\n", poolIndex, d);
+					ps.printf("%30d tag(6) double %s%n", poolIndex, d);
 					poolIndex++;
 					break;
 
 				case 7 :
 					int class_index = in.readUnsignedShort();
 					pool[poolIndex] = Integer.valueOf(class_index);
-					ps.printf("%30d tag(7) constant classs %d\n", poolIndex, class_index);
+					ps.printf("%30d tag(7) constant classs %d%n", poolIndex, class_index);
 					break;
 
 				case 8 :
 					int string_index = in.readUnsignedShort();
 					pool[poolIndex] = Integer.valueOf(string_index);
-					ps.printf("%30d tag(8) constant string %d\n", poolIndex, string_index);
+					ps.printf("%30d tag(8) constant string %d%n", poolIndex, string_index);
 					break;
 
 				case 9 : // Field ref
 					class_index = in.readUnsignedShort();
 					int name_and_type_index = in.readUnsignedShort();
 					pool[poolIndex] = new Assoc((byte) 9, class_index, name_and_type_index);
-					ps.printf("%30d tag(9) field ref %d/%d\n", poolIndex, class_index, name_and_type_index);
+					ps.printf("%30d tag(9) field ref %d/%d%n", poolIndex, class_index, name_and_type_index);
 					break;
 
 				case 10 : // Method ref
 					class_index = in.readUnsignedShort();
 					name_and_type_index = in.readUnsignedShort();
 					pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index);
-					ps.printf("%30d tag(10) method ref %d/%d\n", poolIndex, class_index, name_and_type_index);
+					ps.printf("%30d tag(10) method ref %d/%d%n", poolIndex, class_index, name_and_type_index);
 					break;
 
 				case 11 : // Interface and Method ref
 					class_index = in.readUnsignedShort();
 					name_and_type_index = in.readUnsignedShort();
 					pool[poolIndex] = new Assoc((byte) 11, class_index, name_and_type_index);
-					ps.printf("%30d tag(11) interface and method ref %d/%d\n", poolIndex, class_index,
+					ps.printf("%30d tag(11) interface and method ref %d/%d%n", poolIndex, class_index,
 							name_and_type_index);
 					break;
 
@@ -170,7 +170,7 @@
 					int name_index = in.readUnsignedShort();
 					int descriptor_index = in.readUnsignedShort();
 					pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
-					ps.printf("%30d tag(12) name and type %d/%d\n", poolIndex, name_index, descriptor_index);
+					ps.printf("%30d tag(12) name and type %d/%d%n", poolIndex, name_index, descriptor_index);
 					break;
 
 				default :
@@ -182,8 +182,8 @@
 		printAccess(access);
 		int this_class = in.readUnsignedShort();
 		int super_class = in.readUnsignedShort();
-		ps.printf("%-30s %x %s(#%d)\n", "this_class", access, pool[this_class], this_class);
-		ps.printf("%-30s %s(#%d)\n", "super_class", pool[super_class], super_class);
+		ps.printf("%-30s %x %s(#%d)%n", "this_class", access, pool[this_class], this_class);
+		ps.printf("%-30s %s(#%d)%n", "super_class", pool[super_class], super_class);
 
 		int interfaces_count = in.readUnsignedShort();
 		ps.printf(NUM_COLUMN, "interface count", interfaces_count);
@@ -199,7 +199,7 @@
 			printAccess(access);
 			int name_index = in.readUnsignedShort();
 			int descriptor_index = in.readUnsignedShort();
-			ps.printf("%-30s %x %s(#%d) %s(#%d)\n", "field def", access, pool[name_index], name_index,
+			ps.printf("%-30s %x %s(#%d) %s(#%d)%n", "field def", access, pool[name_index], name_index,
 					pool[descriptor_index], descriptor_index);
 			doAttributes(in, "  ");
 		}
@@ -211,7 +211,7 @@
 			printAccess(access_flags);
 			int name_index = in.readUnsignedShort();
 			int descriptor_index = in.readUnsignedShort();
-			ps.printf("%-30s %x %s(#%d) %s(#%d)\n", "method def", access_flags, pool[name_index], name_index,
+			ps.printf("%-30s %x %s(#%d) %s(#%d)%n", "method def", access_flags, pool[name_index], name_index,
 					pool[descriptor_index], descriptor_index);
 			doAttributes(in, "  ");
 		}
@@ -248,7 +248,7 @@
 		long attribute_length = in.readInt();
 		attribute_length &= 0xFFFF;
 		String attributeName = (String) pool[attribute_name_index];
-		ps.printf("%-30s %s(#%d)\n", indent + "attribute", attributeName, attribute_name_index);
+		ps.printf("%-30s %s(#%d)%n", indent + "attribute", attributeName, attribute_name_index);
 		if ("RuntimeVisibleAnnotations".equals(attributeName))
 			doAnnotations(in, indent);
 		else if ("SourceFile".equals(attributeName))
@@ -272,7 +272,7 @@
 		else if ("Deprecated".equals(attributeName))
 			; // Is Empty
 		else {
-			ps.printf("%-30s %d\n", indent + "Unknown attribute, skipping", attribute_length);
+			ps.printf("%-30s %d%n", indent + "Unknown attribute, skipping", attribute_length);
 			if (attribute_length > 0x7FFFFFFF) {
 				throw new IllegalArgumentException("Attribute > 2Gb");
 			}
@@ -296,7 +296,7 @@
 	 */
 	void doSignature(DataInputStream in, String indent) throws IOException {
 		int signature_index = in.readUnsignedShort();
-		ps.printf("%-30s %s(#%d)\n", indent + "signature", pool[signature_index], signature_index);
+		ps.printf("%-30s %s(#%d)%n", indent + "signature", pool[signature_index], signature_index);
 	}
 
 	/**
@@ -313,7 +313,7 @@
 	void doEnclosingMethod(DataInputStream in, String indent) throws IOException {
 		int class_index = in.readUnsignedShort();
 		int method_index = in.readUnsignedShort();
-		ps.printf("%-30s %s(#%d/c) %s\n", //
+		ps.printf("%-30s %s(#%d/c) %s%n", //
 				indent + "enclosing method", //
 				pool[((Integer) pool[class_index]).intValue()], //
 				class_index, //
@@ -347,7 +347,7 @@
 			sb.append("/c)");
 			del = ", ";
 		}
-		ps.printf("%-30s %d: %s\n", indent + "exceptions", number_of_exceptions, sb);
+		ps.printf("%-30s %d: %s%n", indent + "exceptions", number_of_exceptions, sb);
 	}
 
 	/**
@@ -392,7 +392,7 @@
 			int end_pc = in.readUnsignedShort();
 			int handler_pc = in.readUnsignedShort();
 			int catch_type = in.readUnsignedShort();
-			ps.printf("%-30s %d/%d/%d/%d\n", indent + "exception_table", start_pc, end_pc, handler_pc, catch_type);
+			ps.printf("%-30s %d/%d/%d/%d%n", indent + "exception_table", start_pc, end_pc, handler_pc, catch_type);
 		}
 		doAttributes(in, indent + "  ");
 	}
@@ -419,7 +419,7 @@
 
 	private void doSourceFile(DataInputStream in, String indent) throws IOException {
 		int sourcefile_index = in.readUnsignedShort();
-		ps.printf("%-30s %s(#%d)\n", indent + "Source file", pool[sourcefile_index], sourcefile_index);
+		ps.printf("%-30s %s(#%d)%n", indent + "Source file", pool[sourcefile_index], sourcefile_index);
 	}
 
 	private void doAnnotations(DataInputStream in, String indent) throws IOException {
@@ -455,30 +455,30 @@
 			case 'Z' :
 			case 's' :
 				int const_value_index = in.readUnsignedShort();
-				ps.printf("%-30s %c %s(#%d)\n", indent + "element value", tag, pool[const_value_index],
+				ps.printf("%-30s %c %s(#%d)%n", indent + "element value", tag, pool[const_value_index],
 						const_value_index);
 				break;
 
 			case 'e' :
 				int type_name_index = in.readUnsignedShort();
 				int const_name_index = in.readUnsignedShort();
-				ps.printf("%-30s %c %s(#%d) %s(#%d)\n", indent + "type+const", tag, pool[type_name_index],
+				ps.printf("%-30s %c %s(#%d) %s(#%d)%n", indent + "type+const", tag, pool[type_name_index],
 						type_name_index, pool[const_name_index], const_name_index);
 				break;
 
 			case 'c' :
 				int class_info_index = in.readUnsignedShort();
-				ps.printf("%-30s %c %s(#%d)\n", indent + "element value", tag, pool[class_info_index], class_info_index);
+				ps.printf("%-30s %c %s(#%d)%n", indent + "element value", tag, pool[class_info_index], class_info_index);
 				break;
 
 			case '@' :
-				ps.printf("%-30s %c\n", indent + "sub annotation", tag);
+				ps.printf("%-30s %c%n", indent + "sub annotation", tag);
 				doAnnotation(in, indent);
 				break;
 
 			case '[' :
 				int num_values = in.readUnsignedShort();
-				ps.printf("%-30s %c num_values=%d\n", indent + "sub element value", tag, num_values);
+				ps.printf("%-30s %c num_values=%d%n", indent + "sub element value", tag, num_values);
 				for (int i = 0; i < num_values; i++) {
 					doElementValue(in, indent);
 				}
@@ -514,7 +514,7 @@
 			sb.append(line_number);
 			sb.append(" ");
 		}
-		ps.printf("%-30s %d: %s\n", indent + "line number table", line_number_table_length, sb);
+		ps.printf("%-30s %d: %s%n", indent + "line number table", line_number_table_length, sb);
 	}
 
 	/**
@@ -542,7 +542,7 @@
 			int name_index = in.readUnsignedShort();
 			int descriptor_index = in.readUnsignedShort();
 			int index = in.readUnsignedShort();
-			ps.printf("%-30s %d: %d/%d %s(#%d) %s(#%d)\n", indent, index, start_pc, length, pool[name_index],
+			ps.printf("%-30s %d: %d/%d %s(#%d) %s(#%d)%n", indent, index, start_pc, length, pool[name_index],
 					name_index, pool[descriptor_index], descriptor_index);
 		}
 	}
@@ -579,7 +579,7 @@
 			if (outer_class_info_index != 0)
 				oname = (String) pool[((Integer) pool[outer_class_info_index]).intValue()];
 
-			ps.printf("%-30s %d: %x %s(#%d/c) %s(#%d/c) %s(#%d) \n", indent, i, inner_class_access_flags, iname,
+			ps.printf("%-30s %d: %x %s(#%d/c) %s(#%d/c) %s(#%d) %n", indent, i, inner_class_access_flags, iname,
 					inner_class_info_index, oname, outer_class_info_index, pool[inner_name_index], inner_name_index);
 		}
 	}
diff --git a/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java b/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java
index dfb2c9e..4ed2ff1 100644
--- a/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java
@@ -140,14 +140,14 @@
 	public void progress(float progress, String s, Object... args) {
 		if (out != null) {
 			out.format(s, args);
-			if (!s.endsWith("\n"))
-				out.format("\n");
+			if (!s.endsWith(String.format("%n")))
+				out.format("%n");
 		}
 	}
 
 	public void trace(String s, Object... args) {
 		if (trace && out != null) {
-			out.format("# " + s + "\n", args);
+			out.format("# " + s + "%n", args);
 			out.flush();
 		}
 	}
@@ -223,10 +223,10 @@
 	void report(String title, Collection<String> list, Formatter f) {
 		if (list.isEmpty())
 			return;
-		f.format(title + (list.size() > 1 ? "s" : "") + "\n");
+		f.format(title + (list.size() > 1 ? "s" : "") + "%n");
 		int n = 0;
 		for (String s : list) {
-			f.format("%3s. %s\n", n++, s);
+			f.format("%3s. %s%n", n++, s);
 		}
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java b/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java
index d62a014..37ef451 100644
--- a/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java
+++ b/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java
@@ -759,7 +759,7 @@
 				if (patterns[i] != null) {
 					Matcher m = patterns[i].matcher(args[i]);
 					if (!m.matches())
-						message += String.format("Argument %s (%s) does not match %s\n", i, args[i],
+						message += String.format("Argument %s (%s) does not match %s%n", i, args[i],
 								patterns[i].pattern());
 				}
 			}