Package org.apache.commons.configuration2.tree

# Class DefaultExpressionEngine

java.lang.Object
org.apache.commons.configuration2.tree.DefaultExpressionEngine

All Implemented Interfaces:
  `ExpressionEngine`

public class DefaultExpressionEngine extends Object implements ExpressionEngine

A default implementation of the `ExpressionEngine` interface providing the "native" expression language for hierarchical configurations.

This class implements a rather simple expression language for navigating through a hierarchy of configuration nodes. It supports the following operations:

- Navigating from a node to one of its children using the child node delimiter, which is by the default a dot (".").
- Navigating from a node to one of its attributes using the attribute node delimiter, which by default follows the XPATH like syntax `[@&lt;attributeName&gt;]`.
- If there are multiple child or attribute nodes with the same name, a specific node can be selected using a numerical index. By default indices are written in parenthesis.

As an example consider the following XML document:

      <database>
        <tables>
          <table type="system">
            <name>users</name>
            <fields>
              <field>
                <name>lid</name>
                <type>long</name>
              </field>
              <field>
                <name>usrName</name>
                <type>java.lang.String</type>
              </field>
             ...
            </fields>
          </table>
          <table>
            <name>documents</name>
            <fields>
              <field>
                <name>docid</name>
                <type>long</type>
              </field>
              ...
            </fields>
          </table>
          ...
        </tables>
      </database>

If this document is parsed and stored in a hierarchical configuration object, for instance the key `tables.table(0).name` can be used to find out the name of the first table. In opposite `tables.table.name` would return a collection with the names of all available tables. Similarly the key `tables.table(1).fields.field.name` returns a collection with the names of all fields of the second table. If another index is added after the `field` element, a single field can be accessed: `tables.table(1).fields.field(0).name`. The key `tables.table(0)[@type]` would select the type attribute of the first table.

This example works with the default values for delimiters and index markers. It is also possible to set custom values for these properties so that you can adapt a `DefaultExpressionEngine` to your personal needs.

The concrete symbols used by an instance are determined by a `DefaultExpressionEngineSymbols` object passed to the constructor. By providing a custom symbols object the syntax for querying properties in a hierarchical configuration can be altered.

Instances of this class are thread-safe and can be shared between multiple hierarchical configuration objects.

Since:
  1.3

- ## Field Summary

  Fields
  Modifier and Type
  Field
  Description
  `static final DefaultExpressionEngine`
  `INSTANCE`
  A default instance of this class that is used as expression engine for hierarchical configurations per default.

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `DefaultExpressionEngine(DefaultExpressionEngineSymbols syms)`
  Creates a new instance of `DefaultExpressionEngine` and initializes its symbols.
  `DefaultExpressionEngine(DefaultExpressionEngineSymbols syms, NodeMatcher<String> nodeNameMatcher)`
  Creates a new instance of `DefaultExpressionEngine` and initializes its symbols and the matcher for comparing node names.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `String`
  `attributeKey(String parentKey, String attributeName)`
  Returns the key of an attribute.
  `<T> String`
  `canonicalKey(T node, String parentKey, NodeHandler<T> handler)`
  Determines a "canonical" key for the specified node in the expression language supported by this implementation.
  `protected <T> T`
  `findLastPathNode(DefaultConfigurationKey.KeyIterator keyIt, T node, NodeHandler<T> handler)`
  Finds the last existing node for an add operation.
  `protected <T> void`
  `findNodesForKey(DefaultConfigurationKey.KeyIterator keyPart, T node, Collection<QueryResult<T>> results, NodeHandler<T> handler)`
  Recursive helper method for evaluating a key.
  `DefaultExpressionEngineSymbols`
  `getSymbols()`
  Gets the `DefaultExpressionEngineSymbols` object associated with this instance.
  `<T> String`
  `nodeKey(T node, String parentKey, NodeHandler<T> handler)`
  Returns the key for the specified node in the expression language supported by an implementation.
  `<T> NodeAddData<T>`
  `prepareAdd(T root, String key, NodeHandler<T> handler)`
  Prepares Adding the property with the specified key.
  `<T> List<QueryResult<T>>`
  `query(T root, String key, NodeHandler<T> handler)`
  Finds the nodes and/or attributes that are matched by the specified key.

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Field Details

  - ### INSTANCE

    public static final DefaultExpressionEngine INSTANCE
    A default instance of this class that is used as expression engine for hierarchical configurations per default.

