Tuesday, September 3, 2013

Introduction to the JSR 354 Region API

JSR 354 Region API

Today I would like to introduce you the Region API that is part of JSR 354. First many of you might ask, why we need a Region API at all. The answer is when you are working with monetary values and currencies, regions are a natural part that requires also a flexible and extensible design. Mapping everything  into java.util.Locale might be easy for many use cases, but obviously has its limits, when you start thinking of what a Region really can be:

  • A country, e.g. modelled by ISO
  • A region containing multiple countries, e.g. Central Europe, EMEA, ...
  • A continent
  • A completely different grouping, e.g. legal units (grouped by similar laws or international contracts)
  • Regions with political instability
  • Regions with high economic growth
  • etc.
Of course this list is endless. The important point is, that depending on your use cases, you will have completely different requirements, what kind of regions you want to model and how they should be organized. 

The Region Interface

A Region should be very simple, so JSR 354 defines it as

  public interface Region{
    RegionType getRegionType();
    String getRegionCode();
    int getNumericRegionCode();
    Locale getLocale();
    Collection<String> getTimezoneIds();
  }

Hereby
  • The regionCode must be unique in combination with the RegionType.
  • The numericCode must be unique in combination with the RegionType, but may also be undefined (-1).
  • A region's getLocale maps a Region with the corresponding Locale. Since not every Region can be mapped to a Locale, the method is allowed to return null.
  • The timezone ids finally allow to map a region to its containing time zones, e.g. Europe/Berlin, Europe/London.
The RegionType used above is very simple:


  public final class RegionType 
  implements Comparable<RegionType>, Serializable{
    public RegionType of(String type);
    public RegionType(String type);
    public String getId();
    [...]
  }


So this allows to define a Region very easily. We will later have look how to access regions. First we want to discuss how regions are organized...

The Region Forest

First lets have a look to the following structure:
  • WORLD (world)
    • EUROPE (continent)
      • CH -  Switzerland (country)
        • ZH - Zurich(Canton)
          • Wetzikon (Town)
        • GE - Geneva (Canton)
        • [...]
      • DE - Germany (country)
        • [...]
      • [...]
    • ASIA
      • [...]
    • [...]
  • ISO (standard)
    • DE - Germany
    • CH - Switzerland
    • US - United States
  • ISO3 (standard)
    • DEU - Germany
    • USA - United States
  • ECONOMIC UNIONS (state unions)
    • EU - European Union (economic union)
      • DE - Germany (country)
      • [...]
    • EFTA  (economic union)
      • CH - Switzerland (country)

Experience has shown that the following aspects are important:

  • Regions may have a hierarchical relationship to each other.
  • The hierarchical relationship often has a geographical nature, but any other classification are similarly feasible, e.g. based on economic, legal aspects.
  • The definition of a region must be independent from its hierarchical organization, since a region can be contained in multiple trees.
  • And finally regions are organized in a region forest, containing, depending on the use cases several trees.
As a consequence JSR 354 defines a RegionTreeNode:


  public interface RegionTreeNode {
    Region getRegion();
    RegionTreeNode getParent();
    Collection<RegionTreeNode> getChildren();
    boolean contains(Region region);
    RegionTreeNode selectParent(RegionFilter filter);
    Collection<RegionTreeNode> select(RegionFilter filter);
    RegionTreeNode getRegionTree(String path);
  }

Hereby
  • the Region related to a node is accessible calling getRegion.
  • getParent, getChildren allow to navigate within the tree.
  • getRegionTree allows to access any direct or indirect child node, using a path constructed of the region codes, e.g. WORLD/EUROPE/CH/ZH.
  • select, selectParent allow to search for parent or collect child regions using a RegionFilter predicate. contains simply allows to evaluate if a given Region is contained within the current Region.
So now the questions is how can I access regions and region trees...

The Regions Singleton

Accessing regions and region trees is done using the javax.money.ext.Regions singleton.  The following examples illustrate how to do that. 
First lets access all defined RegionTypes:

  Set<RegionType> regionTypes Regions.getRegionTypes();

Given a RegionType we can access all corresponding regions :

  Collection<Region> regions =
                      Regions.getRegios(RegionType.CONTINENT);

Since region codes in combination with a RegionType are required to be unique, a Region can be accessed directly:

  Region region = Regions.getRegion(RegionType.TERRITORY, "US");

And, if a Region's numeric id or Locale is defined, it is also possible to access a Region accordingly:

  Region region = Regions.getRegion(RegionType.TERRITORY, 2);
  Region region2 = Regions.getRegion(Locale.GERMANY);

Finally region trees can be accessed similarly:

  RegionTreeNode isoTree= Regions.getRegionTree("ISO");
   
Hereby the available region trees can also be evaluated:

  Set<String> treeIds = Regions.getRegionTreeIds();

Extended Region Data

Finally there is also an API for accessing additional data that is related to a Region. Similarly to the other API functionality the available data can be queried, given a Region instance:

  Region region = ...;
  Collection<Class> data = getExtendedRegionDataTypes(region);

Given such a data type class the concrete data can be accessed:

  MyExtendedData data =
                  getExtendedRegionData(
MyExtendedData.class);

Additional Info

It remains to mention that all the APIs are mapped to a set of SPI interfaces. Also the Regions singleton is feeded by a RegionsSingletonSpi, which must be registrated using the JDK's ServiceLoader. I might add a second post adding more details on this later.

In the meantime you may refer to the current JSR's source code repository located under
https://github.com/JavaMoney/javamoney for more details, or go to the JSR 354 project page lcoated at: http://java.net/projects/javamoney