/* * Copyright (C) 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 02. March 2006 by Mauro Talevi */ package com.thoughtworks.acceptance.annotations; import com.thoughtworks.acceptance.AbstractAcceptanceTest; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAsAttribute; import com.thoughtworks.xstream.annotations.XStreamConverter; 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.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; /** * Tests for using annotations to override field converters * * @author Guilherme Silveira * @author Mauro Talevi * @author Jörg Schaible */ public class FieldConverterTest extends AbstractAcceptanceTest { @Override protected XStream createXStream() { XStream xstream = super.createXStream(); xstream.autodetectAnnotations(true); return xstream; } protected void setUp() throws Exception { super.setUp(); xstream.alias("annotatedTask", TaskWithAnnotations.class); xstream.alias("derivedTask", DerivedTask.class); xstream.alias("taskContainer", TaskContainer.class); } public void testAnnotationForFieldsOfSameType() { final TaskWithAnnotations task = new TaskWithAnnotations("Tom", "Dick", "Harry"); final String xml = "" + "<annotatedTask name2=\"_Dick_\">\n" + " <name1 str=\"Tom\"/>\n" + " <name3>Harry</name3>\n" + "</annotatedTask>"; assertBothWays(task, xml); } public void testAnnotationForHiddenFields() { final DerivedTask task = new DerivedTask("Tom", "Dick", "Harry"); final String xml = "" + "<derivedTask name2=\"_Dick_\">\n" + " <name1 defined-in=\"annotatedTask\" str=\"Tom\"/>\n" + " <name3 defined-in=\"annotatedTask\">Harry</name3>\n" + " <name1>Harry</name1>\n" + " <name3 str=\"Tom\"/>\n" + "</derivedTask>"; assertBothWays(task, xml); } public void testIsFoundInReferencedTypes() { final TaskContainer taskContainer = new TaskContainer(); final String xml = "" + "<taskContainer>\n" + " <task name2=\"_Dick_\">\n" + " <name1 defined-in=\"annotatedTask\" str=\"Tom\"/>\n" + " <name3 defined-in=\"annotatedTask\">Harry</name3>\n" + " <name1>Harry</name1>\n" + " <name3 str=\"Tom\"/>\n" + " </task>\n" + "</taskContainer>"; assertEquals(taskContainer, xstream.fromXML(xml)); } public static class TaskWithAnnotations { @XStreamConverter(FirstConverter.class) private final String name1; @XStreamConverter(SecondaryConverter.class) @XStreamAsAttribute private final String name2; private final String name3; public TaskWithAnnotations(final String name1, final String name2, final String name3) { this.name1 = name1; this.name2 = name2; this.name3 = name3; } @Override public boolean equals(final Object obj) { return obj != null && TaskWithAnnotations.class.isAssignableFrom(obj.getClass()) && ((TaskWithAnnotations)obj).name1.equals(name1) && ((TaskWithAnnotations)obj).name2.equals(name2) && ((TaskWithAnnotations)obj).name3.equals(name3); } } public static class DerivedTask extends TaskWithAnnotations { private final String name1; @XStreamConverter(FirstConverter.class) private final String name3; public DerivedTask(final String name1, final String name2, final String name3) { super(name1, name2, name3); this.name1 = name3; this.name3 = name1; } @Override public boolean equals(final Object obj) { return obj != null && DerivedTask.class.isAssignableFrom(obj.getClass()) && ((DerivedTask)obj).name1.equals(name1) && ((DerivedTask)obj).name3.equals(name3) && super.equals(obj); } } public static class TaskContainer { private final DerivedTask task = new DerivedTask("Tom", "Dick", "Harry"); @Override public boolean equals(final Object obj) { return obj != null && TaskContainer.class.equals(obj.getClass()) && task.equals(((TaskContainer)obj).task); } } public static class FirstConverter implements Converter { public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) { final String str = source.toString(); writer.addAttribute("str", str); } public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { final String str = reader.getAttribute("str"); return str; } public boolean canConvert(final Class type) { return type.equals(String.class); } } public static class SecondaryConverter implements SingleValueConverter { public boolean canConvert(final Class type) { return type.equals(String.class); } public Object fromString(String value) { return value.substring(1, value.length() - 1); } public String toString(Object source) { return "_" + source.toString() + "_"; } } public static class CustomConverter implements Converter { private static int total = 0; public CustomConverter() { total++ ; } public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { return null; } public boolean canConvert(Class type) { return type.equals(Double.class); } } public static class Account { @XStreamConverter(CustomConverter.class) private Double value; public Account() { this.value = Math.random(); } } public static class Client { @XStreamConverter(CustomConverter.class) private Double value; public Client() { this.value = Math.random(); } } public void testAreCachedPerField() { int before = CustomConverter.total; toXML(new Account()); int after = CustomConverter.total; assertEquals(before + 1, after); } public void testAreCachedPerFieldInDifferentContexts() { int before = CustomConverter.total; toXML(new Account()); toXML(new Client()); int after = CustomConverter.total; assertEquals(before + 1, after); } }