/*
* Copyright 2011 jmarsden.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package jsonij.json.marshal;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jsonij.json.JSON;
import jsonij.json.Value;
import jsonij.json.annotation.JSONEncoder;
import jsonij.json.marshal.JavaMarshaler.CycleDetector;
/**
*
* @author jmarsden
*/
public class JavaObjectMarshaler {
protected JavaMarshaler marshaler;
final protected Map<Class<?>, Inspector> inspectedClasses;
static JSONCodecStore codecStore;
public JavaObjectMarshaler(JavaMarshaler marshaler) {
this.marshaler = marshaler;
inspectedClasses = new HashMap<Class<?>, Inspector>();
}
public boolean isObjectType(Class<?> objectClass) {
// Find an object inspector
Inspector inspector = null;
synchronized (inspectedClasses) {
if (inspectedClasses.containsKey(objectClass)) {
inspector = inspectedClasses.get(objectClass);
} else {
inspector = new Inspector(objectClass);
inspector.inspect();
inspectedClasses.put(objectClass, inspector);
}
}
Inspector.InspectorProperty[] properties = inspector.getProperties();
if (properties != null) {
for (Inspector.InspectorProperty property : properties) {
if (property.isValidAccessor()) {
return true;
}
}
}
return false;
}
public Value marshalJavaObject(Object o, CycleDetector cycleDetector) {
Class<?> objectClass = o.getClass();
// Check for JSONEncoder Annotation
Method[] methods = objectClass.getDeclaredMethods();
for (Method method : methods) {
if (method.getAnnotation(JSONEncoder.class) != null) {
// TODO: Encode using method.
}
}
// Check for JSONCodec
if (codecStore != null && codecStore.hasCodec(objectClass)) {
JSONCodec codec = codecStore.getCodec(objectClass);
Value value = codec.encode(o);
return value;
}
// Find an object inspector
Inspector inspector = null;
synchronized (inspectedClasses) {
if (inspectedClasses.containsKey(objectClass)) {
inspector = inspectedClasses.get(objectClass);
} else {
inspector = new Inspector(o);
inspector.inspect();
inspectedClasses.put(objectClass, inspector);
}
}
// Inspect Marshal the JSON Object
JSON.Object<JSON.String, Value> marshaledObject = new JSON.Object<JSON.String, Value>();
Inspector.InspectorProperty[] properties = inspector.getProperties();
String name = null;
Value value = null;
for (Inspector.InspectorProperty property : properties) {
if (property.isIgnore() || !property.isValidAccessor()) {
continue;
}
name = property.getName();
if (property.getAccessType() == Inspector.InspectorProperty.TYPE.METHOD) {
String accessorName = property.getAccessName();
try {
Method method = objectClass.getMethod(accessorName);
value = marshalObjectMethodValue(method, o, cycleDetector);
if(value == null) {
continue;
}
}
catch (Exception ex) {
value = new JSON.String(ex.toString());
}
} else if (property.getAccessType() == Inspector.InspectorProperty.TYPE.FIELD) {
String accessorName = property.getAccessName();
try {
Field field = objectClass.getField(accessorName);
value = marshalObjectFieldValue(field, o, cycleDetector);
if(value == null) {
continue;
}
}
catch (Exception ex) {
value = new JSON.String(ex.toString());
}
} else {
value = JSON.NULL;
}
marshaledObject.put(new JSON.String(name), value);
}
// Test if super classes are List
boolean isList = false;
Class<?>[] interfaces = null;
Class<?> parent = o.getClass().getSuperclass();
if (parent != null) {
do {
interfaces = parent.getInterfaces();
for (int i = 0; i < Array.getLength(interfaces); i++) {
if (interfaces[i] == List.class) {
isList = true;
break;
}
}
if(isList == true) {
break;
}
} while (( parent = parent.getSuperclass()) != null);
}
if(isList == true) {
if(marshaledObject.size() > 0) {
marshaledObject.put(new JSON.String("_entries"), marshaler.marshalJavaList(o, cycleDetector));
} else {
return marshaler.marshalJavaList(o, cycleDetector);
}
}
return marshaledObject;
}
protected Value marshalObjectMethodValue(Method method, Object o, CycleDetector cycleDetector) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Value value = null;
Object marshaledObject = method.invoke(o);
if (marshaledObject == null) {
value = null;
} else {
int hashCode = marshaledObject.hashCode();
if(!cycleDetector.hashDetected(hashCode)) {
cycleDetector.addHash(hashCode);
value = marshaler.marshalAnyObject(marshaledObject, cycleDetector.cloneCycleDetector());
} else {
value = null;
}
}
return value;
}
protected Value marshalObjectFieldValue(Field field, Object o, CycleDetector cycleDetector) throws IllegalArgumentException, IllegalAccessException {
Value value = null;
Object marshaledObject = field.get(o);
if (marshaledObject == null) {
value = null;
} else {
int hashCode = marshaledObject.hashCode();
if(!cycleDetector.hashDetected(hashCode)) {
cycleDetector.addHash(hashCode);
value = marshaler.marshalAnyObject(marshaledObject, cycleDetector.cloneCycleDetector());
} else {
value = null;
}
}
return value;
}
public static boolean hasCodec(Class codecType) {
if (codecStore == null) {
return false;
} else {
return codecStore.hasCodec(codecType);
}
}
public static void registerCodec(Class<?> codecClass) {
if (codecStore == null) {
codecStore = new JSONCodecStore();
}
codecStore.registerCodec(codecClass);
}
public static JSONCodec<?> getCodec(Class codecType) {
if (codecStore == null) {
return null;
} else {
return codecStore.getCodec(codecType);
}
}
}