Latest bnd code

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1355520 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java b/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java
index 40c80ca..4943d92 100644
--- a/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java
+++ b/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java
@@ -12,6 +12,22 @@
 		}
 	}
 
+	public ExtList(int size) {
+		super(size);
+	}
+
+	public static ExtList<String> from(String s) {
+		// TODO make sure no \ before comma
+		return from(s, "\\s*,\\s*");
+	}
+	public static ExtList<String> from(String s, String delimeter) {
+		ExtList<String> result = new ExtList<String>();
+		String[] parts = s.split(delimeter);
+		for (String p : parts)
+			result.add(p);
+		return result;
+	}
+
 	public String join() {
 		return join(",");
 	}
diff --git a/bundleplugin/src/main/java/aQute/lib/converter/Converter.java b/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
index f22f840..621b558 100644
--- a/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
+++ b/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
@@ -32,14 +32,18 @@
 	}
 
 	public <T> T convert(TypeReference<T> type, Object o) throws Exception {
-		return (T) convert( type.getType(), o);
+		return (T) convert(type.getType(), o);
 	}
-	
-	public Object convert(Type type, Object o) throws Exception {
-		if (o == null)
-			return null; // compatible with any
 
-		
+	public Object convert(Type type, Object o) throws Exception {
+		Class resultType = getRawClass(type);
+		if (o == null) {
+			if (resultType.isPrimitive()||  Number.class.isAssignableFrom(resultType)) 
+				return convert(type,0);
+
+			return null; // compatible with any
+		}
+
 		Hook hook = hooks.get(type);
 		if (hook != null) {
 			Object value = hook.convert(type, o);
@@ -47,7 +51,6 @@
 				return value;
 		}
 
-		Class resultType = getRawClass(type);
 		Class< ? > actualType = o.getClass();
 
 		// We can always make a string
@@ -111,6 +114,10 @@
 		if (resultType.isAssignableFrom(o.getClass()))
 			return o;
 
+		if (Map.class.isAssignableFrom(actualType) && resultType.isInterface()) {
+			return proxy(resultType, (Map) o);
+		}
+
 		// Simple type coercion
 
 		if (resultType == boolean.class || resultType == Boolean.class) {
@@ -243,6 +250,8 @@
 		return error("No conversion found for " + o.getClass() + " to " + type);
 	}
 
+
+
 	private Number number(Object o) {
 		if (o instanceof Number)
 			return (Number) o;
@@ -311,8 +320,9 @@
 				result = new TreeMap();
 			else if (rawClass.isAssignableFrom(ConcurrentHashMap.class))
 				result = new ConcurrentHashMap();
-			else
+			else {
 				return (Map) error("Cannot find suitable map for map interface " + rawClass);
+			}
 		} else
 			result = rawClass.newInstance();
 
@@ -421,4 +431,44 @@
 		this.hooks.put(type, hook);
 		return this;
 	}
+
+	/**
+	 * Convert a map to an interface.
+	 * 
+	 * @param interfc
+	 * @param properties
+	 * @return
+	 */
+	public <T> T proxy(Class<T> interfc, final Map< ? , ? > properties) {
+		return (T) Proxy.newProxyInstance(interfc.getClassLoader(), new Class[] {
+			interfc
+		}, new InvocationHandler() {
+
+			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+				Object o = properties.get(method.getName());
+				if ( o == null)
+					o = properties.get(mangleMethodName(method.getName()));
+
+				return convert( method.getGenericReturnType(), o);
+			}
+
+		});
+	}
+
+	public static String mangleMethodName(String id) {
+		StringBuilder sb = new StringBuilder(id);
+		for (int i = 0; i < sb.length(); i++) {
+			char c = sb.charAt(i);
+			boolean twice = i < sb.length() - 1 && sb.charAt(i + 1) == c;
+			if (c == '$' || c == '_') {
+				if (twice)
+					sb.deleteCharAt(i + 1);
+				else if (c == '$')
+					sb.deleteCharAt(i--); // Remove dollars
+				else
+					sb.setCharAt(i, '.'); // Make _ into .
+			}
+		}
+		return sb.toString();
+	}
 }
