Index: ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/collections/AbstractCollectionConverter.java
===================================================================
--- ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/collections/AbstractCollectionConverter.java	(revision 1404)
+++ ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/collections/AbstractCollectionConverter.java	Mon Feb 11 22:41:59 CET 2008
@@ -19,6 +19,7 @@
 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
 import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
 import com.thoughtworks.xstream.mapper.Mapper;
+import com.thoughtworks.xstream.unknownnodes.NodeStorage;
 
 /**
  * Base helper class for converters that need to handle
@@ -34,6 +35,9 @@
 
     private final Mapper mapper;
 
+    protected NodeStorage nodeStorage = null;
+    protected boolean cacheUnknownNodes = false;
+
     public abstract boolean canConvert(Class type);
 
     public AbstractCollectionConverter(Mapper mapper) {
Index: ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/UnknownNode.java
===================================================================
--- ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/UnknownNode.java	Mon Feb 11 22:41:59 CET 2008
+++ ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/UnknownNode.java	Mon Feb 11 22:41:59 CET 2008
@@ -0,0 +1,9 @@
+package com.thoughtworks.xstream.unknownnodes;
+
+/**
+ * User: peter
+ * Date: Feb 1, 2008
+ * Time: 3:45:34 PM
+ */
+public class UnknownNode {
+}
Index: ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/NodeStorage.java
===================================================================
--- ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/NodeStorage.java	Mon Feb 11 22:55:56 CET 2008
+++ ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/NodeStorage.java	Mon Feb 11 22:55:56 CET 2008
@@ -0,0 +1,203 @@
+package com.thoughtworks.xstream.unknownnodes;
+
+import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+import java.lang.ref.WeakReference;
+import java.util.*;
+
+/**
+ * User: peter
+ * Date: Jan 31, 2008
+ * Time: 6:36:26 PM
+ */
+public class NodeStorage {
+
+    // every object can have multiple unknown nodes
+    private Map storedNodes = new HashMap();
+    private Map storedAttributes = new HashMap();
+
+    public void saveNodeTree(Object object, HierarchicalStreamReader reader) {
+        // retrieve this node and all subnodes
+        NodeTree nodeTree = new NodeTree();
+        saveNodeRecursive(nodeTree, reader);
+
+        // mutable objects can not be used as keys in Maps
+        // so we wrap them with weak reference objects
+        WeakReference newReference = new WeakReference(object);
+
+        // checking if this reference is already stored
+        ArrayList foundList = null;
+        Iterator it = storedNodes.entrySet().iterator();
+        while (it.hasNext()) {
+            Object o = it.next();
+            Map.Entry entry = (Map.Entry) o;
+            WeakReference mapReference = (WeakReference) entry.getKey();
+            if (mapReference.get().equals(object)) {
+                foundList = (ArrayList) entry.getValue();
+            }
+        }
+
+        if (foundList == null) {
+            // reference not yet stored - we have to create an list to hold unknown nodes
+            List newList = new ArrayList();
+            newList.add(nodeTree);
+            storedNodes.put(newReference, newList);
+        } else {
+            foundList.add(nodeTree);
+        }
+    }
+
+    private void saveNodeRecursive(NodeTree nodeTree, HierarchicalStreamReader reader) {
+        nodeTree.name = reader.getNodeName();
+
+        // save attributes
+        Iterator it = reader.getAttributeNames();
+        while (it.hasNext()) {
+            String attrName = (String) it.next();
+            nodeTree.attributes.put(attrName, reader.getAttribute(attrName));
+        }
+
+        // save node value
+        nodeTree.value = reader.getValue().trim();
+
+        // save subnodes recursivelly
+        while (reader.hasMoreChildren()) {
+            reader.moveDown();
+            NodeTree newNode = new NodeTree();
+            saveNodeRecursive(newNode, reader);
+            nodeTree.subnodes.add(newNode);
+            reader.moveUp();
+        }
+    }
+
+    public void restoreNodeTree(Object object, HierarchicalStreamWriter writer) {
+
+        // find NodeTree belonging to object parameter
+        Iterator it = storedNodes.entrySet().iterator();
+        while (it.hasNext()) {
+            Object entryObj = it.next();
+            Map.Entry entry = (Map.Entry) entryObj;
+            WeakReference mapReference = (WeakReference) entry.getKey();
+            if (mapReference.get().equals(object)) {
+                List trees = (List) storedNodes.get(mapReference);
+                for (int i = 0; i < trees.size(); i++) {
+                    Object treeObj = trees.get(i);
+                    NodeTree nodeTree = (NodeTree) treeObj;
+                    restoreNodeRecursive(writer, nodeTree);
+                }
+            }
+        }
+
+    }
+
+    private void restoreNodeRecursive(HierarchicalStreamWriter writer, NodeTree nodeTree) {
+        ExtendedHierarchicalStreamWriterHelper.startNode(writer, nodeTree.name, UnknownNode.class);
+        for (Iterator it = nodeTree.attributes.keySet().iterator(); it.hasNext();) {
+            Object o = it.next();
+            String attrKey = (String) o;
+            writer.addAttribute(attrKey, (String) nodeTree.attributes.get(attrKey));
+
+        }
+        writer.setValue(nodeTree.value);
+        for (int i = 0; i < nodeTree.subnodes.size(); i++) {
+            Object o = nodeTree.subnodes.get(i);
+            NodeTree subnode = (NodeTree) o;
+            restoreNodeRecursive(writer, subnode);
+        }
+        writer.endNode();
+
+    }
+
+    /**
+     * Saves an attribute for later use.
+     *
+     * @param object    An Object to which attribute is assciated.
+     * @param attrName
+     * @param attrValue
+     */
+    public void saveAttribute(Object object, String attrName, String attrValue) {
+        if (!storedAttributes.containsKey(object)) {
+//            storedAttributes.put(object, new HashMap<String, String>());
+            storedAttributes.put(object, new HashMap());
+        }
+        Map attrMap = (Map) storedAttributes.get(object);
+        attrMap.put(attrName, attrValue);
+    }
+
+    /**
+     * Restores saved attributes to the writer.
+     *
+     * @param object Object with which attributes are associated.
+     * @param writer HierarchicalStreamWriter where attributes will be written to.
+     */
+    public void restoreAttributes(Object object, HierarchicalStreamWriter writer) {
+        if (storedAttributes.containsKey(object)) {
+            Map attrMap = (Map) storedAttributes.get(object);
+            for (Iterator it = attrMap.keySet().iterator(); it.hasNext();) {
+                Object o = it.next();
+                String attrName = (String) o;
+                writer.addAttribute(attrName, (String) attrMap.get(attrName));
+            }
+        }
+    }
+
+
+    /**
+     * Prints out a 
+     * @return
+     */
+    public String toString() {
+        String prefix = " ";
+        StringBuilder str = new StringBuilder();
+        List alreadyPrinted = new ArrayList();
+        for (Iterator it1 = storedNodes.entrySet().iterator(); it1.hasNext();) {
+            Object o = it1.next();
+            Map.Entry entry = (Map.Entry) o;
+            Object ob = entry.getKey();
+            WeakReference ref = (WeakReference) ob;
+            Object value = entry.getValue();
+
+            str.append("\n").append(ref.get().getClass()).append(" : ").append(ref.get().hashCode());
+
+            // attributes for the given object
+            Map attrMap = (Map) storedAttributes.get(ob);
+            if (attrMap != null) {
+                alreadyPrinted.add(ob);
+                for (Iterator it = attrMap.keySet().iterator(); it.hasNext();) {
+                    Object attrKey = it.next();
+                    str.append("\n" + prefix + "unknown attribute ").append(attrKey).append("='").append(attrMap.get(attrKey)).append("'");
+                }
+            }
+
+            //subnodes for the given object
+            ArrayList nodeList = (ArrayList) value;
+            if (nodeList == null) {
+                System.out.println("ERROR: null list of unknown subnodes " + ref.get().getClass() + "  " + ref.get().toString());
+            } else {
+                for (Iterator it = nodeList.iterator(); it.hasNext();) {
+                    Object obj = it.next();
+                    NodeTree nodeTree = (NodeTree) obj;
+                    str.append(nodeTree.toString(prefix, 1));
+                }
+            }
+        }
+
+        // unknown attributes not yet printed
+        for (Iterator it1 = storedAttributes.keySet().iterator(); it1.hasNext();) {
+            Object obj = it1.next();
+            if (!alreadyPrinted.contains(obj)) {
+                str.append("\n").append(obj.getClass()).append(" : ").append(obj.hashCode());
+                Map attrMap = (Map) storedAttributes.get(obj);
+                for (Iterator it = attrMap.keySet().iterator(); it.hasNext();) {
+                    Object attrKey = it.next();
+                    str.append("\n" + prefix + "unknown attribute ").append(attrKey).append("='").append(attrMap.get(attrKey)).append("'");
+                }
+            }
+        }
+
+        return str.toString();
+    }
+
+}
Index: ../trunk/xstream/src/java/com/thoughtworks/xstream/mapper/DefaultMapper.java
===================================================================
--- ../trunk/xstream/src/java/com/thoughtworks/xstream/mapper/DefaultMapper.java	(revision 1404)
+++ ../trunk/xstream/src/java/com/thoughtworks/xstream/mapper/DefaultMapper.java	Mon Feb 11 22:42:00 CET 2008
@@ -13,6 +13,7 @@
 
 import com.thoughtworks.xstream.converters.Converter;
 import com.thoughtworks.xstream.converters.SingleValueConverter;
+import com.thoughtworks.xstream.unknownnodes.UnknownNode;
 
 
 /**
@@ -25,6 +26,9 @@
 public class DefaultMapper implements Mapper {
 
     private final ClassLoader classLoader;
+
+    private boolean caching = false;
+
     /**
      * @deprecated since 1.2, no necessity for field anymore.
      */
@@ -35,6 +39,12 @@
         this.classAttributeIdentifier = "class";
     }
 
+        public DefaultMapper(ClassLoader classLoader, boolean caching) {
+        this.classLoader = classLoader;
+        this.classAttributeIdentifier = "class";
+        this.caching = caching;
+    }
+
     /**
      * @deprecated since 1.2, use XStream.aliasAttrbute() for a different attribute name.
      */
@@ -54,14 +64,18 @@
     public String serializedClass(Class type) {
         return type.getName();
     }
