felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mccu...@apache.org
Subject svn commit: r1347815 [8/19] - in /felix/trunk/bundleplugin: ./ src/main/java/aQute/ src/main/java/aQute/bnd/ src/main/java/aQute/bnd/annotation/ src/main/java/aQute/bnd/annotation/component/ src/main/java/aQute/bnd/annotation/metatype/ src/main/java/aQ...
Date Thu, 07 Jun 2012 21:57:43 GMT
Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,144 @@
+package aQute.bnd.signing;
+
+import java.io.*;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.concurrent.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+import aQute.libg.command.*;
+import aQute.libg.reporter.*;
+
+/**
+ * Sign the jar file.
+ * 
+ * -sign : <alias> [ ';' 'password:=' <password> ] [ ';' 'keystore:=' <keystore> ] [
+ * ';' 'sign-password:=' <pw> ] ( ',' ... )*
+ * 
+ * @author aqute
+ * 
+ */
+public class JartoolSigner implements Plugin, SignerPlugin {
+    String keystore;
+    String storetype;
+    String path = "jarsigner";
+    String storepass;
+    String keypass;
+    String sigFile;
+    String digestalg;
+
+    public void setProperties(Map<String, String> map) {
+        if (map.containsKey("keystore"))
+            this.keystore = map.get("keystore");
+        if (map.containsKey("storetype"))
+            this.storetype = map.get("storetype");
+        if (map.containsKey("storepass"))
+            this.storepass = map.get("storepass");
+        if (map.containsKey("keypass"))
+            this.keypass = map.get("keypass");
+        if (map.containsKey("path"))
+            this.path = map.get("path");
+        if (map.containsKey("sigFile"))
+            this.sigFile = map.get("sigFile");
+        if (map.containsKey("digestalg"))
+            this.digestalg = map.get("digestalg");
+    }
+
+    public void setReporter(Reporter processor) {
+    }
+
+    public void sign(Builder builder, String alias) throws Exception {    	    	
+    	File f = builder.getFile(keystore);
+    	if ( !f.isFile()) {
+    		builder.error("Invalid keystore %s", f.getAbsolutePath() );
+    		return;
+    	}
+    	
+        Jar jar = builder.getJar();
+        File tmp = File.createTempFile("signdjar", ".jar");
+        tmp.deleteOnExit();
+
+        jar.write(tmp);
+
+        Command command = new Command();
+        command.add(path);
+        if (keystore != null) {
+            command.add("-keystore");
+            command.add(f.getAbsolutePath());
+        }
+
+        if (storetype != null) {
+        	command.add("-storetype");
+        	command.add(storetype);
+        }
+
+        if (keypass != null) {
+        	command.add("-keypass");
+        	command.add(keypass);
+        }
+
+        if (storepass != null) {
+        	command.add("-storepass");
+        	command.add(storepass);
+        }
+
+        if (sigFile != null) {
+        	command.add("-sigFile");
+        	command.add(sigFile);
+        }
+
+        if (digestalg != null) {
+        	command.add("-digestalg");
+        	command.add(digestalg);
+	    }
+
+        command.add(tmp.getAbsolutePath());
+        command.add(alias);
+        builder.trace("Jarsigner command: %s", command);
+        command.setTimeout(20, TimeUnit.SECONDS);
+        StringBuilder out = new StringBuilder();
+        StringBuilder err = new StringBuilder();
+        int exitValue = command.execute(System.in, out, err);
+        if (exitValue != 0) {
+            builder.error("Signing Jar out: %s\nerr: %s", out, err);
+        } else {
+            builder.trace("Signing Jar out: %s \nerr: %s", out, err);
+        }
+
+        Jar signed = new Jar(tmp);
+        builder.addClose(signed);
+
+        Map<String, Resource> dir = signed.getDirectories().get("META-INF");
+        for (Entry<String, Resource> entry : dir.entrySet()) {
+            String path = entry.getKey();
+            if (path.matches(".*\\.(DSA|RSA|SF|MF)$")) {
+                jar.putResource(path, entry.getValue());
+            }
+        }
+        jar.setDoNotTouchManifest();
+    }
+
+    StringBuilder collect(final InputStream in) throws Exception {
+        final StringBuilder sb = new StringBuilder();
+        
+        Thread tin = new Thread() {
+            public void run() {
+                try {
+                    BufferedReader rdr = new BufferedReader(new InputStreamReader(in, Constants.DEFAULT_CHARSET));
+                    String line = rdr.readLine();
+                    while (line != null) {
+                        sb.append(line);
+                        line = rdr.readLine();
+                    }
+                    rdr.close();
+                    in.close();
+                } catch (Exception e) {
+                    // Ignore any exceptions
+                }
+            }
+        };
+        tin.start();
+        return sb;
+    }
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,196 @@
+package aQute.bnd.signing;
+
+import java.io.*;
+import java.security.*;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import aQute.lib.base64.*;
+import aQute.lib.io.*;
+import aQute.lib.osgi.*;
+
+/**
+ * This class is used with the aQute.lib.osgi package, it signs jars with DSA
+ * signature.
+ * 
+ * -sign: md5, sha1
+ */
+public class Signer extends Processor {
+    static Pattern  METAINFDIR   = Pattern.compile("META-INF/[^/]*");
+    String         digestNames[]    = new String[] { "MD5" };
+    File           keystoreFile  = new File("keystore");
+    String         password;
+    String         alias;
+
+    public void signJar(Jar jar) {
+        if (digestNames == null || digestNames.length == 0)
+            error("Need at least one digest algorithm name, none are specified");
+
+        if (keystoreFile == null || !keystoreFile.getAbsoluteFile().exists()) {
+            error("No such keystore file: " + keystoreFile);
+            return;
+        }
+
+        if (alias == null) {
+            error("Private key alias not set for signing");
+            return;
+        }
+
+        MessageDigest digestAlgorithms[] = new MessageDigest[digestNames.length];
+
+        getAlgorithms(digestNames, digestAlgorithms);
+
+        try {
+            Manifest manifest = jar.getManifest();
+            manifest.getMainAttributes().putValue("Signed-By", "Bnd");
+
+            // Create a new manifest that contains the
+            // Name parts with the specified digests
+
+            ByteArrayOutputStream o = new ByteArrayOutputStream();
+            manifest.write(o);
+            doManifest(jar, digestNames, digestAlgorithms, o);
+            o.flush();
+            byte newManifestBytes[] = o.toByteArray();
+            jar.putResource("META-INF/MANIFEST.MF", new EmbeddedResource(
+                    newManifestBytes, 0));
+
+            // Use the bytes from the new manifest to create
+            // a signature file
+
+            byte[] signatureFileBytes = doSignatureFile(digestNames,
+                    digestAlgorithms, newManifestBytes);
+            jar.putResource("META-INF/BND.SF", new EmbeddedResource(
+                    signatureFileBytes, 0));
+
+            // Now we must create an RSA signature
+            // this requires the private key from the keystore
+
+            KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+
+            KeyStore.PrivateKeyEntry privateKeyEntry = null;
+
+            java.io.FileInputStream keystoreInputStream = null;
+            try {
+            	keystoreInputStream = new java.io.FileInputStream(
+                        keystoreFile);
+                char[] pw = password == null ? new char[0]
+                        : password.toCharArray();
+                
+                keystore.load(keystoreInputStream, pw);
+                keystoreInputStream.close();
+                privateKeyEntry =  (PrivateKeyEntry) keystore.getEntry(
+                        alias, new KeyStore.PasswordProtection(pw));
+            } catch (Exception e) {
+                error("No able to load the private key from the give keystore("+keystoreFile.getAbsolutePath()+") with alias "+alias+" : "
+                        + e);
+                return;
+            } finally {
+            	IO.close(keystoreInputStream);
+            }
+            PrivateKey privateKey = privateKeyEntry.getPrivateKey();
+
+            Signature signature = Signature.getInstance("MD5withRSA");
+            signature.initSign(privateKey);
+
+            signature.update(signatureFileBytes);
+
+            signature.sign();
+
+            // TODO, place the SF in a PCKS#7 structure ...
+            // no standard class for this? The following
+            // is an idea but we will to have do ASN.1 BER
+            // encoding ...
+
+         
+            
+            ByteArrayOutputStream tmpStream = new ByteArrayOutputStream();
+            jar.putResource("META-INF/BND.RSA", new EmbeddedResource(tmpStream
+                    .toByteArray(), 0));
+        } catch (Exception e) {
+            error("During signing: " + e);
+        }
+    }
+
+    private byte[] doSignatureFile(String[] digestNames,
+            MessageDigest[] algorithms, byte[] manbytes) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        PrintWriter ps = IO.writer(out);
+        ps.print("Signature-Version: 1.0\r\n");
+
+        for (int a = 0; a < algorithms.length; a++) {
+            if (algorithms[a] != null) {
+                byte[] digest = algorithms[a].digest(manbytes);
+                ps.print(digestNames[a] + "-Digest-Manifest: ");
+                ps.print(new Base64(digest));
+                ps.print("\r\n");
+            }
+        }
+        return out.toByteArray();
+    }
+
+    private void doManifest(Jar jar, String[] digestNames,
+            MessageDigest[] algorithms, OutputStream out) throws Exception {
+
+        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(name.getBytes("UTF-8"));
+                out.write("\r\n".getBytes());
+
+                digest(algorithms, entry.getValue());
+                for (int a = 0; a < algorithms.length; a++) {
+                    if (algorithms[a] != null) {
+                        byte[] digest = algorithms[a].digest();
+                        String header = digestNames[a] + "-Digest: "
+                                + new Base64(digest) + "\r\n";
+                        out.write(header.getBytes());
+                    }
+                }
+            }
+        }
+    }
+
+    private void digest(MessageDigest[] algorithms, Resource r)
+            throws Exception {
+        InputStream in = r.openInputStream();
+        byte[] data = new byte[1024];
+        int size = in.read(data);
+        while (size > 0) {
+            for (int a = 0; a < algorithms.length; a++) {
+                if (algorithms[a] != null) {
+                    algorithms[a].update(data, 0, size);
+                }
+            }
+            size = in.read(data);
+        }
+    }
+
+    private void getAlgorithms(String[] digestNames, MessageDigest[] algorithms) {
+        for (int i = 0; i < algorithms.length; i++) {
+            String name = digestNames[i];
+            try {
+                algorithms[i] = MessageDigest.getInstance(name);
+            } catch (NoSuchAlgorithmException e) {
+                error("Specified digest algorithm " + digestNames[i]
+                        + ", but not such algorithm was found: " + e);
+            }
+        }
+    }
+
+    public void setPassword(String string) {
+        password = string;
+    }
+
+    public void setKeystore(File keystore) {
+        this.keystoreFile = keystore;
+    }
+
+    public void setAlias(String string) {
+        this.alias = string;
+    }
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/Signer.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/configurable/Config.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/configurable/Config.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/configurable/Config.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/configurable/Config.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,13 @@
+package aQute.configurable;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) 
+public @interface Config {
+	String NULL = "<<NULL>>";
+	
+	boolean required() default false;
+	String description() default "";
+	String deflt() default NULL;
+	String id() default NULL;
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/configurable/Config.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/configurable/Config.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/configurable/Configurable.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/configurable/Configurable.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/configurable/Configurable.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/configurable/Configurable.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,368 @@
+package aQute.configurable;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.lang.reflect.Proxy;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+
+public class Configurable<T> {
+
+	public static <T> T createConfigurable(Class<T> c, Map< ? , ? > properties) {
+		Object o = Proxy.newProxyInstance(c.getClassLoader(),
+				new Class< ? >[] {c},
+				new ConfigurableHandler(properties, c.getClassLoader()));
+		return c.cast(o);
+	}
+
+	public static <T> T createConfigurable(Class<T> c,
+			Dictionary< ? , ? > properties) {
+		Map<Object, Object> alt = new HashMap<Object, Object>();
+		for (Enumeration< ? > e = properties.keys(); e.hasMoreElements();) {
+			Object key = e.nextElement();
+			alt.put(key, properties.get(key));
+		}
+		return createConfigurable(c, alt);
+	}
+
+	static class ConfigurableHandler implements InvocationHandler {
+		final Map< ? , ? >	properties;
+		final ClassLoader	loader;
+
+		ConfigurableHandler(Map< ? , ? > properties, ClassLoader loader) {
+			this.properties = properties;
+			this.loader = loader;
+		}
+
+		public Object invoke(Object proxy, Method method, Object[] args)
+				throws Throwable {
+			Config ad = method.getAnnotation(Config.class);
+			String id = Configurable.mangleMethodName(method.getName());
+
+			if (ad != null && !ad.id().equals(Config.NULL))
+				id = ad.id();
+
+			Object o = properties.get(id);
+			if (o == null && args != null && args.length == 1)
+				o = args[0];
+
+			if (o == null) {
+				if (ad != null) {
+					if (ad.required())
+						throw new IllegalStateException(
+								"Attribute is required but not set "
+										+ method.getName());
+
+					o = ad.deflt();
+					if (o.equals(Config.NULL))
+						o = null;
+				}
+			}
+			if (o == null) {
+				Class< ? > rt = method.getReturnType();
+				if (rt == boolean.class || rt == Boolean.class)
+					return false;
+				if (method.getReturnType().isPrimitive()
+						|| Number.class
+								.isAssignableFrom(method.getReturnType())) {
+
+					o = "0";
+				}
+				else
+					return null;
+			}
+
+			if (args != null && args.length == 1) {
+				String s = (String) convert(String.class, o);
+
+				// Allow a base to be specified for File and URL
+				if (method.getReturnType() == File.class
+						&& args[0].getClass() == File.class) {
+					return new File((File) args[0], s);
+				}
+				else
+					if (method.getReturnType() == URL.class
+							&& args[0].getClass() == File.class) {
+						return new URL(((File) args[0]).toURI().toURL(), s);
+					}
+					else
+						if (method.getReturnType() == URL.class
+								&& args[0].getClass() == URL.class) {
+							return new URL((URL) args[0], s);
+						}
+			}
+			return convert(method.getGenericReturnType(), o);
+		}
+
+		@SuppressWarnings({"unchecked", "rawtypes"})
+		public Object convert(Type type, Object o) throws Exception {
+
+			// TODO type variables
+			// TODO wildcards
+
+			if (type instanceof ParameterizedType) {
+				ParameterizedType pType = (ParameterizedType) type;
+				return convert(pType, o);
+			}
+
+			if (type instanceof GenericArrayType) {
+				GenericArrayType gType = (GenericArrayType) type;
+				return convertArray(gType.getGenericComponentType(), o);
+			}
+
+			Class< ? > resultType = (Class< ? >) type;
+
+			if (resultType.isArray()) {
+				return convertArray(resultType.getComponentType(), o);
+			}
+
+			Class< ? > actualType = o.getClass();
+			if (actualType.isAssignableFrom(resultType))
+				return o;
+
+			if (resultType == boolean.class || resultType == Boolean.class) {
+				if (actualType == boolean.class || actualType == Boolean.class)
+					return o;
+
+				if (Number.class.isAssignableFrom(actualType)) {
+					double b = ((Number) o).doubleValue();
+					if (b == 0)
+						return false;
+					return true;
+				}
+				return true;
+
+			}
+			else
+				if (resultType == byte.class || resultType == Byte.class) {
+					if (Number.class.isAssignableFrom(actualType))
+						return ((Number) o).byteValue();
+					resultType = Byte.class;
+				}
+				else
+					if (resultType == char.class) {
+						resultType = Character.class;
+					}
+					else
+						if (resultType == short.class) {
+							if (Number.class.isAssignableFrom(actualType))
+								return ((Number) o).shortValue();
+							resultType = Short.class;
+						}
+						else
+							if (resultType == int.class) {
+								if (Number.class.isAssignableFrom(actualType))
+									return ((Number) o).intValue();
+								resultType = Integer.class;
+							}
+							else
+								if (resultType == long.class) {
+									if (Number.class
+											.isAssignableFrom(actualType))
+										return ((Number) o).longValue();
+									resultType = Long.class;
+								}
+								else
+									if (resultType == float.class) {
+										if (Number.class
+												.isAssignableFrom(actualType))
+											return ((Number) o).floatValue();
+										resultType = Float.class;
+									}
+									else
+										if (resultType == double.class) {
+											if (Number.class
+													.isAssignableFrom(actualType))
+												return ((Number) o)
+														.doubleValue();
+											resultType = Double.class;
+										}
+
+			if (resultType.isPrimitive())
+				throw new IllegalArgumentException("Unknown primitive: "
+						+ resultType);
+
+			if (Number.class.isAssignableFrom(resultType)
+					&& actualType == Boolean.class) {
+				Boolean b = (Boolean) o;
+				o = b ? "1" : "0";
+			}
+			else
+				if (actualType == String.class) {
+					String input = (String) o;
+					if (Enum.class.isAssignableFrom(resultType)) {
+						return Enum.valueOf((Class<Enum>) resultType, input);
+					}
+					if (resultType == Class.class && loader != null) {
+						return loader.loadClass(input);
+					}
+					if (resultType == Pattern.class) {
+						return Pattern.compile(input);
+					}
+				}
+
+			try {
+				Constructor< ? > c = resultType.getConstructor(String.class);
+				return c.newInstance(o.toString());
+			}
+			catch (Throwable t) {
+				// handled on next line
+			}
+			throw new IllegalArgumentException("No conversion to " + resultType
+					+ " from " + actualType + " value " + o);
+		}
+
+		private Object convert(ParameterizedType pType, Object o)
+				throws InstantiationException, IllegalAccessException,
+				Exception {
+			Class< ? > resultType = (Class< ? >) pType.getRawType();
+			if (Collection.class.isAssignableFrom(resultType)) {
+				Collection< ? > input = toCollection(o);
+				if (resultType.isInterface()) {
+					if (resultType == Collection.class
+							|| resultType == List.class)
+						resultType = ArrayList.class;
+					else
+						if (resultType == Set.class
+								|| resultType == SortedSet.class)
+							resultType = TreeSet.class;
+						else
+							if (resultType == Queue.class /*
+														 * || resultType ==
+														 * Deque.class
+														 */)
+								resultType = LinkedList.class;
+							else
+								if (resultType == Queue.class /*
+															 * || resultType ==
+															 * Deque.class
+															 */)
+									resultType = LinkedList.class;
+								else
+									throw new IllegalArgumentException(
+											"Unknown interface for a collection, no concrete class found: "
+													+ resultType);
+				}
+
+				@SuppressWarnings("unchecked")
+				Collection<Object> result = (Collection<Object>) resultType
+						.newInstance();
+				Type componentType = pType.getActualTypeArguments()[0];
+
+				for (Object i : input) {
+					result.add(convert(componentType, i));
+				}
+				return result;
+			}
+			else
+				if (pType.getRawType() == Class.class) {
+					return loader.loadClass(o.toString());
+				}
+			if (Map.class.isAssignableFrom(resultType)) {
+				Map< ? , ? > input = toMap(o);
+				if (resultType.isInterface()) {
+					if (resultType == SortedMap.class)
+						resultType = TreeMap.class;
+					else
+						if (resultType == Map.class)
+							resultType = LinkedHashMap.class;
+						else
+							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];
+
+				for (Map.Entry< ? , ? > entry : input.entrySet()) {
+					result.put(convert(keyType, entry.getKey()),
+							convert(valueType, entry.getValue()));
+				}
+				return result;
+			}
+			throw new IllegalArgumentException(
+					"cannot convert to "
+							+ pType
+							+ " because it uses generics and is not a Collection or a map");
+		}
+
+		Object convertArray(Type componentType, Object o) throws Exception {
+			Collection< ? > input = toCollection(o);
+			Class< ? > componentClass = getRawClass(componentType);
+			Object array = Array.newInstance(componentClass, input.size());
+
+			int i = 0;
+			for (Object next : input) {
+				Array.set(array, i++, convert(componentType, next));
+			}
+			return array;
+		}
+
+		private Class< ? > getRawClass(Type type) {
+			if (type instanceof Class)
+				return (Class< ? >) type;
+
+			if (type instanceof ParameterizedType)
+				return (Class< ? >) ((ParameterizedType) type).getRawType();
+
+			throw new IllegalArgumentException(
+					"For the raw type, type must be ParamaterizedType or Class but is "
+							+ type);
+		}
+
+		private Collection< ? > toCollection(Object o) {
+			if (o instanceof Collection)
+				return (Collection< ? >) o;
+
+			if (o.getClass().isArray()) {
+				if (o.getClass().getComponentType().isPrimitive()) {
+					int length = Array.getLength(o);
+					List<Object> result = new ArrayList<Object>(length);
+					for (int i = 0; i < length; i++) {
+						result.add(Array.get(o, i));
+					}
+					return result;
+				}
+				return Arrays.asList((Object[]) o);
+			}
+
+			if (o instanceof String) {
+				String s = (String) o;
+				if (s.indexOf('|') > 0)
+					return Arrays.asList(s.split("\\|"));
+			}
+			return Arrays.asList(o);
+		}
+
+		private Map< ? , ? > toMap(Object o) {
+			if (o instanceof Map)
+				return (Map< ? , ? >) o;
+
+			throw new IllegalArgumentException("Cannot convert " + o
+					+ " to a map as requested");
+		}
+
+	}
+
+	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();
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/configurable/Configurable.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/configurable/Configurable.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/configurable/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/configurable/packageinfo?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/configurable/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/configurable/packageinfo Thu Jun  7 21:57:32 2012
@@ -0,0 +1 @@
+version 1.0.0

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/Base64.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/Base64.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/Base64.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/Base64.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,158 @@
+package aQute.lib.base64;
+
+import java.io.*;
+
+/*
+ * Base 64 converter.
+ * 
+ * TODO Implement string to byte[]
+ */
+public class Base64 {
+	byte[]				data;
+
+	static final String	alphabet	= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	static byte[]		values		= new byte[128];
+
+	static {
+		for (int i = 0; i < values.length; i++) {
+			values[i] = -1;
+		}
+		// Create reverse index
+		for (int i = 0; i < alphabet.length(); i++) {
+			char c = alphabet.charAt(i);
+			values[c] = (byte) i;
+		}
+	}
+
+	public Base64(byte data[]) {
+		this.data = data;
+	}
+
+	
+	
+	public final static byte[] decodeBase64(String string) {
+		ByteArrayOutputStream bout= new ByteArrayOutputStream(string.length()*2/3);
+		StringReader rdr = new StringReader(string.trim());
+		try {
+			decode(rdr,bout);
+		} catch (Exception e) {
+			// cannot happen
+		}
+		return bout.toByteArray();
+	}
+	
+	public final static void decode(Reader rdr, OutputStream out) throws Exception {
+		int register = 0;
+		int i = 0;
+		int pads = 0;
+
+		byte test[] = new byte[3];
+		int c;
+		while ((c=rdr.read()) >= 0) {
+
+			if (c > 0x7F)
+				throw new IllegalArgumentException(
+						"Invalid base64 character in " + rdr
+								+ ", character value > 128 ");
+			
+			int v = 0;
+			if ( c == '=' ) {
+				pads++;
+			} else {
+				v = values[c];
+				if ( v < 0 )
+					throw new IllegalArgumentException(
+							"Invalid base64 character in " + rdr + ", " + c );
+			}					
+			register <<= 6;
+			register |= v;
+			test[2] = (byte) (register & 0xFF);
+			test[1] = (byte) ((register >> 8) & 0xFF);
+			test[0] = (byte) ((register >> 16) & 0xFF);
+
+			i++;
+
+			if ((i % 4) == 0) {
+				flush(out, register, pads);
+				register = 0;
+				pads = 0;
+			}
+		}
+	}
+
+	static private void flush(OutputStream out, int register, int pads) throws IOException {
+		switch (pads) {
+		case 0:
+			out.write(0xFF & (register >> 16));
+			out.write(0xFF & (register >> 8));
+			out.write(0xFF & (register >> 0));
+			break;
+			
+		case 1:
+			out.write(0xFF & (register >> 16));
+			out.write(0xFF & (register >> 8));
+			break;
+			
+		case 2:
+			out.write(0xFF & (register >> 16));
+		}
+	}
+
+	public Base64(String s) {
+		data = decodeBase64(s);
+	}
+
+	public String toString() {
+		return encodeBase64(data);
+	}
+
+	public static String encodeBase64(byte data[]) {
+		StringWriter sw = new StringWriter();
+		ByteArrayInputStream bin = new ByteArrayInputStream(data);
+		try {
+			encode(bin,sw);
+		} catch (IOException e) {
+			// can't happen
+		}
+		return sw.toString();
+	}
+	
+
+	public Object toData() {
+		return data;
+	}
+
+	public static void encode(InputStream in, Appendable sb) throws IOException {
+		//StringBuilder sb = new StringBuilder();
+		int buf = 0;
+		int bits = 0;
+		int out = 0;
+		
+		while (true) {
+			if (bits >= 6) {
+				bits -= 6;
+				int v = 0x3F & (buf >> bits);
+				sb.append(alphabet.charAt(v));
+				out++;
+			} else {
+				int c = in.read();
+				if (c < 0)
+					break;
+
+				buf <<= 8;
+				buf |= 0xFF & c;
+				bits += 8;
+			}
+		}
+		if (bits != 0) {// must be less than 7
+			sb.append(alphabet.charAt(0x3F & (buf << (6 - bits))));
+			out++;
+		}
+		int mod = 4 - (out % 4);
+		if (mod != 4) {
+			for (int i = 0; i < mod; i++)
+				sb.append('=');
+		}
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/Base64.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/Base64.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/Base64.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/packageinfo?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/base64/packageinfo Thu Jun  7 21:57:32 2012
@@ -0,0 +1 @@
+version 1.1.0

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/Codec.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/Codec.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/Codec.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/Codec.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,9 @@
+package aQute.lib.codec;
+
+import java.io.*;
+import java.lang.reflect.*;
+
+public interface Codec {
+	Object decode(Reader in, Type type) throws Exception;	
+	void encode(Type t, Object o, Appendable out) throws Exception;
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/Codec.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/Codec.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/HCodec.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/HCodec.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/HCodec.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/HCodec.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,72 @@
+package aQute.lib.codec;
+
+import java.io.*;
+import java.lang.reflect.*;
+
+public class HCodec implements Codec {
+	final Codec	codec;
+
+	public HCodec(Codec codec) {
+		this.codec = codec;
+	}
+
+	public Object decode(Reader in, Type type) throws Exception {
+		return codec.decode(in, type);
+	}
+
+	public <T> T decode(InputStream in, Class<T> t) throws Exception {
+		return t.cast(decode(in, (Type)t));
+	}
+
+	public <T> T decode(Reader in, Class<T> t) throws Exception {
+		return t.cast(decode(in, (Type) t));
+	}
+
+	public Object decode(InputStream in, Type t) throws Exception {
+		InputStreamReader r = new InputStreamReader(in, "UTF-8");
+		return codec.decode(r, t);
+	}
+
+	public void encode(Type t, Object o, Appendable out) throws Exception {
+		codec.encode(t, o, out);
+	}
+
+	public void encode(Type t, Object o, OutputStream out) throws Exception {
+		OutputStreamWriter wr = new OutputStreamWriter(out, "UTF-8");
+		try {
+			codec.encode(t, o, wr);
+		} finally {
+			wr.flush();
+		}
+	}
+
+	public <T> T decode(File in, Class<T> t) throws Exception {
+		FileInputStream fin = new FileInputStream(in);
+		try {
+			InputStreamReader rdr = new InputStreamReader(fin, "UTF-8");
+			try {
+				return t.cast(decode(rdr, t));
+			} finally {
+				rdr.close();
+			}
+		} finally {
+			fin.close();
+		}
+
+	}
+
+	public void encode(Type t, Object o, File out) throws Exception {
+		OutputStream oout = new FileOutputStream(out);
+		try {
+			Writer wr = new OutputStreamWriter(oout, "UTF-8");
+			try {
+				codec.encode(t, o, wr);
+			} finally {
+				wr.close();
+			}
+		} finally {
+			oout.close();
+		}
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/HCodec.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/HCodec.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/packageinfo?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/codec/packageinfo Thu Jun  7 21:57:32 2012
@@ -0,0 +1 @@
+version 1.0

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/EnumerationIterator.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/EnumerationIterator.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/EnumerationIterator.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/EnumerationIterator.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,42 @@
+package aQute.lib.collections;
+
+import java.util.*;
+
+/**
+ * Simple facade for enumerators so they can be used in for loops.
+ * 
+ * @param <T>
+ */
+public class EnumerationIterator<T> implements Iterable<T>, Iterator<T> {
+	
+	public static <T> EnumerationIterator<T> iterator(Enumeration<T> e) {
+		return new EnumerationIterator<T>(e);
+	}
+	
+	final Enumeration<T>	enumerator;
+	volatile boolean		done	= false;
+
+	public EnumerationIterator(Enumeration<T> e) {
+		enumerator = e;
+	}
+
+	public synchronized Iterator<T> iterator() {
+		if (done)
+			throw new IllegalStateException("Can only be used once");
+		done = true;
+		return this;
+
+	}
+
+	public boolean hasNext() {
+		return enumerator.hasMoreElements();
+	}
+
+	public T next() {
+		return enumerator.nextElement();
+	}
+
+	public void remove() {
+		throw new UnsupportedOperationException("Does not support removes");
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/EnumerationIterator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/EnumerationIterator.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,31 @@
+package aQute.lib.collections;
+
+import java.util.*;
+
+public class ExtList<T> extends ArrayList<T> {
+	private static final long	serialVersionUID	= 1L;
+
+	public ExtList(T ... ts) {
+		super(ts.length);
+		for (T t : ts){
+			add(t);
+		}
+	}
+	
+	public String join() {
+		return join(",");
+	}
+
+	public String join(String del) {
+		StringBuilder sb = new StringBuilder();
+		String d= "";
+		for ( T t : this) {
+			sb.append(d);
+			d=del;
+			if ( t != null)
+				sb.append(t.toString());
+		}
+		return sb.toString();
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/IteratorList.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/IteratorList.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/IteratorList.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/IteratorList.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,12 @@
+package aQute.lib.collections;
+
+import java.util.*;
+
+public class IteratorList<T> extends ArrayList<T> {
+	private static final long	serialVersionUID	= 1L;
+
+	public IteratorList(Iterator<T> i){
+		while(i.hasNext())
+			add(i.next());
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/IteratorList.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/IteratorList.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/LineCollection.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/LineCollection.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/LineCollection.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/LineCollection.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,54 @@
+package aQute.lib.collections;
+
+import java.io.*;
+import java.util.*;
+
+public class LineCollection implements Iterator<String>, Closeable {
+	final BufferedReader	reader;
+	String					next;
+
+	public LineCollection(InputStream in) throws IOException {
+		this(new InputStreamReader(in, "UTF8"));
+	}
+
+	public LineCollection(File in) throws IOException {
+		this(new InputStreamReader( new FileInputStream(in),"UTF-8"));
+	}
+
+	public LineCollection(Reader reader) throws IOException {
+		this(new BufferedReader(reader));
+	}
+
+	public LineCollection(BufferedReader reader) throws IOException {
+		this.reader = reader;
+		next = reader.readLine();
+	}
+
+	public boolean hasNext() {
+		return next != null;
+	}
+
+	public String next() {
+		if (next == null)
+			throw new IllegalStateException("Iterator has finished");
+		try {
+			String result = next;
+			next = reader.readLine();
+			if (next == null)
+				reader.close();
+			return result;
+		} catch (Exception e) {
+			// ignore
+			return null;
+		}
+	}
+
+	public void remove() {
+		if (next == null)
+			throw new UnsupportedOperationException("Cannot remove");
+	}
+
+	public void close() throws IOException {
+		reader.close();
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/LineCollection.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/LineCollection.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/Logic.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/Logic.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/Logic.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/Logic.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,22 @@
+package aQute.lib.collections;
+
+import java.util.*;
+
+public class Logic {
+	
+	public static <T> Collection<T> retain( Collection<T> first, Collection<T> ... sets) {
+		Set<T> result = new HashSet<T>(first);
+		for ( Collection<T> set : sets ) {
+			result.retainAll(set);
+		}
+		return result;
+	}
+	
+	public static <T> Collection<T> remove( Collection<T> first, Collection<T> ... sets) {
+		Set<T> result = new HashSet<T>(first);
+		for ( Collection<T> set : sets ) {
+			result.removeAll(set);
+		}
+		return result;
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/Logic.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/Logic.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/MultiMap.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/MultiMap.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/MultiMap.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/MultiMap.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,155 @@
+package aQute.lib.collections;
+
+import java.util.*;
+
+public class MultiMap<K,V> extends HashMap<K,List<V>> {
+	private static final long	serialVersionUID	= 1L;
+	final boolean noduplicates;
+	final Class<?> keyClass;
+	final Class<?> valueClass;
+	
+	final Set<V> EMPTY = Collections.emptySet();
+	
+	public MultiMap() {
+		noduplicates = false;
+		keyClass = Object.class;
+		valueClass = Object.class;
+	}
+	
+	public MultiMap(Class<K> keyClass, Class<V> valueClass, boolean noduplicates ) {
+		this.noduplicates = noduplicates;		
+		this.keyClass = keyClass;
+		this.valueClass = valueClass;
+	}
+	
+	@SuppressWarnings("unchecked") public boolean add( K key, V value ) {
+		assert keyClass.isInstance(key);
+		assert valueClass.isInstance(value);
+		
+		List<V> set = get(key);
+		if ( set == null) {
+			set=new ArrayList<V>();
+			if ( valueClass != Object.class) {
+				set = Collections.checkedList(set, (Class<V>)valueClass);
+			}
+			put(key,set);
+		}else {
+			if (noduplicates) {
+				if ( set.contains(value))
+					return false;
+			}
+		}
+		return set.add(value);
+	}
+	
+	@SuppressWarnings("unchecked") public boolean addAll( K key, Collection<? extends V> value ) {
+		assert keyClass.isInstance(key);
+		List<V> set = get(key);
+		if ( set == null) {
+			set=new ArrayList<V>();
+			if ( valueClass != Object.class) {
+				set = Collections.checkedList(set, (Class<V>)valueClass);
+			}
+			put(key,set);
+		} else
+		if ( noduplicates) {
+			boolean r=false;
+			for ( V v : value) {
+				assert valueClass.isInstance(v);
+				if ( !set.contains(value))
+					r|=set.add(v);
+			}
+			return r;
+		}
+		return set.addAll(value);
+	}
+	
+	public boolean remove( K key, V value ) {
+		assert keyClass.isInstance(key);
+		assert valueClass.isInstance(value);
+		
+		List<V> set = get(key);
+		if ( set == null) {
+			return false;
+		}
+		boolean result = set.remove(value);
+		if ( set.isEmpty())
+			remove(key);
+		return result;
+	}
+	
+	public boolean removeAll( K key, Collection<V> value ) {
+		assert keyClass.isInstance(key);
+		List<V> set = get(key);
+		if ( set == null) {
+			return false;
+		}
+		boolean result = set.removeAll(value);
+		if ( set.isEmpty())
+			remove(key);
+		return result;
+	}
+	
+	public Iterator<V> iterate(K key) {
+		assert keyClass.isInstance(key);
+		List<V> set = get(key);
+		if ( set == null)
+			return EMPTY.iterator();
+		return set.iterator();
+	}
+	
+	public Iterator<V> all() {
+		return new Iterator<V>() {
+			Iterator<List<V>> master = values().iterator();
+			Iterator<V> current = null;
+			
+			public boolean hasNext() {
+				if ( current == null || !current.hasNext()) {
+					if ( master.hasNext()) {
+						current = master.next().iterator();
+						return current.hasNext();
+					}
+					return false;
+				}
+				return true;
+			}
+
+			public V next() {
+				return current.next();
+			}
+
+			public void remove() {
+				current.remove();
+			}
+			
+		};
+	}
+	
+	public Map<K,V> flatten() {
+		Map<K,V> map = new LinkedHashMap<K,V>();
+		for ( Map.Entry<K, List<V>> entry : entrySet()) {
+			List<V> v = entry.getValue();
+			if ( v == null || v.isEmpty())
+				continue;
+
+			map.put(entry.getKey(), v.get(0));
+		}
+		return map;
+	}
+	
+	public MultiMap<V,K> transpose() {
+		MultiMap<V,K> inverted = new MultiMap<V,K>();
+		for ( Map.Entry<K, List<V>> entry : entrySet()) {
+			K key = entry.getKey();
+			
+			List<V> value = entry.getValue();
+			if ( value == null)
+				continue;
+			
+			for ( V v : value)
+				inverted.add(v, key);
+		}
+		
+		return inverted;
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/MultiMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/MultiMap.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/SortedList.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/SortedList.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/SortedList.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/SortedList.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,423 @@
+package aQute.lib.collections;
+
+import java.util.*;
+
+/**
+ * An immutbale list that sorts objects by their natural order or through a
+ * comparator. It has convenient methods/constructors to create it from
+ * collections and iterators.
+ * 
+ * Why not maintain the lists in their sorted form? Well, TreeMaps are quite
+ * expensive ... I once profiled bnd and was shocked how much memory the Jar
+ * class took due to the TreeMaps. I could not easily change it unfortunately.
+ * The other reason is that Parameters uses a LinkedHashMap because the
+ * preferred order should be the declaration order. However, sometimes you need
+ * to sort the keys by name.
+ * 
+ * Last, and most important reason, is that sometimes you do not know what
+ * collection you have or it is not available in a sort ordering (MultiMap for
+ * example) ... I found myself sorting these things over and over again and
+ * decided to just make an immutable SortedList that is easy to slice and dice
+ * 
+ * @param <T>
+ */
+@SuppressWarnings("unchecked") public class SortedList<T> implements SortedSet<T>, List<T> {
+	static SortedList<?>		empty		= new SortedList<Object>();
+
+	final T[]					list;
+	final int					start;
+	final int					end;
+	final Comparator<T>			cmp;
+	Class<?>					type;
+	static Comparator<Object>	comparator	= //
+
+											new Comparator<Object>() {
+												public int compare(Object o1, Object o2) {
+
+													if (o1 == o2)
+														return 0;
+
+													if (o1.equals(o2))
+														return 0;
+
+													return ((Comparable<Object>) o1).compareTo(o2);
+												}
+											};
+
+	class It implements ListIterator<T> {
+		int	n;
+
+		private It(int n) {
+			this.n = n;
+		}
+
+		public boolean hasNext() {
+			return n < end;
+		}
+
+		public T next() throws NoSuchElementException {
+			if (!hasNext()) {
+				throw new NoSuchElementException("");
+			}
+			return list[n++];
+		}
+
+		public boolean hasPrevious() {
+			return n > start;
+		}
+
+		public T previous() {
+			return get(n - 1);
+		}
+
+		public int nextIndex() {
+			return (n + 1 - start);
+		}
+
+		public int previousIndex() {
+			return (n - 1) - start;
+		}
+
+		@Deprecated public void remove() {
+			throw new UnsupportedOperationException("Immutable");
+		}
+
+		@Deprecated public void set(T e) {
+			throw new UnsupportedOperationException("Immutable");
+		}
+
+		@Deprecated public void add(T e) {
+			throw new UnsupportedOperationException("Immutable");
+		}
+	}
+
+	public SortedList(Collection<? extends Comparable<?>> x) {
+		this((Collection<T>) x, 0, x.size(), (Comparator<T>) comparator);
+	}
+
+	public SortedList(Collection<T> x, Comparator<T> cmp) {
+		this(x, 0, x.size(), cmp);
+	}
+
+	@SuppressWarnings("cast")
+	public SortedList(T... x) {
+		this((T[]) x.clone(), 0, x.length, (Comparator<T>) comparator);
+	}
+
+	@SuppressWarnings("cast")
+	public SortedList(Comparator<T> cmp, T... x) {
+		this((T[]) x.clone(), 0, x.length, cmp);
+	}
+
+	private SortedList(SortedList<T> other, int start, int end) {
+		this.list = other.list;
+		this.cmp = other.cmp;
+		this.start = start;
+		this.end = end;
+	}
+
+	public SortedList(T[] x, int start, int end, Comparator<T> comparator2) {
+		if (start > end) {
+			int tmp = start;
+			start = end;
+			end = tmp;
+		}
+		if (start < 0 || start >= x.length)
+			throw new IllegalArgumentException("Start is not in list");
+
+		if (end < 0 || end > x.length)
+			throw new IllegalArgumentException("End is not in list");
+
+		this.list = x.clone();
+		Arrays.sort(this.list, start, end, comparator2);
+		this.start = start;
+		this.end = end;
+		this.cmp = comparator2;
+	}
+
+	public SortedList(Collection<? extends T> x, int start, int end, Comparator<T> cmp) {
+		if (start > end) {
+			int tmp = start;
+			start = end;
+			end = tmp;
+		}
+		if (start < 0 || start > x.size())
+			throw new IllegalArgumentException("Start is not in list");
+
+		if (end < 0 || end > x.size())
+			throw new IllegalArgumentException("End is not in list");
+
+		this.list = (T[]) x.toArray();
+		Arrays.sort(this.list, start, end, cmp);
+		this.start = start;
+		this.end = end;
+		this.cmp = cmp;
+	}
+
+	private SortedList() {
+		list = null;
+		start = 0;
+		end = 0;
+		cmp = null;
+	}
+
+	public int size() {
+		return end - start;
+	}
+
+	public boolean isEmpty() {
+		return start == end;
+	}
+
+	@SuppressWarnings("cast")
+	public boolean contains(Object o) {
+		assert type != null & type.isInstance(o);
+		return indexOf((T) o) >= 0;
+	}
+
+	public Iterator<T> iterator() {
+		return new It(start);
+	}
+
+	public Object[] toArray() {
+		return list.clone();
+	}
+
+	@SuppressWarnings("hiding") public <T> T[] toArray(T[] a) {
+		if (a == null || a.length < list.length) {
+			return (T[]) list.clone();
+		}
+		System.arraycopy(list, 0, a, 0, list.length);
+		return a;
+	}
+
+	public boolean add(T e) {
+		throw new UnsupportedOperationException("Immutable");
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException("Immutable");
+	}
+
+	public boolean containsAll(Collection<?> c) {
+		if (c.isEmpty())
+			return true;
+
+		if (isEmpty())
+			return false;
+
+		// TODO take advantage of sorted nature for this
+
+		for (Object el : c) {
+			if (!contains(el))
+				return false;
+		}
+		return false;
+	}
+
+	public boolean addAll(Collection<? extends T> c) {
+		throw new UnsupportedOperationException("Immutable");
+	}
+
+	public boolean retainAll(Collection<?> c) {
+		throw new UnsupportedOperationException("Immutable");
+	}
+
+	public boolean removeAll(Collection<?> c) {
+		throw new UnsupportedOperationException("Immutable");
+	}
+
+	public void clear() {
+		throw new UnsupportedOperationException("Immutable");
+	}
+
+	public Comparator<? super T> comparator() {
+		return cmp;
+	}
+
+	public boolean isSubSet() {
+		return start > 0 && end < list.length;
+	}
+
+	public SortedList<T> subSet(T fromElement, T toElement) {
+		int start = indexOf(fromElement);
+		int end = indexOf(toElement);
+		if (isSubSet() && (start < 0 || end < 0))
+			throw new IllegalArgumentException("This list is a subset");
+		if (start < 0)
+			start = 0;
+		if (end < 0)
+			end = list.length;
+
+		return subList(start, end);
+	}
+
+	public int indexOf(Object o) {
+		assert type != null && type.isInstance(o);
+
+		int n = Arrays.binarySearch(list, (T) o, cmp);
+		if (n >= start && n < end)
+			return n - start;
+
+		return -1;
+	}
+
+	public SortedList<T> headSet(T toElement) {
+		int i = indexOf(toElement);
+		if (i < 0) {
+			if (isSubSet())
+				throw new IllegalArgumentException("This list is a subset");
+			i = end;
+		}
+
+		if (i == end)
+			return this;
+
+		return subList(0, i);
+	}
+
+	public SortedSet<T> tailSet(T fromElement) {
+		int i = indexOf(fromElement);
+		if (i < 0) {
+			if (isSubSet())
+				throw new IllegalArgumentException("This list is a subset");
+			i = start;
+		}
+
+		return subList(i, end);
+	}
+
+	public T first() {
+		if (isEmpty())
+			throw new NoSuchElementException("first");
+		return get(0);
+	}
+
+	public T last() {
+		if (isEmpty())
+			throw new NoSuchElementException("last");
+		return get(end - 1);
+	}
+
+	@Deprecated public boolean addAll(int index, Collection<? extends T> c) {
+		throw new UnsupportedOperationException("Immutable");
+	}
+
+	public T get(int index) {
+		return list[index + start];
+	}
+
+	@Deprecated public T set(int index, T element) {
+		throw new UnsupportedOperationException("Immutable");
+	}
+
+	@Deprecated public void add(int index, T element) {
+		throw new UnsupportedOperationException("Immutable");
+	}
+
+	@Deprecated public T remove(int index) {
+		throw new UnsupportedOperationException("Immutable");
+	}
+
+	public int lastIndexOf(Object o) {
+		int n = indexOf(o);
+		if (n < 0)
+			return -1;
+
+		while (cmp.compare(list[n], (T) o) == 0)
+			n++;
+
+		return n;
+	}
+
+	public ListIterator<T> listIterator() {
+		return new It(start);
+	}
+
+	public ListIterator<T> listIterator(int index) {
+		return new It(index + start);
+	}
+
+	public SortedList<T> subList(int fromIndex, int toIndex) {
+		fromIndex += start;
+		toIndex += start;
+
+		if (toIndex < fromIndex) {
+			int tmp = toIndex;
+			toIndex = fromIndex;
+			fromIndex = tmp;
+		}
+
+		toIndex = Math.max(0, toIndex);
+		toIndex = Math.min(toIndex, end);
+		fromIndex = Math.max(0, fromIndex);
+		fromIndex = Math.min(fromIndex, end);
+		if (fromIndex == start && toIndex == end)
+			return this;
+
+		return new SortedList<T>(this, fromIndex, toIndex);
+	}
+
+	@Deprecated public boolean equals(Object other) {
+		return super.equals(other);
+	}
+
+	@Deprecated public int hashCode() {
+		return super.hashCode();
+	}
+
+	public boolean isEqual(SortedList<T> list) {
+		if (size() != list.size())
+			return false;
+
+		for (int as = start, bs = list.start, al = size(); as < al && bs < al; as++, bs++) {
+			if (comparator.compare(this.list[as], this.list[bs]) != 0)
+				return false;
+		}
+		return true;
+	}
+
+	public Class<?> getType() {
+		return type;
+	}
+
+	public void setType(Class<?> type) {
+		this.type = type;
+	}
+
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("[");
+		String del = "";
+		for (T s : list) {
+			sb.append(del);
+			sb.append(s);
+			del = ", ";
+		}
+
+		sb.append("]");
+		return sb.toString();
+	}
+
+	public boolean hasDuplicates() {
+		if (list.length < 2)
+			return false;
+
+		T prev = list[0];
+		for (int i = 1; i < list.length; i++) {
+			if (prev.equals(list[i]))
+				return true;
+		}
+		return false;
+	}
+
+	public static <T extends Comparable<?>> SortedList<T> fromIterator(Iterator<T> it) {
+		IteratorList<T> l = new IteratorList<T>(it);
+		return new SortedList<T>(l);
+	}
+
+	public static <T> SortedList<T> fromIterator(Iterator<T> it, Comparator<T> cmp) {
+		IteratorList<T> l = new IteratorList<T>(it);
+		return new SortedList<T>(l, cmp);
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/SortedList.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/SortedList.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/packageinfo?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/collections/packageinfo Thu Jun  7 21:57:32 2012
@@ -0,0 +1 @@
+version 1.1

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/converter/Converter.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/converter/Converter.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/converter/Converter.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,364 @@
+package aQute.lib.converter;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.regex.*;
+
+import aQute.lib.base64.*;
+
+/**
+ * General Java type converter from an object to any type. Supports number
+ * conversion
+ * 
+ * @author aqute
+ * 
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" }) public class Converter {
+	boolean	fatal	= true;
+
+	public <T> T convert(Class<T> type, Object o) throws Exception {
+		return (T) convert((Type) type, o);
+	}
+
+	public Object convert(Type type, Object o) throws Exception {
+		if (o == null)
+			return null; // compatible with any
+
+		Class resultType = getRawClass(type);
+		Class<?> actualType = o.getClass();
+		// Is it a compatible type?
+		if (resultType.isAssignableFrom(actualType))
+			return o;
+
+		// We can always make a string
+
+		if (resultType == String.class) {
+			if (actualType.isArray()) {
+				if (actualType == char[].class)
+					return new String((char[]) o);
+				if (actualType == byte[].class)
+					return Base64.encodeBase64((byte[]) o);
+				int l = Array.getLength(o);
+				StringBuilder sb = new StringBuilder("[");
+				String del = "";
+				for (int i = 0; i < l; i++) {
+					sb.append(del);
+					del = ",";
+					sb.append(convert(String.class, Array.get(o, i)));
+				}
+				sb.append("]");
+				return sb.toString();
+			}
+			return o.toString();
+		}
+
+		if (Collection.class.isAssignableFrom(resultType))
+			return collection(type, resultType, o);
+
+		if (Map.class.isAssignableFrom(resultType))
+			return map(type, resultType, o);
+
+		if (type instanceof GenericArrayType) {
+			GenericArrayType gType = (GenericArrayType) type;
+			return array(gType.getGenericComponentType(), o);
+		}
+
+		if (resultType.isArray()) {
+			if (actualType == String.class) {
+				String s = (String) o;
+				if (byte[].class == resultType)
+					return Base64.decodeBase64(s);
+
+				if (char[].class == resultType)
+					return s.toCharArray();
+			}
+			if (byte[].class == resultType) {
+				// Sometimes classes implement toByteArray
+				try {
+					Method m = actualType.getMethod("toByteArray");
+					if (m.getReturnType() == byte[].class)
+						return m.invoke(o);
+
+				} catch (Exception e) {
+					// Ignore
+				}
+			}
+
+			return array(resultType.getComponentType(), o);
+		}
+
+		// Simple type coercion
+
+		if (resultType == boolean.class || resultType == Boolean.class) {
+			if (actualType == boolean.class || actualType == Boolean.class)
+				return o;
+			Number n = number(o);
+			if (n != null)
+				return n.longValue() == 0 ? false : true;
+
+			resultType = Boolean.class;
+		} else if (resultType == byte.class || resultType == Byte.class) {
+			Number n = number(o);
+			if (n != null)
+				return n.byteValue();
+			resultType = Byte.class;
+		} else if (resultType == char.class || resultType == Character.class) {
+			Number n = number(o);
+			if (n != null)
+				return (char) n.shortValue();
+			resultType = Character.class;
+		} else if (resultType == short.class || resultType == Short.class) {
+			Number n = number(o);
+			if (n != null)
+				return n.shortValue();
+
+			resultType = Short.class;
+		} else if (resultType == int.class || resultType == Integer.class) {
+			Number n = number(o);
+			if (n != null)
+				return n.intValue();
+
+			resultType = Integer.class;
+		} else if (resultType == long.class || resultType == Long.class) {
+			Number n = number(o);
+			if (n != null)
+				return n.longValue();
+
+			resultType = Long.class;
+		} else if (resultType == float.class || resultType == Float.class) {
+			Number n = number(o);
+			if (n != null)
+				return n.floatValue();
+
+			resultType = Float.class;
+		} else if (resultType == double.class || resultType == Double.class) {
+			Number n = number(o);
+			if (n != null)
+				return n.doubleValue();
+
+			resultType = Double.class;
+		}
+
+		assert !resultType.isPrimitive();
+
+		if (actualType == String.class) {
+			String input = (String) o;
+			if (resultType == char[].class)
+				return input.toCharArray();
+
+			if (resultType == byte[].class)
+				return Base64.decodeBase64(input);
+
+			if (Enum.class.isAssignableFrom(resultType)) {
+				return Enum.valueOf((Class<Enum>) resultType, input);
+			}
+			if (resultType == Pattern.class) {
+				return Pattern.compile(input);
+			}
+
+			try {
+				Constructor<?> c = resultType.getConstructor(String.class);
+				return c.newInstance(o.toString());
+			} catch (Throwable t) {
+			}
+			try {
+				Method m = resultType.getMethod("valueOf", String.class);
+				if (Modifier.isStatic(m.getModifiers()))
+					return m.invoke(null, o.toString());
+			} catch (Throwable t) {
+			}
+
+			if (resultType == Character.class && input.length() == 1)
+				return input.charAt(0);
+		}
+		Number n = number(o);
+		if (n != null) {
+			if (Enum.class.isAssignableFrom(resultType)) {
+				try {
+					Method values = resultType.getMethod("values");
+					Enum[] vs = (Enum[]) values.invoke(null);
+					int nn = n.intValue();
+					if (nn > 0 && nn < vs.length)
+						return vs[nn];
+				} catch (Exception e) {
+					// Ignore
+				}
+			}
+		}
+		return error("No conversion found for " + o.getClass() + " to " + type);
+	}
+
+	private Number number(Object o) {
+		if (o instanceof Number)
+			return (Number) o;
+
+		if (o instanceof Boolean)
+			return ((Boolean) o).booleanValue() ? 1 : 0;
+
+		if (o instanceof Character)
+			return (int) ((Character) o).charValue();
+
+		if (o instanceof String) {
+			String s = (String) o;
+			try {
+				return Double.parseDouble(s);
+			} catch (Exception e) {
+				// Ignore
+			}
+		}
+		return null;
+	}
+
+	private Collection collection(Type collectionType, Class<? extends Collection> rawClass,
+			Object o) throws Exception {
+		Collection collection;
+		if (rawClass.isInterface() || Modifier.isAbstract(rawClass.getModifiers())) {
+			if (rawClass.isAssignableFrom(ArrayList.class))
+				collection = new ArrayList();
+			else if (rawClass.isAssignableFrom(HashSet.class))
+				collection = new HashSet();
+			else if (rawClass.isAssignableFrom(TreeSet.class))
+				collection = new TreeSet();
+			else if (rawClass.isAssignableFrom(LinkedList.class))
+				collection = new LinkedList();
+			else if (rawClass.isAssignableFrom(Vector.class))
+				collection = new Vector();
+			else if (rawClass.isAssignableFrom(Stack.class))
+				collection = new Stack();
+			else if (rawClass.isAssignableFrom(ConcurrentLinkedQueue.class))
+				collection = new ConcurrentLinkedQueue();
+			else
+				return (Collection) error("Cannot find a suitable collection for the collection interface "
+						+ rawClass);
+		} else
+			collection = rawClass.newInstance();
+
+		Type subType = Object.class;
+		if (collectionType instanceof ParameterizedType) {
+			ParameterizedType ptype = (ParameterizedType) collectionType;
+			subType = ptype.getActualTypeArguments()[0];
+		}
+
+		Collection input = toCollection(o);
+
+		for (Object i : input)
+			collection.add(convert(subType, i));
+
+		return collection;
+	}
+
+	private Map map(Type mapType, Class<? extends Map<?, ?>> rawClass, Object o) throws Exception {
+		Map result;
+		if (rawClass.isInterface() || Modifier.isAbstract(rawClass.getModifiers())) {
+			if (rawClass.isAssignableFrom(HashMap.class))
+				result = new HashMap();
+			else if (rawClass.isAssignableFrom(TreeMap.class))
+				result = new TreeMap();
+			else if (rawClass.isAssignableFrom(ConcurrentHashMap.class))
+				result = new ConcurrentHashMap();
+			else
+				return (Map) error("Cannot find suitable map for map interface " + rawClass);
+		} else
+			result = rawClass.newInstance();
+
+		Map<?, ?> input = toMap(o);
+
+		Type keyType = Object.class;
+		Type valueType = Object.class;
+		if (mapType instanceof ParameterizedType) {
+			ParameterizedType ptype = (ParameterizedType) mapType;
+			keyType = ptype.getActualTypeArguments()[0];
+			valueType = ptype.getActualTypeArguments()[1];
+		}
+
+		for (Map.Entry<?, ?> entry : input.entrySet()) {
+			Object key = convert(keyType, entry.getKey());
+			Object value = convert(valueType, entry.getValue());
+			if (value == null)
+				return (Map) error("Key for map must not be null");
+			result.put(key, value);
+		}
+
+		return result;
+	}
+
+	public Object array(Type type, Object o) throws Exception {
+		Collection<?> input = toCollection(o);
+		Class<?> componentClass = getRawClass(type);
+		Object array = Array.newInstance(componentClass, input.size());
+
+		int i = 0;
+		for (Object next : input) {
+			Array.set(array, i++, convert(type, next));
+		}
+		return array;
+	}
+
+	private Class<?> getRawClass(Type type) {
+		if (type instanceof Class)
+			return (Class<?>) type;
+
+		if (type instanceof ParameterizedType)
+			return (Class<?>) ((ParameterizedType) type).getRawType();
+
+		if (type instanceof GenericArrayType) {
+			Type componentType = ((GenericArrayType) type).getGenericComponentType();
+			return Array.newInstance(getRawClass(componentType), 0).getClass();
+		}
+
+		if (type instanceof TypeVariable) {
+			Type componentType = ((TypeVariable) type).getBounds()[0];
+			return Array.newInstance(getRawClass(componentType), 0).getClass();
+		}
+
+		if (type instanceof WildcardType) {
+			Type componentType = ((WildcardType) type).getUpperBounds()[0];
+			return Array.newInstance(getRawClass(componentType), 0).getClass();
+		}
+
+		return Object.class;
+	}
+
+	public Collection<?> toCollection(Object o) {
+		if (o instanceof Collection)
+			return (Collection<?>) o;
+
+		if (o.getClass().isArray()) {
+			if (o.getClass().getComponentType().isPrimitive()) {
+				int length = Array.getLength(o);
+				List<Object> result = new ArrayList<Object>(length);
+				for (int i = 0; i < length; i++) {
+					result.add(Array.get(o, i));
+				}
+				return result;
+			}
+			return Arrays.asList((Object[]) o);
+		}
+
+		return Arrays.asList(o);
+	}
+
+	public Map<?, ?> toMap(Object o) throws Exception {
+		if (o instanceof Map)
+			return (Map<?, ?>) o;
+		Map result = new HashMap();
+		Field fields[] = o.getClass().getFields();
+		for (Field f : fields)
+			result.put(f.getName(), f.get(o));
+		if (result.isEmpty())
+			return null;
+
+		return result;
+	}
+
+	private Object error(String string) {
+		if (fatal)
+			throw new IllegalArgumentException(string);
+		return null;
+	}
+
+	public void setFatalIsException(boolean b) {
+		fatal = b;
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/converter/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/converter/packageinfo?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/converter/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/converter/packageinfo Thu Jun  7 21:57:32 2012
@@ -0,0 +1 @@
+version 2.0.1

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/AllowNull.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/data/AllowNull.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/data/AllowNull.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/data/AllowNull.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,9 @@
+package aQute.lib.data;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(value={ElementType.FIELD})
+public @interface AllowNull {
+	String reason() default "";
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/AllowNull.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/AllowNull.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Data.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Data.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Data.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Data.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,79 @@
+package aQute.lib.data;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.regex.*;
+
+public class Data {
+
+	public static String validate(Object o) throws Exception {
+		StringBuilder sb = new StringBuilder();
+		Formatter formatter = new Formatter(sb);
+
+		Field fields[] = o.getClass().getFields();
+		for (Field f : fields) {
+			Validator patternValidator = f.getAnnotation(Validator.class);
+			Numeric numericValidator = f.getAnnotation(Numeric.class);
+			AllowNull allowNull = f.getAnnotation(AllowNull.class);
+			Object value = f.get(o);
+			if (value == null) {
+				if (allowNull == null)
+					formatter.format("Value for %s must not be null\n", f.getName());
+			} else {
+				
+
+				if (patternValidator != null) {
+					Pattern p = Pattern.compile(patternValidator.value());
+					Matcher m = p.matcher(value.toString());
+					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, patternValidator.value());
+						else
+							formatter.format("Value for %s=%s %s\n", f.getName(), value, reason);
+					}
+				}
+
+				if (numericValidator != null) {
+					if (o instanceof String) {
+						try {
+							o = Double.parseDouble((String) o);
+						} catch (Exception e) {
+							formatter.format("Value for %s=%s %s\n", f.getName(), value, "Not a number");
+						}
+					}
+					
+					try {
+						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, numericValidator.min(), numericValidator.max());
+						}
+					} catch (ClassCastException e) {
+						formatter.format("Value for %s=%s [%s,%s) is not a number\n", f.getName(), value,
+								numericValidator.min(), numericValidator.max());
+					}
+				}
+			}
+		}
+		if ( sb.length() == 0)
+			return null;
+		
+		if ( sb.length() > 0)
+			sb.delete(sb.length() - 1, sb.length());
+		return sb.toString();
+	}
+
+	public static void details(Object data, Appendable out) throws Exception {
+		Field fields[] = data.getClass().getFields();
+		Formatter formatter = new Formatter(out);
+		
+		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));
+		}
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Data.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Data.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Numeric.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Numeric.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Numeric.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Numeric.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,11 @@
+package aQute.lib.data;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(value={ElementType.FIELD})
+public @interface Numeric {
+	long min() default Long.MIN_VALUE;
+	long max() default Long.MAX_VALUE;
+	String reason() default "";
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Numeric.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Numeric.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Validator.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Validator.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Validator.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Validator.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,10 @@
+package aQute.lib.data;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(value={ElementType.FIELD})
+public @interface Validator {
+	String value();
+	String reason() default "";
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Validator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/Validator.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/data/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/data/packageinfo?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/data/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/data/packageinfo Thu Jun  7 21:57:32 2012
@@ -0,0 +1 @@
+version 1.0.0

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,150 @@
+package aQute.lib.deployer;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import aQute.lib.osgi.*;
+import aQute.libg.header.*;
+import aQute.libg.reporter.*;
+import aQute.libg.version.*;
+
+public class FileInstallRepo extends FileRepo {
+
+	String group;
+	boolean dirty;
+	Reporter reporter;
+	Pattern              REPO_FILE   = Pattern
+    .compile("([-a-zA-z0-9_\\.]+)-([0-9\\.]+)\\.(jar|lib)");
+	
+    public void setProperties(Map<String, String> map) {
+    	super.setProperties(map);
+    	group = map.get("group");
+    }
+    public void setReporter(Reporter reporter) {
+    	super.setReporter(reporter);
+        this.reporter = reporter;
+    }
+
+    public File put(Jar jar) throws Exception {
+        dirty = true;
+        Manifest manifest = jar.getManifest();
+        if (manifest == null)
+            throw new IllegalArgumentException("No manifest in JAR: " + jar);
+
+        String bsn = manifest.getMainAttributes().getValue(
+                Analyzer.BUNDLE_SYMBOLICNAME);
+        if (bsn == null)
+            throw new IllegalArgumentException("No Bundle SymbolicName set");
+
+        Parameters b = Processor.parseHeader(bsn, null);
+        if (b.size() != 1)
+            throw new IllegalArgumentException("Multiple bsn's specified " + b);
+
+        for (String key : b.keySet()) {
+            bsn = key;
+            if (!Verifier.SYMBOLICNAME.matcher(bsn).matches())
+                throw new IllegalArgumentException(
+                        "Bundle SymbolicName has wrong format: " + bsn);
+        }
+
+        String versionString = manifest.getMainAttributes().getValue(
+                Analyzer.BUNDLE_VERSION);
+        Version version;
+        if (versionString == null)
+            version = new Version();
+        else
+            version = new Version(versionString);
+
+        File dir;
+        if (group == null) {
+        	dir = getRoot();
+        } else {
+        	dir= new File(getRoot(), group);
+        	dir.mkdirs();
+        }
+        String fName = bsn + "-" + version.getMajor() + "."
+                + version.getMinor() + "." + version.getMicro() + ".jar";
+        File file = new File(dir, fName);
+
+        jar.write(file);
+        fireBundleAdded(jar, file);
+
+        file = new File(dir, bsn + "-latest.jar");
+        if (file.isFile() && file.lastModified() < jar.lastModified()) {
+            jar.write(file);
+        }
+        return file;
+    }
+    public boolean refresh() {
+        if ( dirty ) {
+            dirty = false;
+            return true;
+        } else 
+            return false;
+    }
+	@Override
+	public List<String> list(String regex) {
+	       Instruction pattern = null;
+	        if (regex != null)
+	            pattern = new Instruction(regex);
+
+	        String list[] = getRoot().list();
+	        List<String> result = new ArrayList<String>();
+	        for (String f : list) {
+                Matcher m = REPO_FILE.matcher(f);
+                if (!m.matches()) {
+                	continue;
+                }
+                String s = m.group(1);
+	            if (pattern == null || pattern.matches(s))
+	                result.add(s);
+	        }
+	        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()]);
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision



Mime
View raw message