package com.jsoniter; import com.jsoniter.spi.*; import java.io.IOException; import java.util.*; class ReflectionObjectDecoder { private static Object NOT_SET = new Object() { @Override public String toString() { return "NOT_SET"; } }; private Map<Slice, Binding> allBindings = new HashMap<Slice, Binding>(); private String tempCacheKey; private String ctorArgsCacheKey; private int tempCount; private long expectedTracker; private int requiredIdx; private int tempIdx; private ClassDescriptor desc; public ReflectionObjectDecoder(Class clazz) { try { init(clazz); } catch (Exception e) { throw new JsonException(e); } } private final void init(Class clazz) throws Exception { ClassDescriptor desc = JsoniterSpi.getDecodingClassDescriptor(clazz, true); for (Binding param : desc.ctor.parameters) { addBinding(clazz, param); } this.desc = desc; if (desc.ctor.objectFactory == null && desc.ctor.ctor == null && desc.ctor.staticFactory == null) { throw new JsonException("no constructor for: " + desc.clazz); } for (Binding field : desc.fields) { addBinding(clazz, field); } for (Binding setter : desc.setters) { addBinding(clazz, setter); } for (WrapperDescriptor setter : desc.wrappers) { for (Binding param : setter.parameters) { addBinding(clazz, param); } } if (requiredIdx > 63) { throw new JsonException("too many required properties to track"); } expectedTracker = Long.MAX_VALUE >> (63 - requiredIdx); if (!desc.ctor.parameters.isEmpty() || !desc.wrappers.isEmpty()) { tempCount = tempIdx; tempCacheKey = "temp@" + clazz.getCanonicalName(); ctorArgsCacheKey = "ctor@" + clazz.getCanonicalName(); } } private void addBinding(Class clazz, final Binding binding) { if (binding.asMissingWhenNotPresent) { binding.mask = 1L << requiredIdx; requiredIdx++; } if (binding.asExtraWhenPresent) { binding.decoder = new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { throw new JsonException("found should not present property: " + binding.name); } }; } if (binding.decoder == null) { // field decoder might be special customized binding.decoder = JsoniterSpi.getDecoder(binding.decoderCacheKey()); } if (binding.decoder == null) { binding.decoder = Codegen.getDecoder(binding.valueTypeLiteral.getDecoderCacheKey(), binding.valueType); } binding.idx = tempIdx; for (String fromName : binding.fromNames) { Slice slice = Slice.make(fromName); if (allBindings.containsKey(slice)) { throw new JsonException("name conflict found in " + clazz + ": " + fromName); } allBindings.put(slice, binding); } tempIdx++; } public Decoder create() { if (desc.ctor.parameters.isEmpty()) { if (desc.wrappers.isEmpty()) { return new OnlyField(); } else { return new WithSetter(); } } else { return new WithCtor(); } } public class OnlyField implements Decoder { public Object decode(JsonIterator iter) throws IOException { try { return decode_(iter); } catch (Exception e) { throw new JsonException(e); } } private Object decode_(JsonIterator iter) throws Exception { if (iter.readNull()) { CodegenAccess.resetExistingObject(iter); return null; } Object obj = CodegenAccess.existingObject(iter) == null ? createNewObject() : CodegenAccess.resetExistingObject(iter); if (!CodegenAccess.readObjectStart(iter)) { if (requiredIdx > 0) { if (desc.onMissingProperties == null) { throw new JsonException("missing required properties: " + collectMissingFields(0)); } else { setToBinding(obj, desc.onMissingProperties, collectMissingFields(0)); } } return obj; } Map<String, Object> extra = null; long tracker = 0L; Slice fieldName = CodegenAccess.readObjectFieldAsSlice(iter); Binding binding = allBindings.get(fieldName); if (binding == null) { extra = onUnknownProperty(iter, fieldName, extra); } else { if (binding.asMissingWhenNotPresent) { tracker |= binding.mask; } setToBinding(obj, binding, decodeBinding(iter, obj, binding)); } while (CodegenAccess.nextToken(iter) == ',') { fieldName = CodegenAccess.readObjectFieldAsSlice(iter); binding = allBindings.get(fieldName); if (binding == null) { extra = onUnknownProperty(iter, fieldName, extra); } else { if (binding.asMissingWhenNotPresent) { tracker |= binding.mask; } setToBinding(obj, binding, decodeBinding(iter, obj, binding)); } } if (tracker != expectedTracker) { if (desc.onMissingProperties == null) { throw new JsonException("missing required properties: " + collectMissingFields(tracker)); } else { setToBinding(obj, desc.onMissingProperties, collectMissingFields(tracker)); } } setExtra(obj, extra); return obj; } } public class WithCtor implements Decoder { @Override public Object decode(JsonIterator iter) throws IOException { try { return decode_(iter); } catch (Exception e) { throw new JsonException(e); } } private Object decode_(JsonIterator iter) throws Exception { if (iter.readNull()) { CodegenAccess.resetExistingObject(iter); return null; } if (iter.tempObjects == null) { iter.tempObjects = new HashMap<String, Object>(); } Object[] temp = (Object[]) iter.tempObjects.get(tempCacheKey); if (temp == null) { temp = new Object[tempCount]; iter.tempObjects.put(tempCacheKey, temp); } Arrays.fill(temp, NOT_SET); if (!CodegenAccess.readObjectStart(iter)) { if (requiredIdx > 0) { throw new JsonException("missing required properties: " + collectMissingFields(0)); } return createNewObject(iter, temp); } Map<String, Object> extra = null; long tracker = 0L; Slice fieldName = CodegenAccess.readObjectFieldAsSlice(iter); Binding binding = allBindings.get(fieldName); if (binding == null) { extra = onUnknownProperty(iter, fieldName, extra); } else { if (binding.asMissingWhenNotPresent) { tracker |= binding.mask; } temp[binding.idx] = decodeBinding(iter, binding); } while (CodegenAccess.nextToken(iter) == ',') { fieldName = CodegenAccess.readObjectFieldAsSlice(iter); binding = allBindings.get(fieldName); if (binding == null) { extra = onUnknownProperty(iter, fieldName, extra); } else { if (binding.asMissingWhenNotPresent) { tracker |= binding.mask; } temp[binding.idx] = decodeBinding(iter, binding); } } if (tracker != expectedTracker) { throw new JsonException("missing required properties: " + collectMissingFields(tracker)); } Object obj = createNewObject(iter, temp); setExtra(obj, extra); for (Binding field : desc.fields) { Object val = temp[field.idx]; if (val != NOT_SET) { field.field.set(obj, val); } } for (Binding setter : desc.setters) { Object val = temp[setter.idx]; if (val != NOT_SET) { setter.method.invoke(obj, val); } } applyWrappers(temp, obj); return obj; } } public class WithSetter implements Decoder { @Override public Object decode(JsonIterator iter) throws IOException { try { return decode_(iter); } catch (Exception e) { throw new JsonException(e); } } private Object decode_(JsonIterator iter) throws Exception { if (iter.readNull()) { CodegenAccess.resetExistingObject(iter); return null; } Object obj = createNewObject(); if (!CodegenAccess.readObjectStart(iter)) { if (requiredIdx > 0) { if (desc.onMissingProperties == null) { throw new JsonException("missing required properties: " + collectMissingFields(0)); } else { setToBinding(obj, desc.onMissingProperties, collectMissingFields(0)); } } return obj; } Map<String, Object> extra = null; long tracker = 0L; if (iter.tempObjects == null) { iter.tempObjects = new HashMap<String, Object>(); } Object[] temp = (Object[]) iter.tempObjects.get(tempCacheKey); if (temp == null) { temp = new Object[tempCount]; iter.tempObjects.put(tempCacheKey, temp); } Arrays.fill(temp, NOT_SET); Slice fieldName = CodegenAccess.readObjectFieldAsSlice(iter); Binding binding = allBindings.get(fieldName); if (binding == null) { extra = onUnknownProperty(iter, fieldName, extra); } else { if (binding.asMissingWhenNotPresent) { tracker |= binding.mask; } if (canNotSetDirectly(binding)) { temp[binding.idx] = decodeBinding(iter, obj, binding); } else { setToBinding(obj, binding, decodeBinding(iter, obj, binding)); } } while (CodegenAccess.nextToken(iter) == ',') { fieldName = CodegenAccess.readObjectFieldAsSlice(iter); binding = allBindings.get(fieldName); if (binding == null) { extra = onUnknownProperty(iter, fieldName, extra); } else { if (binding.asMissingWhenNotPresent) { tracker |= binding.mask; } if (canNotSetDirectly(binding)) { temp[binding.idx] = decodeBinding(iter, obj, binding); } else { setToBinding(obj, binding, decodeBinding(iter, obj, binding)); } } } if (tracker != expectedTracker) { if (desc.onMissingProperties == null) { throw new JsonException("missing required properties: " + collectMissingFields(tracker)); } else { setToBinding(obj, desc.onMissingProperties, collectMissingFields(tracker)); } } setExtra(obj, extra); applyWrappers(temp, obj); return obj; } } private void setToBinding(Object obj, Binding binding, Object value) throws Exception { if (binding.field != null) { binding.field.set(obj, value); } else { binding.method.invoke(obj, value); } } private void setExtra(Object obj, Map<String, Object> extra) throws Exception { if (desc.onExtraProperties != null) { setToBinding(obj, desc.onExtraProperties, extra); } } private boolean canNotSetDirectly(Binding binding) { return binding.field == null && binding.method == null; } private Object decodeBinding(JsonIterator iter, Binding binding) throws Exception { Object value; value = binding.decoder.decode(iter); return value; } private Object decodeBinding(JsonIterator iter, Object obj, Binding binding) throws Exception { if (binding.valueCanReuse) { CodegenAccess.setExistingObject(iter, binding.field.get(obj)); } return decodeBinding(iter, binding); } private Map<String, Object> onUnknownProperty(JsonIterator iter, Slice fieldName, Map<String, Object> extra) throws IOException { if (desc.asExtraForUnknownProperties) { if (desc.onExtraProperties == null) { throw new JsonException("unknown property: " + fieldName.toString()); } else { if (extra == null) { extra = new HashMap<String, Object>(); } extra.put(fieldName.toString(), iter.readAny()); } } else { iter.skip(); } return extra; } private List<String> collectMissingFields(long tracker) { List<String> missingFields = new ArrayList<String>(); for (Binding binding : allBindings.values()) { if (binding.asMissingWhenNotPresent) { long mask = binding.mask; CodegenAccess.addMissingField(missingFields, tracker, mask, binding.name); } } return missingFields; } private void applyWrappers(Object[] temp, Object obj) throws Exception { for (WrapperDescriptor wrapper : desc.wrappers) { Object[] args = new Object[wrapper.parameters.size()]; for (int i = 0; i < wrapper.parameters.size(); i++) { args[i] = temp[wrapper.parameters.get(i).idx]; } wrapper.method.invoke(obj, args); } } private Object createNewObject(JsonIterator iter, Object[] temp) throws Exception { if (iter.tempObjects == null) { iter.tempObjects = new HashMap<String, Object>(); } Object[] ctorArgs = (Object[]) iter.tempObjects.get(ctorArgsCacheKey); if (ctorArgs == null) { ctorArgs = new Object[desc.ctor.parameters.size()]; iter.tempObjects.put(ctorArgsCacheKey, ctorArgs); } Arrays.fill(ctorArgs, null); for (int i = 0; i < desc.ctor.parameters.size(); i++) { Object arg = temp[desc.ctor.parameters.get(i).idx]; if (arg != NOT_SET) { ctorArgs[i] = arg; } } return createNewObject(ctorArgs); } private Object createNewObject(Object... args) throws Exception { if (desc.ctor.objectFactory != null) { return desc.ctor.objectFactory.create(desc.clazz); } if (desc.ctor.staticFactory != null) { return desc.ctor.staticFactory.invoke(null, args); } else { return desc.ctor.ctor.newInstance(args); } } }