commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Gary Gregory <garydgreg...@gmail.com>
Subject Re: [convert] Automatic conversion based on proxies
Date Thu, 15 Aug 2013 12:00:20 GMT
Should the framework try to convert transitively?

Gary

On Aug 15, 2013, at 6:56, James Carman <james@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.Schaible@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-unsubscribe@commons.apache.org
>> For additional commands, e-mail: dev-help@commons.apache.org
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Mime
View raw message