All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.feilong.lib.xstream.converters.reflection.SerializableConverter Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.0.8
Show newest version
/*
 * Copyright (C) 2004, 2005, 2006 Joe Walnes.
 * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2014, 2015, 2016 XStream Committers.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 * 
 * Created on 21. December 2004 by Joe Walnes
 */
package com.feilong.lib.xstream.converters.reflection;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputValidation;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.feilong.lib.xstream.converters.ConversionException;
import com.feilong.lib.xstream.converters.MarshallingContext;
import com.feilong.lib.xstream.converters.UnmarshallingContext;
import com.feilong.lib.xstream.core.ClassLoaderReference;
import com.feilong.lib.xstream.core.JVM;
import com.feilong.lib.xstream.core.util.CustomObjectInputStream;
import com.feilong.lib.xstream.core.util.CustomObjectOutputStream;
import com.feilong.lib.xstream.core.util.Fields;
import com.feilong.lib.xstream.core.util.HierarchicalStreams;
import com.feilong.lib.xstream.io.ExtendedHierarchicalStreamWriterHelper;
import com.feilong.lib.xstream.io.HierarchicalStreamReader;
import com.feilong.lib.xstream.io.HierarchicalStreamWriter;
import com.feilong.lib.xstream.io.StreamException;
import com.feilong.lib.xstream.mapper.Mapper;

