/**
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.xml.serialize;
import org.exoplatform.services.common.ThreadSoftRef;
import org.exoplatform.services.token.attribute.Attribute;
import org.exoplatform.services.token.attribute.AttributeParser;
import org.exoplatform.services.token.attribute.Attributes;
import org.exoplatform.services.xml.parser.XMLDocument;
import org.exoplatform.services.xml.parser.XMLNode;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Author : Nhu Dinh Thuan
* Email:nhudinhthuan@yahoo.com
* Apr 13, 2007
*/
public class XML2Bean implements BeanMapper
{
static ThreadSoftRef<XML2Bean> MAPPER = new ThreadSoftRef<XML2Bean>(XML2Bean.class);
public final static XML2Bean getInstance()
{
return MAPPER.getRef();
}
public <T> T toBean(Class<T> clazz, XMLDocument document) throws Exception
{
NodeMap map = clazz.getAnnotation(NodeMap.class);
if (map == null)
return null;
String name = map.value();
T object = clazz.newInstance();
XMLNode node = searchNode(document.getRoot(), name);
if (node == null)
return null;
toBean(clazz, object, node);
return object;
}
private XMLNode searchNode(XMLNode node, String name)
{
if (node.isNode(name))
return node;
List<XMLNode> children = node.getChildren();
if (children == null)
return null;
for (XMLNode ele : children)
{
XMLNode value = searchNode(ele, name);
if (value != null)
return value;
}
return null;
}
public <T> T toBean(Class<T> clazz, XMLNode node) throws Exception
{
T object = clazz.newInstance();
toBean(clazz, object, node);
return object;
}
public <T> void toBean(Class<T> clazz, T object, XMLNode node) throws Exception
{
if (clazz != object.getClass())
throw new Exception("Incompatipable type for object and class");
NodeMap map = clazz.getAnnotation(NodeMap.class);
if (map == null)
return;
toXMLValue(clazz, object, node);
}
private void toXMLValue(Class<?> clazz, Object bean, XMLNode node) throws Exception
{
Attributes attrs = AttributeParser.getAttributes(node);
for (Attribute attr : attrs)
{
Field field = getField(attr.getName(), clazz);
if (field == null)
continue;
Class<?> type = field.getType();
if (type.isPrimitive() || XMLSerialize.REFLECT_UTIL.getRef().isPrimitiveType(type))
{
Object data = toValue(type, attr.getValue());
putField(bean, field, data);
}
}
List<XMLNode> children = node.getChildren();
if (children == null)
return;
for (XMLNode ele : children)
{
if (node.getChildren() == null || node.getChildren().size() < 1)
return;
Field field = getField(ele.getName(), clazz);
if (field == null)
continue;
Object data = toValue(bean, field, ele);
if (data == null)
continue;
putField(bean, field, data);
}
}
private void putField(Object bean, Field field, Object data) throws Exception
{
try
{
Method method = XMLSerialize.REFLECT_UTIL.getRef().getSetterMethod(bean.getClass(), field);
method.setAccessible(true);
method.invoke(bean, new Object[]{data});
}
catch (Exception e)
{
}
field.setAccessible(true);
field.set(bean, data);
}
@SuppressWarnings("unchecked")
private Object toValue(Object bean, Field field, XMLNode node) throws Exception
{
Class<?> type = field.getType();
field.setAccessible(true);
Object current = field.get(bean);
if (current != null)
type = current.getClass();
if (Collection.class.isAssignableFrom(type))
{
Type eleParamType = Object.class;
ParameterizedType paramType = (ParameterizedType)field.getGenericType();
if (paramType.getActualTypeArguments().length > 0)
{
eleParamType = paramType.getActualTypeArguments()[0];
}
Object[] array = (Object[])toArrayValues((Class<?>)eleParamType, node);
Collection collection = null;
if (current != null && current instanceof Collection)
{
collection = (Collection)current;
}
else if (type.isInterface())
{
collection = new ArrayList<Object>();
}
else
{
collection = (Collection)type.newInstance();
}
Collections.addAll(collection, array);
return collection;
}
if (type.isArray())
return toArrayValues(type, node);
return toValue(type, node);
}
private Object toArrayValues(Class<?> type, XMLNode node) throws Exception
{
List<XMLNode> children = node.getChildren();
if (children == null)
return new Object[]{};
Class<?> elementType = type;
if (type.isArray())
elementType = type.getComponentType();
Object array = Array.newInstance(elementType, children.size());
for (int i = 0; i < children.size(); i++)
{
if (children.get(i).getChildren() == null || children.get(i).getChildren().size() < 1)
{
Array.set(array, i, null);
continue;
}
XMLNode elementNode = children.get(i);
if (elementType.isPrimitive() || XMLSerialize.REFLECT_UTIL.getRef().isPrimitiveType(elementType))
{
Array.set(array, i, toValue(elementType, new String(elementNode.getChild(0).getValue())));
}
else
{
Array.set(array, i, toValue(elementType, elementNode));
}
}
return array;
}
private Object toValue(Class<?> type, XMLNode node) throws Exception
{
if (type.isPrimitive() || XMLSerialize.REFLECT_UTIL.getRef().isPrimitiveType(type))
{
String value = new String(node.getChild(0).getValue());
return toValue(type, value);
}
NodeMap valueMap = type.getAnnotation(NodeMap.class);
Object newBean = type.newInstance();
toXMLValue(type, newBean, valueMap == null ? node : node.getChild(0));
return newBean;
}
private Object toValue(Class<?> type, String value)
{
if (type == String.class || value == null)
return value;
if (type == StringBuffer.class)
return new StringBuffer(value);
if (type == StringBuilder.class)
return new StringBuilder(value);
value = value.trim();
if (type == char.class || type == Character.class)
{
if (value.length() < 1)
return new Character(' ');
return new Character(value.trim().charAt(0));
}
if (type == byte.class || type == Byte.class)
return new Byte(value);
if (type == boolean.class || type == Boolean.class)
return new Boolean(value);
if (type == short.class || type == Short.class)
return new Short(value);
if (type == int.class || type == Integer.class)
return new Integer(value);
if (type == long.class || type == Long.class)
return new Long(value);
if (type == float.class || type == Float.class)
return new Float(value);
if (type == double.class || type == Double.class)
return new Double(value);
return value;
}
private Field getField(String name, Class<?> clazz) throws Exception
{
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields)
{
NodeMap map = field.getAnnotation(NodeMap.class);
if (map != null && name.equals(map.value()))
return field;
NodesMap maps = field.getAnnotation(NodesMap.class);
if (maps != null && name.equals(maps.value()))
return field;
}
if (clazz.getSuperclass() == null)
return null;
return getField(name, clazz.getSuperclass());
}
}