commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Ron Blaschke <>
Subject Re: [convert] Initial ideas (was: conversion with default values)
Date Mon, 29 Mar 2004 15:06:53 GMT
OH> I had a look at your ideas and now I have a few questions and remarks.

OH> At first let me say that I like the idea of using a graph representation
I definitely agree.  Things get a lot easier that way.  Besides, we
could even support a special debug mode, where we plug in a graph renderer
and thereby provide an image of the registered converters (just like
the ones in the document I posted, and had drawn by hand).  If a
conversion succeeds we could provide an image of the path chosen (may
be useful if an unexpected result pops up.)

OH> The idea to use a separate lookup class seems interesting, too. But
OH> after thinking a while about it I am not sure if this really helps
OH> registry. So this would imply that for different lookup classes other
OH> data has to be stored in the registry. Can this be handled in a generic
OH> way or wouldn't it be better to leave the lookup mechanism in the 
OH> registry and use different registry implementations?

In my current line of thought the registry and lookup are quite
tightly coupled, but serve different purposes.  The Registry is just a
plain storage, whereas the Lookup provides the algorithm(s?) to work
on them.  For example, the ClassConverterRegistry provides/should
provide all Converters the Java language uses for a class (inheritance,
toString()).  But then again, something simpler like a Set or List
might be better suited for that.

OH> 2. A lookup might be expensive, especially if a search in a complex
OH> graph has to be performed. So it might make sense to implement some
OH> caching functionality. I am not sure if this can be done efficiently in
OH> a lookup class implementation because in your example code always a new
OH> lookup instance is created (though I might miss something here).

You are right, the lookup should definitely be cached, but I didn't go
that far in my implementation.  I guess there are a number of things
we can do, though I haven't thought them through yet.  First, we could
build a single graph for a Lookup and incrementally extend it (there
may be times when it has to be rebuild, eg when a converter is
removed, or the graph gets too big).  Then, we could cache some paths,
in case they are reused.  The shortest path algorithm provides the
shortest path to all destinations from a source, so this computation
may somehow be reused, too.

OH> The approach with the Types handles inheritence, but does it cover all
OH> cases? I am not sure if the following use case could be handled: imagine
OH> a user has the classes A, B, C, and D with B and C extending A. Then
OH> there are special conversion implementations for A->D and B->D. If now
OH> an object of class C is provided, will then the A->D conversion be used?
OH> With other words: inheritence in direction towards the base classes is
OH> surely supported, but can derived classes be handled without extra means?

Yes.  In my model, everything is a conversion, even inheritance,
implementation of an interface, or the String conversion (toString) of
any object.  In your example, a chained converter would be build, to
provide the conversion C -> A -> D.  In the prototype, this is done via
extension of the graph with the converters of the class C (see
addTypeConverters for both the source and destination type).  So, in
the beginning you only have the converter A -> D, but as you ask for a
conversion of an object of type C, the conversion C -> A is added.

public ExtendedConverter lookup(final Type sourceType,
    final Type destinationType) throws NoSuchConversionException {
    addTypeConverters(graph, sourceType);
    addTypeConverters(graph, destinationType);
    return new ChainedExtendedConverter(getConvertersForPath(getShortestPath(sourceType, destinationType)));

OH> What about structural conversions, e.g. a String[] to a int[] or a
OH> collection of Integers to a byte[]? Where would these conversions take
OH> place? (I think this is slightly different from constructing a chain of
OH> conversions because here a base conversion has to be performed multiple
OH> times.)

I started to think about that too, but postponed that for a version 2
of convert.  Here is how far I got:
I think it's better to think about String[] or int[] as a parametric
type, like Array<String> or Array<int>.  That way, it will be easier
to think about conversions as Array<int> to List<Integer> (aside
from supporting Java 1.5).  Now, a converter needs to worry about the
outer structure (Array -> List) and the meat (int -> Integer) (this
would apply for other structures too, eg List -> Set).  I could
imagine an implementation like this:

public class ListToSetConverter extends AbstractConverter {
    final Type srcParam, destParam;
    public ListToSetConverter(final Type srcParam, final Type destParam) {
        this.srcParam = srcParam;
        this.destParam = destParam;
    public Type getSourceType() {
        return Types.parse("List<*>", srcParam);

    public Type getDestinationType() {
        return Types.parse("Set<*>", destParam);

    protected ConversionContext convert(final ConversionContext context)
    throws NoSuchConversionException, ConversionFailedException {
        final Set s = new HashSet();
        for (final Iterator i = context.getValue(); i.hasNext();) {
            s.add(context.convert(, destParam));
        return context.update(s, getDestinationType());

"context.convert()" would provide access to the Lookup that found the
converter, thereby allowing the converter to ask for conversions /
call other converters.

Then, "new ListToSetConverter(Integer.class, Integer.TYPE)" (or
something like that) can be plugged into the graph, and be used like
any other.  (Of course, the Lookup must be prepared to handle
parametric types, like the implicit LinkedList<Integer> ->
List<Number> -> List<Number> conversion).

OH> One last remark about the extended converter interface: I think I 
OH> understand why this ConversionContext is needed - to allow a single
OH> object being treated as of different types. But I strongly agree with a
OH> statement mentioned on this thread: that the converter interface should
OH> be as simple as possible. So if it is possible, I would try to get rid
OH> off this additional complexity.

The ConversionContext would be used to supply additional information
for a conversion.  Currently, it only holds the perceived type and the
object to convert, but could also include
- reference to the Lookup, to allow a converter to ask for other
- isCopy field, that states that the input object is a copy (maybe
  from another converter) and may be used for a "destructive"
  conversion, eg List<Integer> -> List<String> might simply transform
  the Integers to String in the List, without creating a new List.
- isImmutable field, that states that the given input object may not
  be modified, even if the object is mutable.
- ...

I am quite a bit torn between the possible functionality that can be
provided with this information, and the simplicity of the Converter
interface.  Maybe we could come up with some different Converter
interfaces that get increasingly complex, and would be suitable for
different situations.  Adapters (with additional metadata),
could be used to get from the simpler to the more complex converters.

We could even provide a "dynamic converter adapter" that scans any
given object for convert methods (eg, any method that starts with
"convert") and provides adapters for them:

public class Any {
   public String convert(Integer i) throws SomeException {

...getSourceType() -> Integer
...getDestinationType() -> String


To unsubscribe, e-mail:
For additional commands, e-mail:

View raw message