/**
 * Emulates the mechanism used by standard Java Serialization for classes that implement java.io.Serializable AND
 * implement or inherit a custom readObject()/writeObject() method.
 *
 * 

Supported features of serialization

*
    *
  • readObject(), writeObject()
  • *
  • class inheritance
  • *
  • readResolve(), writeReplace()
  • *
  • getFields(), putFields(), writeFields(), readFields()
  • *
  • ObjectStreamField[] serialPersistentFields
  • *
  • ObjectInputValidation
  • *
* * @author Joe Walnes * @author Jörg Schaible */ public class SerializableConverter extends AbstractReflectionConverter{ private static final String ELEMENT_NULL = "null"; private static final String ELEMENT_DEFAULT = "default"; private static final String ELEMENT_UNSERIALIZABLE_PARENTS = "unserializable-parents"; private static final String ATTRIBUTE_CLASS = "class"; private static final String ATTRIBUTE_SERIALIZATION = "serialization"; private static final String ATTRIBUTE_VALUE_CUSTOM = "custom"; private static final String ELEMENT_FIELDS = "fields"; private static final String ELEMENT_FIELD = "field"; private static final String ATTRIBUTE_NAME = "name"; private final ClassLoaderReference classLoaderReference; /** * Construct a SerializableConverter. * * @param mapper * the mapper chain instance * @param reflectionProvider * the reflection provider * @param classLoaderReference * the reference to the {@link ClassLoader} of the XStream instance * @since 1.4.5 */ public SerializableConverter(Mapper mapper, ReflectionProvider reflectionProvider, ClassLoaderReference classLoaderReference){ super(mapper, new UnserializableParentsReflectionProvider(reflectionProvider)); this.classLoaderReference = classLoaderReference; } /** * @deprecated As of 1.4.5 use {@link #SerializableConverter(Mapper, ReflectionProvider, ClassLoaderReference)} */ @Deprecated public SerializableConverter(Mapper mapper, ReflectionProvider reflectionProvider, ClassLoader classLoader){ this(mapper, reflectionProvider, new ClassLoaderReference(classLoader)); } /** * @deprecated As of 1.4 use {@link #SerializableConverter(Mapper, ReflectionProvider, ClassLoaderReference)} */ @Deprecated public SerializableConverter(Mapper mapper, ReflectionProvider reflectionProvider){ this(mapper, new UnserializableParentsReflectionProvider(reflectionProvider), new ClassLoaderReference(null)); } @Override public boolean canConvert(Class type){ return JVM.canCreateDerivedObjectOutputStream() && isSerializable(type); } private boolean isSerializable(Class type){ if (type != null && Serializable.class.isAssignableFrom(type) && !type.isInterface() && (serializationMembers.supportsReadObject(type, true) || serializationMembers.supportsWriteObject(type, true))){ for (Iterator iter = hierarchyFor(type).iterator(); iter.hasNext();){ if (!Serializable.class.isAssignableFrom((Class) iter.next())){ return canAccess(type); } } return true; } return false; } @Override public void doMarshal(final Object source,final HierarchicalStreamWriter writer,final MarshallingContext context){ String attributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_SERIALIZATION); if (attributeName != null){ writer.addAttribute(attributeName, ATTRIBUTE_VALUE_CUSTOM); } // this is an array as it's a non final value that's accessed from an anonymous inner class. final Class[] currentType = new Class[1]; final boolean[] writtenClassWrapper = { false }; CustomObjectOutputStream.StreamCallback callback = new CustomObjectOutputStream.StreamCallback(){ @Override public void writeToStream(Object object){ if (object == null){ writer.startNode(ELEMENT_NULL); writer.endNode(); }else{ ExtendedHierarchicalStreamWriterHelper.startNode(writer, mapper.serializedClass(object.getClass()), object.getClass()); context.convertAnother(object); writer.endNode(); } } @Override public void writeFieldsToStream(Map fields){ ObjectStreamClass objectStreamClass = ObjectStreamClass.lookup(currentType[0]); writer.startNode(ELEMENT_DEFAULT); for (Iterator iterator = fields.keySet().iterator(); iterator.hasNext();){ String name = (String) iterator.next(); if (!mapper.shouldSerializeMember(currentType[0], name)){ continue; } ObjectStreamField field = objectStreamClass.getField(name); Object value = fields.get(name); if (field == null){ throw new MissingFieldException(value.getClass().getName(), name); } if (value != null){ ExtendedHierarchicalStreamWriterHelper .startNode(writer, mapper.serializedMember(source.getClass(), name), value.getClass()); if (field.getType() != value.getClass() && !field.getType().isPrimitive()){ String attributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_CLASS); if (attributeName != null){ writer.addAttribute(attributeName, mapper.serializedClass(value.getClass())); } } context.convertAnother(value); writer.endNode(); } } writer.endNode(); } @Override public void defaultWriteObject(){ boolean writtenDefaultFields = false; ObjectStreamClass objectStreamClass = ObjectStreamClass.lookup(currentType[0]); if (objectStreamClass == null){ return; } ObjectStreamField[] fields = objectStreamClass.getFields(); for (ObjectStreamField field : fields){ Object value = readField(field, currentType[0], source); if (value != null){ if (!writtenClassWrapper[0]){ writer.startNode(mapper.serializedClass(currentType[0])); writtenClassWrapper[0] = true; } if (!writtenDefaultFields){ writer.startNode(ELEMENT_DEFAULT); writtenDefaultFields = true; } if (!mapper.shouldSerializeMember(currentType[0], field.getName())){ continue; } Class actualType = value.getClass(); ExtendedHierarchicalStreamWriterHelper .startNode(writer, mapper.serializedMember(source.getClass(), field.getName()), actualType); Class defaultType = mapper.defaultImplementationOf(field.getType()); if (!actualType.equals(defaultType)){ String attributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_CLASS); if (attributeName != null){ writer.addAttribute(attributeName, mapper.serializedClass(actualType)); } } context.convertAnother(value); writer.endNode(); } } if (writtenClassWrapper[0] && !writtenDefaultFields){ writer.startNode(ELEMENT_DEFAULT); writer.endNode(); }else if (writtenDefaultFields){ writer.endNode(); } } @Override public void flush(){ writer.flush(); } @Override public void close(){ throw new UnsupportedOperationException("Objects are not allowed to call ObjectOutputStream.close() from writeObject()"); } }; try{ boolean mustHandleUnserializableParent = false; Iterator classHieararchy = hierarchyFor(source.getClass()).iterator(); while (classHieararchy.hasNext()){ currentType[0] = (Class) classHieararchy.next(); if (!Serializable.class.isAssignableFrom(currentType[0])){ mustHandleUnserializableParent = true; continue; }else{ if (mustHandleUnserializableParent){ marshalUnserializableParent(writer, context, source); mustHandleUnserializableParent = false; } if (serializationMembers.supportsWriteObject(currentType[0], false)){ writtenClassWrapper[0] = true; writer.startNode(mapper.serializedClass(currentType[0])); if (currentType[0] != mapper.defaultImplementationOf(currentType[0])){ String classAttributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_CLASS); if (classAttributeName != null){ writer.addAttribute(classAttributeName, currentType[0].getName()); } } CustomObjectOutputStream objectOutputStream = CustomObjectOutputStream.getInstance(context, callback); serializationMembers.callWriteObject(currentType[0], source, objectOutputStream); objectOutputStream.popCallback(); writer.endNode(); }else if (serializationMembers.supportsReadObject(currentType[0], false)){ // Special case for objects that have readObject(), but not writeObject(). // The class wrapper is always written, whether or not this class in the hierarchy has // serializable fields. This guarantees that readObject() will be called upon deserialization. writtenClassWrapper[0] = true; writer.startNode(mapper.serializedClass(currentType[0])); if (currentType[0] != mapper.defaultImplementationOf(currentType[0])){ String classAttributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_CLASS); if (classAttributeName != null){ writer.addAttribute(classAttributeName, currentType[0].getName()); } } callback.defaultWriteObject(); writer.endNode(); }else{ writtenClassWrapper[0] = false; callback.defaultWriteObject(); if (writtenClassWrapper[0]){ writer.endNode(); } } } } }catch (IOException e){ throw new StreamException("Cannot write defaults", e); } } protected void marshalUnserializableParent( final HierarchicalStreamWriter writer, final MarshallingContext context, final Object replacedSource){ writer.startNode(ELEMENT_UNSERIALIZABLE_PARENTS); super.doMarshal(replacedSource, writer, context); writer.endNode(); } private Object readField(ObjectStreamField field,Class type,Object instance){ Field javaField = Fields.find(type, field.getName()); return Fields.read(javaField, instance); } protected List hierarchyFor(Class type){ List result = new ArrayList(); while (type != Object.class && type != null){ result.add(type); type = type.getSuperclass(); } // In Java Object Serialization, the classes are deserialized starting from parent class and moving down. Collections.reverse(result); return result; } @Override public Object doUnmarshal(final Object result,final HierarchicalStreamReader reader,final UnmarshallingContext context){ // this is an array as it's a non final value that's accessed from an anonymous inner class. final Class[] currentType = new Class[1]; String attributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_SERIALIZATION); if (attributeName != null && !ATTRIBUTE_VALUE_CUSTOM.equals(reader.getAttribute(attributeName))){ throw new ConversionException("Cannot deserialize object with new readObject()/writeObject() methods"); } CustomObjectInputStream.StreamCallback callback = new CustomObjectInputStream.StreamCallback(){ @Override public Object readFromStream(){ reader.moveDown(); Class type = HierarchicalStreams.readClassType(reader, mapper); Object value = context.convertAnother(result, type); reader.moveUp(); return value; } @Override public Map readFieldsFromStream(){ final Map fields = new HashMap(); reader.moveDown(); if (reader.getNodeName().equals(ELEMENT_FIELDS)){ // Maintain compatibility with XStream 1.1.0 while (reader.hasMoreChildren()){ reader.moveDown(); if (!reader.getNodeName().equals(ELEMENT_FIELD)){ throw new ConversionException("Expected <" + ELEMENT_FIELD + "/> element inside <" + ELEMENT_FIELD + "/>"); } String name = reader.getAttribute(ATTRIBUTE_NAME); Class type = mapper.realClass(reader.getAttribute(ATTRIBUTE_CLASS)); Object value = context.convertAnother(result, type); fields.put(name, value); reader.moveUp(); } }else if (reader.getNodeName().equals(ELEMENT_DEFAULT)){ // New format introduced in XStream 1.1.1 ObjectStreamClass objectStreamClass = ObjectStreamClass.lookup(currentType[0]); while (reader.hasMoreChildren()){ reader.moveDown(); String name = mapper.realMember(currentType[0], reader.getNodeName()); if (mapper.shouldSerializeMember(currentType[0], name)){ String classAttribute = HierarchicalStreams.readClassAttribute(reader, mapper); Class type; if (classAttribute != null){ type = mapper.realClass(classAttribute); }else{ ObjectStreamField field = objectStreamClass.getField(name); if (field == null){ throw new MissingFieldException(currentType[0].getName(), name); } type = field.getType(); } Object value = context.convertAnother(result, type); fields.put(name, value); } reader.moveUp(); } }else{ throw new ConversionException( "Expected <" + ELEMENT_FIELDS + "/> or <" + ELEMENT_DEFAULT + "/> element when calling ObjectInputStream.readFields()"); } reader.moveUp(); return fields; } @Override public void defaultReadObject(){ if (serializationMembers.getSerializablePersistentFields(currentType[0]) != null){ readFieldsFromStream(); return; } if (!reader.hasMoreChildren()){ return; } reader.moveDown(); if (!reader.getNodeName().equals(ELEMENT_DEFAULT)){ throw new ConversionException("Expected <" + ELEMENT_DEFAULT + "/> element in readObject() stream"); } while (reader.hasMoreChildren()){ reader.moveDown(); String fieldName = mapper.realMember(currentType[0], reader.getNodeName()); if (mapper.shouldSerializeMember(currentType[0], fieldName)){ String classAttribute = HierarchicalStreams.readClassAttribute(reader, mapper); final Class type; if (classAttribute != null){ type = mapper.realClass(classAttribute); }else{ type = mapper.defaultImplementationOf(reflectionProvider.getFieldType(result, fieldName, currentType[0])); } Object value = context.convertAnother(result, type); reflectionProvider.writeField(result, fieldName, value, currentType[0]); } reader.moveUp(); } reader.moveUp(); } @Override public void registerValidation(final ObjectInputValidation validation,int priority){ context.addCompletionCallback(() -> { try{ validation.validateObject(); }catch (InvalidObjectException e){ throw new ObjectAccessException("Cannot validate object", e); } }, priority); } @Override public void close(){ throw new UnsupportedOperationException("Objects are not allowed to call ObjectInputStream.close() from readObject()"); } }; while (reader.hasMoreChildren()){ reader.moveDown(); String nodeName = reader.getNodeName(); if (nodeName.equals(ELEMENT_UNSERIALIZABLE_PARENTS)){ super.doUnmarshal(result, reader, context); }else{ String classAttribute = HierarchicalStreams.readClassAttribute(reader, mapper); if (classAttribute == null){ currentType[0] = mapper.defaultImplementationOf(mapper.realClass(nodeName)); }else{ currentType[0] = mapper.realClass(classAttribute); } if (serializationMembers.supportsReadObject(currentType[0], false)){ CustomObjectInputStream objectInputStream = CustomObjectInputStream .getInstance(context, callback, classLoaderReference); serializationMembers.callReadObject(currentType[0], result, objectInputStream); objectInputStream.popCallback(); }else{ try{ callback.defaultReadObject(); }catch (IOException e){ throw new StreamException("Cannot read defaults", e); } } } reader.moveUp(); } return result; } protected void doMarshalConditionally(final Object source,final HierarchicalStreamWriter writer,final MarshallingContext context){ if (isSerializable(source.getClass())){ doMarshal(source, writer, context); }else{ super.doMarshal(source, writer, context); } } protected Object doUnmarshalConditionally(final Object result,final HierarchicalStreamReader reader,final UnmarshallingContext context){ return isSerializable(result.getClass()) ? doUnmarshal(result, reader, context) : super.doUnmarshal(result, reader, context); } private static class UnserializableParentsReflectionProvider extends ReflectionProviderWrapper{ public UnserializableParentsReflectionProvider(final ReflectionProvider reflectionProvider){ super(reflectionProvider); } @Override public void visitSerializableFields(final Object object,final Visitor visitor){ wrapped.visitSerializableFields(object, (name,type,definedIn,value) -> { if (!Serializable.class.isAssignableFrom(definedIn)){ visitor.visit(name, type, definedIn, value); } }); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy