Index: E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ExternalizableConverter.java =================================================================== --- E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ExternalizableConverter.java (revision 865) +++ E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ExternalizableConverter.java (working copy) @@ -67,8 +67,9 @@ throw new UnsupportedOperationException("Objects are not allowed to call ObjecOutput.close() from writeExternal()"); } }; - ObjectOutput objectOutput = CustomObjectOutputStream.getInstance(context, callback); + CustomObjectOutputStream objectOutput = CustomObjectOutputStream.getInstance(context, callback); externalizable.writeExternal(objectOutput); + objectOutput.popCallback(); } catch (IOException e) { throw new ConversionException("Cannot serialize " + source.getClass().getName() + " using Externalization", e); } @@ -102,8 +103,9 @@ throw new UnsupportedOperationException("Objects are not allowed to call ObjectInput.close() from readExternal()"); } }; - ObjectInput objectInput = CustomObjectInputStream.getInstance(context, callback); + CustomObjectInputStream objectInput = CustomObjectInputStream.getInstance(context, callback); externalizable.readExternal(objectInput); + objectInput.popCallback(); return externalizable; } catch (InstantiationException e) { throw new ConversionException("Cannot construct " + type.getClass(), e); Index: E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java =================================================================== --- E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java (revision 865) +++ E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java (working copy) @@ -5,6 +5,7 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.core.util.CustomObjectInputStream; import com.thoughtworks.xstream.core.util.CustomObjectOutputStream; +import com.thoughtworks.xstream.core.util.CustomObjectOutputStream.StreamCallback; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.mapper.Mapper; @@ -185,8 +186,9 @@ if (serializationMethodInvoker.supportsWriteObject(currentType[0], false)) { writtenClassWrapper[0] = true; writer.startNode(mapper.serializedClass(currentType[0])); - ObjectOutputStream objectOutputStream = CustomObjectOutputStream.getInstance(context, callback); + CustomObjectOutputStream objectOutputStream = CustomObjectOutputStream.getInstance(context, callback); serializationMethodInvoker.callWriteObject(currentType[0], source, objectOutputStream); + objectOutputStream.popCallback(); writer.endNode(); } else if (serializationMethodInvoker.supportsReadObject(currentType[0], false)) { // Special case for objects that have readObject(), but not writeObject(). @@ -361,8 +363,9 @@ } else { currentType[0] = mapper.defaultImplementationOf(mapper.realClass(nodeName)); if (serializationMethodInvoker.supportsReadObject(currentType[0], false)) { - ObjectInputStream objectInputStream = CustomObjectInputStream.getInstance(context, callback); + CustomObjectInputStream objectInputStream = CustomObjectInputStream.getInstance(context, callback); serializationMethodInvoker.callReadObject(currentType[0], result, objectInputStream); + objectInputStream.popCallback(); } else { try { callback.defaultReadObject(); Index: E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectInputStream.java =================================================================== --- E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectInputStream.java (revision 865) +++ E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectInputStream.java (working copy) @@ -2,6 +2,7 @@ import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.DataHolder; +import com.thoughtworks.xstream.core.util.CustomObjectOutputStream.StreamCallback; import java.io.IOException; import java.io.InvalidObjectException; @@ -13,7 +14,7 @@ public class CustomObjectInputStream extends ObjectInputStream { - private StreamCallback callback; + private FastStack callbacks = new FastStack(1); private static final String DATA_HOLDER_KEY = CustomObjectInputStream.class.getName(); @@ -32,7 +33,7 @@ result = new CustomObjectInputStream(callback); whereFrom.put(DATA_HOLDER_KEY, result); } else { - result.setCallback(callback); + result.pushCallback(callback); } return result; } catch (IOException e) { @@ -48,7 +49,7 @@ */ public CustomObjectInputStream(StreamCallback callback) throws IOException, SecurityException { super(); - this.callback = callback; + this.callbacks.push(callback); } /** @@ -54,52 +55,54 @@ /** * Allows the CustomObjectInputStream (which is expensive to create) to be reused. */ - public void setCallback(StreamCallback callback) { - this.callback = callback; + public void pushCallback(StreamCallback callback) { + this.callbacks.push(callback); + } + public StreamCallback popCallback(){ + return (StreamCallback) this.callbacks.pop(); } - public void defaultReadObject() throws IOException { - callback.defaultReadObject(); + ((StreamCallback) callbacks.peek()).defaultReadObject(); } protected Object readObjectOverride() throws IOException { - return callback.readFromStream(); + return ((StreamCallback) callbacks.peek()).readFromStream(); } public boolean readBoolean() throws IOException { - return ((Boolean)callback.readFromStream()).booleanValue(); + return ((Boolean)((StreamCallback) callbacks.peek()).readFromStream()).booleanValue(); } public byte readByte() throws IOException { - return ((Byte)callback.readFromStream()).byteValue(); + return ((Byte)((StreamCallback) callbacks.peek()).readFromStream()).byteValue(); } public int readInt() throws IOException { - return ((Integer)callback.readFromStream()).intValue(); + return ((Integer)((StreamCallback) callbacks.peek()).readFromStream()).intValue(); } public char readChar() throws IOException { - return ((Character)callback.readFromStream()).charValue(); + return ((Character)((StreamCallback) callbacks.peek()).readFromStream()).charValue(); } public float readFloat() throws IOException { - return ((Float)callback.readFromStream()).floatValue(); + return ((Float)((StreamCallback) callbacks.peek()).readFromStream()).floatValue(); } public double readDouble() throws IOException { - return ((Double)callback.readFromStream()).doubleValue(); + return ((Double)((StreamCallback) callbacks.peek()).readFromStream()).doubleValue(); } public long readLong() throws IOException { - return ((Long)callback.readFromStream()).longValue(); + return ((Long)((StreamCallback) callbacks.peek()).readFromStream()).longValue(); } public short readShort() throws IOException { - return ((Short)callback.readFromStream()).shortValue(); + return ((Short)((StreamCallback) callbacks.peek()).readFromStream()).shortValue(); } public String readUTF() throws IOException { - return (String) callback.readFromStream(); + return (String) ((StreamCallback) callbacks.peek()).readFromStream(); } public void readFully(byte[] buf) throws IOException { @@ -107,7 +110,7 @@ } public void readFully(byte[] buf, int off, int len) throws IOException { - byte[] b = (byte[])callback.readFromStream(); + byte[] b = (byte[])((StreamCallback) callbacks.peek()).readFromStream(); System.arraycopy(b, 0, buf, off, len); } @@ -112,7 +115,7 @@ } public GetField readFields() throws IOException { - return new CustomGetField(callback.readFieldsFromStream()); + return new CustomGetField(((StreamCallback) callbacks.peek()).readFieldsFromStream()); } private class CustomGetField extends GetField { @@ -174,11 +177,11 @@ } public void registerValidation(ObjectInputValidation validation, int priority) throws NotActiveException, InvalidObjectException { - callback.registerValidation(validation, priority); + ((StreamCallback) callbacks.peek()).registerValidation(validation, priority); } public void close() throws IOException { - callback.close(); + ((StreamCallback) callbacks.peek()).close(); } /****** Unsupported methods ******/ Index: E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectOutputStream.java =================================================================== --- E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectOutputStream.java (revision 865) +++ E:/data/workspace/xstream/src/java/com/thoughtworks/xstream/core/util/CustomObjectOutputStream.java (working copy) @@ -10,7 +10,7 @@ public class CustomObjectOutputStream extends ObjectOutputStream { - private StreamCallback callback; + private FastStack callbacks = new FastStack(1); private FastStack customFields = new FastStack(1); private static final String DATA_HOLDER_KEY = CustomObjectOutputStream.class.getName(); @@ -16,6 +16,7 @@ private static final String DATA_HOLDER_KEY = CustomObjectOutputStream.class.getName(); public static synchronized CustomObjectOutputStream getInstance(DataHolder whereFrom, StreamCallback callback) { + //System.out.println("> CustomObjectOutputStream.getInstance(callback=" + callback + ")"); try { CustomObjectOutputStream result = (CustomObjectOutputStream) whereFrom.get(DATA_HOLDER_KEY); if (result == null) { @@ -22,8 +23,9 @@ result = new CustomObjectOutputStream(callback); whereFrom.put(DATA_HOLDER_KEY, result); } else { - result.setCallback(callback); + result.pushCallback(callback); } + //System.out.println("< CustomObjectOutputStream.getInstance(callback=" + callback + "):" + result); return result; } catch (IOException e) { throw new ConversionException("Cannot create CustomObjectStream", e); @@ -36,6 +38,7 @@ void defaultWriteObject() throws IOException; void flush() throws IOException; void close() throws IOException; + } /** @@ -45,7 +48,7 @@ * @see #getInstance(com.thoughtworks.xstream.converters.DataHolder, com.thoughtworks.xstream.core.util.CustomObjectOutputStream.StreamCallback) */ public CustomObjectOutputStream(StreamCallback callback) throws IOException, SecurityException { - this.callback = callback; + this.callbacks.push(callback); } /** @@ -51,66 +54,68 @@ /** * Allows the CustomObjectOutputStream (which is expensive to create) to be reused. */ - public void setCallback(StreamCallback callback) { - this.callback = callback; + public void pushCallback(StreamCallback callback) { + this.callbacks.push(callback); + } + public StreamCallback popCallback(){ + return (StreamCallback) this.callbacks.pop(); } - /*** Methods to delegate to callback ***/ public void defaultWriteObject() throws IOException { - callback.defaultWriteObject(); + ((StreamCallback) callbacks.peek()).defaultWriteObject(); } protected void writeObjectOverride(Object obj) throws IOException { - callback.writeToStream(obj); + ((StreamCallback) callbacks.peek()).writeToStream(obj); } public void writeBoolean(boolean val) throws IOException { - callback.writeToStream(val ? Boolean.TRUE : Boolean.FALSE); // JDK 1.3 friendly + ((StreamCallback) callbacks.peek()).writeToStream(val ? Boolean.TRUE : Boolean.FALSE); // JDK 1.3 friendly } public void writeByte(int val) throws IOException { - callback.writeToStream(new Byte((byte) val)); + ((StreamCallback) callbacks.peek()).writeToStream(new Byte((byte) val)); } public void writeInt(int val) throws IOException { - callback.writeToStream(new Integer(val)); + ((StreamCallback) callbacks.peek()).writeToStream(new Integer(val)); } public void writeChar(int val) throws IOException { - callback.writeToStream(new Character((char)val)); + ((StreamCallback) callbacks.peek()).writeToStream(new Character((char)val)); } public void writeDouble(double val) throws IOException { - callback.writeToStream(new Double(val)); + ((StreamCallback) callbacks.peek()).writeToStream(new Double(val)); } public void writeFloat(float val) throws IOException { - callback.writeToStream(new Float(val)); + ((StreamCallback) callbacks.peek()).writeToStream(new Float(val)); } public void writeLong(long val) throws IOException { - callback.writeToStream(new Long(val)); + ((StreamCallback) callbacks.peek()).writeToStream(new Long(val)); } public void writeShort(int val) throws IOException { - callback.writeToStream(new Short((short) val)); + ((StreamCallback) callbacks.peek()).writeToStream(new Short((short) val)); } public void write(byte[] buf) throws IOException { - callback.writeToStream(buf); + ((StreamCallback) callbacks.peek()).writeToStream(buf); } public void writeChars(String str) throws IOException { - callback.writeToStream(str.toCharArray()); + ((StreamCallback) callbacks.peek()).writeToStream(str.toCharArray()); } public void writeUTF(String str) throws IOException { - callback.writeToStream(str); + ((StreamCallback) callbacks.peek()).writeToStream(str); } public void write(int val) throws IOException { - callback.writeToStream(new Byte((byte) val)); + ((StreamCallback) callbacks.peek()).writeToStream(new Byte((byte) val)); } public void write(byte[] buf, int off, int len) throws IOException { @@ -116,15 +121,15 @@ public void write(byte[] buf, int off, int len) throws IOException { byte[] b = new byte[len]; System.arraycopy(buf, off, b, 0, len); - callback.writeToStream(b); + ((StreamCallback) callbacks.peek()).writeToStream(b); } public void flush() throws IOException { - callback.flush(); + ((StreamCallback) callbacks.peek()).flush(); } public void close() throws IOException { - callback.close(); + ((StreamCallback) callbacks.peek()).close(); } public PutField putFields() { @@ -135,7 +140,7 @@ public void writeFields() throws IOException { CustomPutField customPutField = (CustomPutField) customFields.pop(); - callback.writeFieldsToStream(customPutField.asMap()); + ((StreamCallback) callbacks.peek()).writeFieldsToStream(customPutField.asMap()); } private class CustomPutField extends PutField { @@ -147,7 +152,7 @@ } public void write(ObjectOutput out) throws IOException { - callback.writeToStream(asMap()); + ((StreamCallback) callbacks.peek()).writeToStream(asMap()); } public void put(String name, Object val) { @@ -205,5 +210,9 @@ public void writeUnshared(Object obj) { throw new UnsupportedOperationException(); } + + public String toString() { + return "CustomObjectOutputstream[top callback=" + callbacks.peek() + ", customFields=" + customFields + "]"; + } } Index: E:/data/workspace/xstream/src/test/com/thoughtworks/acceptance/SerializationNestedWriteObjectsTest.java =================================================================== --- E:/data/workspace/xstream/src/test/com/thoughtworks/acceptance/SerializationNestedWriteObjectsTest.java (revision 0) +++ E:/data/workspace/xstream/src/test/com/thoughtworks/acceptance/SerializationNestedWriteObjectsTest.java (revision 0) @@ -0,0 +1,132 @@ +package com.thoughtworks.acceptance; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.io.StringReader; +import java.io.StringWriter; + +import com.thoughtworks.acceptance.AbstractAcceptanceTest; + +/** + *

+ * A class {@link Serializable} {@link Parent} class implements + * writeObject() and holds a {@link Child} class that also + * implements writeObject() + *

+ * + * @author Cyrille Le Clerc + */ +public class SerializationNestedWriteObjectsTest extends AbstractAcceptanceTest { + + public static class Child implements Serializable { + + private int i = 3; + + public Child(int i) { + this.i = i; + } + + public int getI() { + return i; + } + + private void readObject(java.io.ObjectInputStream in) throws IOException, + ClassNotFoundException { + in.defaultReadObject(); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + } + } + + public static class Parent implements Serializable { + + private String name; + + private transient Child child; + + public Parent(String name, Child child) { + this.name = name; + this.child = child; + } + + public Child getChild() { + return child; + } + + public String getName() { + return name; + } + + private void readObject(java.io.ObjectInputStream in) throws IOException, + ClassNotFoundException { + this.child = (Child) in.readObject(); + in.defaultReadObject(); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.writeObject(this.child); + out.defaultWriteObject(); + } + } + + public void testObjectInputStream() throws Exception { + xstream.alias("parent", Parent.class); + xstream.alias("child", Child.class); + + String sourceXml = "\n" + " \n" + + " \n" + " \n" + + " \n" + " \n" + " 1\n" + + " \n" + " \n" + " \n" + + " \n" + " ze-name\n" + " \n" + + " \n" + " \n" + ""; + + ObjectInputStream objectInputStream = xstream.createObjectInputStream(new StringReader( + sourceXml)); + + Parent parent = (Parent) objectInputStream.readObject(); + + assertEquals("ze-name", parent.getName()); + assertEquals(1, parent.getChild().getI()); + } + + public void testObjectOutputStream() throws Exception { + xstream.alias("parent", Parent.class); + xstream.alias("child", Child.class); + + String expectedXml = "\n" + " \n" + + " \n" + " \n" + + " \n" + " \n" + " 1\n" + + " \n" + " \n" + " \n" + + " \n" + " ze-name\n" + " \n" + + " \n" + " \n" + ""; + + Parent parent = new Parent("ze-name", new Child(1)); + StringWriter stringWriter = new StringWriter(); + ObjectOutputStream os = xstream.createObjectOutputStream(stringWriter); + os.writeObject(parent); + os.close(); + String actualXml = stringWriter.getBuffer().toString(); + assertEquals(expectedXml, actualXml); + } + + public void testToXML() { + + xstream.alias("parent", Parent.class); + xstream.alias("child", Child.class); + + String expected = "\n" + " \n" + + " \n" + " \n" + + " \n" + " 1\n" + " \n" + + " \n" + " \n" + " \n" + + " ze-name\n" + " \n" + " \n" + + ""; + + Parent parent = new Parent("ze-name", new Child(1)); + + assertBothWays(parent, expected); + } +} Property changes on: E:/data/workspace/xstream/src/test/com/thoughtworks/xstream/converters/collections ___________________________________________________________________ Name: svn:ignore + VectorConverterTest.java Property changes on: E:/data/workspace/xstream/src/test/com/thoughtworks/xstream/converters/reflection ___________________________________________________________________ Name: svn:ignore + SerializableConverterNestedWriteObjectTest.java SerializableConverterWithNonSerializableSuperClassTest.java SerializableConverterWithWriteObjectTest.java