package no.ntnu.fp.model;
import java.util.List;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import nu.xom.Attribute;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Elements;
import nu.xom.ParsingException;
import nu.xom.ValidityException;
public class GenericXmlSerializer {
private static String TYPE_ITERABLE = "iterable";
private static String TYPE_OBJECT = "object";
private static String TYPE_FIELD = "field";
private static String TYPE_REF = "ref";
private static final HashSet <Class<?>> WRAPPER_TYPES = getWrapperTypes();
public static void main(String[] args) throws ValidityException, ParsingException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
User u1 = new User("Bjorn", "test", 20, 124124, "test@test.com");
User u2 = new User("Bjorn", "test", 20, 124124, "test@test.com");
User u3 = new User("Bjorn", "test", 20, 124124, "test@test.com");
User u4 = new User("Bjorn", "test", 20, 124124, "test@test.com");
List<User> users = new ArrayList<User>();
users.add(u1);
users.add(u2);
users.add(u3);
users.add(u4);
String test = toXmlSimple(users);
ArrayList<User> usersReceive = (ArrayList<User>)fromXml(test);
for(User u: usersReceive) {
System.out.println(u);
}
}
public static boolean isWrapperType(Class<?> clazz){
return WRAPPER_TYPES.contains(clazz);
}
private static HashSet<Class <?>> getWrapperTypes(){
HashSet<Class<?>> ret = new HashSet<Class<?>>();
ret.add(Boolean.class);
ret.add(Character.class);
ret.add(Byte.class);
ret.add(Short.class);
ret.add(Integer.class);
ret.add(Long.class);
ret.add(Float.class);
ret.add(Double.class);
ret.add(Void.class);
ret.add(String.class);
ret.add(ArrayList.class);
return ret;
}
/**
* @param obj the type to check.
*
* @return Returns <code>true</code> if <code>type</code> is a iterable type, <code>false</code> otherwise.
*/
public static boolean isIterable(Object obj) {
if ( obj instanceof Class && isIterableClass( ( Class ) obj ) ) {
return true;
}
if ( obj instanceof ParameterizedType ) {
return isIterable( ( ( ParameterizedType ) obj ).getRawType() );
}
if ( obj instanceof WildcardType ) {
Type[] upperBounds = ( ( WildcardType ) obj ).getUpperBounds();
return upperBounds.length != 0 && isIterable( upperBounds[0] );
}
return false;
}
/**
* Checks whether the specified class parameter is an instance of a collection class.
*
* @param clazz <code>Class</code> to check.
*
* @return <code>true</code> is <code>clazz</code> is instance of a collection class, <code>false</code> otherwise.
*/
private static boolean isIterableClass(Class<?> clazz) {
ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
computeClassHierarchy( clazz, classes );
return classes.contains( Iterable.class );
}
/**
* Get all superclasses and interfaces recursively.
*
* @param clazz The class to start the search with.
* @param classes List of classes to which to add all found super classes and interfaces.
*/
private static void computeClassHierarchy(Class<?> clazz, ArrayList<Class<?>> classes) {
for ( Class current = clazz; current != null; current = current.getSuperclass() ) {
if ( classes.contains( current ) ) {
return;
}
classes.add( current );
for ( Class currentInterface : current.getInterfaces() ) {
computeClassHierarchy( currentInterface, classes );
}
}
}
public static String toXmlSimple(Object obj) throws IllegalArgumentException, IllegalAccessException{
Class <? extends Object> clazz = obj.getClass();
return toXmlSimple(obj, true).toXML();
}
public static Element toXmlSimple(Object obj, boolean signature) throws IllegalArgumentException, IllegalAccessException{
Class <? extends Object> clazz = obj.getClass();
// Create root
Element root = new Element(clazz.getName());
if (obj instanceof Iterable) {
// List<Model>
Attribute attr = new Attribute("type", TYPE_ITERABLE);
root.addAttribute(attr);
for (Object o : (Iterable) obj) {
root.appendChild(toXmlSimple(o, true));
}
} else {
// Model
Attribute attr = new Attribute("type", TYPE_OBJECT);
root.addAttribute(attr);
for(Field field: clazz.getDeclaredFields()){
if(!field.isAccessible() && !Modifier.isStatic(field.getModifiers())) {
Object value = getFieldValue(obj, field);
if(value != null) {
Element elem = null;
if(value instanceof Iterable) {
elem = toXmlSimple(value, true);
root.appendChild(elem);
} else if (value instanceof Model){
elem = toXmlSimple(value, true);
elem.addAttribute(new Attribute("id", ((Model)value).getId()));
root.appendChild(elem);
} else if(isWrapperType(value.getClass())){
elem = createFieldElement(field, value);
root.appendChild(elem);
}
}
}
}
}
return root;
}
private static Element createFieldElement(Field field, Object value) {
Element elem;
elem = new Element(field.getName());
elem.addAttribute(new Attribute("type", TYPE_FIELD));
elem.appendChild(value.toString());
return elem;
}
private static Object getFieldValue(Object obj, Field field)
throws IllegalAccessException {
field.setAccessible(true);
Object value = field.get(obj);
field.setAccessible(false);
return value;
}
public static String toXmlDeep(Object obj) {
return null;
}
public static Object fromXml(String xml) throws ValidityException, ParsingException, IOException, IllegalArgumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
Builder builder = new Builder();
Document doc = builder.build(xml, null);
Element root = doc.getRootElement();
Map<String, Object> objects = new HashMap<String, Object>();
return fromXml(root, objects);
}
public static Object fromXml(Element root, Map<String, Object> objects) throws ValidityException, ParsingException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String typeName = root.getQualifiedName();
Class <? extends Object> clazz = Class.forName(typeName);
Object object = clazz.newInstance();
String id = root.getAttributeValue("id");
String key = typeName + id;
objects.put(typeName, object);
Elements elements = root.getChildElements();
for(int i=0; i<elements.size(); i++) {
Element element = elements.get(i);
String type = element.getAttributeValue("type");
if (type.equals(TYPE_ITERABLE)) {
Elements elems = element.getChildElements();
for (int j=0; j<elems.size(); j++) {
Element elem = elems.get(j);
}
} else if (type.equals(TYPE_OBJECT)) {
Object value = fromXml(element, objects);
String[] parts = element.getQualifiedName().split("\\.");
String methodName = parts[parts.length-1];
Method method = findAddMethod(clazz, methodName);
method.invoke(object, value);
} else if (type.equals(TYPE_FIELD)) {
String methodName = element.getQualifiedName();
methodName = Character.toUpperCase(methodName.charAt(0)) + methodName.substring(1);
Method method = findSetMethod(clazz, methodName);
String value = element.getValue();
System.out.println(methodName);
method.invoke(object, value);
} else if (type.equals(TYPE_REF)) {
id = element.getAttributeValue("id");
String[] parts = element.getQualifiedName().split("\\.");
String methodName = parts[parts.length-1];
Object value = objects.get(methodName + id);
Method method = findAddMethod(clazz, methodName);
method.invoke(object, value);
}
}
return object;
}
private static String parseValue(Object value) {
return String.format("%s", value);
}
private static Method findSetMethod(Class<? extends Object> clazz, String fieldName) {
Method [] methods = clazz.getMethods();
Object obj = new String();
for(Method method: methods) {
if (method.getName().equals("set" + fieldName)) {
if (method.getParameterTypes()[0].isInstance(obj)) {
return method;
}
}
}
return null;
}
private static Method findAddMethod(Class<? extends Object> clazz, String fieldName) {
Method [] methods = clazz.getMethods();
for(Method method: methods) {
if (method.getName().equals("add" + fieldName)) {
return method;
}
}
return null;
}
}