-
+   
     public Class realClass(String elementName) {
         try {
             return classLoader.loadClass(elementName);
         } catch (ClassNotFoundException e) {
+            if (caching) {
+                return UnknownNode.class;
+            } else {
-            throw new CannotResolveClassException(elementName + " : " + e.getMessage());
-        }
-    }
+                throw new CannotResolveClassException(elementName + " : " + e.getMessage());
+            }
+        }
+    }
 
     public Class defaultImplementationOf(Class type) {
         return type;
Index: ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionConverter.java
===================================================================
--- ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionConverter.java	(revision 1404)
+++ ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionConverter.java	Mon Feb 11 22:41:59 CET 2008
@@ -12,6 +12,7 @@
 package com.thoughtworks.xstream.converters.reflection;
 
 import com.thoughtworks.xstream.mapper.Mapper;
+import com.thoughtworks.xstream.unknownnodes.NodeStorage;
 
 public class ReflectionConverter extends AbstractReflectionConverter {
 
@@ -19,6 +20,13 @@
         super(mapper, reflectionProvider);
     }
 
+    public ReflectionConverter(Mapper mapper, ReflectionProvider reflectionProvider,
+                               NodeStorage nodeStorage) {
+        super(mapper, reflectionProvider);
+        this.nodeStorage = nodeStorage;
+        this.cacheUnknownNodes = true;
+    }
+
     public boolean canConvert(Class type) {
         return true;
     }
Index: ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/XStreamCaching.java
===================================================================
--- ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/XStreamCaching.java	Mon Feb 11 22:41:59 CET 2008
+++ ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/XStreamCaching.java	Mon Feb 11 22:41:59 CET 2008
@@ -0,0 +1,1475 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006 Joe Walnes.
+ * Copyright (C) 2006, 2007, 2008 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 26. September 2003 by Joe Walnes
+ */
+package com.thoughtworks.xstream.unknownnodes;
+
+import com.thoughtworks.xstream.MarshallingStrategy;
+import com.thoughtworks.xstream.XStreamException;
+import com.thoughtworks.xstream.alias.ClassMapper;
+import com.thoughtworks.xstream.converters.*;
+import com.thoughtworks.xstream.converters.basic.*;
+import com.thoughtworks.xstream.converters.collections.*;
+import com.thoughtworks.xstream.converters.extended.*;
+import com.thoughtworks.xstream.converters.reflection.*;
+import com.thoughtworks.xstream.core.*;
+import com.thoughtworks.xstream.core.util.ClassLoaderReference;
+import com.thoughtworks.xstream.core.util.CompositeClassLoader;
+import com.thoughtworks.xstream.core.util.CustomObjectInputStream;
+import com.thoughtworks.xstream.core.util.CustomObjectOutputStream;
+import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.io.StatefulWriter;
+import com.thoughtworks.xstream.io.xml.XppDriver;
+import com.thoughtworks.xstream.mapper.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URL;
+import java.util.*;
+
+
+/**
+ * Simple facade to XStream library, a Java-XML serialization tool. <p/>
+ * <p>
+ * <hr>
+ * <b>Example</b><blockquote>
+ * <p/>
+ * <pre>
+ * XStream xstream = new XStream();
+ * String xml = xstream.toXML(myObject); // serialize to XML
+ * Object myObject2 = xstream.fromXML(xml); // deserialize from XML
+ * </pre>
+ * <p/>
+ * </blockquote>
+ * <hr>
+ * <p/>
+ * <h3>Aliasing classes</h3>
+ * <p/>
+ * <p>
+ * To create shorter XML, you can specify aliases for classes using the <code>alias()</code>
+ * method. For example, you can shorten all occurrences of element
+ * <code>&lt;com.blah.MyThing&gt;</code> to <code>&lt;my-thing&gt;</code> by registering an
+ * alias for the class.
+ * <p>
+ * <hr>
+ * <blockquote>
+ * <p/>
+ * <pre>
+ * xstream.alias(&quot;my-thing&quot;, MyThing.class);
+ * </pre>
+ * <p/>
+ * </blockquote>
+ * <hr>
+ * <p/>
+ * <h3>Converters</h3>
+ * <p/>
+ * <p>
+ * XStream contains a map of {@link com.thoughtworks.xstream.converters.Converter} instances, each
+ * of which acts as a strategy for converting a particular type of class to XML and back again. Out
+ * of the box, XStream contains converters for most basic types (String, Date, int, boolean, etc)
+ * and collections (Map, List, Set, Properties, etc). For other objects reflection is used to
+ * serialize each field recursively.
+ * </p>
+ * <p/>
+ * <p>
+ * Extra converters can be registered using the <code>registerConverter()</code> method. Some
+ * non-standard converters are supplied in the {@link com.thoughtworks.xstream.converters.extended}
+ * package and you can create your own by implementing the
+ * {@link com.thoughtworks.xstream.converters.Converter} interface.
+ * </p>
+ * <p/>
+ * <p>
+ * <hr>
+ * <b>Example</b><blockquote>
+ * <p/>
+ * <pre>
+ * xstream.registerConverter(new SqlTimestampConverter());
+ * xstream.registerConverter(new DynamicProxyConverter());
+ * </pre>
+ * <p/>
+ * </blockquote>
+ * <hr>
+ * <p>
+ * The default converter, ie the converter which will be used if no other registered converter is
+ * suitable, can be configured by either one of the constructors or can be changed using the
+ * <code>changeDefaultConverter()</code> method. If not set, XStream uses
+ * {@link com.thoughtworks.xstream.converters.reflection.ReflectionConverter} as the initial default
+ * converter.
+ * </p>
+ * <p/>
+ * <p>
+ * <hr>
+ * <b>Example</b><blockquote>
+ * <p/>
+ * <pre>
+ * xstream.changeDefaultConverter(new ACustomDefaultConverter());
+ * </pre>
+ * <p/>
+ * </blockquote>
+ * <hr>
+ * <p/>
+ * <h3>Object graphs</h3>
+ * <p/>
+ * <p>
+ * XStream has support for object graphs; a deserialized object graph will keep references intact,
+ * including circular references.
+ * </p>
+ * <p/>
+ * <p>
+ * XStream can signify references in XML using either relative/absolute XPath or IDs. The mode can be changed using
+ * <code>setMode()</code>:
+ * </p>
+ * <p/> <table border="1">
+ * <tr>
+ * <td><code>xstream.setMode(XStream.XPATH_RELATIVE_REFERENCES);</code></td>
+ * <td><i>(Default)</i> Uses XPath relative references to signify duplicate references. This produces XML
+ * with the least clutter.</td>
+ * </tr>
+ * <tr>
+ * <td><code>xstream.setMode(XStream.XPATH_ABSOLUTE_REFERENCES);</code></td>
+ * <td>Uses XPath absolute references to signify duplicate
+ * references. This produces XML with the least clutter.</td>
+ * </tr>
+ * <tr>
+ * <td><code>xstream.setMode(XStream.ID_REFERENCES);</code></td>
+ * <td>Uses ID references to signify duplicate references. In some scenarios, such as when using
+ * hand-written XML, this is easier to work with.</td>
+ * </tr>
+ * <tr>
+ * <td><code>xstream.setMode(XStream.NO_REFERENCES);</code></td>
+ * <td>This disables object graph support and treats the object structure like a tree. Duplicate
+ * references are treated as two separate objects and circular references cause an exception. This
+ * is slightly faster and uses less memory than the other two modes.</td>
+ * </tr>
+ * </table>
+ * <h3>Thread safety</h3>
+ * <p>
+ * The XStream instance is thread-safe. That is, once the XStream instance has been created and
+ * configured, it may be shared across multiple threads allowing objects to be
+ * serialized/deserialized concurrently.
+ * <h3>Implicit collections</h3>
+ * <p/>
+ * <p>
+ * To avoid the need for special tags for collections, you can define implicit collections using one
+ * of the <code>addImplicitCollection</code> methods.
+ * </p>
+ *
+ * @author Joe Walnes
+ * @author J&ouml;rg Schaible
+ * @author Mauro Talevi
+ * @author Guilherme Silveira
+ */
+public class XStreamCaching {
+
+    // storage for unknown nodes
+    public NodeStorage storedUnknownNodes = new NodeStorage();
+
+    // CAUTION: The sequence of the fields is intentional for an optimal XML output of a
+    // self-serialization!
+    private ReflectionProvider reflectionProvider;
+    private HierarchicalStreamDriver hierarchicalStreamDriver;
+    private ClassLoaderReference classLoaderReference;
+    private MarshallingStrategy marshallingStrategy;
+    private ConverterLookup converterLookup;
+    private Mapper mapper;
+
+    private ClassAliasingMapper classAliasingMapper;
+    private FieldAliasingMapper fieldAliasingMapper;
+    private AttributeAliasingMapper attributeAliasingMapper;
+    private AttributeMapper attributeMapper;
+    private DefaultImplementationsMapper defaultImplementationsMapper;
+    private ImmutableTypesMapper immutableTypesMapper;
+    private ImplicitCollectionMapper implicitCollectionMapper;
+    private LocalConversionMapper localConversionMapper;
+    private AnnotationConfiguration annotationConfiguration;
+
+    private transient JVM jvm = new JVM();
+
+    public static final int NO_REFERENCES = 1001;
+    public static final int ID_REFERENCES = 1002;
+    public static final int XPATH_RELATIVE_REFERENCES = 1003;
+    public static final int XPATH_ABSOLUTE_REFERENCES = 1004;
+    /**
+     * @deprecated since 1.2, use {@link #XPATH_RELATIVE_REFERENCES} or
+     *             {@link #XPATH_ABSOLUTE_REFERENCES} instead.
+     */
+    public static final int XPATH_REFERENCES = XPATH_RELATIVE_REFERENCES;
+
+    public static final int PRIORITY_VERY_HIGH = 10000;
+    public static final int PRIORITY_NORMAL = 0;
+    public static final int PRIORITY_LOW = -10;
+    public static final int PRIORITY_VERY_LOW = -20;
+
+    private static final String ANNOTATION_MAPPER_TYPE = "com.thoughtworks.xstream.mapper.AnnotationMapper";
+
+    /**
+     * Constructs a default XStream. The instance will use the {@link XppDriver} as default and tries to determine the best
+     * match for the {@link ReflectionProvider} on its own.
+     *
+     * @throws InitializationException in case of an initialization problem
+     */
+    public XStreamCaching() {
+        this(null, (Mapper) null, new XppDriver());
+    }
+
+    /**
+     * Constructs an XStream with a special {@link ReflectionProvider}. The instance will use the {@link XppDriver} as default.
+     *
+     * @throws InitializationException in case of an initialization problem
+     */
+    public XStreamCaching(ReflectionProvider reflectionProvider) {
+        this(reflectionProvider, (Mapper) null, new XppDriver());
+    }
+
+    /**
+     * Constructs an XStream with a special {@link HierarchicalStreamDriver}. The instance will tries to determine the best
+     * match for the {@link ReflectionProvider} on its own.
+     *
+     * @throws InitializationException in case of an initialization problem
+     */
+    public XStreamCaching(HierarchicalStreamDriver hierarchicalStreamDriver) {
+        this(null, (Mapper) null, hierarchicalStreamDriver);
+    }
+
+    /**
+     * Constructs an XStream with a special {@link HierarchicalStreamDriver} and {@link ReflectionProvider}.
+     *
+     * @throws InitializationException in case of an initialization problem
+     */
+    public XStreamCaching(
+            ReflectionProvider reflectionProvider, HierarchicalStreamDriver hierarchicalStreamDriver) {
+        this(reflectionProvider, (Mapper) null, hierarchicalStreamDriver);
+    }
+
+    /**
+     * @deprecated As of 1.2, use
+     *             {@link #XStreamCaching(ReflectionProvider, Mapper, HierarchicalStreamDriver)}
+     */
+    public XStreamCaching(
+            ReflectionProvider reflectionProvider, ClassMapper classMapper,
+            HierarchicalStreamDriver driver) {
+        this(reflectionProvider, (Mapper) classMapper, driver);
+    }
+
+    /**
+     * @deprecated As of 1.2, use
+     *             {@link #XStreamCaching(ReflectionProvider, Mapper, HierarchicalStreamDriver)} and
+     *             register classAttributeIdentifier as alias
+     */
+    public XStreamCaching(
+            ReflectionProvider reflectionProvider, ClassMapper classMapper,
+            HierarchicalStreamDriver driver, String classAttributeIdentifier) {
+        this(reflectionProvider, (Mapper) classMapper, driver);
+        aliasAttribute(classAttributeIdentifier, "class");
+    }
+
+    /**
+     * Constructs an XStream with a special {@link HierarchicalStreamDriver} and {@link ReflectionProvider} and additionally with a prepared {@link Mapper}.
+     *
+     * @throws InitializationException in case of an initialization problem
+     * @deprecated since upcoming, use {@link } instead
+     */
+    public XStreamCaching(
+            ReflectionProvider reflectionProvider, Mapper mapper, HierarchicalStreamDriver driver) {
+        this(reflectionProvider, driver, new ClassLoaderReference(new CompositeClassLoader()), mapper, new DefaultConverterLookup());
+    }
+
+    /**
+     * Constructs an XStream with a special {@link HierarchicalStreamDriver} and {@link ReflectionProvider} and additionally with a prepared
+     * {@link ClassLoader} to use.
+     *
+     * @throws InitializationException in case of an initialization problem
+     * @since upcoming
+     */
+    public XStreamCaching(
+            ReflectionProvider reflectionProvider, HierarchicalStreamDriver driver, ClassLoader classLoader) {
+        this(reflectionProvider, driver, classLoader, null);
+    }
+
+    /**
+     * Constructs an XStream with a special {@link HierarchicalStreamDriver} and {@link ReflectionProvider} and additionally with a prepared {@link Mapper}
+     * and the {@link ClassLoader} in use.
+     * <p/>
+     * <p>Note, if the class loader should be changed later again, you should provide a {@link ClassLoaderReference} as {@link ClassLoader} that is also
+     * use in the {@link Mapper} chain.</p>
+     *
+     * @throws InitializationException in case of an initialization problem
+     * @since upcoming
+     */
+    public XStreamCaching(
+            ReflectionProvider reflectionProvider, HierarchicalStreamDriver driver, ClassLoader classLoader, Mapper mapper) {
+        this(reflectionProvider, driver, classLoader, mapper, new DefaultConverterLookup());
+    }
+
+    /**
+     * Constructs an XStream with a special {@link HierarchicalStreamDriver}, {@link ReflectionProvider}, a prepared {@link Mapper}
+     * and the {@link ClassLoader} in use and an own {@link ConverterRegistry}.
+     * <p/>
+     * <p>Note, if the class loader should be changed later again, you should provide a {@link ClassLoaderReference} as {@link ClassLoader} that is also
+     * use in the {@link Mapper} chain.</p>
+     *
+     * @throws InitializationException in case of an initialization problem
+     * @since upcoming
+     */
+    public XStreamCaching(
+            ReflectionProvider reflectionProvider, HierarchicalStreamDriver driver,
+            ClassLoader classLoader, Mapper mapper, ConverterRegistry converterRegistry) {
+        jvm = new JVM();
+        if (reflectionProvider == null) {
+            reflectionProvider = jvm.bestReflectionProvider();
+        }
+        this.reflectionProvider = reflectionProvider;
+        this.hierarchicalStreamDriver = driver;
+        this.classLoaderReference = classLoader instanceof ClassLoaderReference ? (ClassLoaderReference) classLoader : new ClassLoaderReference(classLoader);
+        this.converterLookup = converterRegistry;
+        this.mapper = mapper == null ? buildMapper() : mapper;
+
+        setupMappers();
+        setupAliases();
+        setupDefaultImplementations();
+        setupConverters();
+        setupImmutableTypes();
+        setMode(XPATH_RELATIVE_REFERENCES);
+    }
+
+    private Mapper buildMapper() {
+        Mapper mapper = new DefaultMapper(classLoaderReference, true);
+        if (useXStream11XmlFriendlyMapper()) {
+            mapper = new XStream11XmlFriendlyMapper(mapper);
+        }
+        if (jvm.loadClass("net.sf.cglib.proxy.Enhancer") != null) {
+            mapper = buildMapperDynamically(
+                    "com.thoughtworks.xstream.mapper.CGLIBMapper",
+                    new Class[]{Mapper.class}, new Object[]{mapper});
+        }
+        mapper = new DynamicProxyMapper(mapper);
+        mapper = new ClassAliasingMapper(mapper);
+        mapper = new FieldAliasingMapper(mapper);
+        mapper = new AttributeAliasingMapper(mapper);
+        mapper = new AttributeMapper(mapper, converterLookup);
+        mapper = new ImplicitCollectionMapper(mapper);
+        if (JVM.is15()) {
+            mapper = new EnumMapper(mapper);
+        }
+        mapper = new OuterClassMapper(mapper);
+        mapper = new ArrayMapper(mapper);
+        mapper = new LocalConversionMapper(mapper);
+        mapper = new DefaultImplementationsMapper(mapper);
+        mapper = new ImmutableTypesMapper(mapper);
+        if (JVM.is15()) {
+            mapper = buildMapperDynamically(
+                    ANNOTATION_MAPPER_TYPE,
+                    new Class[]{Mapper.class, ConverterRegistry.class, ClassLoader.class, ReflectionProvider.class, JVM.class},
+                    new Object[]{mapper, converterLookup, classLoaderReference, reflectionProvider, jvm});
+        }
+        mapper = wrapMapper((MapperWrapper) mapper);
+        mapper = new CachingMapper(mapper);
+        return mapper;
+    }
+
+    private Mapper buildMapperDynamically(
+            String className, Class[] constructorParamTypes,
+            Object[] constructorParamValues) {
+        try {
+            Class type = Class.forName(className, false, classLoaderReference.getReference());
+            Constructor constructor = type.getConstructor(constructorParamTypes);
+            return (Mapper) constructor.newInstance(constructorParamValues);
+        } catch (Exception e) {
+            throw new InitializationException("Could not instantiate mapper : " + className, e);
+        }
+    }
+
+    protected MapperWrapper wrapMapper(MapperWrapper next) {
+        return next;
+    }
+
+    protected boolean useXStream11XmlFriendlyMapper() {
+        return false;
+    }
+
+    private void setupMappers() {
+        classAliasingMapper = (ClassAliasingMapper) this.mapper
+                .lookupMapperOfType(ClassAliasingMapper.class);
+        fieldAliasingMapper = (FieldAliasingMapper) this.mapper
+                .lookupMapperOfType(FieldAliasingMapper.class);
+        attributeMapper = (AttributeMapper) this.mapper.lookupMapperOfType(AttributeMapper.class);
+        attributeAliasingMapper = (AttributeAliasingMapper) this.mapper
+                .lookupMapperOfType(AttributeAliasingMapper.class);
+        implicitCollectionMapper = (ImplicitCollectionMapper) this.mapper
+                .lookupMapperOfType(ImplicitCollectionMapper.class);
+        defaultImplementationsMapper = (DefaultImplementationsMapper) this.mapper
+                .lookupMapperOfType(DefaultImplementationsMapper.class);
+        immutableTypesMapper = (ImmutableTypesMapper) this.mapper
+                .lookupMapperOfType(ImmutableTypesMapper.class);
+        localConversionMapper = (LocalConversionMapper) this.mapper
+                .lookupMapperOfType(LocalConversionMapper.class);
+        annotationConfiguration = (AnnotationConfiguration) this.mapper
+                .lookupMapperOfType(AnnotationConfiguration.class);
+    }
+
+    protected void setupAliases() {
+        if (classAliasingMapper == null) {
+            return;
+        }
+
+        alias("null", Mapper.Null.class);
+        alias("int", Integer.class);
+        alias("float", Float.class);
+        alias("double", Double.class);
+        alias("long", Long.class);
+        alias("short", Short.class);
+        alias("char", Character.class);
+        alias("byte", Byte.class);
+        alias("boolean", Boolean.class);
+        alias("number", Number.class);
+        alias("object", Object.class);
+        alias("big-int", BigInteger.class);
+        alias("big-decimal", BigDecimal.class);
+
+        alias("string-buffer", StringBuffer.class);
+        alias("string", String.class);
+        alias("java-class", Class.class);
+        alias("method", Method.class);
+        alias("constructor", Constructor.class);
+        alias("date", Date.class);
+        alias("url", URL.class);
+        alias("bit-set", BitSet.class);
+
+        alias("map", Map.class);
+        alias("entry", Map.Entry.class);
+        alias("properties", Properties.class);
+        alias("list", List.class);
+        alias("set", Set.class);
+
+        alias("linked-list", LinkedList.class);
+        alias("vector", Vector.class);
+        alias("tree-map", TreeMap.class);
+        alias("tree-set", TreeSet.class);
+        alias("hashtable", Hashtable.class);
+
+        if (jvm.supportsAWT()) {
+            // Instantiating these two classes starts the AWT system, which is undesirable. Calling
+            // loadClass ensures a reference to the class is found but they are not instantiated.
+            alias("awt-color", jvm.loadClass("java.awt.Color"));
+            alias("awt-font", jvm.loadClass("java.awt.Font"));
+            alias("awt-text-attribute", jvm.loadClass("java.awt.font.TextAttribute"));
+        }
+
+        if (jvm.supportsSQL()) {
+            alias("sql-timestamp", jvm.loadClass("java.sql.Timestamp"));
+            alias("sql-time", jvm.loadClass("java.sql.Time"));
+            alias("sql-date", jvm.loadClass("java.sql.Date"));
+        }
+
+        alias("file", File.class);
+        alias("locale", Locale.class);
+        alias("gregorian-calendar", Calendar.class);
+
+        // since jdk 1.4 included, but previously available as separate package ...
+        Class type = jvm.loadClass("javax.security.auth.Subject");
+        if (type != null) {
+            alias("auth-subject", type);
+        }
+
+        // since jdk 1.5 included, but available separately in JAXB ...
+        type = jvm.loadClass("javax.xml.datatype.Duration");
+        if (type != null) {
+            alias("duration", type);
+        }
+
+        if (JVM.is14()) {
+            alias("linked-hash-map", jvm.loadClass("java.util.LinkedHashMap"));
+            alias("linked-hash-set", jvm.loadClass("java.util.LinkedHashSet"));
+            alias("trace", jvm.loadClass("java.lang.StackTraceElement"));
+            alias("currency", jvm.loadClass("java.util.Currency"));
+            aliasType("charset", jvm.loadClass("java.nio.charset.Charset"));
+        }
+
+        if (JVM.is15()) {
+            alias("enum-set", jvm.loadClass("java.util.EnumSet"));
+            alias("enum-map", jvm.loadClass("java.util.EnumMap"));
+            alias("string-builder", jvm.loadClass("java.lang.StringBuilder"));
+            alias("uuid", jvm.loadClass("java.util.UUID"));
+        }
+    }
+
+    protected void setupDefaultImplementations() {
+        if (defaultImplementationsMapper == null) {
+            return;
+        }
+        addDefaultImplementation(HashMap.class, Map.class);
+        addDefaultImplementation(ArrayList.class, List.class);
+        addDefaultImplementation(HashSet.class, Set.class);
+        addDefaultImplementation(GregorianCalendar.class, Calendar.class);
+    }
+
+    protected void setupConverters() {
+        final ReflectionConverter reflectionConverter =
+                new ReflectionConverter(mapper, reflectionProvider, storedUnknownNodes);
+        registerConverter(reflectionConverter, PRIORITY_VERY_LOW);
+
+        registerConverter(new SerializableConverter(mapper, reflectionProvider), PRIORITY_LOW);
+        registerConverter(new ExternalizableConverter(mapper), PRIORITY_LOW);
+
+        registerConverter(new NullConverter(), PRIORITY_VERY_HIGH);
+        registerConverter(new IntConverter(), PRIORITY_NORMAL);
+        registerConverter(new FloatConverter(), PRIORITY_NORMAL);
+        registerConverter(new DoubleConverter(), PRIORITY_NORMAL);
+        registerConverter(new LongConverter(), PRIORITY_NORMAL);
+        registerConverter(new ShortConverter(), PRIORITY_NORMAL);
+        registerConverter((Converter) new CharConverter(), PRIORITY_NORMAL);
+        registerConverter(new BooleanConverter(), PRIORITY_NORMAL);
+        registerConverter(new ByteConverter(), PRIORITY_NORMAL);
+
+        registerConverter(new StringConverter(), PRIORITY_NORMAL);
+        registerConverter(new StringBufferConverter(), PRIORITY_NORMAL);
+        registerConverter(new DateConverter(), PRIORITY_NORMAL);
+        registerConverter(new BitSetConverter(), PRIORITY_NORMAL);
+        registerConverter(new URLConverter(), PRIORITY_NORMAL);
+        registerConverter(new BigIntegerConverter(), PRIORITY_NORMAL);
+        registerConverter(new BigDecimalConverter(), PRIORITY_NORMAL);
+
+        registerConverter(new ArrayConverter(mapper), PRIORITY_NORMAL);
+        registerConverter(new CharArrayConverter(), PRIORITY_NORMAL);
+        registerConverter(new CollectionConverter(mapper, storedUnknownNodes), PRIORITY_NORMAL);
+        registerConverter(new MapConverter(mapper), PRIORITY_NORMAL);
+        registerConverter(new TreeMapConverter(mapper), PRIORITY_NORMAL);
+        registerConverter(new TreeSetConverter(mapper), PRIORITY_NORMAL);
+        registerConverter(new PropertiesConverter(), PRIORITY_NORMAL);
+        registerConverter(new EncodedByteArrayConverter(), PRIORITY_NORMAL);
+
+        registerConverter(new FileConverter(), PRIORITY_NORMAL);
+        if (jvm.supportsSQL()) {
+            registerConverter(new SqlTimestampConverter(), PRIORITY_NORMAL);
+            registerConverter(new SqlTimeConverter(), PRIORITY_NORMAL);
+            registerConverter(new SqlDateConverter(), PRIORITY_NORMAL);
+        }
+        registerConverter(new DynamicProxyConverter(mapper, classLoaderReference), PRIORITY_NORMAL);
+        registerConverter(new JavaClassConverter(classLoaderReference), PRIORITY_NORMAL);
+        registerConverter(new JavaMethodConverter(classLoaderReference), PRIORITY_NORMAL);
+        if (jvm.supportsAWT()) {
+            registerConverter(new FontConverter(), PRIORITY_NORMAL);
+            registerConverter(new ColorConverter(), PRIORITY_NORMAL);
+            registerConverter(new TextAttributeConverter(), PRIORITY_NORMAL);
+        }
+        if (jvm.supportsSwing()) {
+            registerConverter(new LookAndFeelConverter(mapper, reflectionProvider), PRIORITY_NORMAL);
+        }
+        registerConverter(new LocaleConverter(), PRIORITY_NORMAL);
+        registerConverter(new GregorianCalendarConverter(), PRIORITY_NORMAL);
+
+        // since JDK 1.4 included, but previously available as separate package ...
+        if (jvm.loadClass("javax.security.auth.Subject") != null) {
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.extended.SubjectConverter",
+                    PRIORITY_NORMAL, new Class[]{Mapper.class}, new Object[]{mapper});
+        }
+
+        // since JDK 1.5 included, bas as part of JAXB previously available ...
+        if (jvm.loadClass("javax.xml.datatype.Duration") != null) {
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.extended.DurationConverter",
+                    PRIORITY_NORMAL, null, null);
+        }
+
+        if (JVM.is14()) {
+            // late bound converters - allows XStream to be compiled on earlier JDKs
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.extended.ThrowableConverter",
+                    PRIORITY_NORMAL, new Class[]{Converter.class},
+                    new Object[]{reflectionConverter});
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.extended.StackTraceElementConverter",
+                    PRIORITY_NORMAL, null, null);
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.extended.CurrencyConverter",
+                    PRIORITY_NORMAL, null, null);
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.extended.RegexPatternConverter",
+                    PRIORITY_NORMAL, new Class[]{Converter.class},
+                    new Object[]{reflectionConverter});
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.extended.CharsetConverter",
+                    PRIORITY_NORMAL, null, null);
+        }
+
+        if (JVM.is15()) {
+            // late bound converters - allows XStream to be compiled on earlier JDKs
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.enums.EnumConverter", PRIORITY_NORMAL,
+                    null, null);
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.enums.EnumSetConverter", PRIORITY_NORMAL,
+                    new Class[]{Mapper.class}, new Object[]{mapper});
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.enums.EnumMapConverter", PRIORITY_NORMAL,
+                    new Class[]{Mapper.class}, new Object[]{mapper});
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.basic.StringBuilderConverter", PRIORITY_NORMAL,
+                    null, null);
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.basic.UUIDConverter", PRIORITY_NORMAL,
+                    null, null);
+        }
+
+        if (jvm.loadClass("net.sf.cglib.proxy.Enhancer") != null) {
+            dynamicallyRegisterConverter(
+                    "com.thoughtworks.xstream.converters.reflection.CGLIBEnhancedConverter",
+                    PRIORITY_NORMAL, new Class[]{Mapper.class, ReflectionProvider.class},
+                    new Object[]{mapper, reflectionProvider});
+        }
+
+        registerConverter(new SelfStreamingInstanceChecker(reflectionConverter, this), PRIORITY_NORMAL);
+    }
+
+    private void dynamicallyRegisterConverter(
+            String className, int priority, Class[] constructorParamTypes,
+            Object[] constructorParamValues) {
+        try {
+            Class type = Class.forName(className, false, classLoaderReference.getReference());
+            Constructor constructor = type.getConstructor(constructorParamTypes);
+            Object instance = constructor.newInstance(constructorParamValues);
+            if (instance instanceof Converter) {
+                registerConverter((Converter) instance, priority);
+            } else if (instance instanceof SingleValueConverter) {
+                registerConverter((SingleValueConverter) instance, priority);
+            }
+        } catch (Exception e) {
+            throw new InitializationException("Could not instantiate converter : " + className, e);
+        }
+    }
+
+    protected void setupImmutableTypes() {
+        if (immutableTypesMapper == null) {
+            return;
+        }
+
+        // primitives are always immutable
+        addImmutableType(boolean.class);
+        addImmutableType(Boolean.class);
+        addImmutableType(byte.class);
+        addImmutableType(Byte.class);
+        addImmutableType(char.class);
+        addImmutableType(Character.class);
+        addImmutableType(double.class);
+        addImmutableType(Double.class);
+        addImmutableType(float.class);
+        addImmutableType(Float.class);
+        addImmutableType(int.class);
+        addImmutableType(Integer.class);
+        addImmutableType(long.class);
+        addImmutableType(Long.class);
+        addImmutableType(short.class);
+        addImmutableType(Short.class);
+
+        // additional types
+        addImmutableType(Mapper.Null.class);
+        addImmutableType(BigDecimal.class);
+        addImmutableType(BigInteger.class);
+        addImmutableType(String.class);
+        addImmutableType(URL.class);
+        addImmutableType(File.class);
+        addImmutableType(Class.class);
+
+        if (jvm.supportsAWT()) {
+            addImmutableType(jvm.loadClass("java.awt.font.TextAttribute"));
+        }
+
+        if (JVM.is14()) {
+            // late bound types - allows XStream to be compiled on earlier JDKs
+            Class type = jvm.loadClass("com.thoughtworks.xstream.converters.extended.CharsetConverter");
+            addImmutableType(type);
+        }
+    }
+
+    public void setMarshallingStrategy(MarshallingStrategy marshallingStrategy) {
+        this.marshallingStrategy = marshallingStrategy;
+    }
+
+    /**
+     * Serialize an object to a pretty-printed XML String.
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be serialized
+     */
+    public String toXML(Object obj) {
+        Writer writer = new StringWriter();
+        toXML(obj, writer);
+        return writer.toString();
+    }
+
+    /**
+     * Serialize an object to the given Writer as pretty-printed XML.
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be serialized
+     */
+    public void toXML(Object obj, Writer out) {
+        HierarchicalStreamWriter writer = hierarchicalStreamDriver.createWriter(out);
+        marshal(obj, writer);
+        writer.flush();
+    }
+
+    /**
+     * Serialize an object to the given OutputStream as pretty-printed XML.
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be serialized
+     */
+    public void toXML(Object obj, OutputStream out) {
+        HierarchicalStreamWriter writer = hierarchicalStreamDriver.createWriter(out);
+        marshal(obj, writer);
+        writer.flush();
+    }
+
+    /**
+     * Serialize and object to a hierarchical data structure (such as XML).
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be serialized
+     */
+    public void marshal(Object obj, HierarchicalStreamWriter writer) {
+        marshal(obj, writer, null);
+    }
+
+    /**
+     * Serialize and object to a hierarchical data structure (such as XML).
+     *
+     * @param dataHolder Extra data you can use to pass to your converters. Use this as you want. If
+     *                   not present, XStream shall create one lazily as needed.
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be serialized
+     */
+    public void marshal(Object obj, HierarchicalStreamWriter writer, DataHolder dataHolder) {
+        marshallingStrategy.marshal(writer, obj, converterLookup, mapper, dataHolder);
+    }
+
+    /**
+     * Deserialize an object from an XML String.
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be deserialized
+     */
+    public Object fromXML(String xml) {
+        return fromXML(new StringReader(xml));
+    }
+
+    /**
+     * Deserialize an object from an XML Reader.
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be deserialized
+     */
+    public Object fromXML(Reader xml) {
+        return unmarshal(hierarchicalStreamDriver.createReader(xml), null);
+    }
+
+    /**
+     * Deserialize an object from an XML InputStream.
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be deserialized
+     */
+    public Object fromXML(InputStream input) {
+        return unmarshal(hierarchicalStreamDriver.createReader(input), null);
+    }
+
+    /**
+     * Deserialize an object from an XML String, populating the fields of the given root object
+     * instead of instantiating a new one. Note, that this is a special use case! With the ReflectionConverter
+     * XStream will write directly into the raw memory area of the existing object. Use with care!
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be deserialized
+     */
+    public Object fromXML(String xml, Object root) {
+        return fromXML(new StringReader(xml), root);
+    }
+
+    /**
+     * Deserialize an object from an XML Reader, populating the fields of the given root object
+     * instead of instantiating a new one. Note, that this is a special use case! With the ReflectionConverter
+     * XStream will write directly into the raw memory area of the existing object. Use with care!
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be deserialized
+     */
+    public Object fromXML(Reader xml, Object root) {
+        return unmarshal(hierarchicalStreamDriver.createReader(xml), root);
+    }
+
+    /**
+     * Deserialize an object from an XML InputStream, populating the fields of the given root object
+     * instead of instantiating a new one. Note, that this is a special use case! With the ReflectionConverter
+     * XStream will write directly into the raw memory area of the existing object. Use with care!
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be deserialized
+     */
+    public Object fromXML(InputStream xml, Object root) {
+        return unmarshal(hierarchicalStreamDriver.createReader(xml), root);
+    }
+
+    /**
+     * Deserialize an object from a hierarchical data structure (such as XML).
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be deserialized
+     */
+    public Object unmarshal(HierarchicalStreamReader reader) {
+        return unmarshal(reader, null, null);
+    }
+
+    /**
+     * Deserialize an object from a hierarchical data structure (such as XML), populating the fields
+     * of the given root object instead of instantiating a new one. Note, that this is a special use case! With the ReflectionConverter
+     * XStream will write directly into the raw memory area of the existing object. Use with care!
+     *
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be deserialized
+     */
+    public Object unmarshal(HierarchicalStreamReader reader, Object root) {
+        return unmarshal(reader, root, null);
+    }
+
+    /**
+     * Deserialize an object from a hierarchical data structure (such as XML).
+     *
+     * @param root       If present, the passed in object will have its fields populated, as opposed to
+     *                   XStream creating a new instance. Note, that this is a special use case! With the ReflectionConverter
+     *                   XStream will write directly into the raw memory area of the existing object. Use with care!
+     * @param dataHolder Extra data you can use to pass to your converters. Use this as you want. If
+     *                   not present, XStream shall create one lazily as needed.
+     * @throws com.thoughtworks.xstream.XStreamException
+     *          if the object cannot be deserialized
+     */
+    public Object unmarshal(HierarchicalStreamReader reader, Object root, DataHolder dataHolder) {
+        return marshallingStrategy.unmarshal(root, reader, dataHolder, converterLookup, mapper);
+    }
+
+    /**
+     * Alias a Class to a shorter name to be used in XML elements.
+     *
+     * @param name Short name
+     * @param type Type to be aliased
+     * @throws InitializationException if no {@link ClassAliasingMapper} is available
+     */
+    public void alias(String name, Class type) {
+        if (classAliasingMapper == null) {
+            throw new InitializationException("No "
+                    + ClassAliasingMapper.class.getName()
+                    + " available");
+        }
+        classAliasingMapper.addClassAlias(name, type);
+    }
+
+    /**
+     * Alias a type to a shorter name to be used in XML elements.
+     * Any class that is assignable to this type will be aliased to the same name.
+     *
+     * @param name Short name
+     * @param type Type to be aliased
+     * @throws InitializationException if no {@link ClassAliasingMapper} is available
+     * @since 1.2
+     */
+    public void aliasType(String name, Class type) {
+        if (classAliasingMapper == null) {
+            throw new InitializationException("No "
+                    + ClassAliasingMapper.class.getName()
+                    + " available");
+        }
+        classAliasingMapper.addTypeAlias(name, type);
+    }
+
+    /**
+     * Alias a Class to a shorter name to be used in XML elements.
+     *
+     * @param name                  Short name
+     * @param type                  Type to be aliased
+     * @param defaultImplementation Default implementation of type to use if no other specified.
+     * @throws InitializationException if no {@link DefaultImplementationsMapper} or no {@link ClassAliasingMapper} is available
+     */
+    public void alias(String name, Class type, Class defaultImplementation) {
+        alias(name, type);
+        addDefaultImplementation(defaultImplementation, type);
+    }
+
+    /**
+     * Create an alias for a field name.
+     *
+     * @param alias     the alias itself
+     * @param definedIn the type that declares the field
+     * @param fieldName the name of the field
+     * @throws InitializationException if no {@link FieldAliasingMapper} is available
+     */
+    public void aliasField(String alias, Class definedIn, String fieldName) {
+        if (fieldAliasingMapper == null) {
+            throw new InitializationException("No "
+                    + FieldAliasingMapper.class.getName()
+                    + " available");
+        }
+        fieldAliasingMapper.addFieldAlias(alias, definedIn, fieldName);
+    }
+
+    /**
+     * Create an alias for an attribute
+     *
+     * @param alias         the alias itself
+     * @param attributeName the name of the attribute
+     * @throws InitializationException if no {@link AttributeAliasingMapper} is available
+     */
+    public void aliasAttribute(String alias, String attributeName) {
+        if (attributeAliasingMapper == null) {
+            throw new InitializationException("No "
+                    + AttributeAliasingMapper.class.getName()
+                    + " available");
+        }
+        attributeAliasingMapper.addAliasFor(attributeName, alias);
+    }
+
+    /**
+     * Create an alias for an attribute.
+     *
+     * @param definedIn     the type where the attribute is defined
+     * @param attributeName the name of the attribute
+     * @param alias         the alias itself
+     * @throws InitializationException if no {@link AttributeAliasingMapper} is available
+     * @since 1.2.2
+     */
+    public void aliasAttribute(Class definedIn, String attributeName, String alias) {
+        aliasField(alias, definedIn, attributeName);
+        useAttributeFor(definedIn, attributeName);
+    }
+
+    /**
+     * Use an attribute for a field or a specific type.
+     *
+     * @param fieldName the name of the field
+     * @param type      the Class of the type to be rendered as XML attribute
+     * @throws InitializationException if no {@link AttributeMapper} is available
+     * @since 1.2
+     */
+    public void useAttributeFor(String fieldName, Class type) {
+        if (attributeMapper == null) {
+            throw new InitializationException("No " + AttributeMapper.class.getName() + " available");
+        }
+        attributeMapper.addAttributeFor(fieldName, type);
+    }
+
+    /**
+     * Use an attribute for a field declared in a specific type.
+     *
+     * @param fieldName the name of the field
+     * @param definedIn the Class containing such field
+     * @throws InitializationException if no {@link AttributeMapper} is available
+     * @since 1.2.2
+     */
+    public void useAttributeFor(Class definedIn, String fieldName) {
+        if (attributeMapper == null) {
+            throw new InitializationException("No " + AttributeMapper.class.getName() + " available");
+        }
+        try {
+            final Field field = definedIn.getDeclaredField(fieldName);
+            attributeMapper.addAttributeFor(field);
+        } catch (SecurityException e) {
+            throw new InitializationException("Unable to access field " + fieldName + "@" + definedIn.getName());
+        } catch (NoSuchFieldException e) {
+            throw new InitializationException("Unable to find field " + fieldName + "@" + definedIn.getName());
+        }
+    }
+
+    /**
+     * Use an attribute for an arbitrary type.
+     *
+     * @param type the Class of the type to be rendered as XML attribute
+     * @throws InitializationException if no {@link AttributeMapper} is available
+     * @since 1.2
+     */
+    public void useAttributeFor(Class type) {
+        if (attributeMapper == null) {
+            throw new InitializationException("No " + AttributeMapper.class.getName() + " available");
+        }
+        attributeMapper.addAttributeFor(type);
+    }
+
+    /**
+     * Associate a default implementation of a class with an object. Whenever XStream encounters an
+     * instance of this type, it will use the default implementation instead. For example,
+     * java.util.ArrayList is the default implementation of java.util.List.
+     *
+     * @param defaultImplementation
+     * @param ofType
+     * @throws InitializationException if no {@link DefaultImplementationsMapper} is available
+     */
+    public void addDefaultImplementation(Class defaultImplementation, Class ofType) {
+        if (defaultImplementationsMapper == null) {
+            throw new InitializationException("No "
+                    + DefaultImplementationsMapper.class.getName()
+                    + " available");
+        }
+        defaultImplementationsMapper.addDefaultImplementation(defaultImplementation, ofType);
+    }
+
+    /**
+     * Add immutable types. The value of the instances of these types will always be written into
+     * the stream even if they appear multiple times.
+     *
+     * @throws InitializationException if no {@link ImmutableTypesMapper} is available
+     */
+    public void addImmutableType(Class type) {
+        if (immutableTypesMapper == null) {
+            throw new InitializationException("No "
+                    + ImmutableTypesMapper.class.getName()
+                    + " available");
+        }
+        immutableTypesMapper.addImmutableType(type);
+    }
+
+    public void registerConverter(Converter converter) {
+        registerConverter(converter, PRIORITY_NORMAL);
+    }
+
+    public void registerConverter(Converter converter, int priority) {
+        ((ConverterRegistry) converterLookup).registerConverter(converter, priority);
+    }
+
+    public void registerConverter(SingleValueConverter converter) {
+        registerConverter(converter, PRIORITY_NORMAL);
+    }
+
+    public void registerConverter(SingleValueConverter converter, int priority) {
+        ((ConverterRegistry) converterLookup).registerConverter(new SingleValueConverterWrapper(converter), priority);
+    }
+
+    /**
+     * Register a local {@link Converter} for a field.
+     *
+     * @param definedIn the class type the field is defined in
+     * @param fieldName the field name
+     * @param converter the converter to use
+     * @since upcoming
+     */
+    public void registerLocalConverter(Class definedIn, String fieldName, Converter converter) {
+        if (localConversionMapper == null) {
+            throw new InitializationException("No "
+                    + LocalConversionMapper.class.getName()
+                    + " available");
+        }
+        localConversionMapper.registerLocalConverter(definedIn, fieldName, converter);
+    }
+
+    /**
+     * Register a local {@link SingleValueConverter} for a field.
+     *
+     * @param definedIn the class type the field is defined in
+     * @param fieldName the field name
+     * @param converter the converter to use
+     * @since upcoming
+     */
+    public void registerLocalConverter(Class definedIn, String fieldName, SingleValueConverter converter) {
+        registerLocalConverter(definedIn, fieldName, (Converter) new SingleValueConverterWrapper(converter));
+    }
+
+    /**
+     * @throws ClassCastException if mapper is not really a deprecated {@link ClassMapper} instance
+     * @deprecated As of 1.2, use {@link #getMapper}
+     */
+    public ClassMapper getClassMapper() {
+        if (mapper instanceof ClassMapper) {
+            return (ClassMapper) mapper;
+        } else {
+            return (ClassMapper) Proxy.newProxyInstance(getClassLoader(), new Class[]{ClassMapper.class},
+                    new InvocationHandler() {
+                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                            return method.invoke(mapper, args);
+                        }
+                    });
+        }
+    }
+
+    /**
+     * Retrieve the {@link Mapper}. This is by default a chain of {@link MapperWrapper MapperWrappers}.
+     *
+     * @return the mapper
+     * @since 1.2
+     */
+    public Mapper getMapper() {
+        return mapper;
+    }
+
+    /**
+     * Retrieve the {@link ReflectionProvider} in use.
+     *
+     * @return the mapper
+     * @since 1.2.1
+     */
+    public ReflectionProvider getReflectionProvider() {
+        return reflectionProvider;
+    }
+
+    public ConverterLookup getConverterLookup() {
+        return converterLookup;
+    }
+
+    /**
+     * Change mode for dealing with duplicate references. Valid values are
+     * <code>XPATH_ABSOLUTE_REFERENCES</code>, <code>XPATH_RELATIVE_REFERENCES</code>,
+     * <code>XStream.ID_REFERENCES</code> and <code>XStream.NO_REFERENCES</code>.
+     *
+     * @throws IllegalArgumentException if the mode is not one of the declared types
+     * @see #XPATH_ABSOLUTE_REFERENCES
+     * @see #XPATH_RELATIVE_REFERENCES
+     * @see #ID_REFERENCES
+     * @see #NO_REFERENCES
+     */
+    public void setMode(int mode) {
+        switch (mode) {
+            case NO_REFERENCES:
+                setMarshallingStrategy(new TreeMarshallingStrategy());
+                break;
+            case ID_REFERENCES:
+                setMarshallingStrategy(new ReferenceByIdMarshallingStrategy());
+                break;
+            case XPATH_RELATIVE_REFERENCES:
+                setMarshallingStrategy(new ReferenceByXPathMarshallingStrategy(
+                        ReferenceByXPathMarshallingStrategy.RELATIVE));
+                break;
+            case XPATH_ABSOLUTE_REFERENCES:
+                setMarshallingStrategy(new ReferenceByXPathMarshallingStrategy(
+                        ReferenceByXPathMarshallingStrategy.ABSOLUTE));
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown mode : " + mode);
+        }
+    }
+
+    /**
+     * Adds a default implicit collection which is used for any unmapped xml tag.
+     *
+     * @param ownerType class owning the implicit collection
+     * @param fieldName name of the field in the ownerType. This field must be an
+     *                  <code>java.util.ArrayList</code>.
+     */
+    public void addImplicitCollection(Class ownerType, String fieldName) {
+        if (implicitCollectionMapper == null) {
+            throw new InitializationException("No "
+                    + ImplicitCollectionMapper.class.getName()
+                    + " available");
+        }
+        implicitCollectionMapper.add(ownerType, fieldName, null, null);
+    }
+
+    /**
+     * Adds implicit collection which is used for all items of the given itemType.
+     *
+     * @param ownerType class owning the implicit collection
+     * @param fieldName name of the field in the ownerType. This field must be an
+     *                  <code>java.util.ArrayList</code>.
+     * @param itemType  type of the items to be part of this collection.
+     * @throws InitializationException if no {@link ImplicitCollectionMapper} is available
+     */
+    public void addImplicitCollection(Class ownerType, String fieldName, Class itemType) {
+        if (implicitCollectionMapper == null) {
+            throw new InitializationException("No "
+                    + ImplicitCollectionMapper.class.getName()
+                    + " available");
+        }
+        implicitCollectionMapper.add(ownerType, fieldName, null, itemType);
+    }
+
+    /**
+     * Adds implicit collection which is used for all items of the given element name defined by
+     * itemFieldName.
+     *
+     * @param ownerType     class owning the implicit collection
+     * @param fieldName     name of the field in the ownerType. This field must be an
+     *                      <code>java.util.ArrayList</code>.
+     * @param itemFieldName element name of the implicit collection
+     * @param itemType      item type to be aliases be the itemFieldName
+     * @throws InitializationException if no {@link ImplicitCollectionMapper} is available
+     */
+    public void addImplicitCollection(
+            Class ownerType, String fieldName, String itemFieldName, Class itemType) {
+        if (implicitCollectionMapper == null) {
+            throw new InitializationException("No "
+                    + ImplicitCollectionMapper.class.getName()
+                    + " available");
+        }
+        implicitCollectionMapper.add(ownerType, fieldName, itemFieldName, itemType);
+    }
+
+    /**
+     * Create a DataHolder that can be used to pass data to the converters. The DataHolder is provided with a
+     * call to {@link #marshal(Object, HierarchicalStreamWriter, DataHolder)} or
+     * {@link #unmarshal(HierarchicalStreamReader, Object, DataHolder)}.
+     *
+     * @return a new {@link DataHolder}
+     */
+    public DataHolder newDataHolder() {
+        return new MapBackedDataHolder();
+    }
+
+    /**
+     * Creates an ObjectOutputStream that serializes a stream of objects to the writer using
+     * XStream.
+     * <p>
+     * To change the name of the root element (from &lt;object-stream&gt;), use
+     * {@link #createObjectOutputStream(java.io.Writer, String)}.
+     * </p>
+     *
+     * @see #createObjectOutputStream(com.thoughtworks.xstream.io.HierarchicalStreamWriter, String)
+     * @see #createObjectInputStream(com.thoughtworks.xstream.io.HierarchicalStreamReader)
+     * @since 1.0.3
+     */
+    public ObjectOutputStream createObjectOutputStream(Writer writer) throws IOException {
+        return createObjectOutputStream(hierarchicalStreamDriver.createWriter(writer), "object-stream");
+    }
+
+    /**
+     * Creates an ObjectOutputStream that serializes a stream of objects to the writer using
+     * XStream.
+     * <p>
+     * To change the name of the root element (from &lt;object-stream&gt;), use
+     * {@link #createObjectOutputStream(java.io.Writer, String)}.
+     * </p>
+     *
+     * @see #createObjectOutputStream(com.thoughtworks.xstream.io.HierarchicalStreamWriter, String)
+     * @see #createObjectInputStream(com.thoughtworks.xstream.io.HierarchicalStreamReader)
+     * @since 1.0.3
+     */
+    public ObjectOutputStream createObjectOutputStream(HierarchicalStreamWriter writer)
+            throws IOException {
+        return createObjectOutputStream(writer, "object-stream");
+    }
+
+    /**
+     * Creates an ObjectOutputStream that serializes a stream of objects to the writer using
+     * XStream.
+     *
+     * @see #createObjectOutputStream(com.thoughtworks.xstream.io.HierarchicalStreamWriter, String)
+     * @see #createObjectInputStream(com.thoughtworks.xstream.io.HierarchicalStreamReader)
+     * @since 1.0.3
+     */
+    public ObjectOutputStream createObjectOutputStream(Writer writer, String rootNodeName)
+            throws IOException {
+        return createObjectOutputStream(hierarchicalStreamDriver.createWriter(writer), rootNodeName);
+    }
+
+    /**
+     * Creates an ObjectOutputStream that serializes a stream of objects to the writer using
+     * XStream.
+     * <p>
+     * Because an ObjectOutputStream can contain multiple items and XML only allows a single root
+     * node, the stream must be written inside an enclosing node.
+     * </p>
+     * <p>
+     * It is necessary to call ObjectOutputStream.close() when done, otherwise the stream will be
+     * incomplete.
+     * </p>
+     * <h3>Example</h3>
+     * <p/>
+     * <pre>
+     *  ObjectOutputStream out = xstream.createObjectOutputStream(aWriter, &quot;things&quot;);
+     *   out.writeInt(123);
+     *   out.writeObject(&quot;Hello&quot;);
+     *   out.writeObject(someObject)
+     *   out.close();
+     * </pre>
+     *
+     * @param writer       The writer to serialize the objects to.
+     * @param rootNodeName The name of the root node enclosing the stream of objects.
+     * @see #createObjectInputStream(com.thoughtworks.xstream.io.HierarchicalStreamReader)
+     * @since 1.0.3
+     */
+    public ObjectOutputStream createObjectOutputStream(
+            final HierarchicalStreamWriter writer, String rootNodeName) throws IOException {
+        final StatefulWriter statefulWriter = new StatefulWriter(writer);
+        statefulWriter.startNode(rootNodeName, null);
+        return new CustomObjectOutputStream(new CustomObjectOutputStream.StreamCallback() {
+            public void writeToStream(Object object) {
+                marshal(object, statefulWriter);
+            }
+
+            public void writeFieldsToStream(Map fields) throws NotActiveException {
+                throw new NotActiveException("not in call to writeObject");
+            }
+
+            public void defaultWriteObject() throws NotActiveException {
+                throw new NotActiveException("not in call to writeObject");
+            }
+
+            public void flush() {
+                statefulWriter.flush();
+            }
+
+            public void close() {
+                if (statefulWriter.state() != StatefulWriter.STATE_CLOSED) {
+                    statefulWriter.endNode();
+                    statefulWriter.close();
+                }
+            }
+        });
+    }
+
+    /**
+     * Creates an ObjectInputStream that deserializes a stream of objects from a reader using
+     * XStream.
+     *
+     * @see #createObjectInputStream(com.thoughtworks.xstream.io.HierarchicalStreamReader)
+     * @see #createObjectOutputStream(com.thoughtworks.xstream.io.HierarchicalStreamWriter, String)
+     * @since 1.0.3
+     */
+    public ObjectInputStream createObjectInputStream(Reader xmlReader) throws IOException {
+        return createObjectInputStream(hierarchicalStreamDriver.createReader(xmlReader));
+    }
+
+    /**
+     * Creates an ObjectInputStream that deserializes a stream of objects from a reader using
+     * XStream.
+     * <h3>Example</h3>
+     * <p/>
+     * <pre>
+     * ObjectInputStream in = xstream.createObjectOutputStream(aReader);
+     * int a = out.readInt();
+     * Object b = out.readObject();
+     * Object c = out.readObject();
+     * </pre>
+     *
+     * @see #createObjectOutputStream(com.thoughtworks.xstream.io.HierarchicalStreamWriter, String)
+     * @since 1.0.3
+     */
+    public ObjectInputStream createObjectInputStream(final HierarchicalStreamReader reader)
+            throws IOException {
+        return new CustomObjectInputStream(new CustomObjectInputStream.StreamCallback() {
+            public Object readFromStream() throws EOFException {
+                if (!reader.hasMoreChildren()) {
+                    throw new EOFException();
+                }
+                reader.moveDown();
+                Object result = unmarshal(reader);
+                reader.moveUp();
+                return result;
+            }
+
+            public Map readFieldsFromStream() throws IOException {
+                throw new NotActiveException("not in call to readObject");
+            }
+
+            public void defaultReadObject() throws NotActiveException {
+                throw new NotActiveException("not in call to readObject");
+            }
+
+            public void registerValidation(ObjectInputValidation validation, int priority)
+                    throws NotActiveException {
+                throw new NotActiveException("stream inactive");
+            }
+
+            public void close() {
+                reader.close();
+            }
+        });
+    }
+
+    /**
+     * Change the ClassLoader XStream uses to load classes.
+     *
+     * @since 1.1.1
+     */
+    public void setClassLoader(ClassLoader classLoader) {
+        classLoaderReference.setReference(classLoader);
+    }
+
+    /**
+     * Change the ClassLoader XStream uses to load classes.
+     *
+     * @since 1.1.1
+     */
+    public ClassLoader getClassLoader() {
+        return classLoaderReference.getReference();
+    }
+
+    /**
+     * Prevents a field from being serialized. To omit a field you must always provide the declaring
+     * type and not necessarily the type that is converted.
+     *
+     * @throws InitializationException if no {@link FieldAliasingMapper} is available
+     * @since 1.1.3
+     */
+    public void omitField(Class definedIn, String fieldName) {
+        if (fieldAliasingMapper == null) {
+            throw new InitializationException("No "
+                    + FieldAliasingMapper.class.getName()
+                    + " available");
+        }
+        fieldAliasingMapper.omitField(definedIn, fieldName);
+    }
+
+    /**
+     * Process the annotations of the given types and configure the XStream.
+     *
+     * @param types the types with XStream annotations
+     * @since upcoming
+     */
+    public void processAnnotations(final Class[] types) {
+        if (annotationConfiguration == null) {
+            throw new InitializationException("No " + ANNOTATION_MAPPER_TYPE + " available");
+        }
+        annotationConfiguration.processAnnotations(types);
+    }
+
+    /**
+     * Process the annotations of the given type and configure the XStream. A call of this method
+     * will automatically turn the auto-detection mode for annotations off.
+     *
+     * @param type the type with XStream annotations
+     * @since upcoming
+     */
+    public void processAnnotations(final Class type) {
+        processAnnotations(new Class[]{type});
+    }
+
+    /**
+     * Set the auto-detection mode of the AnnotationMapper. Note that auto-detection implies that
+     * the XStream is configured while it is processing the XML steams. This is a potential concurrency
+     * problem. Also is it technically not possible to detect all class aliases at deserialization. You have
+     * been warned!
+     *
+     * @param mode <code>true</code> if annotations are auto-detected
+     * @since upcoming
+     */
+    public void autodetectAnnotations(boolean mode) {
+        if (annotationConfiguration != null) {
+            annotationConfiguration.autodetectAnnotations(mode);
+        }
+    }
+
+    /**
+     * @deprecated since upcoming, use {@link InitializationException} instead
+     */
+    public static class InitializationException extends XStreamException {
+        public InitializationException(String message, Throwable cause) {
+            super(message, cause);
+        }
+
+        public InitializationException(String message) {
+            super(message);
+        }
+    }
+
+    private Object readResolve() {
+        jvm = new JVM();
+        return this;
+    }
+
+}
Index: ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/NodeTree.java
===================================================================
--- ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/NodeTree.java	Mon Feb 11 22:41:59 CET 2008
+++ ../trunk/xstream/src/java/com/thoughtworks/xstream/unknownnodes/NodeTree.java	Mon Feb 11 22:41:59 CET 2008
@@ -0,0 +1,66 @@
+package com.thoughtworks.xstream.unknownnodes;
+
+import java.util.*;
+
+/**
+ * This class is used for storing unknown nodes in XML.
+ * All data in nodes is stored: name, value, attributes and subnodes.
+ * Subnodes are stored recursivelly, forming a tree.
+ * User: peter
+ * Date: Jan 31, 2008
+ * Time: 6:24:48 PM
+ */
+public class NodeTree {
+
+    public String name;
+    public String value;
+    //    public Map<String, String> attributes = new HashMap<String, String>();
+    //    public List<NodeTree> subnodes = new ArrayList<NodeTree>();
+    public Map attributes = new HashMap();
+    public List subnodes = new ArrayList();
+
+    public String toString(String prefix, int depth) {
+        StringBuilder str = new StringBuilder();
+        StringBuilder of = new StringBuilder();
+        for (int c = 0; c < depth; c++) {
+            of.append(prefix);
+        }
+        String offset = of.append(prefix).toString();
+
+        //node name
+        str.append("\n").append(offset).append("<").append(name);
+
+        // attributes
+        Iterator it = attributes.keySet().iterator();
+        while (it.hasNext()) {
+            Object o = it.next();
+            String attrkey = (String) o;
+            str.append(" ").append(attrkey).append("=\"").append(attributes.get(attrkey)).append("\"");
+        }
+        str.append(">");
+
+        // number of subnodes
+        int subs = subnodes.size();
+
+        // node value (text)
+        if (subs > 0) {
+//            str.append("\n").append(offset);
+        }
+        str.append(value);
+
+        // subnodes (recursivelly)
+        int subdepth = ++depth;
+        for (int i = 0; i < subs; i++) {
+            Object o = subnodes.get(i);
+            NodeTree subnode = (NodeTree) o;
+            str.append(subnode.toString(prefix, subdepth));
+        }
+        if (subs > 0) {
+            str.append("\n").append(offset);
+        }
+        str.append("</").append(name).append(">");
+
+        return str.toString();
+    }
+
+}
Index: ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/collections/CollectionConverter.java
===================================================================
--- ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/collections/CollectionConverter.java	(revision 1404)
+++ ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/collections/CollectionConverter.java	Mon Feb 11 22:41:59 CET 2008
@@ -17,6 +17,8 @@
 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
 import com.thoughtworks.xstream.mapper.Mapper;
