commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From James Carman <ja...@carmanconsulting.com>
Subject Re: [convert] Automatic conversion based on proxies
Date Thu, 15 Aug 2013 13:09:05 GMT
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.


On Thu, Aug 15, 2013 at 8:00 AM, Gary Gregory <garydgregory@gmail.com> wrote:
> 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
>

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


Mime
View raw message