Latest pre-release bnd code

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1387568 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/lib/converter/Converter.java b/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
index a227d71..72b380c 100644
--- a/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
+++ b/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
@@ -22,7 +22,8 @@
 	}
 
 	boolean			fatal	= true;
-	Map<Type,Hook>	hooks	= new HashMap<Type,Converter.Hook>();
+	Map<Type,Hook>	hooks;
+	List<Hook>		allHooks;
 
 	public <T> T convert(Class<T> type, Object o) throws Exception {
 		// Is it a compatible type?
@@ -44,11 +45,21 @@
 			return null; // compatible with any
 		}
 
-		Hook hook = hooks.get(type);
-		if (hook != null) {
-			Object value = hook.convert(type, o);
-			if (value != null)
-				return value;
+		if (allHooks != null) {
+			for (Hook hook : allHooks) {
+				Object r = hook.convert(type, o);
+				if (r != null)
+					return r;
+			}
+		}
+
+		if (hooks != null) {
+			Hook hook = hooks.get(type);
+			if (hook != null) {
+				Object value = hook.convert(type, o);
+				if (value != null)
+					return value;
+			}
 		}
 
 		Class< ? > actualType = o.getClass();
@@ -243,22 +254,23 @@
 						f.set(instance, value);
 					}
 					catch (Exception ee) {
-						
+
 						// We cannot find the key, so try the __extra field
 						Field f = resultType.getField("__extra");
 						Map<String,Object> extra = (Map<String,Object>) f.get(instance);
-						if ( extra == null) {
+						if (extra == null) {
 							extra = new HashMap<String,Object>();
 							f.set(instance, extra);
 						}
-						extra.put(key, convert(Object.class,e.getValue()));
-						
+						extra.put(key, convert(Object.class, e.getValue()));
+
 					}
 				}
 				return instance;
 			}
 			catch (Exception e) {
-				return error("No conversion found for " + o.getClass() + " to " + type + ", error " + e + " on key " + key);
+				return error("No conversion found for " + o.getClass() + " to " + type + ", error " + e + " on key "
+						+ key);
 			}
 		}
 
@@ -441,7 +453,16 @@
 	}
 
 	public Converter hook(Type type, Hook hook) {
-		this.hooks.put(type, hook);
+		if (type != null) {
+			if (hooks == null)
+				hooks = new HashMap<Type,Converter.Hook>();
+			this.hooks.put(type, hook);
+		} else {
+			if (allHooks == null)
+				allHooks = new ArrayList<Converter.Hook>();
+			allHooks.add(hook);
+		}
+
 		return this;
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java b/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
index 7fa7bb3..08e5d02 100644
--- a/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
+++ b/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
@@ -294,15 +294,15 @@
 	protected File putArtifact(File tmpFile, byte[] digest) throws Exception {
 		assert (tmpFile != null);
 
-		Jar jar = new Jar(tmpFile);
+		Jar tmpJar = new Jar(tmpFile);
 		try {
 			dirty = true;
 
-			String bsn = jar.getBsn();
+			String bsn = tmpJar.getBsn();
 			if (bsn == null)
 				throw new IllegalArgumentException("No bsn set in jar: " + tmpFile);
 
-			String versionString = jar.getVersion();
+			String versionString = tmpJar.getVersion();
 			if (versionString == null)
 				versionString = "0";
 			else if (!Verifier.isVersion(versionString))
@@ -322,9 +322,12 @@
 
 			reporter.trace("updating %s ", file.getAbsolutePath());
 
+			// An open jar on file will fail rename on windows
+			tmpJar.close();
+
 			IO.rename(tmpFile, file);
 
-			fireBundleAdded(jar, file);
+			fireBundleAdded(file);
 			afterPut(file, bsn, version, Hex.toHexString(digest));
 
 			// TODO like to beforeGet rid of the latest option. This is only
@@ -338,7 +341,7 @@
 			return file;
 		}
 		finally {
-			jar.close();
+			tmpJar.close();
 		}
 	}
 
@@ -676,18 +679,25 @@
 		exec(beforeGet, root.getAbsolutePath(), bsn, version);
 	}
 
