You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by mc...@apache.org on 2012/06/07 23:57:43 UTC

svn commit: r1347815 [9/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...

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,333 @@
+package aQute.lib.deployer;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.io.*;
+import aQute.lib.osgi.*;
+import aQute.libg.header.*;
+import aQute.libg.reporter.*;
+import aQute.libg.version.*;
+
+public class FileRepo implements Plugin, RepositoryPlugin, Refreshable, RegistryPlugin {
+	public final static String	LOCATION	= "location";
+	public final static String	READONLY	= "readonly";
+	public final static String	NAME		= "name";
+
+	File[]					EMPTY_FILES	= new File[0];
+	protected File			root;
+	Registry				registry;
+	boolean					canWrite	= true;
+	Pattern					REPO_FILE	= Pattern
+												.compile("([-a-zA-z0-9_\\.]+)-([0-9\\.]+|latest)\\.(jar|lib)");
+	Reporter				reporter;
+	boolean					dirty;
+	String					name;
+
+	public FileRepo() {
+	}
+
+	public FileRepo(String name, File location, boolean canWrite) {
+		this.name = name;
+		this.root = location;
+		this.canWrite = canWrite;
+	}
+
+	protected void init() throws Exception {
+		// for extensions
+	}
+
+	public void setProperties(Map<String, String> map) {
+		String location = map.get(LOCATION);
+		if (location == null)
+			throw new IllegalArgumentException("Location must be set on a FileRepo plugin");
+
+		root = new File(location);
+		if (!root.isDirectory())
+			throw new IllegalArgumentException("Repository is not a valid directory " + root);
+
+		String readonly = map.get(READONLY);
+		if (readonly != null && Boolean.valueOf(readonly).booleanValue())
+			canWrite = false;
+
+		name = map.get(NAME);
+	}
+
+	/**
+	 * Get a list of URLs to bundles that are constrained by the bsn and
+	 * versionRange.
+	 */
+	public File[] get(String bsn, String versionRange) throws Exception {
+		init();
+
+		// 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;
+
+		//
+		// Check if the entry exists
+		//
+		File f = new File(root, bsn);
+		if (!f.isDirectory())
+			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[] = f.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) || versionString.equals(versionRange))
+					versions.put(version, instances[i]);
+			}
+		}
+
+		File[] files = versions.values().toArray(EMPTY_FILES);
+		if ("latest".equals(versionRange) && files.length > 0) {
+			return new File[] { files[files.length - 1] };
+		}
+		return files;
+	}
+
+	public boolean canWrite() {
+		return canWrite;
+	}
+
+	public File put(Jar jar) throws Exception {
+		init();
+		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);
+
+		reporter.trace("bsn=%s version=%s",bsn,version);
+		
+		File dir = new File(root, bsn);
+		dir.mkdirs();
+		String fName = bsn + "-" + version.getWithoutQualifier() + ".jar";
+		File file = new File(dir, fName);
+
+		reporter.trace("updating %s ", file.getAbsolutePath());
+		if (!file.exists() || file.lastModified() < jar.lastModified()) {
+			jar.write(file);
+			reporter.progress("updated " + file.getAbsolutePath());
+			fireBundleAdded(jar, file);
+		} else {
+			reporter.progress("Did not update " + jar + " because repo has a newer version");
+			reporter.trace("NOT Updating " + fName + " (repo is newer)");
+		}
+
+		File latest = new File(dir, bsn + "-latest.jar");
+		if (latest.exists() && latest.lastModified() < jar.lastModified()) {
+			jar.write(latest);
+			file = latest;
+		}
+
+		return file;
+	}
+
+	protected void fireBundleAdded(Jar jar, File file) {
+		if (registry == null)
+			return;
+		List<RepositoryListenerPlugin> listeners = registry
+				.getPlugins(RepositoryListenerPlugin.class);
+		for (RepositoryListenerPlugin listener : listeners) {
+			try {
+				listener.bundleAdded(this, jar, file);
+			} catch (Exception e) {
+				if (reporter != null)
+					reporter.warning("Repository listener threw an unexpected exception: %s", e);
+			}
+		}
+	}
+
+	public void setLocation(String string) {
+		root = new File(string);
+		if (!root.isDirectory())
+			throw new IllegalArgumentException("Invalid repository directory");
+	}
+
+	public void setReporter(Reporter reporter) {
+		this.reporter = reporter;
+	}
+
+	public List<String> list(String regex) throws Exception {
+		init();
+		Instruction pattern = null;
+		if (regex != null)
+			pattern = new Instruction(regex);
+
+		List<String> result = new ArrayList<String>();
+		if (root == null) {
+			if (reporter != null)
+				reporter.error("FileRepo root directory is not set.");
+		} else {
+			File[] list = root.listFiles();
+			if (list != null) {
+				for (File f : list) {
+					if (!f.isDirectory())
+						continue; // ignore non-directories
+					String fileName = f.getName();
+					if (fileName.charAt(0) == '.')
+						continue; // ignore hidden files
+					if (pattern == null || pattern.matches(fileName))
+						result.add(fileName);
+				}
+			} else if (reporter != null)
+				reporter.error("FileRepo root directory (%s) does not exist", root);
+		}
+
+		return result;
+	}
+
+	public List<Version> versions(String bsn) throws Exception {
+		init();
+		File dir = new File(root, bsn);
+		if (dir.isDirectory()) {
+			String versions[] = dir.list();
+			List<Version> list = new ArrayList<Version>();
+			for (String v : versions) {
+				Matcher m = REPO_FILE.matcher(v);
+				if (m.matches()) {
+					String version = m.group(2);
+					if (version.equals("latest"))
+						version = Integer.MAX_VALUE + "";
+					list.add(new Version(version));
+				}
+			}
+			return list;
+		}
+		return null;
+	}
+
+	public String toString() {
+		return String.format("%-40s r/w=%s", root.getAbsolutePath(), canWrite());
+	}
+
+	public File getRoot() {
+		return root;
+	}
+
+	public boolean refresh() {
+		if (dirty) {
+			dirty = false;
+			return true;
+		} else
+			return false;
+	}
+
+	public String getName() {
+		if (name == null) {
+			return toString();
+		}
+		return name;
+	}
+
+	public Jar get(String bsn, Version v) throws Exception {
+		init();
+		File bsns = new File(root, bsn);
+		File version = new File(bsns, bsn + "-" + v.getMajor() + "." + v.getMinor() + "."
+				+ v.getMicro() + ".jar");
+		if ( version.exists())
+			return new Jar(version);
+		else
+			return null;
+	}
+
+	public File get(String bsn, String version, Strategy strategy, Map<String, String> properties)
+			throws Exception {
+		if (version == null)
+			version = "0.0.0";
+
+		if (strategy == Strategy.EXACT) {
+			VersionRange vr = new VersionRange(version);
+			if (vr.isRange())
+				return null;
+
+			if (vr.getHigh().getMajor() == Integer.MAX_VALUE)
+				version = "latest";
+
+			File file = IO.getFile(root, bsn + "/" + bsn + "-" + version + ".jar");
+			if (file.isFile())
+				return file;
+			else {
+				file = IO.getFile(root, bsn + "/" + bsn + "-" + version + ".lib");
+				if (file.isFile())
+					return file;
+			}
+			return null;
+
+		}
+		File[] files = get(bsn, version);
+		if (files == null || files.length == 0)
+			return null;
+
+		if (files.length >= 0) {
+			switch (strategy) {
+			case LOWEST:
+				return files[0];
+			case HIGHEST:
+				return files[files.length - 1];
+			}
+		}
+		return null;
+	}
+
+	public void setRegistry(Registry registry) {
+		this.registry = registry;
+	}
+
+	public String getLocation() {
+		return root.toString();
+	}
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/filter/Filter.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/filter/Filter.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/filter/Filter.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/filter/Filter.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,343 @@
+/**
+ * Copyright (c) 2000 Gatespace AB. All Rights Reserved.
+ *
+ * Gatespace grants Open Services Gateway Initiative (OSGi) an irrevocable,
+ * perpetual, non-exclusive, worldwide, paid-up right and license to
+ * reproduce, display, perform, prepare and have prepared derivative works
+ * based upon and distribute and sublicense this material and derivative
+ * works thereof as set out in the OSGi MEMBER AGREEMENT as of January 24
+ * 2000, for use in accordance with Section 2.2 of the BY-LAWS of the
+ * OSGi MEMBER AGREEMENT.
+ */
+
+package aQute.lib.filter;
+
+import java.lang.reflect.*;
+import java.math.*;
+import java.util.*;
+
+public class Filter {
+	final char			WILDCARD	= 65535;
+
+	final static int	EQ			= 0;
+	final static int	LE			= 1;
+	final static int	GE			= 2;
+	final static int	APPROX		= 3;
+
+	private String		filter;
+
+	abstract class Query {
+		static final String	GARBAGE		= "Trailing garbage";
+		static final String	MALFORMED	= "Malformed query";
+		static final String	EMPTY		= "Empty list";
+		static final String	SUBEXPR		= "No subexpression";
+		static final String	OPERATOR	= "Undefined operator";
+		static final String	TRUNCATED	= "Truncated expression";
+		static final String	EQUALITY	= "Only equality supported";
+
+		private String		tail;
+
+		boolean match() throws IllegalArgumentException {
+			tail = filter;
+			boolean val = doQuery();
+			if (tail.length() > 0)
+				error(GARBAGE);
+			return val;
+		}
+
+		private boolean doQuery() throws IllegalArgumentException {
+			if (tail.length() < 3 || !prefix("("))
+				error(MALFORMED);
+			boolean val;
+
+			switch (tail.charAt(0)) {
+			case '&':
+				val = doAnd();
+				break;
+			case '|':
+				val = doOr();
+				break;
+			case '!':
+				val = doNot();
+				break;
+			default:
+				val = doSimple();
+				break;
+			}
+
+			if (!prefix(")"))
+				error(MALFORMED);
+			return val;
+		}
+
+		private boolean doAnd() throws IllegalArgumentException {
+			tail = tail.substring(1);
+			boolean val = true;
+			if (!tail.startsWith("("))
+				error(EMPTY);
+			do {
+				if (!doQuery())
+					val = false;
+			} while (tail.startsWith("("));
+			return val;
+		}
+
+		private boolean doOr() throws IllegalArgumentException {
+			tail = tail.substring(1);
+			boolean val = false;
+			if (!tail.startsWith("("))
+				error(EMPTY);
+			do {
+				if (doQuery())
+					val = true;
+			} while (tail.startsWith("("));
+			return val;
+		}
+
+		private boolean doNot() throws IllegalArgumentException {
+			tail = tail.substring(1);
+			if (!tail.startsWith("("))
+				error(SUBEXPR);
+			return !doQuery();
+		}
+
+		private boolean doSimple() throws IllegalArgumentException {
+			int op = 0;
+			Object attr = getAttr();
+
+			if (prefix("="))
+				op = EQ;
+			else if (prefix("<="))
+				op = LE;
+			else if (prefix(">="))
+				op = GE;
+			else if (prefix("~="))
+				op = APPROX;
+			else
+				error(OPERATOR);
+
+			return compare(attr, op, getValue());
+		}
+
+		private boolean prefix(String pre) {
+			if (!tail.startsWith(pre))
+				return false;
+			tail = tail.substring(pre.length());
+			return true;
+		}
+
+		private Object getAttr() {
+			int len = tail.length();
+			int ix = 0;
+			label: for (; ix < len; ix++) {
+				switch (tail.charAt(ix)) {
+				case '(':
+				case ')':
+				case '<':
+				case '>':
+				case '=':
+				case '~':
+				case '*':
+				case '\\':
+					break label;
+				}
+			}
+			String attr = tail.substring(0, ix).toLowerCase();
+			tail = tail.substring(ix);
+			return getProp(attr);
+		}
+
+		abstract Object getProp(String key);
+
+		private String getValue() {
+			StringBuilder sb = new StringBuilder();
+			int len = tail.length();
+			int ix = 0;
+			label: for (; ix < len; ix++) {
+				char c = tail.charAt(ix);
+				switch (c) {
+				case '(':
+				case ')':
+					break label;
+				case '*':
+					sb.append(WILDCARD);
+					break;
+				case '\\':
+					if (ix == len - 1)
+						break label;
+					sb.append(tail.charAt(++ix));
+					break;
+				default:
+					sb.append(c);
+					break;
+				}
+			}
+			tail = tail.substring(ix);
+			return sb.toString();
+		}
+
+		private void error(String m) throws IllegalArgumentException {
+			throw new IllegalArgumentException(m + " " + tail);
+		}
+
+		private boolean compare(Object obj, int op, String s) {
+			if (obj == null)
+				return false;
+			try {
+				Class<?> numClass = obj.getClass();
+				if (numClass == String.class) {
+					return compareString((String) obj, op, s);
+				} else if (numClass == Character.class) {
+					return compareString(obj.toString(), op, s);
+				} else if (numClass == Long.class) {
+					return compareSign(op, Long.valueOf(s).compareTo((Long) obj));
+				} else if (numClass == Integer.class) {
+					return compareSign(op, Integer.valueOf(s).compareTo((Integer) obj));
+				} else if (numClass == Short.class) {
+					return compareSign(op, Short.valueOf(s).compareTo((Short) obj));
+				} else if (numClass == Byte.class) {
+					return compareSign(op, Byte.valueOf(s).compareTo((Byte) obj));
+				} else if (numClass == Double.class) {
+					return compareSign(op, Double.valueOf(s).compareTo((Double) obj));
+				} else if (numClass == Float.class) {
+					return compareSign(op, Float.valueOf(s).compareTo((Float) obj));
+				} else if (numClass == Boolean.class) {
+					if (op != EQ)
+						return false;
+					int a = Boolean.valueOf(s).booleanValue() ? 1 : 0;
+					int b = ((Boolean) obj).booleanValue() ? 1 : 0;
+					return compareSign(op, a - b);
+				} else if (numClass == BigInteger.class) {
+					return compareSign(op, new BigInteger(s).compareTo((BigInteger) obj));
+				} else if (numClass == BigDecimal.class) {
+					return compareSign(op, new BigDecimal(s).compareTo((BigDecimal) obj));
+				} else if (obj instanceof Collection<?>) {
+					for (Object x : (Collection<?>) obj)
+						if (compare(x, op, s))
+							return true;
+				} else if (numClass.isArray()) {
+					int len = Array.getLength(obj);
+					for (int i = 0; i < len; i++)
+						if (compare(Array.get(obj, i), op, s))
+							return true;
+				}
+			} catch (Exception e) {
+			}
+			return false;
+		}
+	}
+
+	class DictQuery extends Query {
+		private Dictionary<?, ?>	dict;
+
+		DictQuery(Dictionary<?, ?> dict) {
+			this.dict = dict;
+		}
+
+		Object getProp(String key) {
+			return dict.get(key);
+		}
+	}
+
+	public Filter(String filter) throws IllegalArgumentException {
+		// NYI: Normalize the filter string?
+		this.filter = filter;
+		if (filter == null || filter.length() == 0)
+			throw new IllegalArgumentException("Null query");
+	}
+
+	public boolean match(Dictionary<?, ?> dict) {
+		try {
+			return new DictQuery(dict).match();
+		} catch (IllegalArgumentException e) {
+			return false;
+		}
+	}
+
+	public String verify() {
+		try {
+			new DictQuery(new Hashtable<Object, Object>()).match();
+		} catch (IllegalArgumentException e) {
+			return e.getMessage();
+		}
+		return null;
+	}
+
+	public String toString() {
+		return filter;
+	}
+
+	public boolean equals(Object obj) {
+		return obj != null && obj instanceof Filter && filter.equals(((Filter) obj).filter);
+	}
+
+	public int hashCode() {
+		return filter.hashCode();
+	}
+
+	boolean compareString(String s1, int op, String s2) {
+		switch (op) {
+		case EQ:
+			return patSubstr(s1, s2);
+		case APPROX:
+			return fixupString(s2).equals(fixupString(s1));
+		default:
+			return compareSign(op, s2.compareTo(s1));
+		}
+	}
+
+	boolean compareSign(int op, int cmp) {
+		switch (op) {
+		case LE:
+			return cmp >= 0;
+		case GE:
+			return cmp <= 0;
+		case EQ:
+			return cmp == 0;
+		default: /* APPROX */
+			return cmp == 0;
+		}
+	}
+
+	String fixupString(String s) {
+		StringBuilder sb = new StringBuilder();
+		int len = s.length();
+		boolean isStart = true;
+		boolean isWhite = false;
+		for (int i = 0; i < len; i++) {
+			char c = s.charAt(i);
+			if (Character.isWhitespace(c)) {
+				isWhite = true;
+			} else {
+				if (!isStart && isWhite)
+					sb.append(' ');
+				if (Character.isUpperCase(c))
+					c = Character.toLowerCase(c);
+				sb.append(c);
+				isStart = false;
+				isWhite = false;
+			}
+		}
+		return sb.toString();
+	}
+
+	boolean patSubstr(String s, String pat) {
+		if (s == null)
+			return false;
+		if (pat.length() == 0)
+			return s.length() == 0;
+		if (pat.charAt(0) == WILDCARD) {
+			pat = pat.substring(1);
+			for (;;) {
+				if (patSubstr(s, pat))
+					return true;
+				if (s.length() == 0)
+					return false;
+				s = s.substring(1);
+			}
+		}
+		if (s.length() == 0 || s.charAt(0) != pat.charAt(0))
+			return false;
+		return patSubstr(s.substring(1), pat.substring(1));
+	}
+}

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

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

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/Arguments.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/Arguments.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/Arguments.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/Arguments.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,8 @@
+package aQute.lib.getopt;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Arguments {
+	String[] arg();
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,472 @@
+package aQute.lib.getopt;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.regex.*;
+
+import aQute.configurable.*;
+import aQute.lib.justif.*;
+import aQute.libg.generics.*;
+import aQute.libg.reporter.*;
+
+/**
+ * Helps parsing command lines. This class takes target object, a primary
+ * command, and a list of arguments. It will then find the command in the target
+ * object. The method of this command must start with a "_" and take an
+ * parameter of Options type. Usually this is an interface that extends Options.
+ * The methods on this interface are options or flags (when they return
+ * boolean).
+ * 
+ */
+@SuppressWarnings("unchecked") public class CommandLine {
+	static int		LINELENGTH	= 60;
+	static Pattern	ASSIGNMENT	= Pattern.compile("(\\w[\\w\\d]*+)\\s*=\\s*([^\\s]+)\\s*");
+	Reporter		reporter;
+	Justif			justif = new Justif(60);
+	
+	public CommandLine(Reporter reporter) {
+		this.reporter = reporter;
+	}
+
+	/**
+	 * Execute a command in a target object with a set of options and arguments
+	 * and returns help text if something fails. Errors are reported.
+	 */
+
+	public String execute(Object target, String cmd, List<String> input) throws Exception {
+
+		if (cmd.equals("help")) {
+			StringBuilder sb = new StringBuilder();
+			Formatter f = new Formatter(sb);
+			if (input.isEmpty())
+				help(f, target);
+			else {
+				for (String s : input) {
+					help(f, target, s);
+				}
+			}
+			f.flush();
+			justif.wrap(sb);
+			return sb.toString();
+		}
+
+		//
+		// Find the appropriate method
+		//
+
+		List<String> arguments = new ArrayList<String>(input);
+		Map<String, Method> commands = getCommands(target);
+
+		Method m = commands.get(cmd);
+		if (m == null) {
+			reporter.error("No such command %s\n", cmd);
+			return help(target, null, null);
+		}
+
+		//
+		// Parse the options
+		//
+
+		Class<? extends Options> optionClass = (Class<? extends Options>) m.getParameterTypes()[0];
+		Options options = getOptions(optionClass, arguments);
+		if (options == null) {
+			// had some error, already reported
+			return help(target, cmd, null);
+		}
+
+		// Check if we have an @Arguments annotation that
+		// provides patterns for the remainder arguments
+
+		Arguments argumentsAnnotation = optionClass.getAnnotation(Arguments.class);
+		if (argumentsAnnotation != null) {
+			String[] patterns = argumentsAnnotation.arg();
+
+			// Check for commands without any arguments
+
+			if (patterns.length == 0 && arguments.size() > 0) {
+				reporter.error("This command takes no arguments but found %s\n", arguments);
+				return help(target, cmd, null);
+			}
+
+			// Match the patterns to the given command line
+
+			int i = 0;
+			for (; i < patterns.length; i++) {
+				String pattern = patterns[i];
+
+				boolean optional = pattern.matches("\\[.*\\]");
+
+				// Handle vararg
+
+				if (pattern.equals("...")) {
+					i = Integer.MAX_VALUE;
+					break;
+				}
+
+				// Check if we're running out of args
+
+				if (i > arguments.size()) {
+					if (!optional)
+						reporter.error("Missing argument %s\n", patterns[i]);
+					return help(target, cmd, optionClass);
+				}
+			}
+
+			// Check if we have unconsumed arguments left
+
+			if (i < arguments.size()) {
+				reporter.error("Too many arguments specified %s, expecting %s\n", arguments,
+						Arrays.asList(patterns));
+				return help(target, cmd, optionClass);
+			}
+		}
+		if (reporter.getErrors().size() == 0) {
+			m.setAccessible(true);
+			m.invoke(target, options);
+			return null;
+		}
+		return help(target, cmd, optionClass);
+	}
+
+	private String help(Object target, String cmd, Class<? extends Options> type) throws Exception {
+		StringBuilder sb = new StringBuilder();
+		Formatter f = new Formatter(sb);
+		if (cmd == null)
+			help(f, target);
+		else if (type == null)
+			help(f, target, cmd);
+		else
+			help(f, target, cmd, type);
+
+		f.flush();
+		justif.wrap(sb);
+		return sb.toString();
+	}
+
+	/**
+	 * Parse the options in a command line and return an interface that provides
+	 * the options from this command line. This will parse up to (and including)
+	 * -- or an argument that does not start with -
+	 * 
+	 */
+	public <T extends Options> T getOptions(Class<T> specification, List<String> arguments)
+			throws Exception {
+		Map<String, String> properties = Create.map();
+		Map<String, Object> values = new HashMap<String, Object>();
+		Map<String, Method> options = getOptions(specification);
+
+		argloop: while (arguments.size() > 0) {
+
+			String option = arguments.get(0);
+
+			if (option.startsWith("-")) {
+
+				arguments.remove(0);
+
+				if (option.startsWith("--")) {
+
+					if ("--".equals(option))
+						break argloop;
+
+					// Full named option, e.g. --output
+					String name = option.substring(2);
+					Method m = options.get(name);
+					if (m == null)
+						reporter.error("Unrecognized option %s\n", name);
+					else
+						assignOptionValue(values, m, arguments, true);
+
+				} else {
+
+					// Set of single character named options like -a
+
+					charloop: for (int j = 1; j < option.length(); j++) {
+
+						char optionChar = option.charAt(j);
+
+						for (Entry<String, Method> entry : options.entrySet()) {
+							if (entry.getKey().charAt(0) == optionChar) {
+								boolean last = (j + 1) >= option.length();
+								assignOptionValue(values, entry.getValue(),
+										arguments, last);
+								continue charloop;
+							}
+						}
+						reporter.error("No such option -%s\n", optionChar);
+					}
+				}
+			} else {
+				Matcher m = ASSIGNMENT.matcher(option);
+				if (m.matches()) {
+					properties.put(m.group(1), m.group(2));
+				}
+				break;
+			}
+		}
+
+		// check if all required elements are set
+
+		for (Entry<String, Method> entry : options.entrySet()) {
+			Method m = entry.getValue();
+			String name = entry.getKey();
+			if (!values.containsKey(name) && isMandatory(m))
+				reporter.error("Required option --%s not set", name);
+		}
+
+		values.put(".", arguments);
+		values.put(".command", this);
+		values.put(".properties", properties);
+		return Configurable.createConfigurable(specification, values);
+	}
+
+	/**
+	 * Answer a list of the options specified in an options interface
+	 */
+	private Map<String, Method> getOptions(Class<? extends Options> interf) {
+		Map<String, Method> map = new TreeMap<String, Method>();
+
+		for (Method m : interf.getMethods()) {
+			if (m.getName().startsWith("_"))
+				continue;
+
+			String name;
+
+			Config cfg = m.getAnnotation(Config.class);
+			if (cfg == null || cfg.id() == null || cfg.id().equals(Config.NULL))
+				name = m.getName();
+			else
+				name = cfg.id();
+
+			map.put(name, m);
+		}
+		return map;
+	}
+
+	/**
+	 * Assign an option, must handle flags, parameters, and parameters that can
+	 * happen multiple times.
+	 * 
+	 * @param options
+	 *            The command line map
+	 * @param args
+	 *            the args input
+	 * @param i
+	 *            where we are
+	 * @param m
+	 *            the selected method for this option
+	 * @param last
+	 *            if this is the last in a multi single character option
+	 * @return
+	 */
+	public void assignOptionValue(Map<String, Object> options, Method m, List<String> args,
+			boolean last) {
+		String name = m.getName();
+		Type type = m.getGenericReturnType();
+
+		if (isOption(m)) {
+
+			// The option is a simple flag
+
+			options.put(name, true);
+		} else {
+
+			// The option is followed by an argument
+
+			if (!last) {
+				reporter.error(
+						"Option --%s not last in a set of 1-letter options (%s) but it requires an argument of type ",
+						name, name.charAt(0), getTypeDescriptor(type));
+				return;
+			}
+
+			if (args.isEmpty()) {
+				reporter.error("Missing argument %s for option --%s, -%s ",
+						getTypeDescriptor(type), name, name.charAt(0));
+				return;
+			}
+
+			String parameter = args.remove(0);
+
+			if (Collection.class.isAssignableFrom(m.getReturnType())) {
+
+				Collection<Object> optionValues = (Collection<Object>) options.get(m.getName());
+
+				if (optionValues == null) {
+					optionValues = new ArrayList<Object>();
+					options.put(name, optionValues);
+				}
+
+				optionValues.add(parameter);
+			} else {
+
+				if (options.containsKey(name)) {
+					reporter.error("The option %s can only occur once", name);
+					return;
+				}
+
+				options.put(name, parameter);
+			}
+		}
+	}
+
+	/**
+	 * Provide a help text.
+	 */
+
+	public void help(Formatter f, Object target, String cmd, Class<? extends Options> specification) {
+		Description descr = specification.getAnnotation(Description.class);
+		Arguments patterns = specification.getAnnotation(Arguments.class);
+		Map<String, Method> options = getOptions(specification);
+
+		String description = descr == null ? "" : descr.value();
+
+		f.format("NAME\n  %s - %s\n\n", cmd, description);
+		f.format("SYNOPSIS\n   %s [options] ", cmd);
+
+		if (patterns == null)
+			f.format(" ...\n\n");
+		else {
+			String del = " ";
+			for (String pattern : patterns.arg()) {
+				if (pattern.equals("..."))
+					f.format("%s...", del);
+				else
+					f.format("%s<%s>", del, pattern);
+				del = " ";
+			}
+			f.format("\n\n");
+		}
+
+		f.format("OPTIONS\n");
+		for (Entry<String, Method> entry : options.entrySet()) {
+			String optionName = entry.getKey();
+			Method m = entry.getValue();
+
+			Config cfg = m.getAnnotation(Config.class);
+			Description d = m.getAnnotation(Description.class);
+			boolean required = isMandatory(m);
+
+			String methodDescription = cfg != null ? cfg.description() : (d == null ? "" : d
+					.value());
+
+			f.format("   %s -%s, --%s %s%s - %s\n", required ? " " : "[", //
+					optionName.charAt(0), //
+					optionName, //
+					getTypeDescriptor(m.getGenericReturnType()), //
+					required ? " " : "]",//
+					methodDescription);
+		}
+		f.format("\n");
+	}
+
+	static Pattern	LAST_PART	= Pattern.compile(".*[\\$\\.]([^\\$\\.]+)");
+
+	private static String lastPart(String name) {
+		Matcher m = LAST_PART.matcher(name);
+		if (m.matches())
+			return m.group(1);
+		return name;
+	}
+
+	/**
+	 * Show all commands in a target
+	 */
+	public void help(Formatter f, Object target) throws Exception {
+		// TODO get help from the class
+		Description descr = target.getClass().getAnnotation(Description.class);
+		if (descr != null) {
+			f.format("%s\n\n", descr.value());
+		}
+		f.format("Available commands: ");
+
+		String del = "";
+		for (String name : getCommands(target).keySet()) {
+			f.format("%s%s", del, name);
+			del = ", ";
+		}
+		f.format("\n");
+
+	}
+
+	/**
+	 * Show the full help for a given command
+	 */
+	public void help(Formatter f, Object target, String cmd) {
+
+		Method m = getCommands(target).get(cmd);
+		if (m == null)
+			f.format("No such command: %s\n", cmd);
+		else {
+			Class<? extends Options> options = (Class<? extends Options>) m.getParameterTypes()[0];
+			help(f, target, cmd, options);
+		}
+	}
+
+	/**
+	 * Parse a class and return a list of command names
+	 * 
+	 * @param target
+	 * @return
+	 */
+	public Map<String, Method> getCommands(Object target) {
+		Map<String, Method> map = new TreeMap<String, Method>();
+
+		for (Method m : target.getClass().getMethods()) {
+
+			if (m.getParameterTypes().length == 1 && m.getName().startsWith("_")) {
+				Class<?> clazz = m.getParameterTypes()[0];
+				if (Options.class.isAssignableFrom(clazz)) {
+					String name = m.getName().substring(1);
+					map.put(name, m);
+				}
+			}
+		}
+		return map;
+	}
+
+	/**
+	 * Answer if the method is marked mandatory
+	 */
+	private boolean isMandatory(Method m) {
+		Config cfg = m.getAnnotation(Config.class);
+		if (cfg == null)
+			return false;
+
+		return cfg.required();
+	}
+
+	/**
+	 * @param m
+	 * @return
+	 */
+	private boolean isOption(Method m) {
+		return m.getReturnType() == boolean.class || m.getReturnType() == Boolean.class;
+	}
+
+	/**
+	 * Show a type in a nice way
+	 */
+
+	private String getTypeDescriptor(Type type) {
+		if (type instanceof ParameterizedType) {
+			ParameterizedType pt = (ParameterizedType) type;
+			Type c = pt.getRawType();
+			if (c instanceof Class) {
+				if (Collection.class.isAssignableFrom((Class<?>) c)) {
+					return getTypeDescriptor(pt.getActualTypeArguments()[0]) + "*";
+				}
+			}
+		}
+		if (!(type instanceof Class))
+			return "<>";
+
+		Class<?> clazz = (Class<?>) type;
+
+		if (clazz == Boolean.class || clazz == boolean.class)
+			return ""; // Is a flag
+
+		return "<" + lastPart(clazz.getName().toLowerCase()) + ">";
+	}
+
+}

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

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

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

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

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

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

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/Options.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/Options.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/Options.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/getopt/Options.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,11 @@
+package aQute.lib.getopt;
+
+import java.util.*;
+
+public interface Options {
+	List<String> _();
+	CommandLine _command();
+	Map<String,String> _properties();
+	boolean _ok();
+	boolean _help();
+}

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

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/hex/Hex.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/hex/Hex.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/hex/Hex.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/hex/Hex.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,60 @@
+package aQute.lib.hex;
+
+import java.io.*;
+
+
+/*
+ * Hex converter.
+ * 
+ * TODO Implement string to byte[]
+ */
+public class Hex {
+	final static char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+	public final static byte[] toByteArray(String string) {
+		string = string.trim();
+		if ( (string.length() & 1) != 0)
+			throw new IllegalArgumentException("a hex string must have an even length");
+
+		byte[]out = new byte[ string.length()/2];
+		for ( int i=0; i < out.length; i++) {
+			int high = nibble(string.charAt(i*2))<<4;
+			int low = nibble(string.charAt(i*2+1));
+			out[i] = (byte) (high + low);
+		}
+		return out;
+	}
+
+	
+	public final static int nibble( char c) {
+		if (c >= '0' && c <= '9')
+			return c - '0';
+		
+		if ( c>='A' && c<='F')
+			return c - 'A' + 10;
+		if ( c>='a' && c<='f')
+			return c - 'a' + 10;
+		
+		throw new IllegalArgumentException("Not a hex digit: " + c);
+	}
+	
+	public final static String toHexString(byte data[]) {
+		StringBuilder sb = new StringBuilder();
+		try {
+			append(sb,data);
+		} catch (IOException e) {
+			// cannot happen with sb
+		}
+		return sb.toString();
+	}
+	
+	public final static void append( Appendable sb, byte [] data ) throws IOException {
+		for ( int i =0; i<data.length; i++) {
+			sb.append( nibble( data[i] >> 4));
+			sb.append( nibble( data[i]));
+		}
+	}
+
+	private final static char nibble(int i) {	
+		return HEX[i & 0xF];
+	}
+}

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

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

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/index/Index.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/index/Index.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/index/Index.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/index/Index.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,349 @@
+package aQute.lib.index;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.channels.FileChannel.MapMode;
+import java.util.*;
+
+/**
+ * <pre>
+ *   0   ->   0, 122   -> 1
+ *   123 -> 123, 244   -> 2
+ *   245 -> 245, ...
+ * </pre>
+ * 
+ * 
+ */
+public class Index implements Iterable<byte[]> {
+	final static int					LEAF		= 0;
+	final static int					INDEX		= 1;
+
+	final static int					SIGNATURE	= 0;
+	final static int					MAGIC		= 0x494C4458;
+	final static int					KEYSIZE		= 4;
+
+	private FileChannel					file;
+	final int							pageSize	= 4096;
+	final int							keySize;
+	final int							valueSize	= 8;
+	final int							capacity;
+	public Page							root;
+	final LinkedHashMap<Integer, Page>	cache		= new LinkedHashMap<Integer, Index.Page>();
+	final MappedByteBuffer				settings;
+
+	private int							nextPage;
+
+	class Page {
+		final static int		TYPE_OFFSET		= 0;
+		final static int		COUNT_OFFSET	= 2;
+		final static int		START_OFFSET	= 4;
+		final int				number;
+		boolean					leaf;
+		final MappedByteBuffer	buffer;
+		int						n				= 0;
+		boolean					dirty;
+
+		Page(int number) throws IOException {
+			this.number = number;
+			buffer = file.map(MapMode.READ_WRITE, ((long) number) * pageSize, pageSize);
+			n = buffer.getShort(COUNT_OFFSET);
+			int type = buffer.getShort(TYPE_OFFSET);
+			leaf = type != 0;
+		}
+
+		Page(int number, boolean leaf) throws IOException {
+			this.number = number;
+			this.leaf = leaf;
+			this.n = 0;
+			buffer = file.map(MapMode.READ_WRITE, ((long) number) * pageSize, pageSize);
+		}
+
+		Iterator<byte[]> iterator() {
+			return new Iterator<byte[]>() {
+				Iterator<byte[]>	i;
+				int					rover	= 0;
+
+				public byte[] next() {
+					if (leaf) {
+						return k(rover++);
+					}
+
+					return i.next();
+				}
+
+				public boolean hasNext() {
+					try {
+						if (leaf)
+							return rover < n;
+						while (i == null || i.hasNext() == false) {
+							int c = (int) c(rover++);
+							i = getPage(c).iterator();
+						}
+						return i.hasNext();
+					} catch (IOException e) {
+						throw new RuntimeException(e);
+					}
+
+				}
+
+				public void remove() {
+					throw new UnsupportedOperationException();
+				}
+
+			};
+		}
+
+		void write() throws IOException {
+			buffer.putShort(COUNT_OFFSET, (short) n);
+			buffer.put(TYPE_OFFSET, (byte) (leaf ? 1 : 0));
+			buffer.force();
+		}
+
+		int compare(byte[] key, int i) {
+			int index = pos(i);
+			for (int j = 0; j < keySize; j++, index++) {
+				int a = 0;
+				if (j < key.length)
+					a = key[j] & 0xFF;
+
+				int b = buffer.get(index) & 0xFF;
+				if (a == b)
+					continue;
+
+				return a > b ? 1 : -1;
+			}
+			return 0;
+		}
+
+		int pos(int i) {
+			return START_OFFSET + size(i);
+		}
+
+		int size(int n) {
+			return n * (keySize + valueSize);
+		}
+
+		void copyFrom(Page page, int start, int length) {
+			copy(page.buffer, pos(start), buffer, pos(0), size(length));
+		}
+
+		void copy(ByteBuffer src, int srcPos, ByteBuffer dst, int dstPos, int length) {
+			if (srcPos < dstPos) {
+				while (length-- > 0)
+					dst.put(dstPos + length, src.get(srcPos + length));
+
+			} else {
+				while (length-- > 0)
+					dst.put(dstPos++, src.get(srcPos++));
+			}
+		}
+
+		long search(byte[] k) throws Exception {
+			int cmp = 0;
+			int i = n - 1;
+			while (i >= 0 && (cmp = compare(k, i)) < 0)
+				i--;
+
+			if (leaf) {
+				if (cmp != 0)
+					return -1;
+				return c(i);
+			}
+			long value = c(i);
+			Page child = getPage((int) value);
+			return child.search(k);
+		}
+
+		void insert(byte[] k, long v) throws IOException {
+			if (n == capacity) {
+				int t = capacity / 2;
+				Page left = allocate(leaf);
+				Page right = allocate(leaf);
+				left.copyFrom(this, 0, t);
+				left.n = t;
+				right.copyFrom(this, t, capacity - t);
+				right.n = capacity - t;
+				leaf = false;
+				set(0, left.k(0), left.number);
+				set(1, right.k(0), right.number);
+				n = 2;
+				left.write();
+				right.write();
+			}
+			insertNonFull(k, v);
+		}
+
+		byte[] k(int i) {
+			buffer.position(pos(i));
+			byte[] key = new byte[keySize];
+			buffer.get(key);
+			return key;
+		}
+
+		long c(int i) {
+			if (i < 0) {
+				System.err.println("Arghhh");
+			}
+			int index = pos(i) + keySize;
+			return buffer.getLong(index);
+		}
+
+		void set(int i, byte[] k, long v) {
+			int index = pos(i);
+			for (int j = 0; j < keySize; j++) {
+				byte a = 0;
+				if (j < k.length)
+					a = k[j];
+				buffer.put(index + j, a);
+			}
+			buffer.putLong(index + keySize, v);
+		}
+
+		void insertNonFull(byte[] k, long v) throws IOException {
+			int cmp = 0;
+			int i = n - 1;
+			while (i >= 0 && (cmp = compare(k, i)) < 0)
+				i--;
+
+			if (leaf) {
+				if (cmp != 0) {
+					i++;
+					if (i != n)
+						copy(buffer, pos(i), buffer, pos(i + 1), size(n - i));
+				}
+				set(i, k, v);
+				n++;
+				dirty = true;
+			} else {
+				long value = c(i);
+				Page child = getPage((int) value);
+
+				if (child.n == capacity) {
+					Page left = child;
+					int t = capacity / 2;
+					Page right = allocate(child.leaf);
+					right.copyFrom(left, t, capacity - t);
+					right.n = capacity - t;
+					left.n = t;
+					i++; // place to insert
+					if (i < n) // ok if at end
+						copy(buffer, pos(i), buffer, pos(i + 1), size(n - i));
+					set(i, right.k(0), right.number);
+					n++;
+					assert i < n;
+					child = right.compare(k, 0) >= 0 ? right : left;
+					left.dirty = true;
+					right.dirty = true;
+					this.dirty = true;
+				}
+				child.insertNonFull(k, v);
+			}
+			write();
+		}
+
+		public String toString() {
+			StringBuilder sb = new StringBuilder();
+			try {
+				toString(sb, "");
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+			return sb.toString();
+		}
+
+		public void toString(StringBuilder sb, String indent) throws IOException {
+			for (int i = 0; i < n; i++) {
+				sb.append(String.format("%s %02d:%02d %20s %s %d\n", indent, number, i,
+						hex(k(i), 0, 4), leaf ? "==" : "->", c(i)));
+				if (!leaf) {
+					long c = c(i);
+					Page sub = getPage((int) c);
+					sub.toString(sb, indent + " ");
+				}
+			}
+		}
+
+		private String hex(byte[] k, int i, int j) {
+			StringBuilder sb = new StringBuilder();
+
+			while (i < j) {
+				int b = 0xFF & k[i];
+				sb.append(nibble(b >> 4));
+				sb.append(nibble(b));
+				i++;
+			}
+			return sb.toString();
+		}
+
+		private char nibble(int i) {
+			i = i & 0xF;
+			return (char) (i >= 10 ? i + 'A' - 10 : i + '0');
+		}
+
+	}
+
+	public Index(File file, int keySize) throws IOException {
+		capacity = (pageSize - Page.START_OFFSET) / (keySize + valueSize);
+		RandomAccessFile raf = new RandomAccessFile(file, "rw");
+		this.file = raf.getChannel();
+		settings = this.file.map(MapMode.READ_WRITE, 0, pageSize);
+		if (this.file.size() == pageSize) {
+			this.keySize = keySize;
+			settings.putInt(SIGNATURE, MAGIC);
+			settings.putInt(KEYSIZE, keySize);
+			nextPage = 1;
+			root = allocate(true);
+			root.n = 1;
+			root.set(0, new byte[KEYSIZE], 0);
+			root.write();
+		} else {
+			if (settings.getInt(SIGNATURE) != MAGIC)
+				throw new IllegalStateException("No Index file, magic is not " + MAGIC);
+
+			this.keySize = settings.getInt(KEYSIZE);
+			if (keySize != 0 && this.keySize != keySize)
+				throw new IllegalStateException("Invalid key size for Index file. The file is "
+						+ this.keySize + " and was expected to be " + this.keySize);
+
+			root = getPage(1);
+			nextPage = (int) (this.file.size() / pageSize);
+		}
+	}
+
+	public void insert(byte[] k, long v) throws Exception {
+		root.insert(k, v);
+	}
+
+	public long search(byte[] k) throws Exception {
+		return root.search(k);
+	}
+
+	Page allocate(boolean leaf) throws IOException {
+		Page page = new Page(nextPage++, leaf);
+		cache.put(page.number, page);
+		return page;
+	}
+
+	Page getPage(int number) throws IOException {
+		Page page = cache.get(number);
+		if (page == null) {
+			page = new Page(number);
+			cache.put(number, page);
+		}
+		return page;
+	}
+
+	public String toString() {
+		return root.toString();
+	}
+
+	public void close() throws IOException {
+		file.close();
+		cache.clear();
+	}
+
+	public Iterator<byte[]> iterator() {
+		return root.iterator();
+	}
+}

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

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/io/IO.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/io/IO.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/io/IO.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/io/IO.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,388 @@
+package aQute.lib.io;
+
+import java.io.*;
+import java.net.*;
+import java.nio.*;
+import java.security.*;
+import java.util.*;
+
+public class IO {
+
+	public static void copy(Reader r, Writer w) throws IOException {
+		try {
+			char buffer[] = new char[8000];
+			int size = r.read(buffer);
+			while (size > 0) {
+				w.write(buffer, 0, size);
+				size = r.read(buffer);
+			}
+		} finally {
+			r.close();
+			w.flush();
+		}
+	}
+
+	public static void copy(InputStream r, Writer w) throws IOException {
+		copy(r, w, "UTF-8");
+	}
+
+	public static void copy(byte []r, Writer w) throws IOException {
+		copy( new ByteArrayInputStream(r), w, "UTF-8");
+	}
+
+	public static void copy(byte []r, OutputStream w) throws IOException {
+		copy( new ByteArrayInputStream(r), w);
+	}
+
+	public static void copy(InputStream r, Writer w, String charset) throws IOException {
+		try {
+			InputStreamReader isr = new InputStreamReader(r, charset);
+			copy(isr, w);
+		} finally {
+			r.close();
+		}
+	}
+
+	public static void copy(Reader r, OutputStream o) throws IOException {
+		copy(r, o, "UTF-8");
+	}
+
+	public static void copy(Reader r, OutputStream o, String charset) throws IOException {
+		try {
+			OutputStreamWriter osw = new OutputStreamWriter(o, charset);
+			copy(r, osw);
+		} finally {
+			r.close();
+		}
+	}
+
+	public static void copy(InputStream in, OutputStream out) throws IOException {
+		DataOutputStream dos = new DataOutputStream(out);
+		copy(in, (DataOutput) dos);
+		out.flush();
+	}
+
+	public static void copy(InputStream in, DataOutput out) throws IOException {
+		byte[] buffer = new byte[10000];
+		try {
+			int size = in.read(buffer);
+			while (size > 0) {
+				out.write(buffer, 0, size);
+				size = in.read(buffer);
+			}
+		} finally {
+			in.close();
+		}
+	}
+
+	public static void copy(InputStream in, ByteBuffer bb) throws IOException {
+		byte[] buffer = new byte[10000];
+		try {
+			int size = in.read(buffer);
+			while (size > 0) {
+				bb.put(buffer, 0, size);
+				size = in.read(buffer);
+			}
+		} finally {
+			in.close();
+		}
+	}
+
+	public static void copy(URL in, MessageDigest md) throws IOException {
+		copy(in.openStream(), md);
+	}
+
+	public static void copy(File in, MessageDigest md) throws IOException {
+		copy(new FileInputStream(in), md);
+	}
+
+	public static void copy(URLConnection in, MessageDigest md) throws IOException {
+		copy(in.getInputStream(), md);
+	}
+
+
+	public static void copy(InputStream in, MessageDigest md) throws IOException {
+		byte[] buffer = new byte[10000];
+		try {
+			int size = in.read(buffer);
+			while (size > 0) {
+				md.update(buffer, 0, size);
+				size = in.read(buffer);
+			}
+		} finally {
+			in.close();
+		}
+	}
+
+	public static void copy(URL url, File file) throws IOException {
+		URLConnection c = url.openConnection();
+		copy(c, file);
+	}
+
+	public static void copy(URLConnection c, File file) throws IOException {
+		copy(c.getInputStream(), file);
+	}
+
+	public static void copy(InputStream in, URL out) throws IOException {
+		copy(in, out, null);
+	}
+
+	public static void copy(InputStream in, URL out, String method) throws IOException {
+		URLConnection c = out.openConnection();
+		if (c instanceof HttpURLConnection && method != null) {
+			HttpURLConnection http = (HttpURLConnection) c;
+			http.setRequestMethod(method);
+		}
+		c.setDoOutput(true);
+		copy(in, c.getOutputStream());
+	}
+
+	public static void copy(File a, File b) throws IOException {
+		if (a.isFile()) {
+			FileOutputStream out = new FileOutputStream(b);
+			try {
+				copy(new FileInputStream(a), out);
+			} finally {
+				out.close();
+			}
+		} else if (a.isDirectory()) {
+			b.mkdirs();
+			if (!b.isDirectory())
+				throw new IllegalArgumentException(
+						"target directory for a directory must be a directory: " + b);
+			File subs[] = a.listFiles();
+			for (File sub : subs) {
+				copy(sub, new File(b, sub.getName()));
+			}
+		} else
+			throw new FileNotFoundException("During copy: " + a.toString());
+	}
+
+	public static void copy(InputStream a, File b) throws IOException {
+		FileOutputStream out = new FileOutputStream(b);
+		try {
+			copy(a, out);
+		} finally {
+			out.close();
+		}
+	}
+
+	public static void copy(File a, OutputStream b) throws IOException {
+		copy(new FileInputStream(a), b);
+	}
+
+	public static String collect(File a, String encoding) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		copy(a, out);
+		return new String(out.toByteArray(), encoding);
+	}
+
+	public static String collect(URL a, String encoding) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		copy(a.openStream(), out);
+		return new String(out.toByteArray(), encoding);
+	}
+
+	public static String collect(URL a) throws IOException {
+		return collect(a, "UTF-8");
+	}
+
+	public static String collect(File a) throws IOException {
+		return collect(a, "UTF-8");
+	}
+
+	public static String collect(String a) throws IOException {
+		return collect(new File(a), "UTF-8");
+	}
+
+	public static String collect(InputStream a, String encoding) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		copy(a, out);
+		return new String(out.toByteArray(), encoding);
+	}
+
+	public static String collect(InputStream a) throws IOException {
+		return collect(a, "UTF-8");
+	}
+
+	public static String collect(Reader a) throws IOException {
+		StringWriter sw = new StringWriter();
+		char[] buffer = new char[10000];
+		int size = a.read(buffer);
+		while (size > 0) {
+			sw.write(buffer, 0, size);
+			size = a.read(buffer);
+		}
+		return sw.toString();
+	}
+
+	public static File getFile(File base, String file) {
+		File f = new File(file);
+		if (f.isAbsolute())
+			return f;
+		int n;
+
+		f = base.getAbsoluteFile();
+		while ((n = file.indexOf('/')) > 0) {
+			String first = file.substring(0, n);
+			file = file.substring(n + 1);
+			if (first.equals(".."))
+				f = f.getParentFile();
+			else
+				f = new File(f, first);
+		}
+		if (file.equals(".."))
+			return f.getParentFile();
+		return new File(f, file).getAbsoluteFile();
+	}
+
+	public static void delete(File f) {
+		f = f.getAbsoluteFile();
+		if (f.getParentFile() == null)
+			throw new IllegalArgumentException("Cannot recursively delete root for safety reasons");
+
+		if (f.isDirectory()) {
+			File[] subs = f.listFiles();
+			for (File sub : subs)
+				delete(sub);
+		}
+
+		f.delete();
+	}
+
+	public static long drain(InputStream in) throws IOException {
+		long result = 0;
+		byte[] buffer = new byte[10000];
+		try {
+			int size = in.read(buffer);
+			while (size >= 0) {
+				result += size;
+				size = in.read(buffer);
+			}
+		} finally {
+			in.close();
+		}
+		return result;
+	}
+
+	public void copy(Collection<?> c, OutputStream out) throws IOException {
+		Writer w = new OutputStreamWriter(out,"UTF-8");
+		PrintWriter ps = new PrintWriter(w);
+		for (Object o : c) {
+			ps.println(o);
+		}
+		ps.flush();
+		w.flush();
+	}
+
+	public static Throwable close(Closeable in) {
+		if ( in == null)
+			return null;
+		
+		try {
+			in.close();
+			return null;
+		} catch (Throwable e) {
+			return e;
+		}
+	}
+
+	public static URL toURL(String s, File base) throws MalformedURLException {
+		int n = s.indexOf(':');
+		if (n > 0 && n < 10) {
+			// is url
+			return new URL(s);
+		}
+		return getFile(base, s).toURI().toURL();
+	}
+
+	public static void store(Object o, File out) throws IOException {
+		store(o, out, "UTF-8");
+	}
+
+	public static void store(Object o, File out, String encoding) throws IOException {
+		FileOutputStream fout = new FileOutputStream(out);
+		try {
+			store(o, fout, encoding);
+		} finally {
+			fout.close();
+		}
+	}
+
+	public static void store(Object o, OutputStream fout) throws UnsupportedEncodingException,
+			IOException {
+		store(o, fout, "UTF-8");
+	}
+
+	public static void store(Object o, OutputStream fout, String encoding)
+			throws UnsupportedEncodingException, IOException {
+		String s;
+
+		if (o == null)
+			s = "";
+		else
+			s = o.toString();
+
+		try {
+			fout.write(s.getBytes(encoding));
+		} finally {
+			fout.close();
+		}
+	}
+
+	public static InputStream stream(String s) {
+		try {
+			return new ByteArrayInputStream(s.getBytes("UTF-8"));
+		} catch (Exception e) {
+			// Ignore
+			return null;
+		}
+	}
+
+	public static InputStream stream(String s, String encoding) throws UnsupportedEncodingException {
+		return new ByteArrayInputStream(s.getBytes(encoding));
+	}
+
+	public static InputStream stream(File s) throws FileNotFoundException {
+		return new FileInputStream(s);
+	}
+
+	public static InputStream stream(URL s) throws IOException {
+		return s.openStream();
+	}
+
+	public static Reader reader(String s) {
+		return new StringReader(s);
+	}
+
+
+	public static BufferedReader reader(File f, String encoding) throws IOException {
+		return reader( new FileInputStream(f), encoding);
+	}
+	
+	public static BufferedReader reader(File f) throws IOException {
+		return reader(f,"UTF-8");
+	}
+	
+	public static PrintWriter writer(File f, String encoding) throws IOException {
+		return writer( new FileOutputStream(f),encoding);
+	}
+	
+	public static PrintWriter writer(File f) throws IOException {
+		return writer(f, "UTF-8");
+	}
+	
+	public static PrintWriter writer(OutputStream out, String encoding) throws IOException {
+		return new PrintWriter( new OutputStreamWriter( out,encoding));
+	}
+	public static BufferedReader reader(InputStream in, String encoding) throws IOException {
+		return new BufferedReader( new InputStreamReader(in, encoding));
+	}
+	
+	public static BufferedReader reader(InputStream in) throws IOException {
+		return reader(in, "UTF-8");
+	}
+	public static PrintWriter writer(OutputStream out) throws IOException {
+		return writer(out, "UTF-8");
+	}
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/io/LimitedInputStream.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/io/LimitedInputStream.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/io/LimitedInputStream.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/io/LimitedInputStream.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,78 @@
+package aQute.lib.io;
+
+import java.io.*;
+
+public class LimitedInputStream extends InputStream {
+
+	final InputStream	in;
+	final int			size;
+	int					left;
+
+	public LimitedInputStream(InputStream in, int size) {
+		this.in = in;
+		this.left = size;
+		this.size = size;
+	}
+
+	@Override public int read() throws IOException {
+		if (left <= 0) {
+			eof();
+			return -1;
+		}
+
+		left--;
+		return in.read();
+	}
+
+	@Override public int available() throws IOException {		
+		return Math.min(left, in.available());
+	}
+
+	@Override public void close() throws IOException {
+		eof();
+		in.close();
+	}
+
+	protected void eof() {
+	}
+
+	@Override public synchronized void mark(int readlimit) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override public boolean markSupported() {
+		return false;
+	}
+
+	@Override public int read(byte[] b, int off, int len) throws IOException {
+		int min = Math.min(len, left);
+		if (min == 0)
+			return 0;
+
+		int read = in.read(b, off, min);
+		if (read > 0)
+			left -= read;
+		return read;
+	}
+
+	@Override public int read(byte[] b) throws IOException {
+		return read(b,0,b.length);
+	}
+
+	@Override public synchronized void reset() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override public long skip(long n) throws IOException {
+		long count = 0;
+		byte buffer[] = new byte[1024];
+		while ( n > 0 && read() >= 0) {
+			int size = read(buffer);
+			if ( size <= 0)
+				return count;
+			count+=size;
+			n-=size;
+		}
+		return count;
+	}
+}

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

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ArrayHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ArrayHandler.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ArrayHandler.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ArrayHandler.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,40 @@
+package aQute.lib.json;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+public class ArrayHandler extends Handler {
+	Type	componentType;
+
+	ArrayHandler(Class< ? > rawClass, Type componentType) {
+		this.componentType = componentType;
+	}
+
+	@Override
+	void encode(Encoder app, Object object, Map<Object, Type> visited)
+			throws IOException, Exception {
+		app.append("[");
+		String del = "";
+		int l = Array.getLength(object);
+		for (int i = 0; i < l; i++) {
+			app.append(del);
+			app.encode(Array.get(object, i), componentType, visited);
+			del = ",";
+		}
+		app.append("]");
+	}
+
+	@Override
+	Object decodeArray(Decoder r) throws Exception {
+		ArrayList<Object> list = new ArrayList<Object>();
+		r.codec.parseArray(list, componentType, r);
+		Object array = Array.newInstance(r.codec.getRawClass(componentType),
+				list.size());
+		int n = 0;
+		for (Object o : list)
+			Array.set(array, n++, o);
+
+		return array;
+	}
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/BooleanHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/BooleanHandler.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/BooleanHandler.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/BooleanHandler.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,30 @@
+package aQute.lib.json;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+public class BooleanHandler extends Handler {
+
+	@Override void encode(Encoder app, Object object, Map<Object, Type> visited)
+			throws IOException, Exception {
+		app.append( object.toString());
+	}
+	
+	@Override Object decode(boolean s) {
+		return s;
+	}
+
+	@Override Object decode(String s) {
+		return Boolean.parseBoolean(s);
+	}
+
+	@Override Object decode(Number s) {
+		return s.intValue() != 0;
+	}
+
+	@Override Object decode() {
+		return false;
+	}
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,30 @@
+package aQute.lib.json;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import aQute.lib.base64.*;
+
+public class ByteArrayHandler extends Handler {
+
+	@Override void encode(Encoder app, Object object, Map<Object, Type> visited)
+			throws IOException, Exception {
+		StringHandler.string(app, Base64.encodeBase64((byte[]) object));
+	}
+
+	@Override Object decodeArray(Decoder r) throws Exception {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+		ArrayList<Object> list = new ArrayList<Object>();
+		r.codec.parseArray(list, Byte.class, r);
+		for (Object b : list) {
+			out.write(((Byte) b).byteValue());
+		}
+		return out.toByteArray();
+	}
+
+	@Override Object decode(String s) throws Exception {
+		return Base64.decodeBase64(s);
+	}
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/CharacterHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/CharacterHandler.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/CharacterHandler.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/CharacterHandler.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,31 @@
+package aQute.lib.json;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+public class CharacterHandler extends Handler {
+
+	@Override void encode(Encoder app, Object object, Map<Object, Type> visited)
+			throws Exception {	
+		Character c  = (Character) object;
+		int v = (int) c.charValue();
+		app.append( v+"" );
+	}
+	
+	@Override Object decode(boolean s) {
+		return s ? 't' : 'f';
+	}
+
+	@Override Object decode(String s) {
+		return (char) Integer.parseInt(s);
+	}
+
+	@Override Object decode(Number s) {
+		return (char) s.shortValue();
+	}
+
+	@Override Object decode() {
+		return 0;
+	}
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/CollectionHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/CollectionHandler.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/CollectionHandler.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/CollectionHandler.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,57 @@
+package aQute.lib.json;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+public class CollectionHandler extends Handler {
+	Class<?>	rawClass;
+	Type		componentType;
+	
+	CollectionHandler(Class<?> rawClass, Type componentType) {
+		this.componentType = componentType;
+		if (rawClass.isInterface()) {
+			if (rawClass.isAssignableFrom(ArrayList.class))
+				rawClass = ArrayList.class;
+			else if (rawClass.isAssignableFrom(LinkedList.class))
+				rawClass = LinkedList.class;
+			else if (rawClass.isAssignableFrom(HashSet.class))
+				rawClass = HashSet.class;
+			else if (rawClass.isAssignableFrom(TreeSet.class))
+				rawClass = TreeSet.class;
+			else if (rawClass.isAssignableFrom(Vector.class))
+				rawClass = Vector.class;
+			else if (rawClass.isAssignableFrom(ConcurrentLinkedQueue.class))
+				rawClass = ConcurrentLinkedQueue.class;
+			else if (rawClass.isAssignableFrom(CopyOnWriteArrayList.class))
+				rawClass = CopyOnWriteArrayList.class;
+			else if (rawClass.isAssignableFrom(CopyOnWriteArraySet.class))
+				rawClass = CopyOnWriteArraySet.class;
+			else
+				throw new IllegalArgumentException("Unknown interface type for collection: "
+						+ rawClass);
+		}
+		this.rawClass = rawClass;
+	}
+
+	@Override void encode(Encoder app, Object object, Map<Object, Type> visited)
+			throws IOException, Exception {
+		Collection<?> collection = (Collection<?>) object;
+
+		app.append("[");
+		String del = "";
+		for (Object o : collection) {
+			app.append(del);
+			app.encode(o, componentType, visited);
+			del = ",";
+		}
+		app.append("]");
+	}
+
+	@SuppressWarnings("unchecked") @Override Object decodeArray(Decoder r) throws Exception {
+		Collection<Object> c = (Collection<Object>) rawClass.newInstance();
+		r.codec.parseArray(c, componentType, r);
+		return c;
+	}
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/DateHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/DateHandler.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/DateHandler.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/DateHandler.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,30 @@
+package aQute.lib.json;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.text.*;
+import java.util.*;
+
+public class DateHandler extends Handler {
+	final static SimpleDateFormat	sdf	= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+
+	@Override void encode(Encoder app, Object object, Map<Object, Type> visited)
+			throws IOException, Exception {
+		String s;
+		synchronized (sdf) {
+			s = sdf.format((Date) object);
+		}
+		StringHandler.string(app, s);
+	}
+
+	@Override Object decode(String s) throws Exception {
+		synchronized (sdf) {
+			return sdf.parse(s);
+		}
+	}
+
+	@Override Object decode(Number s) throws Exception {
+		return new Date(s.longValue());
+	}
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Decoder.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Decoder.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Decoder.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Decoder.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,137 @@
+package aQute.lib.json;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.security.*;
+import java.util.*;
+
+public class Decoder implements Closeable {
+	final JSONCodec		codec;
+	Reader				reader;
+	int					current;
+	MessageDigest		digest;
+	Map<String, Object>	extra;
+	String encoding = "UTF-8";
+	
+	boolean strict;
+
+	Decoder(JSONCodec codec) {
+		this.codec = codec;
+	}
+
+	public Decoder from(File file) throws Exception {
+		return from(new FileInputStream(file));
+	}
+
+	public Decoder from(InputStream in) throws Exception {
+		return from(new InputStreamReader(in, encoding));
+	}
+	
+	public Decoder charset(String encoding) {
+		this.encoding = encoding;
+		return this;
+	}
+	
+	public Decoder strict() {
+		this.strict = true;
+		return this;
+	}
+
+	public Decoder from(Reader in) throws Exception {
+		reader = in;
+		read();
+		return this;
+	}
+
+	public Decoder faq(String in) throws Exception {
+		return from(in.replace('\'', '"'));
+	}
+
+	public Decoder from(String in) throws Exception {
+		return from(new StringReader(in));
+	}
+
+	public Decoder mark() throws NoSuchAlgorithmException {
+		if (digest == null)
+			digest = MessageDigest.getInstance("SHA1");
+		digest.reset();
+		return this;
+	}
+
+	public byte[] digest() {
+		if (digest == null)
+			return null;
+
+		return digest.digest();
+	}
+
+	@SuppressWarnings("unchecked") public <T> T get(Class<T> clazz) throws Exception {
+		return (T) codec.decode(clazz, this);
+	}
+
+	public Object get(Type type) throws Exception {
+		return codec.decode(type, this);
+	}
+
+	public Object get() throws Exception {
+		return codec.decode(null, this);
+	}
+
+	int read() throws Exception {
+		current = reader.read();
+		if (digest != null) {
+			digest.update((byte) (current / 256));
+			digest.update((byte) (current % 256));
+		}
+		return current;
+	}
+
+	int current() {
+		return current;
+	}
+
+	/**
+	 * Skip any whitespace.
+	 * 
+	 * @return
+	 * @throws Exception
+	 */
+	int skipWs() throws Exception {
+		while (Character.isWhitespace(current()))
+			read();
+		return current();
+	}
+
+	/**
+	 * Skip any whitespace.
+	 * 
+	 * @return
+	 * @throws Exception
+	 */
+	int next() throws Exception {
+		read();
+		return skipWs();
+	}
+
+	void expect(String s) throws Exception {
+		for (int i = 0; i < s.length(); i++)
+			if (!(s.charAt(i) == read()))
+				throw new IllegalArgumentException("Expected " + s + " but got something different");
+		read();
+	}
+
+	public boolean isEof() throws Exception {
+		int c = skipWs();
+		return c < 0;
+	}
+
+	public void close() throws IOException {
+		reader.close();
+	}
+
+	public Map<String, Object> getExtra() {
+		if (extra == null)
+			extra = new HashMap<String, Object>();
+		return extra;
+	}
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Encoder.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Encoder.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Encoder.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Encoder.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,112 @@
+package aQute.lib.json;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.security.*;
+import java.util.*;
+
+public class Encoder implements Appendable, Closeable, Flushable {
+	final JSONCodec	codec;
+	Appendable		app;
+	MessageDigest	digest;
+	boolean			writeDefaults;
+	String			encoding	= "UTF-8";
+
+	Encoder(JSONCodec codec) {
+		this.codec = codec;
+	}
+
+	public Encoder put(Object object) throws Exception {
+		if (app == null)
+			to();
+
+		codec.encode(this, object, null, new IdentityHashMap<Object, Type>());
+		return this;
+	}
+
+	public Encoder mark() throws NoSuchAlgorithmException {
+		if (digest == null)
+			digest = MessageDigest.getInstance("SHA1");
+		digest.reset();
+		return this;
+	}
+
+	public byte[] digest() throws NoSuchAlgorithmException, IOException {
+		if (digest == null)
+			return null;
+		append('\n');
+		return digest.digest();
+	}
+
+	public Encoder to() throws IOException {
+		to(new StringWriter());
+		return this;
+	}
+
+	public Encoder to(File file) throws IOException {
+		return to(new FileOutputStream(file));
+	}
+
+	public Encoder charset(String encoding) {
+		this.encoding = encoding;
+		return this;
+	}
+
+	public Encoder to(OutputStream out) throws IOException {
+		return to(new OutputStreamWriter(out, encoding));
+	}
+
+	public Encoder to(Appendable out) throws IOException {
+		app = out;
+		return this;
+	}
+
+	public Appendable append(char c) throws IOException {
+		if (digest != null) {
+			digest.update((byte) (c / 256));
+			digest.update((byte) (c % 256));
+		}
+		app.append(c);
+		return this;
+	}
+
+	public Appendable append(CharSequence sq) throws IOException {
+		return append(sq, 0, sq.length());
+	}
+
+	public Appendable append(CharSequence sq, int start, int length) throws IOException {
+		if (digest != null) {
+			for (int i = start; i < length; i++) {
+				char c = sq.charAt(i);
+				digest.update((byte) (c / 256));
+				digest.update((byte) (c % 256));
+			}
+		}
+		app.append(sq, start, length);
+		return this;
+	}
+
+	public String toString() {
+		return app.toString();
+	}
+
+	public void close() throws IOException {
+		if (app != null && app instanceof Closeable)
+			((Closeable) app).close();
+	}
+
+	void encode(Object object, Type type, Map<Object, Type> visited) throws Exception {
+		codec.encode(this, object, type, visited);
+	}
+
+	public Encoder writeDefaults() {
+		writeDefaults = true;
+		return this;
+	}
+
+	public void flush() throws IOException {
+		if (app instanceof Flushable) {
+			((Flushable) app).flush();
+		}
+	}
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/EnumHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/EnumHandler.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/EnumHandler.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/EnumHandler.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,24 @@
+package aQute.lib.json;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+public class EnumHandler extends Handler {
+	@SuppressWarnings("rawtypes")
+	final Class				type;
+
+	public EnumHandler(Class<?> type) {
+		this.type = type;
+	}
+
+	@Override void encode(Encoder app, Object object, Map<Object, Type> visited)
+			throws IOException, Exception {
+		StringHandler.string(app, object.toString());
+	}
+
+	@SuppressWarnings("unchecked") Object decode(String s) throws Exception {
+		return Enum.valueOf(type, s);
+	}
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/FileHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/FileHandler.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/FileHandler.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/FileHandler.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,38 @@
+package aQute.lib.json;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import aQute.lib.base64.*;
+
+public class FileHandler extends Handler {
+
+	@Override void encode(Encoder app, Object object, Map<Object, Type> visited)
+			throws IOException, Exception {
+		File f = (File) object;
+		if ( !f.isFile())
+			throw new RuntimeException("Encoding a file requires the file to exist and to be a normal file " + f );
+		
+		FileInputStream in = new FileInputStream(f);
+		try {
+			app.append('"');
+			Base64.encode(in, app);
+			app.append('"');
+		} finally {
+			in.close();
+		}
+	}
+
+	Object decode(String s) throws Exception {
+		File tmp = File.createTempFile("json", ".bin");
+		FileOutputStream fout = new FileOutputStream(tmp);
+		try {
+			Base64.decode(new StringReader(s), fout);
+		} finally {
+			fout.close();
+		}
+		return tmp;
+	}
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Handler.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Handler.java?rev=1347815&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Handler.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/Handler.java Thu Jun  7 21:57:32 2012
@@ -0,0 +1,35 @@
+package aQute.lib.json;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+abstract class Handler {
+	abstract void encode(Encoder app, Object object, Map<Object, Type> visited)
+			throws IOException, Exception;
+
+	Object decodeObject(Decoder isr) throws Exception {
+		throw new UnsupportedOperationException("Cannot be mapped to object " + this);
+	}
+
+	Object decodeArray(Decoder isr) throws Exception {
+		throw new UnsupportedOperationException("Cannot be mapped to array " + this);
+	}
+
+	Object decode(String s) throws Exception {
+		throw new UnsupportedOperationException("Cannot be mapped to string " + this);
+	}
+
+	Object decode(Number s) throws Exception {
+		throw new UnsupportedOperationException("Cannot be mapped to number " + this);
+	}
+
+	Object decode(boolean s) {
+		throw new UnsupportedOperationException("Cannot be mapped to boolean " + this);
+	}
+
+	Object decode() {
+		return null;
+	}
+
+}

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

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