- ## Constructor Details

  - ### DefaultExpressionEngine

    public DefaultExpressionEngine(DefaultExpressionEngineSymbols syms)
    Creates a new instance of `DefaultExpressionEngine` and initializes its symbols.
    Parameters:
      `syms` - the object with the symbols (must not be null)
    Throws:
      `IllegalArgumentException` - if the symbols are null

  - ### DefaultExpressionEngine

    public DefaultExpressionEngine(DefaultExpressionEngineSymbols syms, NodeMatcher<String> nodeNameMatcher)
    Creates a new instance of `DefaultExpressionEngine` and initializes its symbols and the matcher for comparing node names. The passed in matcher is always used when the names of nodes have to be matched against parts of configuration keys.
    Parameters:
      `syms` - the object with the symbols (must not be null)
      `nodeNameMatcher` - the matcher for node names; can be null, then a default matcher is used
    Throws:
      `IllegalArgumentException` - if the symbols are null

- ## Method Details

  - ### attributeKey

    public String attributeKey(String parentKey, String attributeName)
    Description copied from interface: `ExpressionEngine`
    Returns the key of an attribute. The passed in `parentKey` must reference the parent node of the attribute. A concrete implementation must concatenate this parent key with the attribute name to a valid key for this attribute.
    Specified by:
      `attributeKey` in interface `ExpressionEngine`
    Parameters:
      `parentKey` - the key to the node owning this attribute
      `attributeName` - the name of the attribute in question
    Returns:
      the resulting key referencing this attribute

  - ### canonicalKey

    public <T> String canonicalKey(T node, String parentKey, NodeHandler<T> handler)
    Determines a "canonical" key for the specified node in the expression language supported by this implementation. This means that always a unique key if generated pointing to this specific node. For most concrete implementations, this means that an index is added to the node name to ensure that there are no ambiguities with child nodes having the same names. This implementation works similar to `nodeKey()`; however, each key returned by this method has an index (except for the root node). The parent key is prepended to the name of the current node in any case and without further checks. If it is null, only the name of the current node with its index is returned.
    Specified by:
      `canonicalKey` in interface `ExpressionEngine`
    Type Parameters:
      `T` - the type of the node to be processed
    Parameters:
      `node` - the node, for which the key must be constructed
      `parentKey` - the key of this node's parent (can be null for the root node)
      `handler` - the `NodeHandler` for accessing the node
    Returns:
      the canonical key of this node

  - ### findLastPathNode

    protected <T> T findLastPathNode(DefaultConfigurationKey.KeyIterator keyIt, T node, NodeHandler<T> handler)
    Finds the last existing node for an add operation. This method traverses the node tree along the specified key. The last existing node on this path is returned.
    Type Parameters:
      `T` - the type of the nodes to be dealt with
    Parameters:
      `keyIt` - the key iterator
      `node` - the current node
      `handler` - the node handler
    Returns:
      the last existing node on the given path

  - ### findNodesForKey

    protected <T> void findNodesForKey(DefaultConfigurationKey.KeyIterator keyPart, T node, Collection<QueryResult<T>> results, NodeHandler<T> handler)
    Recursive helper method for evaluating a key. This method processes all facets of a configuration key, traverses the tree of properties and fetches the results of all matching properties.
    Type Parameters:
      `T` - the type of nodes to be dealt with
    Parameters:
      `keyPart` - the configuration key iterator
      `node` - the current node
      `results` - here the found results are stored
      `handler` - the node handler

  - ### getSymbols

    public DefaultExpressionEngineSymbols getSymbols()
    Gets the `DefaultExpressionEngineSymbols` object associated with this instance.
    Returns:
      the `DefaultExpressionEngineSymbols` used by this engine
    Since:
      2.0

  - ### nodeKey

    public <T> String nodeKey(T node, String parentKey, NodeHandler<T> handler)
    Returns the key for the specified node in the expression language supported by an implementation. This method is called whenever a property key for a node has to be constructed, for example by the `getKeys()` method. This implementation takes the given parent key, adds a property delimiter, and then adds the node's name. The name of the root node is a blank string. Note that no indices are returned.
    Specified by:
      `nodeKey` in interface `ExpressionEngine`
    Type Parameters:
      `T` - the type of the node to be processed
    Parameters:
      `node` - the node, for which the key must be constructed
      `parentKey` - the key of this node's parent (can be null for the root node)
      `handler` - the `NodeHandler` for accessing the node
    Returns:
      this node's key

  - ### prepareAdd

    public <T> NodeAddData<T> prepareAdd(T root, String key, NodeHandler<T> handler)

    Prepares Adding the property with the specified key.

    To be able to deal with the structure supported by hierarchical configuration implementations the passed in key is of importance, especially the indices it might contain. The following example should clarify this: Suppose the current node structure looks like the following:

          tables
             +-- table
                     +-- name = user
                     +-- fields
                             +-- field
                                     +-- name = uid
                             +-- field
                                     +-- name = firstName
                             ...
             +-- table
                     +-- name = documents
                     +-- fields
                            ...

    In this example a database structure is defined, for example all fields of the first table could be accessed using the key `tables.table(0).fields.field.name`. If now properties are to be added, it must be exactly specified at which position in the hierarchy the new property is to be inserted. So to add a new field name to a table it is not enough to say just

         config.addProperty("tables.table.fields.field.name", "newField");

    The statement given above contains some ambiguity. For instance it is not clear, to which table the new field should be added. If this method finds such an ambiguity, it is resolved by following the last valid path. Here this would be the last table. The same is true for the `field`; because there are multiple fields and no explicit index is provided, a new `name` property would be added to the last field - which is probably not what was desired.

    To make things clear explicit indices should be provided whenever possible. In the example above the exact table could be specified by providing an index for the `table` element as in `tables.table(1).fields`. By specifying an index it can also be expressed that at a given position in the configuration tree a new branch should be added. In the example above we did not want to add an additional `name` element to the last field of the table, but we want a complete new `field` element. This can be achieved by specifying an invalid index (like -1) after the element where a new branch should be created. Given this our example would run:

         config.addProperty("tables.table(1).fields.field(-1).name", "newField");

    With this notation it is possible to add new branches everywhere. We could for instance create a new `table` element by specifying

         config.addProperty("tables.table(-1).fields.field.name", "newField2");

    (Note that because after the `table` element a new branch is created indices in following elements are not relevant; the branch is new so there cannot be any ambiguities.)

    Specified by:
      `prepareAdd` in interface `ExpressionEngine`
    Type Parameters:
      `T` - the type of the nodes to be dealt with
    Parameters:
      `root` - the root node of the nodes hierarchy
      `key` - the key of the new property
      `handler` - the node handler
    Returns:
      a data object with information needed for the add operation

  - ### query

    public <T> List<QueryResult<T>> query(T root, String key, NodeHandler<T> handler)
    Finds the nodes and/or attributes that are matched by the specified key. This is the main method for interpreting property keys. An implementation must traverse the given root node and its children to find all results that are matched by the given key. If the key is not correct in the syntax provided by that implementation, it is free to throw a (runtime) exception indicating this error condition. The passed in `NodeHandler` can be used to gather the required information from the node object. This method supports the syntax as described in the class comment.
    Specified by:
      `query` in interface `ExpressionEngine`
    Type Parameters:
      `T` - the type of the node to be processed
    Parameters:
      `root` - the root node of a hierarchy of nodes
      `key` - the key to be evaluated
      `handler` - the `NodeHandler` for accessing the node
    Returns:
      a list with the results that are matched by the key (should never be null)