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;
+ }
+
+}