/**
* 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.TypeToken;
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.util.Collection;
import java.util.List;
/**
* Author : Nhu Dinh Thuan
* Email:nhudinhthuan@yahoo.com
* Apr 9, 2007
*/
public class Bean2XML implements XMLMapper
{
static ThreadSoftRef<Bean2XML> MAPPER = new ThreadSoftRef<Bean2XML>(Bean2XML.class);
public final static Bean2XML getInstance()
{
return MAPPER.getRef();
}
public XMLDocument toXMLDocument(Object bean) throws Exception
{
NodeMap map = bean.getClass().getAnnotation(NodeMap.class);
if (map == null)
return null;
XMLNode node = new XMLNode(map.value().toCharArray(), map.value(), TypeToken.TAG);
toXML(bean, node);
return new XMLDocument(node);
}
public void toXML(Object bean, XMLNode node) throws Exception
{
NodeMap map = bean.getClass().getAnnotation(NodeMap.class);
if (map == null)
return;
toXMLValue(bean.getClass(), map.depth(), bean, node);
}
private void toXMLValue(Class<?> clazz, int depth, Object bean, XMLNode node) throws Exception
{
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields)
{
if (field.getType().isArray())
{
toArray(field, bean, node);
}
else if (Collection.class.isAssignableFrom(field.getType()))
{
toCollection(field, bean, node);
}
else
{
toField(field, bean, node);
}
}
depth--;
if (depth <= 0 || clazz.getSuperclass() == null)
return;
toXMLValue(clazz.getSuperclass(), depth, bean, node);
}
protected void toField(Field field, Object bean, XMLNode node) throws Exception
{
if (bean == null)
return;
NodeMap map = field.getAnnotation(NodeMap.class);
if (map == null)
return;
String name = map.value();
boolean isAttribute = map.attribute();
Class<?> type = field.getType();
Object value = null;
field.setAccessible(true);
if (type.isPrimitive())
{
try
{
value = getValue(type, field, bean);
}
catch (Exception e)
{
}
if (isAttribute)
{
setAttribute(node, name, (String)value);
}
else
{
setChild(node, name, (String)value);
}
return;
}
value = getValue(field, bean);
if (XMLSerialize.REFLECT_UTIL.getRef().isPrimitiveType(type))
{
if (value == null)
value = "";
if (isAttribute)
{
setAttribute(node, name, value.toString());
}
else
{
setChild(node, name, value.toString());
}
return;
}
XMLMapper mapper = XMLSerialize.getInstance().getXMLMapper(type);
if (mapper == null)
{
if (value == null)
value = "";
if (isAttribute)
{
setAttribute(node, name, value.toString());
}
else
{
setChild(node, name, value.toString());
}
return;
}
XMLNode child = new XMLNode(name.toCharArray(), name, TypeToken.TAG);
node.getChildren().add(child);
NodeMap valueMap = value.getClass().getAnnotation(NodeMap.class);
if (valueMap == null)
{
toXMLValue(value.getClass(), 1, value, child);
return;
}
XMLNode valueNode = new XMLNode(valueMap.value().toCharArray(), valueMap.value(), TypeToken.TAG);
child.addChild(valueNode);
toXMLValue(value.getClass(), map.depth(), value, valueNode);
}
private void toCollection(Field field, Object bean, XMLNode parent)
{
NodesMap map = field.getAnnotation(NodesMap.class);
if (map == null)
return;
field.setAccessible(true);
String name = map.value();
XMLNode node = new XMLNode(name.toCharArray(), name, TypeToken.TAG);
parent.getChildren().add(node);
if (bean == null)
return;
Object values = getValue(field, bean);
Collection<?> collection = (Collection<?>)values;
Object[] array = new Object[collection.size()];
collection.toArray(array);
toArray(node, array, map);
}
private void toArray(Field field, Object bean, XMLNode parent)
{
NodesMap map = field.getAnnotation(NodesMap.class);
if (map == null)
return;
field.setAccessible(true);
String name = map.value();
XMLNode node = new XMLNode(name.toCharArray(), name, TypeToken.TAG);
parent.getChildren().add(node);
if (bean == null)
return;
Object values = getValue(field, bean);
toArray(node, values, map);
}
private void toArray(XMLNode node, Object values, NodesMap map)
{
if (values == null)
return;
int length = Array.getLength(values);
Class<?> type;
for (int i = 0; i < length; i++)
{
String itemName = map.item();
Object value = Array.get(values, i);
if (value == null)
continue;
type = value.getClass();
XMLNode child = new XMLNode(itemName.toCharArray(), itemName, TypeToken.TAG);
node.getChildren().add(child);
if (type.isPrimitive() || XMLSerialize.REFLECT_UTIL.getRef().isPrimitiveType(type))
{
child.addChild(new XMLNode(value.toString().toCharArray(), null, TypeToken.CONTENT));
continue;
}
XMLMapper mapper = XMLSerialize.getInstance().getXMLMapper(value.getClass());
if (mapper == null)
{
child.addChild(new XMLNode(value.toString().toCharArray(), null, TypeToken.CONTENT));
continue;
}
try
{
NodeMap valueMap = value.getClass().getAnnotation(NodeMap.class);
if (valueMap == null)
{
mapper.toXML(value, child);
return;
}
XMLNode valueNode = new XMLNode(valueMap.value().toCharArray(), valueMap.value(), TypeToken.TAG);
child.addChild(valueNode);
mapper.toXML(value, valueNode);
}
catch (Exception e)
{
}
}
}
private Object getValue(Field field, Object bean)
{
Object value = null;
try
{
Method method = XMLSerialize.REFLECT_UTIL.getRef().getGetterMethod(bean.getClass(), field);
if (method != null)
value = method.invoke(bean);
}
catch (Exception e)
{
}
if (value != null)
return value;
try
{
value = field.get(bean);
}
catch (Exception e)
{
value = null;
}
return value;
}
private String getValue(Class<?> type, Field field, Object bean) throws Exception
{
try
{
Method method = XMLSerialize.REFLECT_UTIL.getRef().getGetterMethod(bean.getClass(), field);
if (method != null)
return String.valueOf(method.invoke(bean));
}
catch (Exception e)
{
}
if (type == boolean.class)
return String.valueOf(field.getBoolean(bean));
if (type == byte.class)
return String.valueOf(field.getByte(bean));
if (type == char.class)
return String.valueOf(field.getChar(bean));
if (type == short.class)
return String.valueOf(field.getShort(bean));
if (type == int.class)
return String.valueOf(field.getInt(bean));
if (type == long.class)
return String.valueOf(field.getLong(bean));
if (type == float.class)
return String.valueOf(field.getFloat(bean));
if (type == double.class)
return String.valueOf(field.getDouble(bean));
return "";
}
private void setChild(XMLNode parent, String name, String value)
{
List<XMLNode> children = parent.getChildren();
for (XMLNode ele : children)
{
if (ele.getName().equals(name))
{
ele.addChild(new XMLNode(value.toCharArray(), null, TypeToken.CONTENT));
return;
}
}
XMLNode node = new XMLNode(name.toCharArray(), name, TypeToken.TAG);
node.addChild(new XMLNode(value.toCharArray(), null, TypeToken.CONTENT));
children.add(node);
}
private void setAttribute(XMLNode parent, String name, String value)
{
Attributes attrs = AttributeParser.getAttributes(parent);
for (Attribute ele : attrs)
{
if (ele.getName().equals(name))
{
ele.setValue(value);
return;
}
}
Attribute attr = new Attribute(name, value);
attrs.set(attr);
}
}