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.put( Object.class, new BeanObjectFactory() );
factories.put( Collection.class, new ListObjectFactory() );
factories.put( List.class, new ListObjectFactory() );
factories.put( Set.class, new SetObjectFactory() );
factories.put( SortedSet.class, new SortedSetObjectFactory() );
factories.put( Map.class, new MapObjectFactory() );
factories.put( Integer.class, new IntegerObjectFactory() );
factories.put( int.class, new IntegerObjectFactory() );
factories.put( Float.class, new FloatObjectFactory() );
factories.put( float.class, new FloatObjectFactory() );
factories.put( Double.class, new DoubleObjectFactory() );
factories.put( double.class, new DoubleObjectFactory() );
factories.put( Short.class, new ShortObjectFactory() );
factories.put( short.class, new ShortObjectFactory() );
factories.put( Long.class, new LongObjectFactory() );
factories.put( long.class, new LongObjectFactory() );
factories.put( Byte.class, new ByteObjectFactory() );
factories.put( byte.class, new ByteObjectFactory() );
factories.put( Boolean.class, new BooleanObjectFactory() );
factories.put( boolean.class, new BooleanObjectFactory() );
factories.put( Character.class, new CharacterObjectFactory() );
factories.put( char.class, new CharacterObjectFactory() );
factories.put( Enum.class, new EnumObjectFactory() );
factories.put( Date.class, new DateObjectFactory() );
factories.put( String.class, new StringObjectFactory() );
factories.put( Array.class, new ArrayObjectFactory() );
factories.put( BigDecimal.class, new BigDecimalFactory() );
factories.put( BigInteger.class, new BigIntegerFactory() );
}
public ObjectBinder use(Path path, ObjectFactory factory) {
pathFactories.put( path, factory );
return this;
}
public ObjectBinder use(Class clazz, ObjectFactory factory) {
factories.put( clazz, factory );
return this;
}
public Path getCurrentPath() {
return currentPath;
}
public Object bind( Object input ) {
return this.bind( input, null );
}
public Object bind( Object source, Object target ) {
if( target instanceof Map ) {
bindIntoMap( (Map)source, (Map<Object,Object>)target, null, null );
} else if( target instanceof Collection ) {
bindIntoCollection( (Collection)source, (Collection<Object>)target, null );
} else {
bindIntoObject( (Map)source, target, target.getClass() );
}
return target;
}
public Object bind( Object input, Type targetType ) {
jsonStack.add( input );
try {
if( input == null ) return null;
Class targetClass = findClassName( input, getTargetClass( targetType ) );
ObjectFactory factory = findFactoryFor( targetClass );
if( factory == null ) throw new JSONException( currentPath + ": + Could not find a suitable ObjectFactory for " + targetClass );
return factory.instantiate( this, input, targetType, targetClass );
} finally {
jsonStack.removeLast();
}
}
public <T extends Collection<Object>> T bindIntoCollection(Collection value, T target, Type targetType) {
Type valueType = null;
if( targetType instanceof ParameterizedType) {
valueType = ((ParameterizedType)targetType).getActualTypeArguments()[0];
}
jsonStack.add( value );
objectStack.add( target );
getCurrentPath().enqueue("values");
for( Object obj : value ) {
target.add( bind( obj, valueType ) );
}
getCurrentPath().pop();
objectStack.removeLast();
jsonStack.removeLast();
return target;
}
public Object bindIntoMap(Map input, Map<Object, Object> result, Type keyType, Type valueType) {
jsonStack.add( input );
objectStack.add( result );
for( Object inputKey : input.keySet() ) {
currentPath.enqueue("keys");
Object key = bind( inputKey, keyType );
currentPath.pop();
currentPath.enqueue("values");
Object value = bind( input.get(inputKey), valueType );
currentPath.pop();
result.put( key, value );
}
objectStack.removeLast();
jsonStack.removeLast();
return result;
}
public Object bindIntoObject(Map jsonOwner, Object target, Type targetType) {
try {
objectStack.add( target );
BeanAnalyzer analyzer = BeanAnalyzer.analyze( target.getClass() );
for( BeanProperty descriptor : analyzer.getProperties() ) {
Object value = findFieldInJson( jsonOwner, descriptor );
if( value != null ) {
currentPath.enqueue( descriptor.getName() );
Method setMethod = descriptor.getWriteMethod();
if( setMethod != null ) {
Type[] types = setMethod.getGenericParameterTypes();
if( types.length == 1 ) {
Type paramType = types[0];
setMethod.invoke( objectStack.getLast(), bind( value, resolveParameterizedTypes( paramType, 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();
if( field != null ) {
field.setAccessible( true );
field.set( target, bind( value, 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 JSONException( String.format("%s: Can not convert %s into %s", currentPath, value.getClass().getName(), targetType.getName() ) );
}
private Class getTargetClass(Type targetType) {
if( targetType == null ) {
return null;
} else if( targetType instanceof Class ) {
return (Class)targetType;
} else if( targetType instanceof ParameterizedType ) {
return (Class)((ParameterizedType)targetType).getRawType();
} else if( targetType instanceof GenericArrayType ) {
return Array.class;
} else if( targetType instanceof WildcardType ) {
return null; // nothing you can do about these. User will have to specify this with use()
} else if( targetType 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) {
if( genericType instanceof Class ) {
return genericType;
} else if( genericType instanceof ParameterizedType ) {
return genericType;
} else if( genericType instanceof TypeVariable ) {
return targetType;
} else if( genericType instanceof WildcardType ) {
return targetType;
} else if( genericType instanceof GenericArrayType ) {
return ((GenericArrayType)genericType).getGenericComponentType();
} else {
throw new JSONException( currentPath + ": Unknown generic type " + genericType + ".");
}
}
private Class findClassName( Object map, Class targetType ) throws JSONException {
if( !pathFactories.containsKey( currentPath ) ) {
Class mostSpecificType = useMostSpecific( targetType, map instanceof Map ? findClassInMap( (Map)map, null ) : null );
if( mostSpecificType == null ) {
return map.getClass();
} else {
return mostSpecificType;
}
} else {
return null;
}
}
protected Class useMostSpecific(Class classFromTarget, Class typeFound) {
if( classFromTarget != null && typeFound != null ) {
return typeFound.isAssignableFrom( classFromTarget ) ? classFromTarget : typeFound;
} else if( typeFound != null ) {
return typeFound;
} else if( classFromTarget != null ) {
return classFromTarget;
} else {
return null;
}
}
protected Class findClassInMap( Map map, Class override ) {
if( override == null ) {
String classname = (String)map.remove("class");
try {
if( classname != null ) {
return Class.forName( classname );
} else {
return null;
}
} catch( ClassNotFoundException e ) {
throw new JSONException( String.format( "%s: Could not load %s", currentPath, classname ), e );
}
} else {
return override;
}
}
private ObjectFactory findFactoryFor(Class targetType) {
ObjectFactory factory = pathFactories.get( currentPath );
if( factory == null ) {
if( targetType != null && targetType.isArray() ) return factories.get(Array.class);
return findFactoryByTargetClass(targetType);
}
return factory;
}
private ObjectFactory findFactoryByTargetClass(Class targetType) {
ObjectFactory factory;
factory = factories.get( targetType );
if( factory == null && targetType != null && targetType.getSuperclass() != null ) {
for( Class intf : targetType.getInterfaces() ) {
factory = findFactoryByTargetClass( intf );
if( factory != null ) return factory;
}
return findFactoryByTargetClass( targetType.getSuperclass() );
} else {
return factory;
}
}
protected Object instantiate( Class clazz ) {
try {
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible( true );
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 findFieldInJson( Map map, BeanProperty property ) {
Object value = map.get( property.getName() );
if( value == null ) {
String field = property.getName();
value = map.get( upperCase(field) );
}
return value;
}
private String upperCase(String field) {
return Character.toUpperCase( field.charAt(0) ) + field.substring(1);
}
public Object getTarget() {
return objectStack.getLast();
}
public Object getSource() {
return jsonStack.getLast();
}
public Object bindPrimitive(Object value, Class clazz) {
if( value.getClass() == clazz ) {
return value;
} else if( value instanceof Number && clazz.equals(Double.class) ) {
return ((Number)value).doubleValue();
} else if( value instanceof Number && clazz.equals(Integer.class) ) {
return ((Number)value).intValue();
} else if( value instanceof Number && clazz.equals(Long.class) ) {
return ((Number)value).longValue();
} else if( value instanceof Number && clazz.equals(Short.class) ) {
return ((Number)value).shortValue();
} else if( value instanceof Number && clazz.equals(Byte.class) ) {
return ((Number)value).byteValue();
} else if( value instanceof Number && clazz.equals(Float.class) ) {
return ((Number)value).floatValue();
} else if( value instanceof Boolean && clazz.equals(Boolean.class) ) {
return value;
} else if( value 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 currentPath) throws ClassNotFoundException {
ObjectFactory factory = pathFactories.get( currentPath );
if( factory instanceof ClassLocatorObjectFactory ) {
return ((ClassLocatorObjectFactory)factory).getLocator().locate( this, currentPath );
} else {
return null;
}
}
}
|