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: [email protected]
For additional commands, e-mail: [email protected]