Index: /home/guilherme/workspace/xstream/jdk-1.5-minimum.txt =================================================================== --- /home/guilherme/workspace/xstream/jdk-1.5-minimum.txt (revision 701) +++ /home/guilherme/workspace/xstream/jdk-1.5-minimum.txt (working copy) @@ -3,3 +3,5 @@ com/thoughtworks/acceptance/annotations/** com/thoughtworks/xstream/annotations/** +com/thoughtworks/xstream/converters/reflection/JavaAnnotation.* +com/thoughtworks/xstream/converters/reflection/JavaAnnotationsReflectionProvider.* Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamConverter.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamConverter.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/annotations/XStreamConverter.java (working copy) @@ -1,5 +1,6 @@ package com.thoughtworks.xstream.annotations; +import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -12,7 +13,8 @@ * @author Chung-Onn Cheong */ @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) +@Target({ElementType.TYPE, ElementType.FIELD}) +@Documented public @interface XStreamConverter { Class value(); } Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/MarshallingContext.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/MarshallingContext.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/MarshallingContext.java (working copy) @@ -1,8 +1,18 @@ package com.thoughtworks.xstream.converters; - public interface MarshallingContext extends DataHolder { + /** + * Converts another object searching for the default converter + * @param nextItem the next item to convert + */ void convertAnother(Object nextItem); + + /** + * Converts another object using the specified converter + * @param nextItem the next item to convert + * @param converter the converter to use + */ + void convertAnother(Object nextItem, Converter converter); } Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/UnmarshallingContext.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/UnmarshallingContext.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/UnmarshallingContext.java (working copy) @@ -4,6 +4,8 @@ Object convertAnother(Object current, Class type); + Object convertAnother(Object current, Class type, Converter converter); + Object currentObject(); Class getRequiredType(); Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/Annotation.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/Annotation.java (revision 0) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/Annotation.java (revision 0) @@ -0,0 +1,12 @@ +package com.thoughtworks.xstream.converters.reflection; + +/** + * A java annotation (in order to be java 1.3 compatible) + * @author Guilherme Silveira + * + */ +public interface Annotation { + + Object get(String name); + +} Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AnnotationsReflectionProvider.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AnnotationsReflectionProvider.java (revision 0) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/AnnotationsReflectionProvider.java (revision 0) @@ -0,0 +1,22 @@ +package com.thoughtworks.xstream.converters.reflection; + +import java.lang.reflect.Field; + +/** + * Annotations reflection provider (can be a pure 1.5 java or an asm 2.0 + * implementation for example) + * + * @author Guilherme Silveira + * + */ +public interface AnnotationsReflectionProvider { + + /** + * Returns an specific annotation value + * @param field the field + * @param annotationClass annotation class + * @return the annotation value + */ + Annotation getAnnotation(Field field, String annotationClass); + +} Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java (working copy) @@ -9,6 +9,10 @@ import java.util.Iterator; import java.util.Map; +/** + * A field dictionary instance caches information about classes fields. + * + */ public class FieldDictionary { private final Map keyedByFieldNameCache = Collections.synchronizedMap(new HashMap()); @@ -14,6 +18,11 @@ private final Map keyedByFieldNameCache = Collections.synchronizedMap(new HashMap()); private final Map keyedByFieldKeyCache = Collections.synchronizedMap(new HashMap()); + /** + * Returns an iterator for all serializable fields for some class + * @param cls the class you are interested on + * @return an iterator for its serializable fields + */ public Iterator serializableFieldsFor(Class cls) { return buildMap(cls, true).values().iterator(); } @@ -18,6 +27,15 @@ return buildMap(cls, true).values().iterator(); } + /** + * Returns an specific field of some class. If definedIn is null, it searchs for the field named 'name' inside the class cls. + * If definedIn is different than null, tries to find the specified field name in the specified class cls which should be defined in + * class definedIn (either equals cls or a one of it's superclasses) + * @param cls the class where the field is to be searched + * @param name the field name + * @param definedIn the superclass (or the class itself) of cls where the field was defined + * @return the field itself + */ public Field field(Class cls, String name, Class definedIn) { Map fields = buildMap(cls, definedIn != null); Field field = (Field) fields.get(definedIn != null ? (Object) new FieldKey(name, definedIn, 0) : (Object) name); Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/JavaAnnotation.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/JavaAnnotation.java (revision 0) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/JavaAnnotation.java (revision 0) @@ -0,0 +1,23 @@ +package com.thoughtworks.xstream.converters.reflection; + +import java.util.Map; + +/** + * A java wrapper for tiger (java 1.5) annotations + * + * @author Guilherme Silveira + * + */ +public class JavaAnnotation implements Annotation { + + private Map values; + + public JavaAnnotation(Map values) { + this.values = values; + } + + public Object get(String name) { + return values.get(name); + } + +} Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/JavaAnnotationsReflectionProvider.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/JavaAnnotationsReflectionProvider.java (revision 0) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/JavaAnnotationsReflectionProvider.java (revision 0) @@ -0,0 +1,53 @@ +package com.thoughtworks.xstream.converters.reflection; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * A pure java implementation of AnnotationsReflectionProvider. + * + * @author Guilherme Silveira + * + */ +public class JavaAnnotationsReflectionProvider implements + AnnotationsReflectionProvider { + + @SuppressWarnings("unchecked") + public Annotation getAnnotation(Field field, String annotationClass) { + try { + java.lang.annotation.Annotation annotation = field + .getAnnotation((Class) Class + .forName(annotationClass)); + if (annotation == null) { + return null; + } + Class annotationType = annotation.annotationType(); + // remember there is no annotation extension... + Map values = new HashMap(); + for (Method method : annotationType.getDeclaredMethods()) { + try { + String key = method.getName(); + Object value = method.invoke(annotation); + values.put(key, value); + } catch (IllegalArgumentException e) { + // so what? + e.printStackTrace(); + } catch (IllegalAccessException e) { + // so what? + e.printStackTrace(); + } catch (InvocationTargetException e) { + // so what? + e.printStackTrace(); + } + } + return new JavaAnnotation(values); + } catch (ClassNotFoundException e) { + throw new ObjectAccessException("Invalid annotation class " + + annotationClass); + } + } + +} Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java (working copy) @@ -32,8 +32,31 @@ public class PureJavaReflectionProvider implements ReflectionProvider { private final Map serializedDataCache = Collections.synchronizedMap(new HashMap()); + + private AnnotationsReflectionProvider annotationsProvider; + + protected FieldDictionary fieldDictionary = new FieldDictionary(); - protected FieldDictionary fieldDictionary = new FieldDictionary(); + public PureJavaReflectionProvider() { + try { + if (JVM.is15()) { + // be careful with public method invocation on constructor + annotationsProvider = (AnnotationsReflectionProvider) Class + .forName("com.thoughtworks.xstream.converters.reflection.JavaAnnotationsReflectionProvider").newInstance(); + } else { + annotationsProvider = new UselessAnnotationProvider(); + } + } catch (ClassNotFoundException e) { + // unable to initialize annotations provider!!! what should I throw? + e.printStackTrace(); + } catch (InstantiationException e) { + // unable to initialize annotations provider!!! what should I throw? + e.printStackTrace(); + } catch (IllegalAccessException e) { + // unable to initialize annotations provider!!! what should I throw? + e.printStackTrace(); + } + } public Object newInstance(Class type) { try { @@ -157,4 +180,18 @@ } } + public Class getOverridenConverter(Class definedIn, String fieldName) { + if (JVM.is15()) { + // i don't know where the field was actually defined, so pass null as an argument + Field field = fieldDictionary.field(definedIn, fieldName, null); + Annotation annotation = annotationsProvider.getAnnotation(field, + "com.thoughtworks.xstream.annotations.XStreamConverter"); + if (annotation == null) { + return null; + } + return (Class) annotation.get("value"); + } + return null; + } + } Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionConverter.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionConverter.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionConverter.java (working copy) @@ -64,7 +64,7 @@ Collection list = (Collection) newObj; for (Iterator iter = list.iterator(); iter.hasNext();) { Object obj = iter.next(); - writeField(mapping.getItemFieldName(), mapping.getItemType(), definedIn, obj); + writeField(fieldName, mapping.getItemFieldName(), mapping.getItemType(), definedIn, obj); } } else { context.convertAnother(newObj); @@ -70,7 +70,7 @@ context.convertAnother(newObj); } } else { - writeField(fieldName, fieldType, definedIn, newObj); + writeField(fieldName, fieldName, fieldType, definedIn, newObj); seenFields.add(fieldName); } } @@ -76,11 +76,11 @@ } } - private void writeField(String fieldName, Class fieldType, Class definedIn, Object newObj) { - if (!mapper.shouldSerializeMember(definedIn, fieldName)) { + private void writeField(String fieldName, String aliasName, Class fieldType, Class definedIn, Object newObj) { + if (!mapper.shouldSerializeMember(definedIn, aliasName)) { return; } - writer.startNode(mapper.serializedMember(definedIn, fieldName)); + writer.startNode(mapper.serializedMember(definedIn, aliasName)); Class actualType = newObj.getClass(); @@ -89,7 +89,7 @@ writer.addAttribute(mapper.attributeForImplementationClass(), mapper.serializedClass(actualType)); } - if (seenFields.contains(fieldName)) { + if (seenFields.contains(aliasName)) { writer.addAttribute(mapper.attributeForClassDefiningField(), mapper.serializedClass(definedIn)); } @@ -94,7 +94,12 @@ } if (source != newObj) { - context.convertAnother(newObj); + Class converter = reflectionProvider.getOverridenConverter(definedIn, fieldName); + if (converter != null) { + context.convertAnother(newObj, (Converter) reflectionProvider.newInstance(converter)); + } else { + context.convertAnother(newObj); + } } else { writer.addAttribute("self", ""); } @@ -137,7 +142,19 @@ final Object value; String self = reader.getAttribute("self"); if (self == null) { - value = context.convertAnother(result, type); + if (fieldExistsInClass) { + Class converter = reflectionProvider.getOverridenConverter( + result.getClass(), fieldName); + if (converter != null) { + value = context.convertAnother(result, type, + (Converter) reflectionProvider + .newInstance(converter)); + } else { + value = context.convertAnother(result, type); + } + } else { + value = context.convertAnother(result, type); + } } else { value = result; } Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProvider.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProvider.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProvider.java (working copy) @@ -14,8 +14,28 @@ Class getFieldType(Object object, String fieldName, Class definedIn); boolean fieldDefinedInClass(String fieldName, Class type); + + /** + * Returns a overriden converter for a specific field. + * @param definedIn the class where the field is defined in + * @param fieldName the field name + * @return the overriden converter class or null if none was found + */ + Class getOverridenConverter(Class definedIn, String fieldName); + /** + * A visitor interface for serializable fields defined in a class. + * + */ interface Visitor { + + /** + * Callback for each visit + * @param name field name + * @param type field type + * @param definedIn where the field was defined + * @param value field value + */ void visit(String name, Class type, Class definedIn, Object value); } } Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/UselessAnnotationProvider.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/UselessAnnotationProvider.java (revision 0) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/reflection/UselessAnnotationProvider.java (revision 0) @@ -0,0 +1,20 @@ +package com.thoughtworks.xstream.converters.reflection; + +import java.lang.reflect.Field; + +/** + * Does nothing! + * + * @author Guilherme Silveira + * + */ +public class UselessAnnotationProvider implements AnnotationsReflectionProvider { + + /** + * Returns no annotation + */ + public Annotation getAnnotation(Field field, String annotationClass) { + return null; + } + +} Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByIdMarshaller.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByIdMarshaller.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByIdMarshaller.java (working copy) @@ -50,9 +50,7 @@ this(writer, converterLookup, classMapper, new SequenceGenerator(1)); } - public void convertAnother(Object item) { - Converter converter = converterLookup.lookupConverterForType(item.getClass()); - + public void convert(Object item, Converter converter) { if (getMapper().isImmutableValueType(item.getClass())) { // strings, ints, dates, etc... don't bother using references. converter.marshal(item, writer, this); Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByIdUnmarshaller.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByIdUnmarshaller.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByIdUnmarshaller.java (working copy) @@ -1,6 +1,10 @@ package com.thoughtworks.xstream.core; +import java.util.HashMap; +import java.util.Map; + import com.thoughtworks.xstream.alias.ClassMapper; +import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.ConverterLookup; import com.thoughtworks.xstream.core.util.FastStack; import com.thoughtworks.xstream.io.HierarchicalStreamReader; @@ -6,9 +10,6 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.mapper.Mapper; -import java.util.HashMap; -import java.util.Map; - public class ReferenceByIdUnmarshaller extends TreeUnmarshaller { private Map values = new HashMap(); @@ -27,7 +28,7 @@ super(root, reader, converterLookup, classMapper); } - public Object convertAnother(Object parent, Class type) { + protected Object convert(Object parent, Class type, Converter converter) { if (parentIdStack.size() > 0) { // handles circular references Object parentId = parentIdStack.peek(); if (!values.containsKey(parentId)) { // see AbstractCircularReferenceTest.testWeirdCircularReference() @@ -40,7 +41,7 @@ } else { String currentId = reader.getAttribute("id"); parentIdStack.push(currentId); - Object result = super.convertAnother(parent, type); + Object result = super.convert(parent, type, converter); values.put(currentId, result); parentIdStack.popSilently(); return result; @@ -47,4 +48,5 @@ } } + } Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByXPathMarshaller.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByXPathMarshaller.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByXPathMarshaller.java (working copy) @@ -27,9 +27,7 @@ this(writer, converterLookup, (Mapper)classMapper); } - public void convertAnother(Object item) { - Converter converter = converterLookup.lookupConverterForType(item.getClass()); - + protected void convert(Object item, Converter converter) { if (getMapper().isImmutableValueType(item.getClass())) { // strings, ints, dates, etc... don't bother using references. converter.marshal(item, writer, this); @@ -45,5 +43,4 @@ } } } - } Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByXPathUnmarshaller.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByXPathUnmarshaller.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/ReferenceByXPathUnmarshaller.java (working copy) @@ -1,6 +1,10 @@ package com.thoughtworks.xstream.core; +import java.util.HashMap; +import java.util.Map; + import com.thoughtworks.xstream.alias.ClassMapper; +import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.ConverterLookup; import com.thoughtworks.xstream.core.util.FastStack; import com.thoughtworks.xstream.io.HierarchicalStreamReader; @@ -9,9 +13,6 @@ import com.thoughtworks.xstream.io.path.PathTrackingReader; import com.thoughtworks.xstream.mapper.Mapper; -import java.util.HashMap; -import java.util.Map; - public class ReferenceByXPathUnmarshaller extends TreeUnmarshaller { private Map values = new HashMap(); @@ -32,7 +33,7 @@ this(root, reader, converterLookup, (Mapper)classMapper); } - public Object convertAnother(Object parent, Class type) { + protected Object convert(Object parent, Class type, Converter converter) { if (parentPathStack.size() > 0) { // handles circular references Object parentPath = parentPathStack.peek(); if (!values.containsKey(parentPath)) { // see AbstractCircularReferenceTest.testWeirdCircularReference() @@ -45,7 +46,7 @@ return values.get(currentPath.apply(new Path(relativePathOfReference))); } else { parentPathStack.push(currentPath); - Object result = super.convertAnother(parent, type); + Object result = super.convert(parent, type, converter); values.put(currentPath, result); parentPathStack.popSilently(); return result; Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/TreeMarshaller.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/TreeMarshaller.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/TreeMarshaller.java (working copy) @@ -45,6 +45,15 @@ } public void convertAnother(Object item) { + Converter converter = converterLookup.lookupConverterForType(item.getClass()); + convert(item, converter); + } + + public void convertAnother(Object item, Converter converter) { + convert(item, converter); + } + + protected void convert(Object item, Converter converter) { if (parentObjects.containsId(item)) { throw new CircularReferenceException(); } @@ -49,7 +58,6 @@ throw new CircularReferenceException(); } parentObjects.associateId(item, ""); - Converter converter = converterLookup.lookupConverterForType(item.getClass()); converter.marshal(item, writer, this); parentObjects.removeId(item); } Index: /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/TreeUnmarshaller.java =================================================================== --- /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/TreeUnmarshaller.java (revision 701) +++ /home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/TreeUnmarshaller.java (working copy) @@ -40,9 +40,18 @@ this(root, reader, converterLookup, (Mapper)classMapper); } + public Object convertAnother(Object parent, Class type) { - try { - Converter converter = converterLookup.lookupConverterForType(type); + Converter converter = converterLookup.lookupConverterForType(type); + return convert(parent, type,converter); + } + + public Object convertAnother(Object parent, Class type, Converter converter) { + return convert(parent, type, converter); + } + + protected Object convert(Object parent, Class type, Converter converter) { + try { types.push(mapper.defaultImplementationOf(type)); Object result = converter.unmarshal(reader, this); types.popSilently(); @@ -55,7 +64,7 @@ addInformationTo(conversionException, type); throw conversionException; } - } + } private void addInformationTo(ErrorWriter errorWriter, Class type) { errorWriter.add("class", type.getName()); Index: /home/guilherme/workspace/xstream/src/test/com/thoughtworks/acceptance/annotations/AnnotationsTest.java =================================================================== --- /home/guilherme/workspace/xstream/src/test/com/thoughtworks/acceptance/annotations/AnnotationsTest.java (revision 701) +++ /home/guilherme/workspace/xstream/src/test/com/thoughtworks/acceptance/annotations/AnnotationsTest.java (working copy) @@ -17,6 +17,7 @@ import com.thoughtworks.xstream.io.HierarchicalStreamWriter; /** + * Simple tests for class annotations * * @author Chung-Onn Cheong * @author Mauro Talevi @@ -23,7 +24,7 @@ */ public class AnnotationsTest extends AbstractAcceptanceTest { - public void testAnnotations() { + public void testAnnotations() { Annotations.configureAliases(xstream, Person.class, AddressBookInfo.class); Map map = new HashMap(); map.put("first person", new Person("john doe")); Index: /home/guilherme/workspace/xstream/src/test/com/thoughtworks/acceptance/annotations/DepthOverridingTest.java =================================================================== --- /home/guilherme/workspace/xstream/src/test/com/thoughtworks/acceptance/annotations/DepthOverridingTest.java (revision 0) +++ /home/guilherme/workspace/xstream/src/test/com/thoughtworks/acceptance/annotations/DepthOverridingTest.java (revision 0) @@ -0,0 +1,83 @@ +package com.thoughtworks.acceptance.annotations; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import com.thoughtworks.acceptance.AbstractAcceptanceTest; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + +/** + * Tests for depth converter overriding + * + * @author Guilherme Silveira + */ +public class DepthOverridingTest extends AbstractAcceptanceTest { + + public static class Task { + + @XStreamConverter(GregorianTimeConverter.class) + private Calendar date; + + @XStreamConverter(GregorianTimeConverter.class) + private Calendar time; + + public Task(Calendar date, Calendar time) { + this.date = date; + this.time = time; + } + + @Override + public boolean equals(Object obj) { + return obj != null && Task.class.equals(obj.getClass()) + && ((Task) obj).date.equals(date) + && ((Task) obj).time.equals(time); + } + + } + + public void testConverterOverriding() { + Task task = new Task(new GregorianCalendar(1981, 9, 18), + new GregorianCalendar(0, 0, 0, 30, 20)); + String xml = "\n" + + " \n" + + " 372222000000\n" + + " \n" + + " \n" + + ""; + assertBothWays(task, xml); + } + + public static class GregorianTimeConverter implements Converter { + + public void marshal(Object source, HierarchicalStreamWriter writer, + MarshallingContext context) { + Calendar calendar = (Calendar) source; + writer.startNode("cal"); + writer.setValue(String.valueOf(calendar.getTime().getTime())); + writer.endNode(); + } + + public Object unmarshal(HierarchicalStreamReader reader, + UnmarshallingContext context) { + reader.moveDown(); + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(new Date(Long.parseLong(reader.getValue()))); + reader.moveUp(); + return calendar; + } + + public boolean canConvert(Class type) { + return type.equals(Calendar.class); + } + + } + +} Index: /home/guilherme/workspace/xstream/src/test/com/thoughtworks/acceptance/annotations/SimpleOverridingTest.java =================================================================== --- /home/guilherme/workspace/xstream/src/test/com/thoughtworks/acceptance/annotations/SimpleOverridingTest.java (revision 0) +++ /home/guilherme/workspace/xstream/src/test/com/thoughtworks/acceptance/annotations/SimpleOverridingTest.java (revision 0) @@ -0,0 +1,75 @@ +package com.thoughtworks.acceptance.annotations; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import com.thoughtworks.acceptance.AbstractAcceptanceTest; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + +/** + * Tests for converter overriding + * + * @author Guilherme Silveira + */ +public class SimpleOverridingTest extends AbstractAcceptanceTest { + + public static class Task { + + @XStreamConverter(GregorianTimeConverter.class) + private Calendar date; + + @XStreamConverter(GregorianTimeConverter.class) + private Calendar time; + + public Task(Calendar date, Calendar time) { + this.date = date; + this.time = time; + } + + @Override + public boolean equals(Object obj) { + return obj != null && Task.class.equals(obj.getClass()) + && ((Task) obj).date.equals(date) + && ((Task) obj).time.equals(time); + } + + } + + public void testConverterOverriding() { + Task task = new Task(new GregorianCalendar(1981, 9, 18), + new GregorianCalendar(0, 0, 0, 30, 20)); + String xml = "\n" + + " 372222000000\n" + + " \n" + + ""; + assertBothWays(task, xml); + } + + public static class GregorianTimeConverter implements Converter { + + public void marshal(Object source, HierarchicalStreamWriter writer, + MarshallingContext context) { + Calendar calendar = (Calendar) source; + writer.setValue(String.valueOf(calendar.getTime().getTime())); + } + + public Object unmarshal(HierarchicalStreamReader reader, + UnmarshallingContext context) { + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(new Date(Long.parseLong(reader.getValue()))); + return calendar; + } + + public boolean canConvert(Class type) { + return type.equals(Calendar.class); + } + + } + +}