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 13:24:36 GMT
On Thu, Aug 15, 2013 at 9:09 AM, James Carman <james@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 <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
>
>


-- 
E-Mail: garydgregory@gmail.com | ggregory@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

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message