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 10:55:19 GMT
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


Mime
View raw message