Use local copy of latest bndlib code for pre-release testing purposes

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1347815 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/lib/json/ObjectHandler.java b/bundleplugin/src/main/java/aQute/lib/json/ObjectHandler.java
new file mode 100644
index 0000000..fc6f6bd
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/json/ObjectHandler.java
@@ -0,0 +1,147 @@
+package aQute.lib.json;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+public class ObjectHandler extends Handler {
+	@SuppressWarnings("rawtypes")
+	final Class		rawClass;
+	final Field		fields[];
+	final Type		types[];
+	final Object	defaults[];
+	final Field		extra;
+
+	ObjectHandler(JSONCodec codec, Class<?> c) throws Exception {
+		rawClass = c;
+		fields = c.getFields();
+
+		// Sort the fields so the output is canonical
+		Arrays.sort(fields, new Comparator<Field>() {
+			public int compare(Field o1, Field o2) {
+				return o1.getName().compareTo(o2.getName());
+			}
+		});
+
+		types = new Type[fields.length];
+		defaults = new Object[fields.length];
+
+		Field x = null;
+		for (int i = 0; i < fields.length; i++) {
+			if (fields[i].getName().equals("__extra"))
+				x = fields[i];
+			types[i] = fields[i].getGenericType();
+		}
+		if (x != null && Map.class.isAssignableFrom(x.getType()))
+			extra = x;
+		else
+			extra = null;
+		
+		try {
+			Object template = c.newInstance();
+
+			for (int i = 0; i < fields.length; i++) {
+				defaults[i] = fields[i].get(template);
+			}
+		} catch (Exception e) {
+			// Ignore
+		}
+	}
+
+	@Override void encode(Encoder app, Object object, Map<Object, Type> visited) throws Exception {
+		app.append("{");
+		String del = "";
+		for (int i = 0; i < fields.length; i++) {
+			if (fields[i].getName().startsWith("__"))
+				continue;
+
+			Object value = fields[i].get(object);
+			if (!app.writeDefaults) {
+				if (value == defaults[i])
+					continue;
+
+				if (value != null && value.equals(defaults[i]))
+					continue;
+			}
+
+			app.append(del);
+			StringHandler.string(app, fields[i].getName());
+			app.append(":");
+			app.encode(value, types[i], visited);
+			del = ",";
+		}
+		app.append("}");
+	}
+
+	@SuppressWarnings("unchecked") @Override Object decodeObject(Decoder r) throws Exception {
+		assert r.current() == '{';
+		Object targetObject = rawClass.newInstance();
+
+		int c = r.next();
+		while (JSONCodec.START_CHARACTERS.indexOf(c) >= 0) {
+
+			// Get key
+			String key = r.codec.parseString(r);
+
+			// Get separator
+			c = r.skipWs();
+			if (c != ':')
+				throw new IllegalArgumentException("Expected ':' but got " + (char) c);
+
+			c = r.next();
+
+			// Get value
+
+			Field f = getField(key);
+			if (f != null) {
+				// We have a field and thus a type
+				Object value = r.codec.decode(f.getGenericType(), r);
+				f.set(targetObject, value);
+			} else {
+				// No field, but may extra is defined
+				if (extra == null) {
+					if (r.strict)
+						throw new IllegalArgumentException("No such field " + key);
+					Object value = r.codec.decode(null, r);
+					r.getExtra().put(rawClass.getName() + "." + key, value);
+				} else {
+
+					Map<String, Object> map = (Map<String, Object>) extra.get(targetObject);
+					if (map == null) {
+						map = new LinkedHashMap<String, Object>();
+						extra.set(targetObject, map);
+					}
+					Object value = r.codec.decode(null, r);
+					map.put(key, value);
+				}
+			}
+
+			c = r.skipWs();
+
+			if (c == '}')
+				break;
+
+			if (c == ',') {
+				c = r.next();
+				continue;
+			}
+
+			throw new IllegalArgumentException(
+					"Invalid character in parsing object, expected } or , but found " + (char) c);
+		}
+		assert r.current() == '}';
+		r.read(); // skip closing
+		return targetObject;
+	}
+
+	private Field getField(String key) {
+		for (int i = 0; i < fields.length; i++) {
+			int n = key.compareTo(fields[i].getName());
+			if (n == 0)
+				return fields[i];
+			if (n < 0)
+				return null;
+		}
+		return null;
+	}
+
+}