Am 15.08.2013 15:33, schrieb James Carman: > Perhaps you'd have a different method on the "registry" like this: > > Converter<F,T> createConverterPath(Class<F> fromType, Class<T> toType); > > This way, you're specifically letting the registry know that you're > okay with it filling in the blanks in between fromType and toType. >
The problem is that this stuff becomes pretty fast very complex. Therefore, a strategy could be to handle this in separate layers. - There would be a core layer providing basic converters from type A to B. AIUI, this is what [convert] currently has. - Then there is a layer of handling more complex conversions, e.g. the transitive stuff and maybe also conversions with collections and arrays involved. I guess, to implement this layer, you will need a registry and some more meta data about the converters available. - Another layer would be an integration layer into custom applications. Here different patterns could be provided for triggering conversions with more or less magic. Oliver > > On Thu, Aug 15, 2013 at 9:24 AM, Gary Gregory <garydgreg...@gmail.com> wrote: >> On Thu, Aug 15, 2013 at 9:09 AM, James Carman >> <ja...@carmanconsulting.com>wrote: >> >>> You mean if it has a converter from A -> B and B -> C and you ask for >>> a conversion from A -> C, it would figure it out? That's and >>> interesting idea. I guess you'd need to make sure there is no loss of >>> fidelity when doing the conversions. >>> >> >> Yes, and be careful of the shortest path too: >> >> I can have: >> >> String -> Long >> String -> Date >> Date -> Long >> >> So when I ask for String -> Long, I do not want String -> Date -> Long! >> >> I have something like this at work I use for for (complex) objects. >> >> The other aspect is caching, let's say I have a Customer and I want to >> convert it to XML and JSON, I want to cache the results of the conversion >> and invalidate that cache if I change the customer. Is that out of scope? I >> would be some kind of project object that holds on to a Customer, a JSON >> string, and an XML document (both bytes and String). >> >> Gary >> >> >>> >>> >>> On Thu, Aug 15, 2013 at 8:00 AM, Gary Gregory <garydgreg...@gmail.com> >>> wrote: >>>> 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 >>>> >>> >>> --------------------------------------------------------------------- >>> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org >>> For additional commands, e-mail: dev-h...@commons.apache.org >>> >>> >> >> >> -- >> E-Mail: garydgreg...@gmail.com | ggreg...@apache.org >> Java Persistence with Hibernate, Second >> Edition<http://www.manning.com/bauer3/> >> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >> Spring Batch in Action <http://www.manning.com/templier/> >> Blog: http://garygregory.wordpress.com >> Home: http://garygregory.com/ >> Tweet! http://twitter.com/GaryGregory > > --------------------------------------------------------------------- > 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