abdera-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From James M Snell <jasn...@gmail.com>
Subject The Converter Code
Date Fri, 14 Sep 2007 15:18:57 GMT
Ok, so here's a quick rundown of the Converter code...

The mechanism is split into two parts:

 1. The Converter Core
 2. The Annotation Converter

The Core provides the base interfaces and framework for a Converter.  A
Converter takes one kind of object and converts it into another.  How
that conversion is done is entirely up to the Converter and
ConversionContext implementation being used.  The Core provides a means
of registering and managing converters and a bunch of base logic that
can be reused by converter implementations.

The How It Works is simple. Consider the following example taken from
the unit tests:

1     ConversionContext context = new DefaultConversionContext();
2     context.setConverter(Foo.class, new FooConverter());
3     Foo foo = new Foo();
4
5     ObjectContext fooContext = new ObjectContext(foo);
6     Converter<Element> converter = context.getConverter(fooContext);
7
8     assertNotNull(converter);
9     assertTrue(converter instanceof FooConverter);
10
11    Element fooEl = context.convert(foo,fooContext);
12
13    assertNotNull(fooEl);

Line 1 creates a new instance of the DefaultConversionContext.  Anyone
can provide a different implementation of the ConversionContext.  It's
job is to act as a kind of controller for the conversion process.

Line 2 illustrates one way of registering a converter with the default
context.  What this is basically saying is that the Foo.class can be
converted to some other kind of object using the FooConverter object.
The Converter interface is a Generic.  The type of object that the
Converter produces is determined by whatever Generic type the Converter
assumes.  e.g., Converter<Entry> will produce Entry objects.

The other way converters can be registered with default context is using
the same basic mechanism we use to register extension factories, named
writers, etc.. using the classpath and files located in the
META-INF/services directory.  Look at the unit tests to see an example
of how this works.

Line 5 creates an ObjectContext for the Foo object.  An ObjectContext is
a helper class that handles all of the Java Reflection tasks that may be
necessary for the conversion.  We create this once so that if there are
multiple steps to the conversion, an application does not have to waste
time and resources doing multiple calls into the Java reflection API. We
do not have to create this object here, but we're doing so for the sake
of the test case.

Line 6 is just a test to ensure that the converter was registered with
the context properly and that it can be discovered.

Line 11 demonstrates the actual conversion step.  At this point, the
context locates the appropriate converter for the object, and passes the
object off to that converter to be transformed.  In this case,
FooConverter is creating an org.apache.abdera.model.Element instance.

The Converter Core code does not provide a default conversion
implementation.  That is provided by the Annotation Converter in the
extensions module.

The Annotation Converter uses a combination of Java5 Annotations and
naming conventions to convert Java objects into Abdera objects.  It is
not yet 100% complete, but it is generally functional.

For instance, suppose that I have a Java class Foo.

  public class Foo {

    public String getID() { ... }

    public String getTitle() { ... }

    public String getSummary() { ... }

    public FooAuthor[] getAuthors() { ... }

    public String getType() { ... }

    public String[] getTags() { ... }

    public URI getPermalink() { ... }

    public URI getEnclosure() { ... }

  }

I want this object to be converted into an Atom entry.  I would start by
inserting some Annotations.

  @Entry
  public class Foo {

    public String getID() { ... }

    public String getTitle() { ... }

    @TextType(Text.Type.HTML)
    public String getSummary() { ... }

    public FooAuthor[] getAuthors() { ... }

    @Category(scheme="http://example.org/types")
    public String getType() { ... }

    @Category
    public String[] getTags() { ... }

    @Link(rel="alternate")
    public URI getPermalink() { ... }

    @Link(rel="enclosure")
    public URI getEnclosure() { ... }

  }

It's important to note that the annotation converter only pays attention
to public methods and fields.  Methods that do not return a value or
that have parameters are ignored.

Once the object is annotated, I would run it through the
ConventionConversionContext implementation:

1    Abdera abdera = new Abdera();
2    ConversionContext context =
3       new ConventionConversionContext(abdera);
4    Foo foo = new Foo();
5    Entry entry = context.convert(entry);

The entry produced would look something like:

1 <entry xmlns="http://www.w3.org/2005/Atom">
2   <id>...</id>
3   <title type="text">...</title>
4   <summary type="html">...</summary>
5   <author>...</author>
6   <author>...</author>
7   <category scheme="http://example.org/types" term="..."/>
8   <category term="..." />
9   <category term="..." />
10  <link rel="alternate" href="..." />
11  <link rel="enclosure" href="..." />
12 </entry>

Notice that some of the methods do not need annotations because of the
way they are named (e.g. getID maps to atom:id).

And that's basically all there is to it :-)

- James

Mime
View raw message