What are Location Rules?

Location Rules are special declarations that allow defining a recurrent interpretation of several interacting Location Paths.

Each Location Rule is specified as a text expression (called Location Rule Specification), which has the following form:

matching_ETs [matching_condition] ->
  element_location_path
The rule can be interpreted against a certain initial (context) element and produce a set of other (result) elements. At that, the settings above have the following meaning:

matching_ETs

Specify the rule's Matching Element Type(s), which may have one of the forms: The rule interpretation starts from testing if the context element complies with at least one of the specified Matching Element Types. If it does not, the rule is passed over.
matching_condition
Specify an additional (optional) Matching Condition for the context element.

This should be a boolean FlexQuery expression. If it is specified, once the rule's context element is found to comply with the Matching Element Type(s), it is set as the generator context element and the query is executed. If it returns true, the rules is processed further, otherwise, it is passed over.

element_location_path
Once both matching tests passed, the Element Location Path specified in the right part of the rule (after the arrow) is processed against the context element. This produces a new set of elements, which becomes the rule's interpretation result.

Examples:

The following are specifications of some Location Rules used in real applications:


* -> descendant::class
package -> child-or-self::package/child::class 
*[isVisible()] -> superclass^::ClassDoc
(Package|ProgramElement) -> AnnotationDesc

Location Rule Sets

To program searching of elements, several Location Rules are defined together as a single set.

Some of the rules may be tagged with a special Recursive Flag. Such rules are called Recursive Location Rules.

The whole set of Location Rules is interpreted against a certain initial context element according to the following steps:

Step 1:  All Location Rules specified in the set are interpreted against the initial context element. The elements generated by each rule are added to the total result set.

Step N+1:  For each new element generated at the previous step, those Location Rules marked with the recursive flag are interpreted with this element selected as the rule's context element. The new generated elements are added to the total result set. This step is repeated until at the previous step no new elements have been produced.

At the end, the elements accumulated in the total result set become the final interpretation result.

Using Location Rules

Location Rules are used in Element Iterator sections of templates to define collecting of the iterated elements.

However, they can be equally used within FlexQuery expressions.

A set of Location Rules can be created as an array (using Array() function), whose elements must be objects returned by calls LocationRule() function, which defines a single Location Rule. Further, that array must be passed to findElementsByLRules() function along with the initial element against which the rules are to be interpreted. That function will return an enumeration of all found elements.

Example:

As example of using Location Rules, let's consider the following model.

The model describes a certain Java project and may be built of elements of various types. We shall focus on only two Element Types:

'Class' -- represents a Java class
'Interface' -- represents a Java interface
Java classes and interfaces have the following relationships:
Classes/interfaces may extend other classes/interfaces, which is reflected by the 'extends' attribute in both 'Class' and 'Interface' Element Types.
Classes may also implement certain interfaces, which is reflected by the 'implements' attribute in the 'Class' Element Types.
Using DTD specification, those attributes may be described as the following:

<!ATTLIST Class extends IDREF>
<!ATTLIST Interface extends IDREFS>
<!ATTLIST Class implements IDREFS>
Now, let's we want to collect all interfaces implemented by a certain class. This would include:
  1. All interface directly implemented by the given class.
  2. All interfaces directly implemented by all ancestors of the given class.
  3. All ancestors of all directly implemented interfaces.
The following FlexQuery expression will return an enumeration of all interfaces implemented by a class specified in the 'classElement' parameter:
/* defining the set of Location Rules (the 'true' parameter means 'recursive rule') */

lrules = Array (
  LocationRule("* -> extends^::(Class|Interface)", true),
  LocationRule("Class -> implements^::Interace", true)
);
/* calling interpretation of the Location Rules (the last parameter specify that the elements included in the result enumeration should be filtered out to comply with the 'Interface' element type) */

findElementsByLRules(classElement, lrules, "Interface")