Open Source Repository

Home /json/flexjson-2.1 | Repository Home



flexjson/ObjectBinder.java
package flexjson;

import flexjson.factories.*;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.lang.reflect.*;

public class ObjectBinder {

    private LinkedList<Object> objectStack = new LinkedList<Object>();
    private LinkedList<Object> jsonStack = new LinkedList<Object>();
    private Path currentPath = new Path();
    private Map<Class,ObjectFactory> factories;
    private Map<Path,ObjectFactory> pathFactories = new HashMap<Path,ObjectFactory>();

    public ObjectBinder() {
        factories = new HashMap<Class,ObjectFactory>();
        factories.putObject.class, new BeanObjectFactory() );
        factories.putCollection.class, new ListObjectFactory() );
        factories.putList.class, new ListObjectFactory() );
        factories.putSet.class, new SetObjectFactory() );
        factories.putSortedSet.class, new SortedSetObjectFactory() );
        factories.putMap.class, new MapObjectFactory() );
        factories.putInteger.class, new IntegerObjectFactory() );
        factories.putint.class, new IntegerObjectFactory() );
        factories.putFloat.class, new FloatObjectFactory() );
        factories.putfloat.class, new FloatObjectFactory() );
        factories.putDouble.class, new DoubleObjectFactory() );
        factories.putdouble.class, new DoubleObjectFactory() );
        factories.putShort.class, new ShortObjectFactory() );
        factories.putshort.class, new ShortObjectFactory() );
        factories.putLong.class, new LongObjectFactory() );
        factories.putlong.class, new LongObjectFactory() );
        factories.putByte.class, new ByteObjectFactory() );
        factories.putbyte.class, new ByteObjectFactory() );
        factories.putBoolean.class, new BooleanObjectFactory() );
        factories.putboolean.class, new BooleanObjectFactory() );
        factories.putCharacter.class, new CharacterObjectFactory() );
        factories.putchar.class, new CharacterObjectFactory() );
        factories.putEnum.class, new EnumObjectFactory() );
        factories.putDate.class, new DateObjectFactory() );
        factories.putString.class, new StringObjectFactory() );
        factories.putArray.class, new ArrayObjectFactory() );
        factories.putBigDecimal.class, new BigDecimalFactory() );
        factories.putBigInteger.class, new BigIntegerFactory() );
    }

    public ObjectBinder use(Path path, ObjectFactory factory) {
        pathFactories.putpath, factory );
        return this;
    }

    public ObjectBinder use(Class clazz, ObjectFactory factory) {
        factories.putclazz, factory );
        return this;
    }

    public Path getCurrentPath() {
        return currentPath;
    }

    public Object bindObject input ) {
        return this.bindinput, null );
    }

    public Object bindObject source, Object target ) {
        iftarget instanceof Map ) {
            bindIntoMap( (Map)source, (Map<Object,Object>)target, null, null );
        else iftarget instanceof Collection ) {
            bindIntoCollection( (Collection)source, (Collection<Object>)target, null );
        else {
            bindIntoObject( (Map)source, target, target.getClass() );
        }
        return target;
    }

    public Object bindObject input, Type targetType ) {
        jsonStack.addinput );
        try {
            ifinput == null return null;
            Class targetClass = findClassNameinput, getTargetClasstargetType ) );
            ObjectFactory factory = findFactoryFortargetClass );
            iffactory == null throw new JSONExceptioncurrentPath + ": + Could not find a suitable ObjectFactory for " + targetClass );
            return factory.instantiatethis, input, targetType, targetClass );
        finally {
            jsonStack.removeLast();
        }
    }

    public <T extends Collection<Object>> T bindIntoCollection(Collection value, T target, Type targetType) {
        Type valueType = null;
        iftargetType instanceof ParameterizedType) {
            valueType = ((ParameterizedType)targetType).getActualTypeArguments()[0];
        }
        jsonStack.addvalue );
        objectStack.addtarget );
        getCurrentPath().enqueue("values");
        forObject obj : value ) {
            target.addbindobj, valueType ) );
        }
        getCurrentPath().pop();
        objectStack.removeLast();
        jsonStack.removeLast();
        return target;
    }

    public Object bindIntoMap(Map input, Map<Object, Object> result, Type keyType, Type valueType) {
        jsonStack.addinput );
        objectStack.addresult );
        forObject inputKey : input.keySet() ) {
            currentPath.enqueue("keys");
            Object key = bindinputKey, keyType );
            currentPath.pop();
            currentPath.enqueue("values");
            Object value = bindinput.get(inputKey), valueType );
            currentPath.pop();
            result.putkey, value );
        }
        objectStack.removeLast();
        jsonStack.removeLast();
        return result;
    }

    public Object bindIntoObject(Map jsonOwner, Object target, Type targetType) {
        try {
            objectStack.addtarget );
            BeanAnalyzer analyzer = BeanAnalyzer.analyzetarget.getClass() );
            forBeanProperty descriptor : analyzer.getProperties() ) {
                Object value = findFieldInJsonjsonOwner, descriptor );
                ifvalue != null ) {
                    currentPath.enqueuedescriptor.getName() );
                    Method setMethod = descriptor.getWriteMethod();
                    ifsetMethod != null ) {
                        Type[] types = setMethod.getGenericParameterTypes();
                        iftypes.length == ) {
                            Type paramType = types[0];
                            setMethod.invokeobjectStack.getLast(), bindvalue, resolveParameterizedTypesparamType, targetType ) ) );
                        else {
                            throw new JSONException(currentPath + ":  Expected a single parameter for method " + target.getClass().getName() "." + setMethod.getName() " but got " + types.length );
                        }
                    else {
                        Field field = descriptor.getProperty();
                        iffield != null ) {
                            field.setAccessibletrue );
                            field.settarget, bindvalue, field.getGenericType() ) );
                        }
                    }
                    currentPath.pop();
                }
            }
            return objectStack.removeLast();
        catch (IllegalAccessException e) {
            throw new JSONException(currentPath + ":  Could not access the no-arg constructor for " + target.getClass().getName(), e);
        catch (InvocationTargetException ex ) {
            throw new JSONException(currentPath + ":  Exception while trying to invoke setter method.", ex );
        }
    }

    public JSONException cannotConvertValueToTargetType(Object value, Class targetType) {
        return new JSONExceptionString.format("%s:  Can not convert %s into %s", currentPath, value.getClass().getName(), targetType.getName() ) );
    }

    private Class getTargetClass(Type targetType) {
        iftargetType == null ) {
            return null;
        else iftargetType instanceof Class ) {
            return (Class)targetType;
        else iftargetType instanceof ParameterizedType ) {
            return (Class)((ParameterizedType)targetType).getRawType();
        else iftargetType instanceof GenericArrayType ) {
            return Array.class;
        else iftargetType instanceof WildcardType ) {
            return null// nothing you can do about these.  User will have to specify this with use()
        else iftargetType instanceof TypeVariable ) {
            return null// nothing you can do about these.  User will have to specify this with use()
        else {
            throw new JSONException(currentPath + ":  Unknown type " + targetType );
        }
    }

    private Type resolveParameterizedTypes(Type genericType, Type targetType) {
        ifgenericType instanceof Class ) {
            return genericType;
        else ifgenericType instanceof ParameterizedType ) {
            return genericType;
        else ifgenericType instanceof TypeVariable ) {
            return targetType;
        else ifgenericType instanceof WildcardType ) {
            return targetType;
        else ifgenericType instanceof GenericArrayType ) {
            return ((GenericArrayType)genericType).getGenericComponentType();
        else {
            throw new JSONExceptioncurrentPath + ":  Unknown generic type " + genericType + ".");
        }
    }


    private Class findClassNameObject map, Class targetType throws JSONException {
        if!pathFactories.containsKeycurrentPath ) ) {
            Class mostSpecificType = useMostSpecifictargetType, map instanceof Map ? findClassInMap( (Map)map, null null );
            ifmostSpecificType == null ) {
                return map.getClass();
            else {
                return mostSpecificType;
            }
        else {
            return null;
        }
    }

    protected Class useMostSpecific(Class classFromTarget, Class typeFound) {
        ifclassFromTarget != null && typeFound != null ) {
            return typeFound.isAssignableFromclassFromTarget ? classFromTarget : typeFound;
        else iftypeFound != null ) {
            return typeFound;
        else ifclassFromTarget != null ) {
            return classFromTarget;
        else {
            return null;
        }
    }

    protected Class findClassInMapMap map, Class override ) {
        ifoverride == null ) {
            String classname = (String)map.remove("class");
            try {
                ifclassname != null ) {
                    return Class.forNameclassname );
                else {
                    return null;
                }
            catchClassNotFoundException e ) {
                throw new JSONExceptionString.format"%s:  Could not load %s", currentPath, classname ), e );
            }
        else {
            return override;
        }
    }

    private ObjectFactory findFactoryFor(Class targetType) {
        ObjectFactory factory = pathFactories.getcurrentPath );
        iffactory == null ) {
            iftargetType != null && targetType.isArray() ) return factories.get(Array.class);
            return findFactoryByTargetClass(targetType);
        }
        return factory;
    }

    private ObjectFactory findFactoryByTargetClass(Class targetType) {
        ObjectFactory factory;
        factory = factories.gettargetType );
        iffactory == null && targetType != null && targetType.getSuperclass() != null ) {
            forClass intf : targetType.getInterfaces() ) {
                factory = findFactoryByTargetClassintf );
                iffactory != null return factory;
            }
            return findFactoryByTargetClasstargetType.getSuperclass() );
        else {
            return factory;
        }
    }

    protected Object instantiateClass clazz ) {
        try {
            Constructor constructor = clazz.getDeclaredConstructor();
            constructor.setAccessibletrue );
            return constructor.newInstance();
        catch (InstantiationException e) {
            throw new JSONException(currentPath + ":There was an exception trying to instantiate an instance of " + clazz.getName(), e );
        catch (IllegalAccessException e) {
            throw new JSONException(currentPath + ":There was an exception trying to instantiate an instance of " + clazz.getName(), e );
        catch (InvocationTargetException e) {
            throw new JSONException(currentPath + ":There was an exception trying to instantiate an instance of " + clazz.getName(), e );
        catch (NoSuchMethodException e) {
            throw new JSONException(currentPath + ": " + clazz.getName() " lacks a no argument constructor.  Flexjson will instantiate any protected, private, or public no-arg constructor.", e );
        }
    }

    private Object findFieldInJsonMap map, BeanProperty property ) {
        Object value = map.getproperty.getName() );
        ifvalue == null ) {
            String field = property.getName();
            value = map.getupperCase(field) );
        }

        return value;
    }

    private String upperCase(String field) {
        return Character.toUpperCasefield.charAt(0) ) + field.substring(1);
    }

    public Object getTarget() {
        return objectStack.getLast();
    }

    public Object getSource() {
        return jsonStack.getLast();
    }

    public Object bindPrimitive(Object value, Class clazz) {
        ifvalue.getClass() == clazz ) {
            return value;
        else ifvalue instanceof Number && clazz.equals(Double.class) ) {
            return ((Number)value).doubleValue();
        else ifvalue instanceof Number && clazz.equals(Integer.class) ) {
            return ((Number)value).intValue();
        else ifvalue instanceof Number && clazz.equals(Long.class) ) {
            return ((Number)value).longValue();
        else ifvalue instanceof Number && clazz.equals(Short.class) ) {
            return ((Number)value).shortValue();
        else ifvalue instanceof Number && clazz.equals(Byte.class) ) {
            return ((Number)value).byteValue();
        else ifvalue instanceof Number && clazz.equals(Float.class) ) {
            return ((Number)value).floatValue();
        else ifvalue instanceof Boolean && clazz.equals(Boolean.class) ) {
            return value;
        else ifvalue instanceof Long && clazz == Date.class ) {
            return new Date( (Long)value );
        else {
            throw new JSONException(String.format("%s: Don't know how to bind %s into class %s.  You might need to use an ObjectFactory instead of a plain class.", getCurrentPath().toString(), value, clazz.getName()) );
        }
    }

    public Class findClassAtPath(Path currentPaththrows ClassNotFoundException {
        ObjectFactory factory = pathFactories.getcurrentPath );
        iffactory instanceof ClassLocatorObjectFactory ) {
            return ((ClassLocatorObjectFactory)factory).getLocator().locatethis, currentPath );
        else {
            return null;
        }
    }
}