+import com.thoughtworks.xstream.unknownnodes.NodeStorage;
+import com.thoughtworks.xstream.unknownnodes.UnknownNode;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -40,6 +42,12 @@
         super(mapper);
     }
 
+    public CollectionConverter(Mapper mapper, NodeStorage nodeStorage) {
+        super(mapper);
+        this.nodeStorage = nodeStorage;
+        this.cacheUnknownNodes = true;
+    }
+
     public boolean canConvert(Class type) {
         return type.equals(ArrayList.class)
                 || type.equals(HashSet.class)
@@ -54,7 +62,11 @@
             Object item = iterator.next();
             writeItem(item, context, writer);
         }
+
+        if (cacheUnknownNodes) {
+            nodeStorage.restoreNodeTree(source, writer);
-    }
+        }
+    }
 
     public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
         Collection collection = (Collection) createCollection(context.getRequiredType());
@@ -65,8 +77,23 @@
     protected void populateCollection(HierarchicalStreamReader reader, UnmarshallingContext context, Collection collection) {
         while (reader.hasMoreChildren()) {
             reader.moveDown();
+
+             // determine type
+            String classAttribute = reader.getAttribute(mapper().aliasForAttribute("class"));
+            Class type;
+            if (classAttribute == null) {
+                type = mapper().realClass(reader.getNodeName());
+            } else {
+                type = mapper().realClass(classAttribute);
+            }
+
+            // UnknownNode happens only when unknownnodes unknown nodes is enabled
+            if (UnknownNode.class.equals(type)) {
+                nodeStorage.saveNodeTree(collection, reader);
+            } else {
-            Object item = readItem(reader, context, collection);
-            collection.add(item);
+                Object item = readItem(reader, context, collection);
+                collection.add(item);
+            }
             reader.moveUp();
         }
     }
