Class WKTDictionary

All Implemented Interfaces:
org.opengis.referencing.AuthorityFactory, org.opengis.util.Factory

public class WKTDictionary extends GeodeticAuthorityFactory
A factory providing CRS objects parsed from WKT definitions associated to authority codes. Each WKT definition is associated to a key according the authority:version:code pattern where code is mandatory and authority:version are optional. Coordinate Reference Systems or other kinds of objects are created from WKT definitions when a create(…) method is invoked for the first time for a given key.

Sub-classing and instantiation

Newly constructed WKTDictionary are initially empty. The dictionary can be populated in the following ways: Sub-classing may be necessary even if fetchDefinition(…) is not overridden because WKTDictionary does not implement any of the CRSAuthorityFactory, CSAuthorityFactory or DatumAuthorityFactory. The choice of interfaces to implement is left to subclasses.
Example: extend the set of Coordinate Reference Systems recognized by CRS.forCode(String). The additional CRS are defined by Well-Known Text strings in a "MyCRS.txt" file. First step is to create a CRS factory with those definitions: The second step is to register this factory as a service with a META-INF/services/org.opengis.referencing.crs.CRSAuthorityFactory file on the classpath. That file shall contain the fully qualified class name of above MyCRS class.

Errors management

Well-Known Text parsing is performed in two steps, each of them executed at a different time:

Early validation

WKT strings added by load(…) or addDefinitions(…) methods are verified for matching quotes, balanced parenthesis or brackets, and valid number or date formats. If a syntax error is detected, the loading process is interrupted at the point the error occurred; CRS definitions after the error location are not loaded. However, WKT keywords and geodetic parameters (e.g. map projections) are not validated at this stage.

Late validation

WKT keywords and geodetic parameters inside WKT elements are validated only when createObject(String) is invoked. If an error occurs at this stage, only the CRS (or other geodetic object) for the code given to the createFoo(…) method become invalid. Objects associated to other codes are not impacted.

Multi-threading

