Location Tracking

Starting with Modello 1.4, some parsers (only XPP3 in 1.4, Snake Yaml and Jackson in addition in 1.8) support the tracking of line/column information for the input data. This means that additional metadata is stored in the model that can be used to query the location of some model element in the input source, e.g. for means of better error reporting to the user.

Base Principle

For performance reasons, generation of location metadata during parsing has to be explicitly requested. Usually, this means to use an extended reader instead of the normal reader generated by Modello, for example xpp3-extended-reader.

To store the line/column information, one class of the model has to be specifically attributed:

<class locationTracker="locations" java.clone="shallow">
  <name>Location</name>
  <version>1.0.0+</version>
  <fields>
    <!-- line and column fields are auto-generated by Modello, and optional reference to source (see "Source Tracking" section below) -->
  </fields>
</class>

The attribute locationTracker in the snippet above signals to Modello that this class should be used to record line/column metadata during parsing. The class can be modelled as usual, but the line and column fields to save the line and column number along with their accessors are automatically generated by Modello (and optional reference to source).

A Tracker interface will be generated to factor out accessors to locationTracker:

public interface LocationTracker // named by adding "Tracker" to location tracker class name
{
    public Location getLocation( Object field );
    public void setLocation( Object field, Location location );
}

All other model classes will be equipped with a field to hold instances of the Location class and implement Tracker accessors to query them. The value of locationTracker attribute is used to name this field. This way, each model class keeps track of the input locations for its fields:

public class Model implements LocationTracker
{

    private Map<Object, Location> locations; // named after .mdo locationTracker attribute value

    public Location getLocation( Object field )
    {
        return ( locations != null ) ? locations.get( field ) : null;
    }

    [...]

}

Location Map Keys

The location map shown above is keyed by field name. An empty string is used to query the location of the bean itself (or its text contents).

For collections or maps, the returned Location instance can be further queried for its items (Location implements LocationTracker). The key to query an item depends on the type of collection. For lists the zero-based index is used, for sets the item itself is used and for maps/properties the mapping key is used.

For example, consider this input XML:

<model>
  <someString>hello</someString>
  <listItems>
    <listItem>list</listItem>
  </listItems>
  <setItems>
    <setItem>set</setItem>
  </setItems>
  <properties>
    <key>value</key>
  </properties>
</model>

For this data model, one could query the location metadata like this (neglecting null checking):

Model model = <parser invocation>;

// to query the location of the <model> element itself
Location location1 = model.getLocation( "" );

// to query the location of the contents of <someString>, i.e. "hello"
Location location2 = model.getLocation( "someString" );

// to query the location of the first list item, i.e. "list"
Location location3 = model.getLocation( "listItems" ).getLocation( Integer.valueOf( 0 ) );

// to query the location of the set item, i.e. "set"
Location location4 = model.getLocation( "setItems" ).getLocation( "set" );

// to query the location of the properties data, i.e. "value"
Location location5 = model.getLocation( "properties" ).getLocation( "key" );

Source Tracking

Occasionally, just tracking the line/column number is not enough but the input file of the model needs to be recorded as well. For this reason, another class of the model can be marked as a source tracker:

<class sourceTracker="source">
  <name>Source</name>
  <version>1.0.0+</version>
  <fields>
    <!-- modelled by end-user, e.g. fields to hold pathname/URL etc. -->
  </fields>
</class>

The fields of this source tracking class are entirely up to the application, Modello will merely extend the generated Location class to support a reference to the Source class named after the value of sourceTracker attribute.

An instance of the source tracking class has to be provided to the extended readers, which will then use it to populate all the generated Location instances.