/*
* 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.xml.object;
import org.exoplatform.commons.utils.ClassLoading;
import org.exoplatform.commons.utils.SecurityHelper;
import org.exoplatform.container.xml.Configuration;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IMarshallingContext;
import org.jibx.runtime.IUnmarshallingContext;
import org.jibx.runtime.JiBXException;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @author Tuan Nguyen (tuan08@users.sourceforge.net)
* @since Apr 10, 2005
* @version $Id: XMLObject.java 11659 2007-01-05 15:35:06Z geaz $
*/
public class XMLObject
{
/**
* The logger
*/
private static final Log LOG = ExoLogger.getLogger("exo.kernel.container.XMLObject");
public static String CURRENT_VERSION = "1.0";
static Map<Class<?>, Map<String, Field>> cacheFields_ = new HashMap<Class<?>, Map<String, Field>>();
private Map<String, XMLField> fields_ = new HashMap<String, XMLField>();
private String type;
public XMLObject()
{
}
public XMLObject(Object obj) throws Exception
{
Class<?> clazz = obj.getClass();
Map<String, Field> fields = getFields(clazz);
setType(obj.getClass().getName());
Iterator<Field> i = fields.values().iterator();
while (i.hasNext())
{
Field field = i.next();
Object value = field.get(obj);
if (value == null
|| (!value.getClass().isPrimitive() && Configuration.hasComponent(field.getType().getName())))
{
// The current field is a component so we ignore it or its value is null
continue;
}
addField(new XMLField(field.getName(), field.getType(), value));
}
}
public String getType()
{
return type;
}
public void setType(String s)
{
type = s;
}
public XMLField getField(String name)
{
return fields_.get(name);
}
public void addField(Object o)
{
XMLField field = (XMLField)o;
fields_.put(field.getName(), field);
}
public void addField(XMLField field)
{
fields_.put(field.getName(), field);
}
public Iterator<XMLField> getFieldIterator()
{
return fields_.values().iterator();
}
public Collection<XMLField> getFields()
{
return fields_.values();
}
public void setFields(Collection<XMLField> fields)
{
Iterator<XMLField> i = fields.iterator();
while (i.hasNext())
{
XMLField field = i.next();
fields_.put(field.getName(), field);
}
}
public void setFields(Map<String, XMLField> fields)
{
fields_.putAll(fields);
}
public Object getFieldValue(String fieldName) throws Exception
{
XMLField field = fields_.get(fieldName);
if (field != null)
return field.getObjectValue();
return null;
}
public void renameField(String oldName, String newName)
{
XMLField field = fields_.remove(oldName);
field.setName(newName);
fields_.put(newName, field);
}
public void removeField(String name)
{
fields_.remove(name);
}
public void addField(String name, Class<?> fieldType, Object obj) throws Exception
{
addField(new XMLField(name, fieldType, obj));
}
@Override
public String toString()
{
StringBuffer b = new StringBuffer();
b.append("type: ").append(type).append("\n");
Iterator<XMLField> i = fields_.values().iterator();
while (i.hasNext())
{
XMLField field = i.next();
b.append(field.toString()).append("\n");
}
return b.toString();
}
public Object toObject() throws Exception
{
Class<?> clazz = ClassLoading.forName(type, this);
Map<String, Field> fields = getFields(clazz);
Object instance = clazz.newInstance();
Iterator<XMLField> i = fields_.values().iterator();
while (i.hasNext())
{
XMLField xmlfield = i.next();
try
{
Object value = xmlfield.getObjectValue();
Field field = fields.get(xmlfield.getName());
field.set(instance, value);
}
catch (Exception ex)
{
LOG.error("ERROR: Cannot set field: " + xmlfield.getName() + " of " + type, ex);
throw ex;
}
}
return instance;
}
public String toXML() throws Exception
{
return toXML("UTF-8");
}
public String toXML(String encoding) throws Exception
{
return new String(toByteArray(encoding), encoding);
}
public byte[] toByteArray() throws Exception
{
return toByteArray("UTF-8");
}
public byte[] toByteArray(String encoding) throws Exception
{
IBindingFactory bfact = getBindingFactoryInPriviledgedMode(XMLObject.class);
IMarshallingContext mctx = bfact.createMarshallingContext();
mctx.setIndent(2);
ByteArrayOutputStream os = new ByteArrayOutputStream();
mctx.marshalDocument(this, encoding, null, os);
return os.toByteArray();
}
public static XMLObject getXMLObject(InputStream is) throws Exception
{
IBindingFactory bfact = getBindingFactoryInPriviledgedMode(XMLObject.class);
IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
XMLObject xmlobject = (XMLObject)uctx.unmarshalDocument(is, "UTF-8");
return xmlobject;
}
public static Object getObject(InputStream is) throws Exception
{
return getXMLObject(is).toObject();
}
static Map<String, Field> getFields(Class<?> clazz)
{
Map<String, Field> fields = (Map<String, Field>)cacheFields_.get(clazz);
if (fields != null)
return fields;
synchronized (cacheFields_)
{
fields = new HashMap<String, Field>();
findFields(fields, clazz);
cacheFields_.put(clazz, fields);
}
return fields;
}
static void findFields(Map<String, Field> fields, Class<?> clazz)
{
if (clazz.getName().startsWith("java.lang"))
return;
findFields(fields, clazz.getSuperclass());
Field[] field = clazz.getDeclaredFields();
for (int i = 0; i < field.length; i++)
{
int modifier = field[i].getModifiers();
if (Modifier.isStatic(modifier) || Modifier.isTransient(modifier))
continue;
final Field fld = field[i];
SecurityHelper.doPrivilegedAction(new PrivilegedAction<Void>()
{
public Void run()
{
fld.setAccessible(true);
return null;
}
});
fields.put(field[i].getName(), field[i]);
}
}
protected static IBindingFactory getBindingFactoryInPriviledgedMode(final Class<?> clazz) throws JiBXException
{
try
{
return SecurityHelper.doPrivilegedExceptionAction(new PrivilegedExceptionAction<IBindingFactory>()
{
public IBindingFactory run() throws Exception
{
return BindingDirectory.getFactory(clazz);
}
});
}
catch (PrivilegedActionException pae)
{
Throwable cause = pae.getCause();
if (cause instanceof JiBXException)
{
throw (JiBXException)cause;
}
else if (cause instanceof RuntimeException)
{
throw (RuntimeException)cause;
}
else
{
throw new RuntimeException(cause);
}
}
}
}