package org.simpleframework.xml.strategy; import java.io.StringWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Constructor; import java.util.Map; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; import org.simpleframework.xml.Serializer; import org.simpleframework.xml.ValidationTestCase; import org.simpleframework.xml.core.Persister; import org.simpleframework.xml.stream.InputNode; import org.simpleframework.xml.stream.NodeMap; import org.simpleframework.xml.stream.OutputNode; public class AnnotationConverterTest extends ValidationTestCase { @Retention(RetentionPolicy.RUNTIME) private static @interface Convert{ public Class<? extends Converter> value(); } private static interface Converter<T> { public T read(InputNode node) throws Exception; public void write(OutputNode node, T value) throws Exception; } private static class CowConverter implements Converter<Cow> { public Cow read(InputNode node) throws Exception { String name = node.getAttribute("name").getValue(); String age = node.getAttribute("age").getValue(); return new Cow(name, Integer.parseInt(age)); } public void write(OutputNode node, Cow cow) throws Exception { node.setAttribute("name", cow.getName()); node.setAttribute("age", String.valueOf(cow.getAge())); node.setAttribute("legs", String.valueOf(cow.getLegs())); } } private static class ChickenConverter implements Converter<Chicken> { public Chicken read(InputNode node) throws Exception { String name = node.getAttribute("name").getValue(); String age = node.getAttribute("age").getValue(); return new Chicken(name, Integer.parseInt(age)); } public void write(OutputNode node, Chicken chicken) throws Exception { node.setAttribute("name", chicken.getName()); node.setAttribute("age", String.valueOf(chicken.getAge())); node.setAttribute("legs", String.valueOf(chicken.getLegs())); } } private static class Animal { private final String name; private final int age; private final int legs; public Animal(String name, int age, int legs) { this.name = name; this.legs = legs; this.age = age; } public String getName() { return name; } public int getAge(){ return age; } public int getLegs() { return legs; } } private static class Chicken extends Animal { public Chicken(String name, int age) { super(name, age, 2); } } private static class Cow extends Animal { public Cow(String name, int age) { super(name, age, 4); } } private static class AnnotationStrategy implements Strategy { private final Strategy strategy; public AnnotationStrategy(Strategy strategy) { this.strategy = strategy; } public Value read(Type type, NodeMap<InputNode> node, Map map) throws Exception { Value value = strategy.read(type, node, map); Convert convert = type.getAnnotation(Convert.class); InputNode parent = node.getNode(); if(convert != null) { Class<? extends Converter> converterClass = convert.value(); Constructor<? extends Converter> converterConstructor = converterClass.getDeclaredConstructor(); if(!converterConstructor.isAccessible()) { converterConstructor.setAccessible(true); } Converter converter = converterConstructor.newInstance(); Object result = converter.read(parent); return new Wrapper(result); } return value; } public boolean write(Type type, Object value, NodeMap<OutputNode> node, Map map) throws Exception { Convert convert = type.getAnnotation(Convert.class); OutputNode parent = node.getNode(); if(convert != null) { Class<? extends Converter> converterClass = convert.value(); Constructor<? extends Converter> converterConstructor = converterClass.getDeclaredConstructor(); if(!converterConstructor.isAccessible()) { converterConstructor.setAccessible(true); } Converter converter = converterConstructor.newInstance(); converter.write(parent, value); return true; } return strategy.write(type, value, node, map); } } public static class Wrapper implements Value { private Object data; public Wrapper(Object data){ this.data = data; } public int getLength() { return 0; } public Class getType() { return data.getClass(); } public Object getValue() { return data; } public boolean isReference() { return true; } public void setValue(Object data) { this.data = data; } } private static final String SOURCE = "<farmExample>"+ " <chicken name='Hen' age='1' legs='2'/>"+ " <cow name='Bull' age='4' legs='4'/>"+ "</farmExample>"; @Root private static class FarmExample { @Element @Convert(ChickenConverter.class) private Chicken chicken; @Element @Convert(CowConverter.class) private Cow cow; public FarmExample(@Element(name="chicken") Chicken chicken, @Element(name="cow") Cow cow) { this.chicken = chicken; this.cow = cow; } public Chicken getChicken() { return chicken; } public Cow getCow() { return cow; } } public void testAnnotationConversion() throws Exception { Strategy strategy = new TreeStrategy(); Strategy converter = new AnnotationStrategy(strategy); Serializer serializer = new Persister(converter); StringWriter writer = new StringWriter(); FarmExample example = serializer.read(FarmExample.class, SOURCE); assertEquals(example.getCow().getName(), "Bull"); assertEquals(example.getCow().getAge(), 4); assertEquals(example.getCow().getLegs(), 4); assertEquals(example.getChicken().getName(), "Hen"); assertEquals(example.getChicken().getAge(), 1); assertEquals(example.getChicken().getLegs(), 2); serializer.write(example, System.out); serializer.write(example, writer); String text = writer.toString(); assertElementHasAttribute(text, "/farmExample/chicken", "name", "Hen"); assertElementHasAttribute(text, "/farmExample/chicken", "age", "1"); assertElementHasAttribute(text, "/farmExample/chicken", "legs", "2"); assertElementHasAttribute(text, "/farmExample/cow", "name", "Bull"); assertElementHasAttribute(text, "/farmExample/cow", "age", "4"); assertElementHasAttribute(text, "/farmExample/cow", "legs", "4"); } }