-	protected void fireBundleAdded(Jar jar, File file) {
+	protected void fireBundleAdded(File file) {
 		if (registry == null)
 			return;
 		List<RepositoryListenerPlugin> listeners = registry.getPlugins(RepositoryListenerPlugin.class);
+		Jar jar = null;
 		for (RepositoryListenerPlugin listener : listeners) {
 			try {
+				if (jar == null)
+					jar = new Jar(file);
 				listener.bundleAdded(this, jar, file);
 			}
 			catch (Exception e) {
 				if (reporter != null)
 					reporter.warning("Repository listener threw an unexpected exception: %s", e);
 			}
+			finally {
+				if (jar != null)
+					jar.close();
+			}
 		}
 	}
 
@@ -699,34 +709,53 @@
 	 * @param target
 	 */
 	void exec(String line, Object... args) {
-		if (line == null)
+		if (line == null) {
 			return;
+		}
 
 		try {
-			if (args != null)
+			if (args != null) {
 				for (int i = 0; i < args.length; i++) {
-					if (i == 0)
-						line = line.replaceAll("\\$\\{@\\}", args[0].toString());
-					line = line.replaceAll("\\$" + i, args[i].toString());
+					if (i == 0) {
+						// replaceAll backslash magic ensures windows paths
+						// remain intact
+						line = line.replaceAll("\\$\\{@\\}", args[0].toString().replaceAll("\\\\", "\\\\\\\\"));
+					}
+					// replaceAll backslash magic ensures windows paths remain
+					// intact
+					line = line.replaceAll("\\$" + i, args[i].toString().replaceAll("\\\\", "\\\\\\\\"));
 				}
-
-			if (shell == null) {
-				shell = System.getProperty("os.name").toLowerCase().indexOf("win") > 0 ? "cmd.exe" : "sh";
 			}
-			Command cmd = new Command(shell);
+			// purge remaining placeholders
+			line = line.replaceAll("\\s*\\$[0-9]\\s*", "");
 
-			if (path != null) {
-				cmd.inherit();
-				String oldpath = cmd.var("PATH");
-				path = path.replaceAll("\\s*,\\s*", File.pathSeparator);
-				path = path.replaceAll("\\$\\{@\\}", oldpath);
-				cmd.var("PATH", path);
-			}
-
-			cmd.setCwd(getRoot());
+			int result = 0;
 			StringBuilder stdout = new StringBuilder();
 			StringBuilder stderr = new StringBuilder();
-			int result = cmd.execute(line, stdout, stderr);
+			if (System.getProperty("os.name").toLowerCase().indexOf("win") >= 0) {
+
+				// FIXME ignoring possible shell setting stdin approach used
+				// below does not work in windows
+				Command cmd = new Command("cmd.exe /C " + line);
+				cmd.setCwd(getRoot());
+				result = cmd.execute(stdout, stderr);
+
+			} else {
+				if (shell == null) {
+					shell = "sh";
+				}
+				Command cmd = new Command(shell);
+				cmd.setCwd(getRoot());
+
+				if (path != null) {
+					cmd.inherit();
+					String oldpath = cmd.var("PATH");
+					path = path.replaceAll("\\s*,\\s*", File.pathSeparator);
+					path = path.replaceAll("\\$\\{@\\}", oldpath);
+					cmd.var("PATH", path);
+				}
+				result = cmd.execute(line, stdout, stderr);
+			}
 			if (result != 0) {
 				reporter.error("Command %s failed with %s %s %s", line, result, stdout, stderr);
 			}
diff --git a/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java b/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java
index 7622ae7..349b381 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java
@@ -3,15 +3,18 @@
 import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
+import java.util.regex.*;
 
+import aQute.lib.base64.*;
 import aQute.lib.hex.*;
 
 /**
- * 
  * Will now use hex for encoding byte arrays
- *
  */
 public class ByteArrayHandler extends Handler {
+	Pattern	ENCODING	= Pattern
+								.compile("((:?[\\dA-Za-z][\\dA-Za-z])*)|((:?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/)+={1,3})");
+
 	@Override
 	void encode(Encoder app, Object object, Map<Object,Type> visited) throws IOException, Exception {
 		StringHandler.string(app, Hex.toHexString((byte[]) object));
@@ -31,6 +34,47 @@
 
 	@Override
 	Object decode(Decoder dec, String s) throws Exception {
-		return Hex.toByteArray(s);
+		boolean hex = true;
+		StringBuilder sb = new StringBuilder(s);
+		for (int i = sb.length() - 1; i >= 0; i--) {
+			char c = sb.charAt(i);
+			if (Character.isWhitespace(c))
+				sb.delete(i, i + 1);
+			else {
+				switch (c) {
+					case '0' :
+					case '1' :
+					case '2' :
+					case '3' :
+					case '4' :
+					case '5' :
+					case '6' :
+					case '7' :
+					case '8' :
+					case '9' :
+					case 'A' :
+					case 'B' :
+					case 'C' :
+					case 'D' :
+					case 'E' :
+					case 'F' :
+					case 'a' :
+					case 'b' :
+					case 'c' :
+					case 'd' :
+					case 'e' :
+					case 'f' :
+						break;
+
+					default :
+						hex = false;
+						break;
+				}
+			}
+		}
+		if ( hex)
+			return Hex.toByteArray(sb.toString());
+		else
+			return Base64.decodeBase64(sb.toString());
 	}
 }