Index: ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractReflectionConverter.java
===================================================================
--- ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractReflectionConverter.java	(revision 1404)
+++ ../trunk/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractReflectionConverter.java	Mon Feb 11 22:41:59 CET 2008
@@ -11,25 +11,18 @@
  */
 package com.thoughtworks.xstream.converters.reflection;
 
-import com.thoughtworks.xstream.converters.ConversionException;
-import com.thoughtworks.xstream.converters.Converter;
-import com.thoughtworks.xstream.converters.MarshallingContext;
-import com.thoughtworks.xstream.converters.SingleValueConverter;
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.converters.*;
 import com.thoughtworks.xstream.core.util.Primitives;
+import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
-import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
 import com.thoughtworks.xstream.mapper.Mapper;
+import com.thoughtworks.xstream.unknownnodes.NodeStorage;
+import com.thoughtworks.xstream.unknownnodes.UnknownNode;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 public abstract class AbstractReflectionConverter implements Converter {
 
@@ -38,6 +31,9 @@
     protected transient SerializationMethodInvoker serializationMethodInvoker;
     private transient ReflectionProvider pureJavaReflectionProvider;
 
+    protected NodeStorage nodeStorage = null;
+    protected boolean cacheUnknownNodes = false;
+
     public AbstractReflectionConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
         this.mapper = mapper;
         this.reflectionProvider = reflectionProvider;
@@ -58,8 +54,13 @@
         final Set seenFields = new HashSet();
         final Map defaultFieldDefinition = new HashMap();
 
+        // restore unknown attributes that were saved to nodeStorage
+        if (cacheUnknownNodes) {
+            nodeStorage.restoreAttributes(source, writer);
+        }
+
         // Attributes might be preferred to child elements ...
-         reflectionProvider.visitSerializableFields(source, new ReflectionProvider.Visitor() {
+        reflectionProvider.visitSerializableFields(source, new ReflectionProvider.Visitor() {
             public void visit(String fieldName, Class type, Class definedIn, Object value) {
                 if (!mapper.shouldSerializeMember(definedIn, fieldName)) {
                     return;
@@ -72,9 +73,9 @@
                     // }
                     defaultFieldDefinition.put(fieldName, reflectionProvider.getField(lookupType, fieldName));
                 }
-                
+
                 SingleValueConverter converter = mapper.getConverterFromItemType(fieldName, type, definedIn);
-                if(converter == null) {
+                if (converter == null) {
                     converter = mapper.getConverterFromItemType(fieldName, type);
                 }
                 if (converter == null) {
@@ -83,8 +84,8 @@
                 if (converter != null) {
                     if (value != null) {
                         if (seenFields.contains(fieldName)) {
-                            throw new ConversionException("Cannot write field with name '" + fieldName 
-                                + "' twice as attribute for object of type " + source.getClass().getName());
+                            throw new ConversionException("Cannot write field with name '" + fieldName
+                                    + "' twice as attribute for object of type " + source.getClass().getName());
                         }
                         final String str = converter.toString(value);
                         if (str != null) {
@@ -122,7 +123,7 @@
             }
 
             private void writeField(String fieldName, String aliasName, Class fieldType, Class definedIn, Object newObj) {
-                ExtendedHierarchicalStreamWriterHelper.startNode(writer, mapper.serializedMember(source.getClass(), aliasName), fieldType); 
+                ExtendedHierarchicalStreamWriterHelper.startNode(writer, mapper.serializedMember(source.getClass(), aliasName), fieldType);
 
                 Class actualType = newObj.getClass();
 
@@ -131,18 +132,23 @@
                     writer.addAttribute(mapper.aliasForAttribute("class"), mapper.serializedClass(actualType));
                 }
 
-                final Field defaultField = (Field)defaultFieldDefinition.get(fieldName);
+                final Field defaultField = (Field) defaultFieldDefinition.get(fieldName);
                 if (defaultField.getDeclaringClass() != definedIn) {
                     writer.addAttribute(mapper.aliasForAttribute("defined-in"), mapper.serializedClass(definedIn));
                 }
 
-                Field field = reflectionProvider.getField(definedIn,fieldName);
+                Field field = reflectionProvider.getField(definedIn, fieldName);
                 marshallField(context, newObj, field);
                 writer.endNode();
             }
 
         });
+
+        // restore unknown subnodes stored to nodeStorage
+        if (cacheUnknownNodes) {
+            nodeStorage.restoreNodeTree(source, writer);
-    }
+        }
+    }
 
     protected void marshallField(final MarshallingContext context, Object newObj, Field field) {
         context.convertAnother(newObj, mapper.getLocalConverter(field.getDeclaringClass(), field.getName()));
@@ -188,6 +194,8 @@
                     reflectionProvider.writeField(result, attrName, value, classDefiningField);
                     seenFields.add(classDefiningField, attrName);
                 }
+            } else if (cacheUnknownNodes) {
+                nodeStorage.saveAttribute(result, attrAlias, reader.getAttribute(attrAlias));
             }
         }
 
@@ -203,10 +211,13 @@
             boolean fieldExistsInClass = implicitCollectionMapping == null && reflectionProvider.fieldDefinedInClass(fieldName, result.getClass());
 
             Class type = implicitCollectionMapping == null
-                ? determineType(reader, fieldExistsInClass, result, fieldName, classDefiningField) 
-                : implicitCollectionMapping.getItemType();
+                    ? determineType(reader, fieldExistsInClass, result, fieldName, classDefiningField)
+                    : implicitCollectionMapping.getItemType();
+
+            // is subnode that we try to process actually an unknown node?
+            boolean isUnknownNode = UnknownNode.class.equals(type);
             final Object value;
-            if (fieldExistsInClass) {
+            if (fieldExistsInClass && !isUnknownNode) {
                 Field field = reflectionProvider.getField(classDefiningField != null ? classDefiningField : result.getClass(), fieldName);
                 if (Modifier.isTransient(field.getModifiers())) {
                     reader.moveUp();
@@ -218,10 +229,15 @@
                 if (!definedType.isPrimitive()) {
                     type = definedType;
                 }
+            } else if (isUnknownNode) {
+                // save subnodes tree to nodeStorage
+                nodeStorage.saveNodeTree(result, reader);
+                reader.moveUp();
+                continue;
             } else {
                 value = type != null ? context.convertAnother(result, type) : null;
             }
-            
+
             if (value != null && !type.isAssignableFrom(value.getClass())) {
                 throw new ConversionException("Cannot convert type " + value.getClass().getName() + " to type " + type.getName());
             }
@@ -259,7 +275,7 @@
                 if (pureJavaReflectionProvider == null) {
                     pureJavaReflectionProvider = new PureJavaReflectionProvider();
                 }
-                collection = (Collection)pureJavaReflectionProvider.newInstance(fieldType);
+                collection = (Collection) pureJavaReflectionProvider.newInstance(fieldType);
                 reflectionProvider.writeField(result, fieldName, collection, null);
                 implicitCollections.put(fieldName, collection);
             }
@@ -314,7 +330,8 @@
             } else {
                 String originalNodeName = reader.getNodeName();
                 if (definedInCls == null) {
-                    for(definedInCls = result.getClass(); definedInCls != null; definedInCls = definedInCls.getSuperclass()) {
+                    for (definedInCls = result.getClass(); definedInCls != null; definedInCls = definedInCls.getSuperclass())
+                    {
                         if (!mapper.shouldSerializeMember(definedInCls, originalNodeName)) {
                             return null;
                         }
@@ -326,7 +343,7 @@
             return mapper.defaultImplementationOf(reflectionProvider.getFieldType(result, fieldName, definedInCls));
         }
     }
-    
+
     private Object readResolve() {
         serializationMethodInvoker = new SerializationMethodInvoker();
         return this;
Index: ../trunk/xstream/src/test/com/thoughtworks/xstream/unknownnodes/UnknownNodesTest.java
===================================================================
--- ../trunk/xstream/src/test/com/thoughtworks/xstream/unknownnodes/UnknownNodesTest.java	Mon Feb 11 22:41:59 CET 2008
+++ ../trunk/xstream/src/test/com/thoughtworks/xstream/unknownnodes/UnknownNodesTest.java	Mon Feb 11 22:41:59 CET 2008
@@ -0,0 +1,111 @@
+package com.thoughtworks.xstream.unknownnodes;
+
+import com.thoughtworks.xstream.XStream;
+import junit.framework.TestCase;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ * User: peter
+ * Date: Feb 1, 2008
+ * Time: 3:47:10 PM
+ */
+public class UnknownNodesTest extends TestCase {
+
+    String xml = "<person personAttribute=\"justPerson\">"
+            + "<number lastattr=\"AAA\">42</number>"
+            + "<firstname>"
+            + "Joe"
+            + "<unkNode>nnn</unkNode>"
+            + "</firstname>"
+            + "<fax unknownAttrib=\"xxx\">"
+            + "justAValue"
+            + "<code>321</code>"
+            + "<number>9999-999</number>"
+            + "</fax>"
+            + "<phone newAttrib=\"unknown??\">"
+            + "    <code>123</code>"
+            + "<number>1234-456</number>"
+            + "</phone>"
+            + "</person>";
+
+    String xml2 = "<person personAttribute=\"justPerson\">"
+            + "<number lastattr=\"AAA\">42</number>"
+            + "<firstname>Joe</firstname>"
+            + "<numbers>"
+            + "<fax unknownAttrib=\"xxx\">"
+            + "justAValue"
+            + "  <code>321</code>"
+            + "  <number>9999-999</number>"
+            + "</fax>"
+            + "<secondUnknownNode>xxx</secondUnknownNode>"
+            + "<phone newAttrib=\"unknown??\">"
+            + "  <code>123</code>"
+            + "  <number>1234-456</number>"
+            + "</phone>"
+            + "</numbers>"
+            + "</person>";
+
+    XStreamCaching xc;
+    XStream xs;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        xc = new XStreamCaching();
+//        xc.autodetectAnnotations(true);
+        xc.alias("person", Person.class);
+        xc.alias("phone", PhoneNumber.class);
+        xs = new XStream();
+        xs.alias("person", Person.class);
+        xs.alias("phone", PhoneNumber.class);
+    }
+
+
+    public void testPerson() {
+
+        Person person = (Person) xc.fromXML(xml);
+        assertEquals(person.firstname, "Joe");
+        assertEquals(person.number, 42);
+
+        String out = xc.toXML(person);
+        System.out.println(out);
+
+        System.out.println(xc.storedUnknownNodes.toString());
+
+//        printObject(person);
+    }
+
+    //    @XStreamAlias("person")
+    public static class Person {
+        public String firstname;
+        public int number;
+        public List numbers;
+        public PhoneNumber phone;
+
+        //this is an unknown node
+//    public PhoneNumber fax;
+    }
+
+    //    @XStreamAlias("phone")
+    public static class PhoneNumber {
+        public int code;
+        public String number;
+    }
+
+    public static void printObject(Object object) {
+        System.out.println(object.getClass() + " " + object.hashCode());
+        Field[] fields = object.getClass().getFields();
+        for (int i = 0; i < fields.length; i++) {
+            try {
+                System.out.println("  field: " + fields[i].get(object).getClass() + " " + fields[i].get(object).hashCode());
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+}
+
+
+