diff --git a/bundleplugin/src/main/java/aQute/lib/converter/TypeReference.java b/bundleplugin/src/main/java/aQute/lib/converter/TypeReference.java
index 2b52308..3271227 100644
--- a/bundleplugin/src/main/java/aQute/lib/converter/TypeReference.java
+++ b/bundleplugin/src/main/java/aQute/lib/converter/TypeReference.java
@@ -4,6 +4,9 @@
 
 public class TypeReference<T> implements Type {
 
+	protected TypeReference() {
+		// Make sure it cannot be directly instantiated
+	}
 	public Type getType() {
 		return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
 	}
diff --git a/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java b/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
index 4d0f5b9..9b01ff7 100644
--- a/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
+++ b/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
@@ -7,8 +7,8 @@
 
 import aQute.lib.osgi.*;
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
 import aQute.libg.version.*;
+import aQute.service.reporter.*;
 
 public class FileInstallRepo extends FileRepo {
 
diff --git a/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java b/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
index 4be5ec8..3703b8b 100644
--- a/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
+++ b/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
@@ -9,8 +9,8 @@
 import aQute.lib.io.*;
 import aQute.lib.osgi.*;
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
 import aQute.libg.version.*;
+import aQute.service.reporter.*;
 
 public class FileRepo implements Plugin, RepositoryPlugin, Refreshable, RegistryPlugin {
 	public final static String	LOCATION	= "location";
@@ -157,10 +157,10 @@
 		reporter.trace("updating %s ", file.getAbsolutePath());
 		if (!file.exists() || file.lastModified() < jar.lastModified()) {
 			jar.write(file);
-			reporter.progress("updated " + file.getAbsolutePath());
+			reporter.progress(-1, "updated " + file.getAbsolutePath());
 			fireBundleAdded(jar, file);
 		} else {
-			reporter.progress("Did not update " + jar + " because repo has a newer version");
+			reporter.progress(-1, "Did not update " + jar + " because repo has a newer version");
 			reporter.trace("NOT Updating " + fName + " (repo is newer)");
 		}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
index c36727f..e1b7560 100644
--- a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
+++ b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
@@ -9,6 +9,7 @@
 import aQute.lib.justif.*;
 import aQute.libg.generics.*;
 import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 /**
  * Helps parsing command lines. This class takes target object, a primary
@@ -101,7 +102,7 @@
 
 				// Handle vararg
 
-				if (pattern.equals("...")) {
+				if (pattern.contains("...")) {
 					i = Integer.MAX_VALUE;
 					break;
 				}
diff --git a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLineMessages.java b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLineMessages.java
index afeb0b9..4f2cce3 100644
--- a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLineMessages.java
+++ b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLineMessages.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
 package aQute.lib.getopt;
 
 import java.util.*;
diff --git a/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java b/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java
index 032f08f..8e608f8 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java
@@ -190,6 +190,8 @@
 						h = new CollectionHandler(rawClass, pt.getActualTypeArguments()[0]);
 					else if (Map.class.isAssignableFrom(rawClass))
 						h = new MapHandler(rawClass, pt.getActualTypeArguments()[0], pt.getActualTypeArguments()[1]);
+					else if (Dictionary.class.isAssignableFrom(rawClass))
+						h = new MapHandler(Hashtable.class, pt.getActualTypeArguments()[0], pt.getActualTypeArguments()[1]);
 					else
 						throw new IllegalArgumentException("Found a parameterized type that is not a map or collection");
 				}
diff --git a/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java b/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java
index 349737a..62bf07f 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java
@@ -21,6 +21,8 @@
 				rawClass = Hashtable.class;
 			else if (rawClass.isAssignableFrom(LinkedHashMap.class))
 				rawClass = LinkedHashMap.class;
+			else if (rawClass.isAssignableFrom(Dictionary.class))
+				rawClass = Hashtable.class;
 			else
 				throw new IllegalArgumentException("Unknown map interface: " + rawClass);
 		}
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/AnalyzerMessages.java b/bundleplugin/src/main/java/aQute/lib/osgi/AnalyzerMessages.java
index 3efea0a..a91a0f0 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/AnalyzerMessages.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/AnalyzerMessages.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
 package aQute.lib.osgi;
 
 import aQute.libg.reporter.*;
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
index f49d6e3..b31be5c 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
@@ -117,7 +117,6 @@
 		dot.setName(getBsn());
 
 		sign(dot);
-		doDigests(dot);
 		doSaveManifest(dot);
 
 		doDiff(dot); // check if need to diff this bundle
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/CombinedResource.java b/bundleplugin/src/main/java/aQute/lib/osgi/CombinedResource.java
index d76b695..3886c48 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/CombinedResource.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/CombinedResource.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
 package aQute.lib.osgi;
 
 import java.io.*;
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java b/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java
index eefc1e2..47ba4ac 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
 package aQute.lib.osgi;
 
 import java.io.*;
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Domain.java b/bundleplugin/src/main/java/aQute/lib/osgi/Domain.java
index f520378..e705534 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Domain.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Domain.java
@@ -6,8 +6,8 @@
 import java.util.jar.*;
 
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
 import aQute.libg.version.*;
+import aQute.service.reporter.*;
 
 /**
  * This class abstracts domains that have properties holding OSGi meta data. It
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java b/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
index 76a4b0d..725b065 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
@@ -11,7 +11,7 @@
 
 import aQute.lib.base64.*;
 import aQute.lib.io.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class Jar implements Closeable {
 	public enum Compression {
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java b/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
index 608e9a0..c6b5dd9 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
@@ -13,7 +13,7 @@
 import aQute.lib.io.*;
 import aQute.libg.generics.*;
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class Processor extends Domain implements Reporter, Registry, Constants, Closeable {
 
@@ -113,41 +113,64 @@
 		return p;
 	}
 
-	public void warning(String string, Object... args) {
+	public SetLocation warning(String string, Object... args) {
 		Processor p = current();
 		String s = formatArrays(string, args);
 		if (!p.warnings.contains(s))
 			p.warnings.add(s);
 		p.signal();
+		return location(s);
 	}
 
-	public void error(String string, Object... args) {
+	public SetLocation error(String string, Object... args) {
 		Processor p = current();
-		if (p.isFailOk())
-			p.warning(string, args);
-		else {
-			String s = formatArrays(string, args == null ? new Object[0] : args);
-			if (!p.errors.contains(s))
-				p.errors.add(s);
+		try {
+			if (p.isFailOk())
+				return p.warning(string, args);
+			else {
+				String s = formatArrays(string, args == null ? new Object[0] : args);
+				if (!p.errors.contains(s))
+					p.errors.add(s);
+				return location(s);
+			}
 		}
-		p.signal();
+		finally {
+			p.signal();
+		}
 	}
 
-	public void error(String string, Throwable t, Object... args) {
+	public void progress(float progress, String format, Object... args) {
+		format = String.format("[%2d] %s", (int)progress, format);
+		trace(format, args);
+	}
+
+	public void progress(String format, Object... args) {
+		progress(-1f, format, args);
+	}
+
+	public SetLocation exception(Throwable t, String format, Object... args) {
+		return error(format, t, args);
+	}
+
+	public SetLocation error(String string, Throwable t, Object... args) {
 		Processor p = current();
-
-		if (p.isFailOk())
-			p.warning(string + ": " + t, args);
-		else {
-			p.errors.add("Exception: " + t.getMessage());
-			String s = formatArrays(string, args == null ? new Object[0] : args);
-			if (!p.errors.contains(s))
-				p.errors.add(s);
+		try {
+			if (p.exceptions)
+				t.printStackTrace();
+			if (p.isFailOk()) {
+				return p.warning(string + ": " + t, args);
+			}
+			else {
+				p.errors.add("Exception: " + t.getMessage());
+				String s = formatArrays(string, args == null ? new Object[0] : args);
+				if (!p.errors.contains(s))
+					p.errors.add(s);
+				return location(s);
+			}
 		}
-		if (p.exceptions)
-			t.printStackTrace();
-
-		p.signal();
+		finally {
+			p.signal();
+		}
 	}
 
 	public void signal() {}
@@ -184,10 +207,6 @@
 		toBeClosed.remove(jar);
 	}
 
-	public void progress(String s, Object... args) {
-		trace(s, args);
-	}
-
 	public boolean isPedantic() {
 		return current().pedantic;
 	}
@@ -1584,4 +1603,64 @@
 
 		return s + newExtension;
 	}
+
+	/**
+	 * Create a location object and add it to the locations
+	 * 
+	 * @param s
+	 * @return
+	 */
+	List<Location>	locations	= new ArrayList<Location>();
+
+	static class SetLocationImpl extends Location implements SetLocation {
+		public SetLocationImpl(String s) {
+			this.message = s;
+		}
+
+		public SetLocation file(String file) {
+			this.file = file;
+			return this;
+		}
+
+		public SetLocation header(String header) {
+			this.header = header;
+			return this;
+		}
+
+		public SetLocation context(String context) {
+			this.context = context;
+			return this;
+		}
+
+		public SetLocation method(String methodName) {
+			this.methodName = methodName;
+			return this;
+		}
+
+		public SetLocation line(int n) {
+			this.line = n;
+			return this;
+		}
+
+		public SetLocation reference(String reference) {
+			this.reference = reference;
+			return this;
+		}
+
+	}
+
+	private SetLocation location(String s) {
+		SetLocationImpl loc = new SetLocationImpl(s);
+		locations.add(loc);
+		return loc;
+	}
+
+	public Location getLocation(String msg) {
+		for (Location l : locations)
+			if ((l.message != null) && l.message.equals(msg))
+				return l;
+
+		return null;
+	}
+
 }
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 cb511e7..c146477 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
@@ -9,7 +9,7 @@
 import org.w3c.dom.*;
 import org.xml.sax.*;
 
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 /**
  * Parse the Eclipse project information for the classpath. Unfortunately, it is
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java b/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java
index 77bb9f6..179ab25 100644
--- a/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java
+++ b/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java
@@ -29,7 +29,7 @@
 		/**
 		 * Create an empty text store.
 		 */
-		private StringTextStore() {
+		StringTextStore() {
 			super();
 		}
 
@@ -39,7 +39,7 @@
 		 * @param text
 		 *            the initial content
 		 */
-		private StringTextStore(String text) {
+		StringTextStore(String text) {
 			super();
 			set(text);
 		}
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/PropertiesReader.java b/bundleplugin/src/main/java/aQute/lib/properties/PropertiesReader.java
new file mode 100644
index 0000000..58426af
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/PropertiesReader.java
@@ -0,0 +1,115 @@
+package aQute.lib.properties;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.lib.io.*;
+
+public class PropertiesReader {
+	static Pattern	PROPERTY	= Pattern.compile("(\\s*#.*$)|(([^\\s]+)\\s*[:=]?\\s*([^#])(#.*)$)|\\s+([^#]*)(#.*)$)",
+										Pattern.MULTILINE);
+
+	public static Properties read(Properties p, File f) throws Exception {
+		return read(p, IO.reader(f));
+	}
+
+	public static Properties read(Properties p, InputStream in, String charset) throws IOException {
+		return read(p, IO.reader(in, charset));
+	}
+
+	public static Properties read(Properties p, InputStream in) throws IOException {
+		return read(p, IO.reader(in));
+	}
+
+	public static Properties read(Properties p, URL in) throws IOException {
+		return read(p, IO.reader(in.openStream()));
+	}
+
+	private static Properties read(Properties p, BufferedReader reader) throws IOException {
+		if (p != null)
+			p = new Properties();
+
+		String line = reader.readLine();
+		String key = null;
+		StringBuilder value = new StringBuilder();
+
+		while (line != null) {
+			Matcher m = PROPERTY.matcher(line);
+			if (m.matches()) {
+
+				if (m.group(1) != null)
+					continue; // comment line
+
+				if (m.group(2) != null) {
+					// header
+					if (key != null) {
+						cleanup(value);
+						p.put(key.toString(), value.toString());
+						key = null;
+						value.delete(0, value.length());
+					}
+					key = m.group(3);
+					value.append(m.group(4));
+				} else {
+					value.append(m.group(6));
+				}
+			} else {
+				System.out.println("Assume empty: " + line);
+			}
+			line = reader.readLine();
+		}
+		if (key != null) {
+			cleanup(value);
+			p.put(key.toString(), value.toString());
+		}
+		return p;
+	}
+
+	private static void cleanup(StringBuilder value) {
+		for (int i = 0; i < value.length(); i++) {
+			if (value.charAt(i) == '\\') {
+				value.deleteCharAt(i);
+				if (i < value.length()) {
+					char c = value.charAt(i);
+					switch (c) {
+						case 't' :
+							value.setCharAt(i, '\t');
+							break;
+						case 'r' :
+							value.setCharAt(i, '\r');
+							break;
+						case 'n' :
+							value.setCharAt(i, '\n');
+							break;
+						case 'f' :
+							value.setCharAt(i, '\f');
+							break;
+						case 'b' :
+							value.setCharAt(i, '\b');
+							break;
+
+						case 'u' :
+							if (i + 5 >= value.length())
+								throw new IllegalArgumentException("Invalid unicode escape " + value.substring(i));
+
+							String n = value.substring(i + 1, i + 5);
+							try {
+								int code = Integer.valueOf(n, 16);
+								value.delete(i + 1, i + 5);
+								value.setCharAt(i, (char) code);
+							}
+							catch (Exception e) {
+								throw new IllegalArgumentException("Invalid unicode escape " + value.substring(i));
+							}
+							break;
+						default :
+							throw new IllegalArgumentException("Invalid  escape " + value);
+					}
+				}
+
+			}
+		}
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/tag/Tag.java b/bundleplugin/src/main/java/aQute/lib/tag/Tag.java
index 03ae971..cf8668e 100755
--- a/bundleplugin/src/main/java/aQute/lib/tag/Tag.java
+++ b/bundleplugin/src/main/java/aQute/lib/tag/Tag.java
@@ -392,7 +392,7 @@
 		}
 		String suri = sn == null ? mapping.getAttribute("xmlns") : mapping.getAttribute("xmlns:" + sn);
 		String turi = tn == null ? child.findRecursiveAttribute("xmlns") : child.findRecursiveAttribute("xmlns:" + tn);
-		return turi == suri || (turi != null && suri != null && turi.equals(suri));
+		return ((turi == null) && (suri == null)) || ((turi != null) && turi.equals(suri));
 	}
 
 	public String getString(String path) {