Should the framework try to convert transitively? Gary
On Aug 15, 2013, at 6:56, James Carman <ja...@carmanconsulting.com> wrote: > I personally think we're over-thinking this thing. Keep it simple, folks: > > public interface Converter<F,T> > { > T convert(F from); > } > > You can auto-detect the F/T parameters when a Converter is registered. > > On Thu, Aug 15, 2013 at 4:42 AM, Jörg Schaible > <joerg.schai...@scalaris.com> wrote: >> Hi, >> >> Emmanuel Bourg wrote: >> >>> Le 14/08/2013 17:39, Adrian Crum a écrit : >>> >>>> Instead of >>>> >>>> int columnInt = record.getValueAsInt(1); >>>> >>>> the developer would use >>>> >>>> Integer columnInt = Util.convertTo(record.getValue(1), Integer.class); >>> >>> +1 for the static method, that would allow the use of a static import >>> and a very concise syntax like: >>> >>> Integer columnInt = to(Integer.class, record.getValue(1)); >>> >>> >>> That being said, [convert] could offer several patterns to perform type >>> conversion, and the use of proxies could be one of them. >> >> I never had a look at [convert], but this proposed syntax reminds me >> strongly to an own little framework, where I have following methods in an >> interface to convert strings into arbitrary objects: >> >> ================= %< ============== >> <T> T get(Class<T> type, String key); >> <T> T get(ValueConverter<T> converter, String key); >> ================= %< ============== >> >> The value converter itself is very primitive: >> >> ================= %< ============== >> public interface ValueConverter<T> >> { >> T get(CharSequence value); >> } >> ================= %< ============== >> >> The question is now, how to know about existing converters. I was inspired >> by XStream and use a class ConverterLookup that has following method: >> >> ================= %< ============== >> public <T> ValueConverter<T> lookup(final Class<T> type) >> { >> ValueConverter<?> converter = converterCache.get(type); >> if (converter == null) { >> for (final Iterator<ConverterFactory> iter = converters.iterator(); >> converter == null && iter.hasNext();) { >> converter = iter.next().willConvert(type); >> } >> >> synchronized (converterCache) { >> if (converter != null) { >> converterCache.putIfAbsent(type, converter); >> } >> } >> } >> >> @SuppressWarnings("unchecked") >> final ValueConverter<T> checkedConverter = (ValueConverter<T>) converter; >> return checkedConverter; >> } >> ================= %< ============== >> >> I.e. the ConverterLookup has a (priorized) set of ConverterFactory >> implementations that can be requested for a ValueConverter of the given >> type. >> >> The ConverterLookup has additionally one static method "getDefaultLookup()" >> that returns an instance of the ConverterLookup where the set of >> ConverterFactory implementations is detected and instantiated using the Java >> SPI mechanism. That makes it very convenient to add new ConverterFactory >> implementations even to the default ConverterLookup. Therefore the >> implementation of the two get methods is quite simple: >> >> ================= %< ============== >> @Override >> public <T> T get(final Class<T> type, final String key) >> { >> final ValueConverter<T> converter = >> ConverterFactory.getDefaultLookup().lookup(type); >> return get(converter, key); >> } >> >> @Override >> public <T> T get(final ValueConverter<T> converter, final String key) >> { >> final String value = retrieveString(key); // get String to convert >> return converter.get(value); >> } >> ================= %< ============== >> >> You may ask, why there is an additional ConverterFactory to create the >> ValueConverter instances? Actually it can be useful for a converter to >> implement both interfaces: >> >> ================= %< ============== >> public abstract class AbstractFactoryConverter<T> implements >> ValueConverter<T>, ConverterFactory >> { >> private final Class<? super T> type; >> protected AbstractFactoryConverter(final Class<? super T> type) >> { >> this.type = type; >> } >> >> @Override >> public int getPriority() >> { >> return getClass().getAnnotation(Priority.class).value(); >> } >> >> @Override >> public ValueConverter<?> willConvert(final Class<?> type) >> { >> return this.type == type ? this : null; >> } >> } >> >> @Priority >> public class StringConverter extends AbstractFactoryConverter<String> >> { >> public StringConverter() >> { >> super(String.class); >> } >> >> @Override >> public String get(final CharSequence value) >> { >> return value == null ? null : value.toString(); >> } >> } >> ================= %< ============== >> >> However, to handle types in a generic way, the factory provides a much >> better possibility. See the implementation of my EnumConverterFactory: >> >> ================= %< ============== >> public class EnumConverterFactory implements ConverterFactory >> { >> @Override >> public ValueConverter<?> willConvert(final Class<?> type) >> { >> if (Enum.class.isAssignableFrom(type)) { >> return new ValueConverter<Enum<?>>() { >> @Override >> @SuppressWarnings({"rawtypes", "unchecked"}) >> public Enum<?> get(final CharSequence value) >> { >> return Enum.valueOf((Class<Enum>) type, value.toString()); >> } >> }; >> } >> return null; >> } >> >> @Override >> public int getPriority() >> { >> return Priority.LOW; >> } >> } >> ================= %< ============== >> >> Apart from those factories, I have one for all the primitive types, one for >> arrays and one based on reflection that uses a given type's constructor >> taking a single String. That allows me to write following code for an >> instance 'store' that owns the two get methods above: >> >> ================= %< ============== >> int i = store.get(int.class, "42"); >> Long l = store.get(Long.class, "42"); >> URL url = store.get(URL.class, "http://www.apache.org/"); >> Priority p = store.get(Priority.class, "LOW"); // an enum >> Priority[] pArray = store.get(Priority[].class, "LOW,HIGH"); >> >> ValueConverter<URL[].class> converter = >> new ArrayConverterFactory('|').willConvert(URL[].class); >> URL[] urlArray = store.get(converter, >> "http://www.apache.org/|http://commons.apache.org/"); >> ================= %< ============== >> >> The code above is a bit simplified (stripped exception handling), but >> the complete stuff contains just 2 interfaces, 1 annotation, one exception >> and 7 classes with not too much code. I always intended to add this to >> [lang] in a package 'converter', when I learned that we have a [convert] >> component. Now I am not sure what to do with it ... >> >> - Jörg >> >> >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org >> For additional commands, e-mail: dev-h...@commons.apache.org > > --------------------------------------------------------------------- > To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org > For additional commands, e-mail: dev-h...@commons.apache.org > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org