Index: C:/home/guilherme/workspace/xstream/src/test/com/thoughtworks/xstream/core/DefaultConverterLookupTest.java =================================================================== --- C:/home/guilherme/workspace/xstream/src/test/com/thoughtworks/xstream/core/DefaultConverterLookupTest.java (revision 719) +++ C:/home/guilherme/workspace/xstream/src/test/com/thoughtworks/xstream/core/DefaultConverterLookupTest.java (working copy) @@ -13,7 +13,7 @@ public class DefaultConverterLookupTest extends TestCase { public void testConverterChange() { - final DefaultConverterLookup lookup = new DefaultConverterLookup( + final DefaultConverterLookup lookup = new DefaultConverterLookup(new JVM().bestReflectionProvider(), new DefaultMapper(Thread.currentThread() .getContextClassLoader())); Converter currentConverter = new SingleValueConverterWrapper(new StringConverter()); Index: C:/home/guilherme/workspace/xstream/src/test/com/thoughtworks/xstream/StringConvertableTest.java =================================================================== --- C:/home/guilherme/workspace/xstream/src/test/com/thoughtworks/xstream/StringConvertableTest.java (revision 0) +++ C:/home/guilherme/workspace/xstream/src/test/com/thoughtworks/xstream/StringConvertableTest.java (revision 0) @@ -0,0 +1,40 @@ +package com.thoughtworks.xstream; + +import com.thoughtworks.acceptance.AbstractAcceptanceTest; +import com.thoughtworks.xstream.converters.StringConvertable; + +public class StringConvertableTest extends AbstractAcceptanceTest { + + public static class Base implements StringConvertable { + + private int id; + + public Base(int id) { + this.id = id; + } + + public String encode() { + return "int_" + id; + } + + public void decode(String value) { + this.id = Integer.parseInt(value.substring(4)); + } + + @Override + public boolean equals(Object obj) { + if(obj==null || !(obj instanceof Base)) { + return false; + } + return ((Base) obj).id == id; + } + + } + + public void testConversion() { + Base base = new Base(15); + String xml = "int_15"; + assertBothWays(base, xml); + } + +} Index: C:/home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/XStream.java =================================================================== --- C:/home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/XStream.java (revision 719) +++ C:/home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/XStream.java (working copy) @@ -57,6 +57,7 @@ import com.thoughtworks.xstream.converters.basic.NullConverter; import com.thoughtworks.xstream.converters.basic.ShortConverter; import com.thoughtworks.xstream.converters.basic.StringBufferConverter; +import com.thoughtworks.xstream.converters.basic.StringConvertableConverter; import com.thoughtworks.xstream.converters.basic.StringConverter; import com.thoughtworks.xstream.converters.basic.URLConverter; import com.thoughtworks.xstream.converters.collections.ArrayConverter; @@ -280,7 +281,7 @@ this.hierarchicalStreamDriver = driver; this.classLoaderReference = new ClassLoaderReference(new CompositeClassLoader()); this.mapper = mapper == null ? buildMapper(classAttributeIdentifier) : mapper; - converterLookup = new DefaultConverterLookup(this.mapper); + converterLookup = new DefaultConverterLookup(reflectionProvider, this.mapper); attributeAliasingMapper.setConverterLookup(converterLookup); setupAliases(); setupDefaultImplementations(); Index: C:/home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java =================================================================== --- C:/home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java (revision 719) +++ C:/home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java (working copy) @@ -1,17 +1,20 @@ package com.thoughtworks.xstream.core; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + import com.thoughtworks.xstream.alias.ClassMapper; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.ConverterLookup; +import com.thoughtworks.xstream.converters.StringConvertable; +import com.thoughtworks.xstream.converters.basic.StringConvertableConverter; +import com.thoughtworks.xstream.converters.reflection.ReflectionProvider; import com.thoughtworks.xstream.core.util.PrioritizedList; import com.thoughtworks.xstream.mapper.Mapper; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - /** * The default implementation of converters lookup. * @@ -24,8 +27,10 @@ private final PrioritizedList converters = new PrioritizedList(); private final Map typeToConverterMap = Collections.synchronizedMap(new HashMap()); private final Mapper mapper; + private ReflectionProvider reflectionProvider; - public DefaultConverterLookup(Mapper mapper) { + public DefaultConverterLookup(ReflectionProvider reflectionProvider, Mapper mapper) { + this.reflectionProvider = reflectionProvider; this.mapper = mapper; } @@ -33,23 +38,34 @@ * @deprecated As of 1.2, use {@link #DefaultConverterLookup(Mapper)} */ public DefaultConverterLookup(ClassMapper classMapper) { - this((Mapper)classMapper); + // only for compatibility issues I instantiate jvm and so on + // if this constructor was not made public (javadoc + told everybody about it) + // then it might make sense to change its arguments... + this(new JVM().bestReflectionProvider(),(Mapper)classMapper); } public Converter lookupConverterForType(Class type) { Converter cachedConverter = (Converter) typeToConverterMap.get(type); if (cachedConverter != null) return cachedConverter; - Class mapType = mapper.defaultImplementationOf(type); + Class mapType = mapper.defaultImplementationOf(type); + Converter converter = findConverter(mapType); + typeToConverterMap.put(type, converter); + return converter; + } + + private Converter findConverter(Class type) { + if(type!=null && StringConvertable.class.isAssignableFrom(type)) { + return new StringConvertableConverter(this.reflectionProvider, type); + } Iterator iterator = converters.iterator(); while (iterator.hasNext()) { Converter converter = (Converter) iterator.next(); - if (converter.canConvert(mapType)) { - typeToConverterMap.put(type, converter); + if (converter.canConvert(type)) { return converter; } } throw new ConversionException("No converter specified for " + type); - } + } public void registerConverter(Converter converter, int priority) { converters.add(converter, priority); Index: C:/home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/StringConvertable.java =================================================================== --- C:/home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/StringConvertable.java (revision 0) +++ C:/home/guilherme/workspace/xstream/src/java/com/thoughtworks/xstream/converters/StringConvertable.java (revision 0) @@ -0,0 +1,27 @@ +package com.thoughtworks.xstream.converters; + +/** + * Basic conversion interface for objects that want to do conversion themselves. + * + * @author Guilherme Silveira + * @since 1.2 + */ +public interface StringConvertable { + + // 1. the name is really bad + + // 2. joe suggested to use toString method + // i don't know if it would be ok as some people might use it for (UGHHHH) something else... + // rename it if desired + /** + * Encodes the object as a string. + */ + String encode(); + + /** + * Decodes the string as an object + * @param value + */ + void decode(String value); + +}