/**
* Copyright (c) 2009 - 2011 AppWork UG(haftungsbeschränkt) <e-mail@appwork.org>
*
* This file is part of org.appwork.storage.simplejson.mapper
*
* This software is licensed under the Artistic License 2.0,
* see the LICENSE file or http://www.opensource.org/licenses/artistic-license-2.0.php
* for details
*/
package org.appwork.storage.simplejson.mapper;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.appwork.storage.TypeRef;
import org.appwork.storage.simplejson.JSonArray;
import org.appwork.storage.simplejson.JSonNode;
import org.appwork.storage.simplejson.JSonObject;
import org.appwork.storage.simplejson.JSonValue;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
/**
* @author thomas
*
*/
public class JSonMapper {
/**
* @param value
* @param type
* @return
*/
public static Object cast(Object v, final Class<?> type) {
if (type.isPrimitive()) {
if (type == boolean.class) {
v = ((Boolean) v).booleanValue();
} else if (type == char.class) {
v = (char) ((Long) v).byteValue();
} else if (type == byte.class) {
v = ((Long) v).byteValue();
} else if (type == short.class) {
v = ((Long) v).shortValue();
} else if (type == int.class) {
v = ((Long) v).intValue();
} else if (type == long.class) {
v = ((Long) v).longValue();
} else if (type == float.class) {
v = ((Double) v).floatValue();
} else if (type == double.class) {
//
v = ((Double) v).doubleValue();
}
} else if (type == Boolean.class) {
v = ((Boolean) v).booleanValue();
} else if (type == Character.class) {
v = (char) ((Long) v).byteValue();
} else if (type == Byte.class) {
v = ((Long) v).byteValue();
} else if (type == Short.class) {
v = ((Long) v).shortValue();
} else if (type == Integer.class) {
v = ((Long) v).intValue();
} else if (type == Long.class) {
v = ((Long) v).longValue();
} else if (type == Float.class) {
v = ((Double) v).floatValue();
} else if (type == Double.class) {
//
v = ((Double) v).doubleValue();
}
return v;
}
private boolean ignorePrimitiveNullMapping = false;
private boolean ignoreIllegalArgumentMappings = false;
/**
* @param value
* @param type
* @return
*/
private boolean ignoreIllegalEnumMappings = false;
public JSonMapper() {
}
/**
* @param obj
* @return
* @throws MapperException
*/
@SuppressWarnings("unchecked")
public JSonNode create(final Object obj) throws MapperException {
try {
if (obj == null) { return new JSonValue(null); }
final Class<? extends Object> clazz = obj.getClass();
if (clazz.isPrimitive()) {
if (clazz == boolean.class) {
return new JSonValue((Boolean) obj);
} else if (clazz == char.class) {
return new JSonValue(0 + ((Character) obj).charValue());
} else if (clazz == byte.class) {
return new JSonValue(((Byte) obj).longValue());
} else if (clazz == short.class) {
return new JSonValue(((Short) obj).longValue());
} else if (clazz == int.class) {
return new JSonValue(((Integer) obj).longValue());
} else if (clazz == long.class) {
return new JSonValue(((Long) obj).longValue());
} else if (clazz == float.class) {
return new JSonValue(((Float) obj).doubleValue());
} else if (clazz == double.class) { return new JSonValue(((Double) obj).doubleValue()); }
} else if (clazz.isEnum()) {
return new JSonValue(obj + "");
} else if (obj instanceof Boolean) {
return new JSonValue(((Boolean) obj).booleanValue());
} else if (obj instanceof Character) {
return new JSonValue(0 + ((Character) obj).charValue());
} else if (obj instanceof Byte) {
return new JSonValue(((Byte) obj).longValue());
} else if (obj instanceof Short) {
return new JSonValue(((Short) obj).longValue());
} else if (obj instanceof Integer) {
return new JSonValue(((Integer) obj).longValue());
} else if (obj instanceof Long) {
return new JSonValue(((Long) obj).longValue());
} else if (obj instanceof Float) {
return new JSonValue(((Float) obj).doubleValue());
} else if (obj instanceof Double) {
return new JSonValue(((Double) obj).doubleValue());
} else if (obj instanceof String) {
return new JSonValue((String) obj);
} else if (obj instanceof Map) {
final JSonObject ret = new JSonObject();
Entry<Object, Object> next;
for (final Iterator<Entry<Object, Object>> it = ((Map<Object, Object>) obj).entrySet().iterator(); it.hasNext();) {
next = it.next();
if (!(next.getKey() instanceof String)) { throw new MapperException("Map keys have to be Strings: " + clazz); }
ret.put(next.getKey().toString(), this.create(next.getValue()));
}
return ret;
} else if (obj instanceof List) {
final JSonArray ret = new JSonArray();
for (final Object o : (List<?>) obj) {
ret.add(this.create(o));
}
return ret;
} else if (clazz.isArray()) {
final JSonArray ret = new JSonArray();
for (int i = 0; i < Array.getLength(obj); i++) {
ret.add(this.create(Array.get(obj, i)));
}
return ret;
} else/* if (obj instanceof Storable) */{
final ClassCache cc = ClassCache.getClassCache(clazz);
final JSonObject ret = new JSonObject();
for (final Getter g : cc.getGetter()) {
ret.put(g.getKey(), this.create(g.getValue(obj)));
}
return ret;
}
} catch (final IllegalArgumentException e) {
e.printStackTrace();
} catch (final IllegalAccessException e) {
e.printStackTrace();
} catch (final InvocationTargetException e) {
e.printStackTrace();
} catch (final SecurityException e) {
e.printStackTrace();
} catch (final NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
public boolean isIgnoreIllegalArgumentMappings() {
return this.ignoreIllegalArgumentMappings;
}
public boolean isIgnoreIllegalEnumMappings() {
return this.ignoreIllegalEnumMappings;
}
/**
* if json maps null to a primitive field
*
* @return
*/
public boolean isIgnorePrimitiveNullMapping() {
return this.ignorePrimitiveNullMapping;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Object jsonToObject(final JSonNode json, final Type type) {
final ClassCache cc;
try {
if (json instanceof JSonValue) {
switch (((JSonValue) json).getType()) {
case BOOLEAN:
case DOUBLE:
case LONG:
if (type instanceof Class) {
return JSonMapper.cast(((JSonValue) json).getValue(), (Class) type);
} else {
return ((JSonValue) json).getValue();
}
case STRING:
if (type instanceof Class && ((Class<?>) type).isEnum()) {
try {
return Enum.valueOf((Class<Enum>) type, ((JSonValue) json).getValue() + "");
} catch (final IllegalArgumentException e) {
if (this.isIgnoreIllegalArgumentMappings() || this.isIgnoreIllegalEnumMappings()) { return null; }
throw e;
}
} else {
return ((JSonValue) json).getValue();
}
case NULL:
return null;
}
}
if (type instanceof Class) {
final Class<?> clazz = (Class<?>) type;
if (List.class.isAssignableFrom(clazz)) {
final List<Object> inst = (List<Object>) clazz.newInstance();
final JSonArray obj = (JSonArray) json;
final Type gs = clazz.getGenericSuperclass();
final Type gType;
if (gs instanceof ParameterizedTypeImpl) {
gType = ((ParameterizedTypeImpl) gs).getActualTypeArguments()[0];
} else {
gType = void.class;
}
for (final JSonNode n : obj) {
inst.add(this.jsonToObject(n, gType));
}
return inst;
} else if (Map.class.isAssignableFrom(clazz)) {
final Map<String, Object> inst = (Map<String, Object>) clazz.newInstance();
final JSonObject obj = (JSonObject) json;
final Type gs = clazz.getGenericSuperclass();
final Type gType;
if (gs instanceof ParameterizedTypeImpl) {
gType = ((ParameterizedTypeImpl) gs).getActualTypeArguments()[1];
} else {
gType = void.class;
}
Entry<String, JSonNode> next;
for (final Iterator<Entry<String, JSonNode>> it = obj.entrySet().iterator(); it.hasNext();) {
next = it.next();
inst.put(next.getKey(), this.jsonToObject(next.getValue(), gType));
}
return inst;
} else if (clazz.isArray()) {
final JSonArray obj = (JSonArray) json;
final Object arr = Array.newInstance(clazz.getComponentType(), obj.size());
for (int i = 0; i < obj.size(); i++) {
final Object v = this.jsonToObject(obj.get(i), clazz.getComponentType());
Array.set(arr, i, v);
}
return arr;
} else {
if (json instanceof JSonArray) {
final ArrayList<Object> inst = new ArrayList<Object>();
final JSonArray obj = (JSonArray) json;
final Type gs = clazz.getGenericSuperclass();
final Type gType;
if (gs instanceof ParameterizedTypeImpl) {
gType = ((ParameterizedTypeImpl) gs).getActualTypeArguments()[0];
} else {
gType = void.class;
}
for (final JSonNode n : obj) {
inst.add(this.jsonToObject(n, gType));
}
return inst;
} else {
final JSonObject obj = (JSonObject) json;
cc = ClassCache.getClassCache(clazz);
final Object inst = cc.getInstance();
JSonNode value;
Object v;
for (final Setter s : cc.getSetter()) {
value = obj.get(s.getKey());
if (value == null) {
continue;
}
v = this.jsonToObject(value, s.getType());
try {
s.setValue(inst, v);
} catch (final IllegalArgumentException e) {
if (this.isIgnoreIllegalArgumentMappings()) {
continue;
} else if (v == null && this.isIgnorePrimitiveNullMapping()) {
continue;
}
throw e;
}
}
return inst;
}
}
} else if (type instanceof ParameterizedTypeImpl) {
final ParameterizedTypeImpl pType = (ParameterizedTypeImpl) type;
if (List.class.isAssignableFrom(pType.getRawType())) {
final List<Object> inst = (List<Object>) pType.getRawType().newInstance();
final JSonArray obj = (JSonArray) json;
for (final JSonNode n : obj) {
inst.add(this.jsonToObject(n, pType.getActualTypeArguments()[0]));
}
return inst;
} else if (Map.class.isAssignableFrom(pType.getRawType())) {
final Map<String, Object> inst = (Map<String, Object>) pType.getRawType().newInstance();
final JSonObject obj = (JSonObject) json;
Entry<String, JSonNode> next;
for (final Iterator<Entry<String, JSonNode>> it = obj.entrySet().iterator(); it.hasNext();) {
next = it.next();
inst.put(next.getKey(), this.jsonToObject(next.getValue(), pType.getActualTypeArguments()[1]));
}
return inst;
} else {
System.err.println("TYPE?!");
}
} else {
System.err.println("TYPE?!");
}
} catch (final SecurityException e) {
e.printStackTrace();
} catch (final NoSuchMethodException e) {
e.printStackTrace();
} catch (final IllegalArgumentException e) {
e.printStackTrace();
} catch (final InstantiationException e) {
e.printStackTrace();
} catch (final IllegalAccessException e) {
e.printStackTrace();
} catch (final InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
/**
* @param <T>
* @param json
* @param typeRef
*/
@SuppressWarnings("unchecked")
public <T> T jsonToObject(final JSonNode json, final TypeRef<T> type) {
return (T) this.jsonToObject(json, type.getType());
}
public void setIgnoreIllegalArgumentMappings(final boolean ignoreIllegalArgumentMappings) {
this.ignoreIllegalArgumentMappings = ignoreIllegalArgumentMappings;
}
public void setIgnoreIllegalEnumMappings(final boolean ignoreIllegalEnumMappings) {
this.ignoreIllegalEnumMappings = ignoreIllegalEnumMappings;
}
public void setIgnorePrimitiveNullMapping(final boolean ignoreIllegalNullArguments) {
this.ignorePrimitiveNullMapping = ignoreIllegalNullArguments;
}
}