This class is thread-safe but not necessarily concurrent. This class is designed for a relatively small amount of WKT; it is not a replacement for database-backed factory such as EPSGFactory.
Since:
1.1
Version:
1.1
  • Field Details

    • authority

      private volatile org.opengis.metadata.citation.Citation authority
      The organization or specification that defines the codes recognized by this factory. May be null if not yet determined.
      See Also:
    • authorities

      private final Set<String> authorities
      Authorities declared in all "ID[CITATION[…]]" elements found in WKT definitions. This set is null if an authority value has been explicitly specified at construction time. If non-null, this is used for creating a default authority.
    • codespaces

      private final Set<String> codespaces
      Code spaces of authority codes recognized by this factory. This set is computed from the "ID[…]" elements found in WKT definitions.
      See Also:
    • codeCaches

      private final Map<Class<?>,Set<String>> codeCaches
      Cache of authority codes computed by getAuthorityCodes(Class). This cache can be cleared at any time; values are recomputed when needed.
    • parser

      protected final WKTFormat parser
      The parser to use for creating geodetic objects from WKT definitions. Subclasses can modify the WKTFormat configuration in their constructor, but should not use it directly after construction (for thread safety reasons).
    • lock

      private final ReadWriteLock lock
      The write lock for parser and the read/write locks for definitions accesses. All parser usages after WKTDictionary construction shall be synchronized by the ReadWriteLock.writeLock().
      Implementation note: we manage the locks ourselves instead of using a ConcurrentHashMap because if a definitions value needs to be computed, then we need to block all other threads anyway since parser is not thread-safe. Consequently, the high concurrency capability provided by ConcurrentHashMap does not help us in this case.
    • definitions

      private final Map<String,Object> definitions
      CRS definitions associated to authority:version:code keys. Keys are authority codes, ignoring code space (authority) and version. For example, in "EPSG:9.1:4326" the key would be only "4326". Values can be one of the following 4 types:
      1. StoredTree: this is the initial state when there are no duplicated codes. This is the root of a tree of WKT keywords with their values as children. A tree can be parsed later as an IdentifiedObject when first requested.
      2. IdentifiedObject: the result of parsing the StoredTree when createObject(String) is invoked for a given authority code. The parsing result replaces the previous StoredTree value.
      3. WKTDictionary.Disambiguation: if the same code is used by two or more authorities or versions, then above-cited StoredTree or IdentifiedObject alternatives are wrapped in a WKTDictionary.Disambiguation object.
      4. String if parsing failed, in which case the string is the error message.

      Synchronization

      All read operations in this map shall be synchronized by the lock.readLock() and write operations synchronized by the lock.writeLock().
      See Also:
  • Constructor Details

    • WKTDictionary

      public WKTDictionary(org.opengis.metadata.citation.Citation authority)
      Creates an initially empty factory. The authority can specified explicitly or inferred from the WKTs. In the latter case (when the given authority is null), an authority will be inferred from all ID[…] or AUTHORITY[…] elements found in WKT strings as below, in preference order:
      1. Most frequent CITATION[…] value.
      2. If there is no citation, then most frequent code space in ID[…] or AUTHORITY[…] elements.
      The WKT strings are specified by calls to load(BufferedReader) or addDefinitions(Stream) after construction.
      Parameters:
      authority - organization that defines the codes recognized by this factory, or null.
  • Method Details

    • updateAuthority

      private void updateAuthority()
      If authority is not yet defined, computes a value from ID[…] found in all WKT strings. This method should be invoked after new WKTs have been added.
    • load

      public void load(BufferedReader source) throws org.opengis.util.FactoryException
      Adds to this factory all definitions read from the given source. Each Coordinate Reference System (or other geodetic object) is defined by a string in WKT format. The key associated to each object is given by the ID[…] or AUTHORITY[…] element, which is typically the last element of a WKT string and is mandatory for definitions in this file.

      WKT strings can span many lines. All lines after the first line shall be indented with at least one white space. Non-indented lines start new definitions.

      Blank lines and lines starting with the # character (ignoring white spaces) are ignored.

      Aliases for WKT fragments

      Files with more than one WKT definition tend to repeat the same WKT fragments many times. For example, the same BaseGeogCRS[…] element may be repeated in every ProjectedCRS definitions. Redundant fragments can be replaced by aliases for making the file more compact, easier to read, faster to parse and with smaller memory footprint.

      Each line starting with "SET <identifier>=<WKT>" defines an alias for a fragment of WKT string. The WKT can span many lines as described above. Aliases are local to the file where they are defined. Aliases can be expanded in other WKT strings by "$<identifier>".

      Validation

      This method verifies that definitions have matching quotes, balanced parenthesis or brackets, and valid number or date formats. It does not verify WKT keywords or geodetic parameters. See class javadoc for more details.

      Example

      An example is available here.
      Parameters:
      source - the source of WKT definitions.
      Throws:
      org.opengis.util.FactoryException - if the definition file cannot be read.
    • addDefinition

      private void addDefinition(StoredTree tree) throws FactoryDataException
      Adds the definition of a CRS (or other geodetic objects) from a tree of WKT elements. The authority code is inferred from the ID[…] or AUTHORITY[…] element. Caller must own the write lock before to invoke this method. updateAuthority() should be invoked after this method.
      Parameters:
      tree - a tree of WKT elements.
      Throws:
      IllegalArgumentException - if a codespace:version:code tuple is assigned twice.
      FactoryDataException - if the WKT does not have an ID[…] or AUTHORITY[…] element.
      See Also:
    • addDefinitions

      public void addDefinitions(Stream<String> objects) throws org.opengis.util.FactoryException
      Adds definitions of CRS (or other geodetic objects) from Well-Known Texts. Blank strings are ignored. Each non-blank String shall contain the complete definition of exactly one geodetic object. A geodetic object cannot have its definition splitted in two or more Strings.

      The key associated to each object is given by the ID[…] or AUTHORITY[…] element, which is typically the last element of a WKT string and is mandatory. WKT strings can contain line separators for human readability.

      Parameters:
      objects - CRS (or other geodetic objects) definitions as WKT strings.
      Throws:
      org.opengis.util.FactoryException - if a WKT cannot be parsed, or does not contain an ID[…] or AUTHORITY[…] element, or if the same codespace:version:code tuple is used for two objects.
    • parseAndAdd

      private org.opengis.referencing.IdentifiedObject parseAndAdd(String codespace, String version, String code, String wkt, DefaultIdentifier defaultIdentifier) throws org.opengis.util.FactoryException
      Parses immediately the given WKT and caches the result under the given identifier. This method is invoked only if subclass overrides fetchDefinition(DefaultIdentifier) for producing WKT on-the-fly.
      Parameters:
      codespace - the authority (or other kind of code space) providing CRS definitions.
      version - version of the CRS definition, or null if unspecified.
      code - code allocated by the authority for the CRS definition.
      wkt - the Well-Known Text to parse immediately.
      defaultIdentifier - identifier to assign to the object if the WKT does not provide one.
      Returns:
      the parsed object.
      Throws:
      org.opengis.util.FactoryException - if parsing failed.
    • fetchDefinition

      protected String fetchDefinition(DefaultIdentifier identifier) throws org.opengis.util.FactoryException
      Fetches the Well-Known Text for a user-specified identifier not found in this WKTDictionary. Subclasses can override this method if WKT strings are not loaded or specified in advance, but instead fetched when first needed. An example of such scenario is WKTs provided by the "spatial_ref_sys" table of a spatial database. If no WKT is found for the given identifier, then this method returns null.

      On input, identifier contains only the pieces of information provided by user. For example if user invoked createGeographicCRS("Foo"), then the identifier code will be "Foo" but the codespace and version will be undefined (null). On output, identifier should be completed with missing code space and version (if available).

      Overriding

      The default implementation returns null. If a subclass overrides this method, then it should also override getAuthorityCodes(Class) because WKTDictionary does not know the codes that this method can recognize.
      Parameters:
      identifier - the code specified by user, possible with code space and version.
      Returns:
      Well-Known Text (WKT) for the given identifier, or null if none.
      Throws:
      org.opengis.util.FactoryException - if an error occurred while fetching the WKT.
    • unexpectedText

      private String unexpectedText(int lineNumber, String wkt, int end)
      Produces an error message for unexpected characters at the end of WKT string.
      Parameters:
      lineNumber - line where the error occurred.
      wkt - the WKT being parsed.
      end - end of WKT parsing.
      Returns:
      message to give to exception constructor.
    • resources

      private Resources resources()
      Convenience methods for resources in the language used for error messages.
    • trimOrNull

      private static String trimOrNull(Object value)
      Trims the leading and trailing spaces of the string representation of given object. If null, empty or contains only spaces, then this method returns null.
    • forEachValue

      final void forEachValue(Consumer<Object> addTo)
      Adds all definition values to the given supplier. This is for testing purposes only. This method performs no locking because it is not needed for current JUnit tests.
      See Also:
    • getAuthority

      public org.opengis.metadata.citation.Citation getAuthority()
      Returns the authority or specification that defines the codes recognized by this factory. This is the first of the following values, in preference order:
      1. The authority explicitly specified at construction time.
      2. A citation built from the most frequent value found in CITATION elements.
      3. A citation built from the most frequent value found in ID or AUTHORITY elements.
      Specified by:
      getAuthority in interface org.opengis.referencing.AuthorityFactory
      Specified by:
      getAuthority in class GeodeticAuthorityFactory
      Returns:
      the organization responsible for CRS definitions, or null if unknown.
      See Also:
    • getCodeSpaces

      public Set<String> getCodeSpaces()
      Returns all namespaces recognized by this factory. Those namespaces can appear before codes in calls to createFoo(String) methods, for example "ESRI" in "ESRI:102018". Namespaces are case-insensitive.
      Overrides:
      getCodeSpaces in class GeodeticAuthorityFactory
      Returns:
      the namespaces recognized by this factory.
    • getAuthorityCodes

      public Set<String> getAuthorityCodes(Class<? extends org.opengis.referencing.IdentifiedObject> type) throws org.opengis.util.FactoryException
      Returns the set of authority codes for objects of the given type. The type argument specifies the base type of identified objects.
      Parameters:
      type - the spatial reference objects type.
      Returns:
      the set of authority codes for spatial reference objects of the given type.
      Throws:
      org.opengis.util.FactoryException - if an error occurred while fetching the codes.
    • getDescriptionText

      public org.opengis.util.InternationalString getDescriptionText(String code) throws org.opengis.util.FactoryException
      Gets a description of the object corresponding to a code.
      Specified by:
      getDescriptionText in interface org.opengis.referencing.AuthorityFactory
      Overrides:
      getDescriptionText in class GeodeticAuthorityFactory
      Parameters:
      code - value allocated by authority.
      Returns:
      a description of the object, or null if null if none.
      Throws:
      org.opengis.referencing.NoSuchAuthorityCodeException - if the specified code was not found.
      org.opengis.util.FactoryException - if the query failed for some other reason.
    • createObject

      public org.opengis.referencing.IdentifiedObject createObject(String code) throws org.opengis.util.FactoryException
      Returns an arbitrary object from a code.
      Specified by:
      createObject in interface org.opengis.referencing.AuthorityFactory
      Specified by:
      createObject in class GeodeticAuthorityFactory
      Parameters:
      code - value allocated by authority.
      Returns:
      the object for the given code.
      Throws:
      org.opengis.referencing.NoSuchAuthorityCodeException - if the specified code was not found.
      org.opengis.util.FactoryException - if the object creation failed for some other reason.
      See Also:
    • getOrCreate

      private Object getOrCreate(String code, boolean create) throws org.opengis.util.FactoryException
      Returns the object associated to the given code.
      Parameters:
      code - value allocated by authority.
      create - whether to create IdentifiedObject from StoredTree.
      Returns:
      the object for the given code, possibly as a StoredTree if create is false.
      Throws:
      org.opengis.referencing.NoSuchAuthorityCodeException - if the specified code was not found.
      org.opengis.util.FactoryException - if the object creation failed for some other reason.