Skip Headers

Oracle® Business Rules Language Reference 10g Release 3 (10.1.3) B15985-01 |
| Home | Solution Area | Contents | Index

Previous | Next |

# 2 Rule Language Reference

This chapter contains a detailed and complete reference to the Oracle Business Rules RL Language (RL Language) syntax, semantics, and built-in functions.

Grammar rules define the RL Language. Each grammar rule defines a non-terminal symbol on the left of the `::=` symbol in terms of one or more non-terminal and terminal symbols on the right of the `::=` symbol.

Reserved Words

autofocus, boolean, break, catch, char, class, constant, continue, double, else, exists, extends, fact, false, final, finally, float, for, function, if, import, include, instanceof, int, logical, long, new, null, priority, property, public, query, return, returns, rule, ruleset, short, synchronized, throw, true, try, while, var

Note: Reserved words not in shown in bold are planned for a future RL Language release, and include the words: break, continue, and query.

## Ruleset

A ruleset groups a set of definitions. A ruleset is a collection of rules and other definitions that are all intended to be evaluated at the same time. A ruleset may also contain executable actions, may include or contain other rulesets, and may import Java classes and packages.

Format

ruleset ::= named-ruleset | unnamed-ruleset

named-ruleset ::= ruleset ruleset-name { unnamed-ruleset }

unnamed-ruleset ::= ( import | include | named-ruleset | definition | action | fact-class )*

ruleset-name ::= identifier

Usage Notes

A named-ruleset creates or adds definitions to the specified ruleset named ruleset-name.

An unnamed-ruleset adds definitions to the default ruleset named `main`.

Rulesets may be nested, that is they may contain or include other rulesets. Nesting does not affect ruleset naming, but it does affect ruleset visibility in a way similar to Java import's affect on package visibility.

You can execute a ruleset using the RL Language command-line, or using the Java RuleSession API.

A named-ruleset ruleset-name must be unique within a `RuleSession`.

Examples

Example 2-1 contains two definitions, `enterRoom` and `sayHello`, and two actions (assert and run).

The rule shown in Example 2-1 will not fire until:

1. An `enterRoom` fact is asserted.

2. The run function executes, which pushes the rule's containing ruleset, `hello` onto the ruleset stack.

Example 2-1 Using a Named Ruleset

    ruleset hello {
      class enterRoom { String who; }
      rule sayHello {
        if (fact enterRoom) {
          println("Hello " + enterRoom.who);
        }
      }
      assert(new enterRoom(who: "Bob"));
      run("hello");
    }

In Example 2-2, if ruleset R2 is nested in ruleset R1, the name R2 must be unique within the rule session. R2 is not named relative to R1. For example, the class C2 defined in R2 is globally named R2.C2, not R1.R2.C2. If R2 is nested in R1, a public class C1 defined in R1 may be referenced in R2 using either the full name R1.C1 or the short name C1 (assuming R2 does not also define C1).

Example 2-2 Using a Nested Ruleset

    ruleset R1 {
      public class C1 {
        public String s;
      }
      C1 apple = new C1(s: "apple");
      ruleset R2 {
        public class C2 {
          public String s;
        }
        C1 c1 = apple;        // finds C1 and apple in containing ruleset R1
        c1.s = "delicious";
        C2 c2 = new C2(s: "pear");
      }
      R2.C2 pear = R2.c2; // finds R2.C2 and R2.c2 because they are fully qualified
      println(apple.s + " " + pear.s); // prints "delicious pear"

      pear = c2;  // UndefinedException: c2 not in R1 or a containing ruleset
    }

## Types

RL Language is a strongly typed language. Each variable and value has a specified type.

Format

type ::= simple-type [ [ ] ]

simple-type ::= primitive | object-type

primitive ::= boolean | numeric

numeric ::= int | double | float | long | short | byte | char

object-type ::= class-definition-name | Java-class-name

class-definition-name ::= qname

Java-class-name ::= qname

Type Conversion

There are several ways that a value can be converted from one type to another:

1. Conversion from any type to `String` using the `String` concatenation operator `+` .

2. Implicitly from context. For example, by adding an `int` to a `double` first converts the `int` to a `double` and then adds the 2 `doubles`.

3. Casting between 2 numeric types.

4. Casting between 2 classes related by inheritance.

5. Invoking a function or method that performs the conversion. For example, `toString`.

Table 2-1 summarizes the implicit conversions for various types. Rows indicate how the type in the From column may be implicitly converted, as shown in the list of types shown in the To column.

Table 2-1 Implicit Type Conversions

From | To
--- | ---
`int` | `double`, `float`, `long`
`float` | `double`
`long` | `double`, `float`
`short` | `int`, `double`, `float`, `long`
`byte` | `int`, `double`, `float`, `long`, `short`
`char` | `int`, `double`, `float`, `long`
`String` | `Object`
`Object` | `Object` (if the From Object is a subclass of the To Object)
`fact set` | `boolean`
`array` | `Object`

Note: An Object is an instance of a Java or RL Language class or array. Type conversion is possible only if the classes are related by inheritance (implements or extends).

Table 2-2 summarizes the allowed cast conversions for various types where a cast can be used to convert a primitive with more bits to a primitive with fewer bits, without throwing an exception.

The type conversions shown in Table 2-2 require an explicit cast operator. For example,

    int i = 1;
    short s = (short)i;

Note: Type conversions such as those shown in Table 2-2 that involve numeric types may lose high order bits, and such conversions involving Objects may throw a `RLClassCastExeption`.

Table 2-2 Explicit Type Conversions

From | To
--- | ---
`double` | `float`, `long`, `int`, `short`, `byte`, `char`
`float` | `long`, `int`, `short`, `byte`, `char`
`long` | `int`, `short`, `byte`, `char`
`short` | `byte`, `char`
`byte` | `char`
`char` | `byte`

When you use a cast to convert a primitive with more bits, to a primitive with fewer bits, the RL Language discards extra, high order, bits without throwing an exception.

For example,

    short s = -134;
    byte b = (byte)s;
    println("s = " + s + ", b = " + b);
    prints: s = -134, b = 122

Primitive Types

A primitive type may be any of the following

- An `int`, which is a 32 bit integer. Literal values are scanned by `java.lang.Integer.parseInt`

- A `long`. Literal values are scanned by `java.lang.Long.parseLong`

- A `short`. Literal values are scanned by `java.lang.Short.parseShort`

- A `byte`. Literal values are scanned by `java.lang.Byte.parseByte`

- A `char`.

- A `double`. Literal values are scanned by `java.lang.Double.parseDouble`

- A `float`. Literal values are scanned by `java.lang.Float.parseFloat`

- A `boolean` `true` or `false`

Object Types

An object type may be:

- A java `Object`, identified by the qualified name, qname, of its class. For example, `java.lang.String`.

- An RL Language `Object`, identified by the qualified name, qname of its class. For example, `ruleset1.Class1`.

String Types

RL Language uses Java strings, where:

- Strings are instances of the class `java.lang.String`.

- A string literal is delimited by double quotes ("string").

  Use \" to include the double quote character in a string.

- Strings may be concatenated using the `+` operator as follows:

  - If any operand of a `+` operator is a String then the remaining operands are converted to String and the operands are concatenated.

  - An Object is converted to a String using its `toString` method.

  - An instance of an RL Language class is converted to a String using a built-in conversion.

Array Types

Square brackets `[``]` denote arrays. An array in RL Language has the same syntax and semantics as a Java 1-dimensional array.

Note: RL Language does not support multi-dimensional arrays.

## Identifiers

RL Language supports both the Java and the XML variant of identifiers and namespace packages. To use the XML variant, the identifier must be enclosed in back quotes.

Format

identifier ::= java-identifier | xml-identifier

java-identifier ::= valid-Java-identifier

xml-identifier ::= `valid-xml-identifier or URI `

Where:

valid-Java-identifier is: a legal Java identifier, for example, JLd_0.

valid-xml-identifier is: a legal XML identifier, for example x-1.

URI is: a legal Uniform Resource Identifier, for example, http://www.oracle.com/rules

Usage Notes

An xml-identifier can contain characters that are illegal Java identifier characters, for example, ':' and '-'. The JAXB specification defines a standard mapping of xml identifiers to Java identifiers, and includes preserving the Java conventions of capitalization. The JAXB specification also defines a standard mapping from the schema target namespace URI to a Java package name, and a mapping from anonymous types to Java static nested classes.

Examples

RL Language supports both the Java and the XML variant of identifiers and namespaces or packages. Enclose an identifier in back quotes to use the XML variant, as shown in Example 2-3.

You can use the back quote notation anywhere an identifier or package name is legal in RL Language. To use the XML variant of identifiers in `String` arguments to assertXPath, back quotes are not needed. Example 2-4 and Example 2-5 are equivalent.

Example 2-3 Sample Mapping for XML Identifiers Using Back Quotes

    `http://www.mycompany.com/po.xsd` -> com.mycompany.po
    `my-attribute` -> myAttribute
    `Items/item` -> Items$ItemType

Example 2-4 Using an XML Identifier in a String with assertXPath

    import `http://www.mycompany.com/po.xsd`.*;
    fact class `purchaseOrder` supports xpath;
    fact class `Items/item` supports xpath;
    assertXPath("http://www.mycompany.com/po.xsd", root,
       ".//item[ship-date > 20-jul-2004]");
    rule r {
      if (fact `a-global-element-name`(`my-attribute`: 0)) {
        println("my-attribute is zero");
      }
    }

Example 2-5 Using Java Identifiers with assertXPath

    import com.mycompany.po.*;
    fact class PurchaseOrder supports xpath;
    fact class Items$ItemType supports xpath;
    assertXPath("com.mycompany.po",
                root,
                ".//item[shipDate > 20-jul-2004]");
    rule r {
      if (fact AGlobalElementName (myAttribute: 0)) {
        println("my-attribute is zero");
      }
    }

## Literals

Table 2-3 summarizes the RL Language literals. The literals are the same as Java literals.

Table 2-3 RL Language Literals

A literal such as | Can be assigned to variables of these types
--- | ---
An integer in range 0..127 or a `char` with UCS2 encoding in range 0...127 | `byte`, `char`, `short`, `int`, `long`, `float`, `double`
An integer in range 0..65535 or a `char` | `char`, `int`, `long`, `float`, `double`
An integer in range -128..127 | `byte`, `short`, `int`, `long`, `float`, `double`
An integer in range -32768..32767 | `short`, `int`, `long`, `float`, `double`
An integer | `int`, `long`, `float`, `double`
An integer with `L` suffix | `long`, `float`, `double`
A floating point constant | `double`
A floating point constant with `F` suffix | `float`, `double`
A String enclosed in "" | `String`, `Object`

## Definitions

When a definition within a ruleset is executed, it is checked for correctness and then saved for use later in the rule session.

Format

definition ::= variable | rule | rl-class-definition | function

name ::= identifier

qname ::= [ ruleset-or-packagename. ]name

ruleset-or-packagename ::= qname

Usage Notes

Every definition has a unique name within its containing ruleset, and thus a unique qualified name, qname, within the rule session.

Variables defined at the ruleset level are global. Global variables are visible to all expressions contained in the ruleset using the name of the variable and visible to expressions in other rulesets using the variable qname. Functions and public classes may also be referenced from other rulesets using the respective qname.

Java classes and their methods and properties also have qnames.

Example

The qname of the class definition in Example 2-6 is `hello.enterRoom`.

Example 2-6 Class Definition Within a Named Ruleset

    ruleset hello {
      class enterRoom { String who; }
      rule sayHello {
        if (fact enterRoom) {
          println("Hello " + enterRoom.who);
        }
      }
      assert(new enterRoom(who: "Bob"));
      run("hello");
    }

### Variable Definitions

Variables are declared as in Java, but initialization is always required.

Format

variable ::= [ final ] ( numeric name = numeric-expression

| boolean name = boolean-expression

| type [ ] name = array-expression | null

| object-type name = object-expression | null )

) ;

Usage Notes

The type of the array initialized with the array-expression must be the same as the type specified for the array elements.

A variable can have a primitive type, a Java class name, or an RL Language class name, and may be an array of elements of the same type.

The type of the object-expression must be the same as the object-type of the variable being declared. A class instance or array may be initialized to null.

Variables may be local or global in scope. The initialization expression is required. Local variables may not be final.

#### Global Variables

Variables immediately enclosed in a ruleset, that is, in a definition, are global to a rule session in scope. The initialization expression for a final global variable is executed when the global variable is defined.

The initialization expression for a non-final global variable is executed both:

- When the global variable is defined.

- Each time the `reset` function is called.

Global variables declared as final may not be modified after they are initialized.

Global variables referenced in a rule condition (fact-set-condition) must be final.

Examples

Example 2-7 shows that the reset function performs initialization for the non-final global variable `i`. Thus, this example prints 0, not 1.

Example 2-7 Non-Final Global Variable Initialization After Reset Function

    RL> int i = 0;
    RL> i++;
    RL> reset();
    RL> println(i);

Be careful when initializing global variables with functions that have side effects. If you do not want the side effects repeated when calling reset, you should declare the variable `final`. For example, Example 2-8 prints "once" twice and Example 2-9 prints "once" once.

Example 2-8 Initializing a Global Variable with Side Effects with Reset

    RL> clear;
    RL> function once() returns int
    {
      println("once");
      return 1;
    }
    RL> int i = once();
    once
    RL> reset();
    once
    RL>

Example 2-9 Initializing a Final Global Variable to Avoid Side Effects with Reset

    RL> clear;
    RL> function once() returns int
    {
      println("once");
      return 1;
    }
    RL> final int i = once();
    once
    RL> reset();
    RL>

### Rule Definitions

The Oracle Rules Engine matches facts against the fact-set-conditions of all rules in the rule session to build the agenda of rules to execute. A fact set row is a combination of facts that makes the conditions of a rule true. An activation is a fact set row paired with a reference to the action-block of the rule. The agenda is the list of all activations in the rules session. The Oracle Rules Engine matches facts and rules when the state of working memory changes, typically when a fact is asserted or retracted.

The run, runUntilHalt, and step functions execute activations. Activations are removed from the agenda after they are executed, or if the facts referenced in their fact set row are modified or retracted such that they no longer match the rule's condition.

Activations are executed in order of the ruleset stack. You can manage the ruleset stack with the getRulesetStack, clearRulesetStack, pushRuleset, and popRuleset functions.

In order for a rule to fire, three things must occur:

1. An activation of that rule must be on the agenda.

2. The containing ruleset must be at the top of the ruleset stack.

3. You must invoke run, runUntilHalt, or step.

The fact set produced in a fact-set-condition is available to the rule actions. For each row in the fact set, the action-block is activated as follows:

- The rule's action-block is scheduled for execution at the specified rule priority.

- References from the action-block to the matched facts are bound to the current row.

- If a matched fact is retracted before the action-block is executed, the dependent activations are destroyed (removed from the agenda).

Format

rule ::= rule rule-name { property* fact-set-condition action-block }

rule-name ::= name

property ::= priority | autofocus | logical

priority ::= priority = numeric-expression

autofocus ::= autofocus = boolean-literal

logical ::= logical = ( boolean-literal | positive-integer-literal )

Where:

positive-integer-literal is: an integer literal that is > 0

Usage Notes

The priority property specifies the priority for a rule. Within a set of activations of rules from the same ruleset, activations are executed in priority order (see "Ordering Rule Firing"). When rules have different priorities, the rules with a higher priority are activated before those with a lower priority. The default priority is 0. Within a set of activations of rules of the same priority, the most recently added activations are executed first, but this behavior can be changed (see the getStrategy and setStrategy functions).

A rule with the autofocus property equal to `true` automatically pushes its containing ruleset onto the ruleset stack whenever it is activated.

A rule with the logical property makes all facts asserted by the rule's action block dependent on some or all facts matched by the rule's condition. An integer value of n for the logical property makes the dependency on the first n top-level `&&`ed fact set expressions in the rule's condition. A boolean value of `true` for the logical property makes the dependency on the fact set expression of the condition. Anytime a fact referenced in a row of the fact set changes such that the rule's logical conditions no longer apply, the facts asserted by the activation associated with that fact set row are automatically retracted.

Examples

Example 2-10 shows a rule with the inference, Socrates is mortal, which depends on the fact, Socrates is a man.

Example 2-10 Defining and Using Rule allMenAreMortal

    RL> clear;
    RL> class Man    {String name;}
    RL> class Mortal {String name;}
    RL> Mortal lastMortal = null;
    RL> rule allMenAreMortal {
      logical = true;
      if (fact Man)
      {
        assert(lastMortal = new Mortal(name: Man.name));
      }
    }
    RL> watchAll();
    RL> Man socrates = new Man(name: "Socrates");
    RL> assert(socrates);
     ==> f-1 main.Man (name : "Socrates")
    ==> Activation: main.allMenAreMortal :  f-1
    RL> run();
    Fire 1 main.allMenAreMortal f-1
     ==> f-2 main.Mortal (name : "Socrates")
     <== Focus main, Ruleset stack: {}
    RL> retract(socrates);
     <== f-1 main.Man (name : "Socrates")
     <== f-2 main.Mortal (name : "Socrates")
    RL> showFacts();
    f-0   initial-fact()

Example 2-11 shows that it is possible for the same fact to be asserted by more than one rule, or to be asserted by a top-level ruleset action or function. Such a fact will not be automatically retracted unless all asserters have logical clauses that call for automatic retraction. A fact that is asserted by a top level action or function will never be automatically retracted.

Note that the fact that Socrates is mortal is not retracted, because it was asserted by a top level action that is not dependent on the fact that Socrates is a man.

Example 2-11 Asserting Facts Unconditionally

    RL> assert(socrates);
     ==> f-3 main.Man(name : "Socrates")
    ==> Activation: main.allMenAreMortal :  f-3
    RL> run();
    Fire 1 main.allMenAreMortal f-3
     ==> f-4 main.Mortal(name : "Socrates")
     <== Focus main, Ruleset stack: {}
    RL> assert(lastMortal);
     <=> f-4 main.Mortal(name : "Socrates")
    RL> retract(socrates);
     <== f-3 main.Man(name: "Socrates")
    RL> showFacts();
    f-0   initial-fact()
    f-2   main.Mortal(name: "Socrates")

### Class Definitions

All referenced classes must be defined with an RL Language class definition or must be on the Java classpath (Java classes must be imported).

Both RL Language classes and Java classes can support xpath using the supports keyword, with a supplied xpath.

Format

rl-class-definition ::= [ public ] [ final ] class name [ extends ] [ supports ] { type-property* }

type-property ::= [ public ] type name [ = expression ] ;

extends ::= extends qname extended-class-name

extended-class-name ::= qname

Usage Notes

The type of the optional initialization expression must be the same as the type of the property or implicitly convertible to that type.

A public class is visible from all rulesets. A non-public class is visible only in the containing ruleset.

A final class cannot be extended.

The extended class must be a defined RL Language class not an imported Java class.

Each property may have an optional initializer. The initializer is evaluated when the class is instantiated by new. If an initial value is also passed to new, the value passed to new overwrites the value computed by the initializer in the class definition.

A public property is visible from all rulesets. A non-public property is visible only within its containing ruleset.

Examples

In RL Language, the type of an property may be the name of the containing class definition (see Example 2-12). RL Language, unlike Java, does not support forward references to class definitions (see Example 2-13).

Example 2-12 Class Definition with Type of Property with Name of Containing Class

    class C0 {
      C0 next;
    }

Example 2-13 Class Definitions with Forward References are Not Allowed

    class C1 {
      C2 c2;  // causes an UndefinedException
    }
    class C2 {
      C1 c1;
    }

#### xpath Support

Both RL Language classes and Java classes support xpath.An xml identifier does not need to be surrounded by back quotes within an xpath.

The built-in assertXPath function supports a simple xpath-like syntax to assert a tree of objects as facts. The nodes in the tree are objects from classes in the same package or ruleset that support xpath. The links between parent and child nodes are instances of the `XLink` class. All of the properties in a class that supports xpath may be used in the xpath expression.

Format

supports ::= supports xpath

xpath ::= first-step next-step*

first-step ::= ( . | /* | [ // ] ( identifier | * ) ) predicate*

predicate ::= [ identifier xrelop literal ]

next-step ::= ( / | // ) ( identifier | * ) predicate*

xrelop ::= eq | lt | gt | le | ge | ne | == | < | > | <= | >= | !=

literal ::= integer-literal | decimal-literal | double-literal | string-literal | true | false | dateTime-literal | date-literal | time-literal

integer-literal ::= [-] d+

d ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

decimal-literal ::= [-] ( . d+ | d+ . d*)

double-literal ::= [-] ( . d+ | d+ [ . d* ] ) ( e | E ) [+ | -] d+

string-literal ::= " char* " | ' char* '

dateTime-literal ::= local-date T time-literal

date-literal ::= local-date [ time-zone ]

time-zone ::= Z | ( + | - ) d d : d d

local-date ::= d d d d - d d - d d

time-literal ::= d d : d d : d d [. d+] [ time-zone ]

Usage Notes

RL Language xpath support was designed to work with classes that conform to the Java XML Binding (JAXB) 1.0 standard. All JAXB elements from the given root to the elements selected by the xpath, inclusive, are asserted. Additional XLink facts are asserted to aid in writing rules about the parent-child relationships among the asserted elements.

If a JAXB element is retracted or re-asserted, using assert, then all of its children, and XLinks, are retracted. Instead of re-asserting, use assertXPath again.

Note that RL Language Xpath is not a proper subset of W3C Xpath 2.0. Note the following differences:

- The `lt` and `<`, `gt` and `>`, are synonymous in RL Language but different in W3C.

- Date literals must use `xs:date()` and other constructors in W3C.

- Constructors are not supported in RL Language, and literals, other than string literals, must not be quoted in RL Language.

Examples

Table 2-4 shows the xpath selection options for use with the built-in assertXPath function. In the descriptions, select means that the element is asserted as a fact, and the selected property of the XLink whose element property refers to the asserted element is `true`. The ancestors of a selected element, up to and including the root element, are always asserted, but not necessarily selected.

Table 2-4 xpath Selection Strings

xpath Select String | Description of Selection
--- | ---
//* | Select all elements including the root
.//* | Select all but the root
. | Select only the root
//foo | Select all objects that are the value of a property named foo.
.[x==1]/y | Select children or attributes of root named y only if the root has a child element or attribute named x and equal to 1

Example 2-14 instantiates an RL Language class called Person to build a family tree, as follows:

    First Generation   Second Generation     Third Generation
    Ida
                       Mary
                                              Fred
                                              John
                       Rachel
                                              Sally
                                              Evan

Example 2-14 uses the assertXPath function twice, with two xpaths:

    //kids[male==true]
    //kids[male==false]

Example 2-14 defines two rules:

- `sibling`: prints all pairs of siblings.

- `brotherSister`: prints all pairs of brothers and all pairs of sisters.

Example 2-15 shows the output from running Example 2-14.

Example 2-14 Sample Family Tree Rule Using supports xpath

    import java.util.*;
    ruleset xp {
      public class Person supports xpath {
        public String name;
        public boolean male;
        public List kids;
      }
            // Build the Family Tree
      Person p = new Person(name: "Fred", male: true);
      List k = new ArrayList();
      k.add(p);
      p = new Person(name: "John", male: true);
      k.add(p);
      p = new Person(name: "Mary", male: false, kids: k);
      Person gramma = new Person(name: "Ida", male: false, kids: new ArrayList());
      gramma.kids.add(p);
      p = new Person(name: "Sally", male: false);
      k = new ArrayList();
      k.add(p);
      p = new Person(name: "Evan", male: true);
      k.add(p);
      p = new Person(name: "Rachel", male: false, kids: k);
      gramma.kids.add(p);
      // test for siblings.
      // Note the test id(p1) < id(p2) halves the Cartesian product p1 X p2.
      rule sibling {
        if (fact Person p1 && fact Person p2 && id(p1) < id(p2) &&
            exists(fact XLink(element: p1) x &&
                   fact XLink(element: p2, parent: x.parent))) {
          println(p1.name + " is sibling of " + p2.name);
        }
      }
      // test for brothers and sisters, given the following 2 assertXPath() calls
      rule brotherSister {
        if (fact Person p1 && fact Person p2 && id(p1) < id(p2) &&
            exists(fact XLink(element: p1, selected: true) x &&
                   fact XLink(element: p2, selected: true,
                              parent: x.parent) y &&
                   x.samePath(y))) {
          println(p1.name + " and " + p2.name + " are " +
                  (p1.male ? "brothers" : "sisters"));
        }
      }
      assertXPath("xp", gramma, "//kids[male==true]");
      assertXPath("xp", gramma, "//kids[male==false]");
      run("xp");
    }

Example 2-15 Output from Run of Family Tree Example

    Mary and Rachel are sisters
    Evan is sibling of Sally
    Fred and John are brothers
    Fred is sibling of John
    Mary is sibling of Rachel

Example 2-16 shows that when you retract an element that was asserted with `assertXPath`, all its descendents are retracted as well.

The result is:

    f-0   initial-fact()

For a total of 1 fact.

Example 2-16 Retract the Family Tree

    retract(xp.gramma);
    showFacts();

Example 2-17 prints all pairs of ancestors. First, the family tree is asserted. Example 2-18 shows the output of a run of the code from Example 2-17.

Example 2-17 Print Ancestor Pairs with Class Ancestor

    assertXPath("xp", xp.gramma, "//*");
    class Ancestor { Object element; Object ancestor; }
    rule parents {
      if (fact XLink x) {
        assert(new Ancestor(element: x.element, ancestor: x.parent));
      }
    }
    rule ancestors {
      if (fact XLink x && fact Ancestor(ancestor: x.element) a) {
        assert(new Ancestor(element: a.element, ancestor: x.parent));
      }
    }
    rule printAncestor {
      if (fact xp.Person p && fact xp.Person a &&
          fact Ancestor(element: p, ancestor: a) {
        println(a.name + " is an ancestor of " p.name);
      }
    }
    run();

Example 2-18 Output from Run of Ancestor Example

    Mary is an ancestor of John
    Ida is an ancestor of John
    Mary is an ancestor of Fred
    Ida is an ancestor of Fred
    Ida is an ancestor of Mary
    Rachel is an ancestor of Evan
    Ida is an ancestor of Evan
    Rachel is an ancestor of Sally
    Ida is an ancestor of Sally
    Ida is an ancestor of Rachel

### Function Definitions

A function is similar to a Java static method.

Format

function ::= function name parameters [ returns type ] action-block

parameters ::= ( [ type identifier ( , type identifier )* ] )

Usage Notes

The action-block may invoke the function being defined. However, the action-block may not contain a forward reference to a function that has not already been defined (see Example 2-19 and Example 2-20).

Functions may be overloaded. For example, the built-in println function is overloaded.

Examples

Example 2-19 Valid Function Definition Containing Recursive Reference

    function factorial(long x) returns long {
      if (x <= 1) { return 1; }
      else { return x * factorial(x - 1); }
    }

Example 2-20 Invalid Function Definition Containing Reference to Undefined Function

    function f1() {
      f2();  // causes an UndefinedException
    }
    function f2() {
    }

## Fact Class Declarations

Any Java class can be used as an RL Language fact in a fact context.

A fact context is one of:

- The class of a fact-class declaration.

- The class of a fact-set-pattern.

- The declared class of an argument to the assert function.

- The declared class of an argument to the retract function.

- The declared class of an element argument to the assertXPath function.

If a class or interface `B` implements or extends class or interface `A`, and both `A` and `B` appear in fact contexts, then `A` must appear before `B`. Failure to follow this rule will result in a `FactClassException`.

Fact class definitions are not required when using RL Language classes.

For xpath support, use the supports xpath clause of the RL Language class definition.

Format

fact-class ::= fact class class-name [ supports ] ( fact-class-body | ; )

class-name ::= qname

fact-class-body ::= { hidden-properties | properties }

hidden-properties ::= hide property * ; | ( hide property ( ( name , )* name | * ) ; )+

properties ::= property * ; | ( property ( ( name , )* name | * ) ; )+

Usage Notes

The fact-class-body is optional in a fact-class declaration. The default fact-class-body is:

    { property *; }

Either the `property` or `hide property` keywords can be used in a body, but not both.

If `hide property` is used with a list of property names, then those property names are hidden and not available for use in RL Language.

If `hide property` is used with the wildcard "*", then no properties other than those exposed by a superclass or superinterface are available for use in RL Language.

If `property` is used with a list of property names, then those properties are exposed and available for use in RL Language. If property is used with the wildcard `*`, then all properties other than those hidden by a superclass or superinterface are available for use in RL Language.

A `HiddenPropertyException` will be thrown if a superclass exposes a property that its subclass hides or if a subclass exposes a property that its superclass hides.

Examples

Suppose a Java class `Vehicle` has subclasses `Car` and `Truck`. The rule shown in Example 2-21, `matchVehicle`, generates a `TypeCheckException` wrapping a `FactClassException` because the subclasses are referenced before the superclass. Wrapping is used instead of subclassing for both `FactClassException` and `MultipleInheritanceException` because in some fact contexts, these exceptions are not thrown until runtime and then are wrapped by a `RLRuntimeException`.

Example 2-21 matchVehicle Rule with Subclasses Referenced Before the Superclass

    assert(new Car());   // fact context for Car
    assert(new Truck()); // fact context for Truck
    rule matchVehicle {
      if (fact Vehicle v) { // fact context for Vehicle - too late!
        if (v instanceof Car) {
          println("car");
         } else {
          println("truck");
         }
       }
     } // generates a TypeCheckException wrapping a FactClassException

In Example 2-22, the `matchVehicle` rule is the first reference to the superclass, so no exception is thrown.

Example 2-22 matchVehicle Rule with References to Superclass First

    clear;
    rule matchVehicle {
      if (fact Vehicle v) {
        if (v instanceof Car) {
          println("car");
         } else {
          println("truck");
         }
       }
     }
    assert(new Car());
    assert(new Truck());
    run();  // prints "truck" then "car"

In Example 2-23, a fact class declaration is the first reference to the superclass, so no exception is thrown.

Example 2-23 matchVehicle Rule with Fact Class Declaration with Reference to Superclass First

    clear;
    fact class Vehicle;
    assert(new Car());
    assert(new Truck());
    rule matchVehicle {
      if (fact Vehicle v) {
        if (v instanceof Car) {
          println("car");
         } else {
          println("truck");
         }
       }
     }
    run();  // prints "truck" then "car"

Facts do not support multiple inheritance. Consider the Java classes and interfaces shown in Example 2-24.

Example 2-24 Java Classes and Sample Multiple Inheritance

    package example;
    public class Car {}
    public interface Sporty {}
    public class SportsCar extends Car implements Sporty {}

Example 2-25 entered at the command-line results in a `TypeCheckException` that wraps a `MultipleInheritanceException`. Use the `getCause` method on the `TypeCheckException` to retrieve the wrapped `MultipleInheritanceException` exception.

Example 2-25 MultipleInheritance Exception for Facts

    import example.*;
    fact class Sporty;
    fact class Car;
    fact class SportsCar; // throws TypeCheckException wrapping a MultipleInheritanceException

Example 2-26 illustrates an exception that occurs at runtime when the Oracle Rules Engine attempts to assert the `rx8` object and discovers its true type is `SportsCar`, not `Object`. To avoid the `MultipleInheritanceException`, you must choose whether to use `Sporty` or `Car` in a fact class context. You cannot use both.

Example 2-26 RLRuntimeException wraps MultipleInheritanceException

    import example.*;
    fact class Sporty;
    fact class Car;
    Object rx8 = new SportsCar();
    assert(rx8);  // throws RLRuntimeException wrapping a MultipleInheritanceException

Example 2-27 FactClassException Possible Cause

    oracle.rules.rl.FactClassException: fact class for 'X' should be declared earlier in rule session

Note the fact context rule is:

If X is a subclass or subinterface, of Y, then Y must appear in a fact context before X. A fact context is a fact-class declaration, a rule fact pattern, or the argument of assert, assertXPath, or retract.

In some cases you need to consider the fact context. For example, with an XML schema such as the following:

    <schema>
      <element name=A type=T/>
      <complexType name=T>
        <sequence>
          <element name=B type=T/>
        </sequence>
      </complexType>
    </schema>

JAXB generates:

    interface T {
      List getB();  // List has TImpl objects
    }
    interface A extends T;
    class AImpl implements A extends TImpl;
    class TImpl implements T;

In an example with the following order of appearance in fact contexts:

1. `fact` `class` `T`

2. `assertXPath` `AImpl`

3. `assert` `TImpl` (performed internally by the `assertXPath` implementation)

The, `AImpl` precedes `TImpl` in the ordering, yet `AImpl` extends `TImpl`, which would give the exception. The fix for this fact context is to explicitly issue `fact class TImpl;` anywhere before Step 2.

## Import Statement

An import statement makes it possible to omit the package name qualification when referencing Java classes.

Format

import ::= import ( Java-class-name | Java-package-name.* ) ;

Java-package-name ::= qname

Usage Notes

Import commands can be placed inside a ruleset, implying that the scope of the import is that ruleset, but the import actually applies globally. The following code demonstrates this behavior, if imports were scoped, then the `PrintWriter` reference in `r2` would fail compilation.

    class X { }ruleset rl {  import java.io.*;   rule A {  if ( fact X ) {

        @ PrintWriter pw = null;  }} }

    ruleset r2 {   rule B {    if ( fact X ) {      @ PrintWriter pw = null;    }  }}

## Include Statement

Include the ruleset at the location specified by the URL.

Format

include ::= include URL ;

Where:

URL is: A legal Uniform Resource Locator.

Usage Notes

The `file:` and `http:` schemes are supported.

Example

    include file:example.rl;

## Using Expressions

Expressions in RL Language use familiar Java syntax (with minor variations as noted). For example,

    (a + 1) * (b - 2)

Use expressions in a condition or in an action, with some restrictions. Expressions are strongly typed.

Format

expression ::= boolean-expression

| numeric-expression

| string-expression

| array-expression

| fact-set-expression | object-expression

### Boolean Expressions

Boolean expressions, as in Java, may be either `true` or `false`.

Format

boolean-expression ::= boolean-assignment

| boolean-expression ? boolean-expression : boolean-expression

| boolean-expression || boolean-expression

| boolean-expression && boolean-expression

| numeric-expression equal-op numeric-expression

| object-expression equal-op object-expression

| boolean-expression equal-op boolean-expression

| object-expression instanceof type-name

| numeric-expression relop numeric-expression

| string-expression relop string-expression

| ! boolean-expression

| boolean-primary-expression

boolean-assignment ::= boolean-target-expression = boolean-expression

equal-op ::= == | !=

relop ::= < | > | <= | >=

type-name ::= qname

Example

Example 2-28 shows use of a boolean expression in RL Language.

Example 2-28 RL Boolean Expression

    if (
      (true ? "a" < "b" : false)
      && (1 == 0 || 1.0 > 0)
      && "x" instanceof Object )
    {
      println("all true");
    };

### Numeric Expressions

Numeric expressions, as in Java, implicitly convert integer operands to floating point if other operands are floating point. Table 2-1 shows other implicit conversions.

Format

numeric-expression ::= numeric-assignment

| boolean-expression ? numeric-expression : numeric-expression

| numeric-expression( + | - ) numeric-expression

| numeric-expression ( * | / | % ) numeric-expression

| numeric-expression ** numeric-expression

| ( numeric-cast ) numeric-expression

| ( + | - ) numeric-expression

| ( ++ | -- ) numeric-primary-expression

| numeric-primary-expression [ ++ | -- ]

numeric-assignment ::= numeric-target-expression ( = | += | -= | *= | /= | %= ) numeric-expression

numeric-cast ::= numeric

Usage Notes

Table 2-5 shows the precedence order, from highest to lowest, for a numeric-expression.

Table 2-5 Expression Operator Precedence

Symbols | Category | Description
--- | --- | ---
`++` `--` | Post-increment or Post-decrement | numeric-primary-expression [ ++ | -- ]
`++` `--` | Pre-increment or Pre-decrement | ( ++ | -- ) numeric-primary-expression
- + | Unary minus or Unary plus | ( + | - ) numeric-expression
(type) | Type cast | ( numeric cast ) numeric-expression
`**` | Exponentiation | numeric-expression ** numeric-expression
`*`, `/`, `%` | Multiply or Divide or Remainder | numeric-expression ( * | / | % ) numeric-expression
`+` , `-` | Addition or Subtraction | numeric-expression( + | - ) numeric-expression
| Conditional | boolean-expression ? numeric-expression : numeric-expression
= | Assignment Operators | numeric-target-expression ( = | += | -= | *= | /= | %= ) numeric-expression

### String Expressions

As in Java, any expression can be converted to a string using the concatenation `+` operator. In RL Language, unlike Java, when an array is converted to a string, the array contents are converted to a string, with array elements separated by commas and surrounded with curly braces. When an instance of an RL Language class is converted to a string, the class name appears followed by property value pairs separated by commas and surrounded with parentheses. This RL Language feature is useful for logging, tracing, and debugging.

When `+` operator is applied to an operand that is a `String`, then all operands are converted to `Strings` and the operands are concatenated.

Format

string-expression ::= string-assignment

| boolean-expression ? string-expression : string-expression

| string-expression + expression

| expression + string-expression

| string-primary-expression

|

string-assignment ::= string-target-expression ( = | += ) string-expression

Example

Example 2-29 shows use of a string expression in RL Language. The example prints "1 2.0 true {1,2}"

Example 2-29 RL String Expression

    int i = 1;
    double f = 2.0;
    boolean b = true;
    int[] v = new int[]{i, 2};
    println(i + " " + f + " " + b + " " + v);

### Array Expressions

RL Language arrays behave just like Java arrays, but are limited to one dimension. The base type of an array is the type of the members of the array. All members must be of the same type. An array element may contain an array but only if the containing array is of type `Object[]`.

Note: RL Language does not directly support multi-dimensional arrays.

Format

array-expression ::= array-assignment

| boolean-expression ? array-expression : array-expression

| ( array-cast ) ( array-expression | object-expression )

| array-primary-expression

array-assignment ::= array-target-expression = array-expression

array-cast ::= type

Usage Notes

The type of an array-cast must be an array type.

### Fact Set Expressions

A fact-set-expression matches, filters, and returns facts from working memory. A fact-set-expression is legal only in a rule fact-set-condition. The `if` keyword indicates a fact-set-condition; however, a fact-set-condition is different from an if action. A rule's fact-set-condition iterates through all the rows in a fact set that match the fact-set-condition. The if action tests a boolean expression.

Format

fact-set-condition ::= if fact-set-expression

fact-set-expression ::= fact-set-expression || fact-set-expression

| fact-set-expression && fact-set-expression

| fact-set-expression && boolean-expression

| ! fact-set-expression

| exists fact-set-expression

| fact-set-pattern

| (fact-set-expression)

fact-set-pattern ::= fact fsp-class [ ( property-pattern ( , property-pattern )* ) ]

[ var ] local-object-variable

fsp-class ::= qname

local-object-variable ::= identifier

property-pattern ::= property-name : field-pattern

field-pattern ::= var local-property-variable | constraint

local-property-variable ::= identifier

simple-expression ::= string literal

| object-target-expression

| numeric literal

| numeric-target-expression

| boolean-literal

| boolean-target-expression

constraint ::= simple-expression

property-name ::= name

Usage Notes

A fact-set-expression can limit the facts it returns using either a simple-expression as a constraint in a fact-set-pattern or using a supported operator with the fact-set-expression.

A fact-set-expression may not contain any of the following:

- assert

- `new`

- References to non-final global variables.

Operator precedence is as in Java. Use parentheses to force desired precedence. For example,

    fact person var p && (p.age < 21 || p.age > 65)

Without the parentheses, the `p` in `p.age` is undefined (see Table 2-5 for more details on operator precedence).

A local-object-variable or local-property-variable is in scope for all expressions following the pattern that are following the pattern and connected with the `&&` operator. If the pattern is not contained in an `exists`, `||`, or `!` expression, the variable is also in scope in the rule's action-block. The `&&`'ed expressions may filter the returned facts, so that only the facts surviving the filter are returned.

Fact Set Pattern - Fetch From Working Memory

The most primitive fact-set-expression is the fact-set-pattern that returns some or all facts of the given class that have been asserted into working memory. A fact-set-pattern searches working memory for facts of the given class and with the optional constraint on the property values. The returned fact set contains a row for each matching fact. A local row variable can be defined to refer to each row, or local field variables can be defined to refer to fields within a row. If no local row variable is supplied, the name part of the class qname can be used to refer to each row (see Example 2-33).

Join Operator

The `&&` operator defines the cross product or join of two fact-set-expression operands. The left-hand-side of a fact-set-expression `&&` operator must be a fact set. The right-hand-side of a join operator is another fact-set-expression. The result of applying the `&&` operator to two fact sets is the joined fact set.

Filter Operator

The `&&` operator defines a filter operator that rejects facts in its left-hand-side fact-set-expression that do not match the right-hand-side boolean-expression. The left-hand-side of filter must be a fact-set-expression. The right-hand-side of a filter is a boolean-expression.

A filter right-hand-side may include references to variables defined, using the `var` keyword, in the left-hand-side.

Union Operator

The `||` operator defines the union of two fact-set-expression operands. When the`||` operator is applied to fact-set-expressions, the following is true:

- The expression's `var`s cannot be referenced outside the containing expression.

- The`||` returns the concatenation of its input fact sets, but the contents of the produced fact set are not accessible. Thus, `||` is typically used in a `!` or `exists` expression. Rather than a top-level `||` in a condition, it is usually better to use two or more rules with top-level `&&` operators so that `var`s defined in the condition can be referenced in the action-block.

Note: In the following construction:
    if (fact X || fact W) {}
If both an `X` and a `W` are both asserted, this rule fires twice, once for each fact.

Empty Operator

The `!` operator tests if the fact-set-expression is empty. When the `!` is applied to the fact-set-expression, the following is true:

- The expression's `var`s cannot be referenced outside the containing `!` expression.

- The `!` operator returns a single row if the fact-set-expression is empty, else the `!` operator returns an empty fact set.

Exists (Not Empty) Operator

The `exists` operator tests if the fact-set-expression is not empty.

When the `exists` operator is applied to the fact-set-expression, the following is true:

- The expression's `var`s cannot be referenced outside the containing `exists` expression.

- The `exists` returns a single row if the expression is not empty, else `exists` returns an empty fact set.

Var Keyword

Note that when you use `var`, the fact is only visible using the `var` defined variable (and not using the original name). Thus, the following example works, assuming `action.kind` is defined:

    if (fact action) {
       println(action.kind);
    }

However, for the following example, after `var a` is defined, the `action.kind` reference produces a syntax error because you need to use `a.kind` after the `var a` definition.

    if (fact action var a) {
      println(action.kind);
    }

Examples

Example 2-30 shows the action is placed on the agenda for all `Counter` facts with a value of 1.

Example 2-30 Fact Set Express for Counter.value

    class Counter  { int id; int value; }
    rule ex1a {
      if (fact Counter c && c.value == 1)
      { println("counter id " + c.id + " is 1"); }
    }

Example 2-31 shows an equivalent way to express the rule from Example 2-30, using a constraint.

Example 2-31 Using a Fact Set Constraint

    rule ex1b {
      if (fact Counter(value: 1) c)
      { println("counter id " + c.id + " is 1"); }
    }
    assert(new Counter(id: 99, value: 1));
    run();  // prints twice, once for each rule

Example 2-32 shows an illegal use of a fact set, because `c` is used before it is defined.

Example 2-32 Illegal Use of Fact Set

    rule ex2 {
      if (c.value == 1 && fact Counter c)
      { println("counter id " + c.id + " is 1"); }
    }

Example 2-33 shows an action is placed on the agenda for all `AttFacts` with the property `a2==0` and without a matching, equal first elements, Counter.

Example 2-33 Using a Fact Set with &&Operator for Counter Fact

    class AttFact {int a1; int a2;}
    rule ex3 {
      if (fact AttFact(a2: 0) && ! fact Counter(id: AttFact.a1))
      { println(AttFact.a1); }
    }
    assert(new AttFact()); // will match because a1=a2=0
    assert(new AttFact(a1: 1, a2: 0));  // will not match
    run();  // rule fires once

Example 2-34 shows the condition, `if (fact Ca a && fact Cb(v: a.v) b)` is interpreted as follows:

- The `fact Ca a` returns a fact set containing `a(v: 1), a(v: 2), a(v: 3)`

- The `&&` operator returns a fact set containing the two rows `{a(v: 1),b(v: 1)}, {a(v: 2),b(v: 2)}`

Example 2-34 Using a Fact Set with && Operator

    class Ca {int v;}
    assert(new Ca(v: 1));
    assert(new Ca(v: 2));
    assert(new Ca(v: 3));
    class Cb {int v;}
    assert(new Cb(v: 0));
    assert(new Cb(v: 1));
    assert(new Cb(v: 2));
    rule r {
      if (fact Ca a && fact Cb(v: a.v) b) {
        println("row: " + a + " " + b);
      }
    }
    run();  // prints 2 rows

### Object Expressions

The only expression operators for objects are assignment and cast.

Format

object-expression ::= object-assignment | ( ob-cast ) object-expression | boolean-expression ? object-expression : object-expression

object-assignment ::= object-target-expression = object-primary-expression

ob-cast ::= object-type

### Primary expressions

Primary expressions include assignment targets such as variables, properties, array elements, class members and other tightly binding expression syntax such as literals, method and function calls, and object and fact construction. The syntax is very similar to Java except where noted.

Format

primary-expression ::= array-primary-expression

| string-primary-expression

| numeric-primary-expression

| boolean-primary-expression

| object-primary-expression

array-primary-expression ::=

array-constructor

| function-call returning array

| method-call* returning 1-dim Java array

| ( array-expression )

| array-target-expression

array-constructor ::= new (

simple-type [ numeric-expression integer ]

| numeric [ ] { numeric-expression ( , numeric-expression )* } numeric expression must be implicitly convertible to base

| boolean [ ] { boolean-expression ( , boolean-expression )* }

| object-type [ ] { object-expression ( , object-expression )* }

)

array-target-expression ::=

qname variable of type array

| member of type array

| array-primary-expression base type is Object [ numeric-expression int ]

string-primary-expression ::=

string literal (see "Literals")

| object-primary-expression object is java.lang.String

string-target-expression ::= object-target-expression object is java.lang.String

numeric-primary-expression ::=

numeric literal

| function-call returning numeric

| method-call returning numeric

| array-primary-expression . length

| ( numeric-expression )

| numeric-target-expression

numeric-target-expression ::=

qname variable of type numeric

| member of type numeric

| array-primary-expression base type is numeric [ numeric-expression ]

boolean-primary-expression ::=

boolean-literal

| function-call returning boolean

| method-call returning boolean

| ( boolean-expression )

| boolean-target-expression

boolean-literal ::= true | false

boolean-target-expression ::=

qname variable of type boolean

| member of type boolean

| array-primary-expression base type is boolean [ numeric-expression int ]

object-primary-expression ::=

new class-definition-name ( [ expression ( , expression )* ] argument list )

| new class-definition-name ( [ property-pattern ( , property-pattern )* ] property-value pairs )

| function-call returning Java object

| method-call returning Java object

| object-target-expression

object-target-expression ::=

qname variable of type object

| member of type Java object

| array-primary-expression base type is object [ numeric-expression int ]

function-call ::= qname function name ( [ expression ( , expression )* ] argument list )

method-call ::= object-primary-expression . identifier method name ( [ expression ( , expression )* ] argument list )

member ::= object-primary-expression . identifier member name

Examples

Example 2-35 shows the RL Language literal syntax (which is the same as Java).

Example 2-35 Use of Literals

    String s = "This is a string."
    int i = 23;
    double f = 3.14;
    boolean b = false;

Methods and functions can be overloaded. However, unlike Java, RL Language uses a first fit algorithm to match an actual argument list to the overloaded functions.

Example 2-36 shows an example of example of overloading

Example 2-36 Overloading

    function f(int i);
    function f(Object o);
    function f(String s);  // can never be called

    f(1);        // calls first f
    f("a"); // calls second f, because "a" is an Object

#### new

RL Language classes do not have user-defined constructors. The default constructor initializes properties to their default values. The RL Language `new` operator permits specifying some property values (this works for Java bean properties, too).

A Java bean property may have a getter but no setter. Such a property may not be modified.

Example

Example 2-37 Initialization Using the New Operator

    class C { int i = 1; int j = 2; }
    C c = new C();
    println(c); // c.i == 1 and c.j == 2
    c = new C(i: 3);
    println(c); // c.i == 3 and c.j == 2
    c = new C(i: 0, j: 0);
    println(c); // c.i == c.j == 0

## Actions and Action Blocks

RL Language, unlike Java, requires action blocks and does not allow a single semi-colon terminated action.

Format

action ::= action-block | if | while | for | try | synchronized | return | throw

| assign | incr-decr-expression | primary-action

action-block ::= { ( variable | action )* }

Usage Notes

An action block is any number of local variable declarations and actions. The variables are visible to subsequent variable initialization expressions and actions within the same action block.

In RL Language, unlike in Java, all local variables must be initialized when they are declared. Local variables may not be final.

To exit, you can invoke the System.exit(int) method from within an action.

Example

Example 2-38 Action Block Sample

    RL> {
      int i = 2;
      while (i-- > 0) { println("bye"); }
    }
    bye
    bye
    RL>

### If Else Action Block

Using the if else action, if the test is `true`, execute the first action block, and if the test is `false`, execute the optional else part, which may be another if action or an action block.

RL Language, unlike Java, requires action blocks and does not allow a single semi-colon terminated action.

Format

if ::= if if-test action-block [ else if | action-block ]

if-test ::= boolean-expression

Examples

Example 2-39 shows an RL Language if else action block. Example 2-40 shows that an action block is required.

Example 2-39 Sample If Else Action

    String s = "b";

    if (s=="a") { println("no"); } else
    if (s=="b") { println("yes");}
    else        { println("no"); }

Example 2-40 Illegal If Action Without an Action Block

    if (s=="a") println("no");

### While Action Block

While the test is `true`, execute the action block. A return, throw, or halt may exit the action block.

Format

while ::= while while-test action-block

while-test ::= boolean-expression

Usage Notes

RL Language, unlike Java, requires action blocks and does not allow single semi-colon terminated action.

Examples

Example 2-41 prints "bye" twice.

Example 2-41 Sample While Action

    int i = 2;
    while (i-- > 0) {
      println("bye");
    }

Example 2-42 Illegal While Action Without an Action Block

    while (i-- > 0) println("no");

### For Action Block

RL Language, like Java, has a for loop. Using the for action block, the for-init portion executes, then while the boolean-expression is `true`, first the specified action block is executed then the for-update executes. A return, throw, or halt may exit the action block.

Format

for ::= for ( for-init ; boolean-expression ; for-update ) action-block

for-init ::= variable | for-update

for-update ::= incr-decr-expression | assign | primary-expression

Usage Notes

RL Language does not allow a comma separated list of expressions in the `for init` or for update clauses (Java does allow this).

Example

Example 2-43 shows RL Language code that converts an `int[]` to a `double[]`.

Example 2-43 For Action

    int[]    is = new int[]{1,2,3};
    double[] fs = is; // error!
    double[] fs = new double[3];
    for (int i = 0; i < is.length; ++i) {
      fs[i] = is[i];
    }
    println(fs);

### Try Catch Finally Action Block

Execute the first action block. Catch exceptions thrown during executions that match the `Throwable` class in a catch clause. For the first match, execute the associated catch action block. Bind the `Throwable` class instance to the given identifier and make it available to the catch action block. Whether or not an exception is thrown in the try action block, execute the finally action block, if given.

Uncaught exceptions are printed as error messages when using the RL Language command-line and are thrown as `RLExceptions` when using one of the RuleSession's `executeRuleset` or `callFunction` methods. The `try`, `catch`, and `finally` in RL Language is like Java both in syntax and in semantics. There must be at least one `catch` or `finally` clause.

Format

try ::= try action-block

( catch (class-implementing-throwable identifier ) action-block )*

[ finally action-block ]

class-implementing-throwable ::= qname

Usage Notes

In order to fully understand how to catch exceptions in RL Language, one must understand how the stack frames are nested during rule execution. Rules do not call other rules the way that functions or methods may call functions or methods. Therefore, you cannot use a catch block in one rule's action block to catch exceptions in another rule's action block. Exceptions thrown during rule firing must either be handled by the firing rule's action block, or must be handled by a caller to the run, runUntilHalt, or step functions that caused the rule to fire.

Examples

Example 2-44 shows the try catch and finally actions. The output from running this example is:

    exception in invoked Java method
    this is really bad!
    but at least it's over!

Example 2-44 Try Catch and Finally Action Blocks

    try {
      throw new Exception("this is really bad!");
    } catch (Exception e) {
      println(e.getMessage());
      println(e.getCause().getMessage());
    } finally {
      println("but at least it's over!");
    }

Note that RL Language treats the explicitly thrown `Exception ("this is really bad!")` as an exception from an invoked Java method, and wraps the `Exception` in a `JavaException`. The explicitly thrown `Exception` is available as the cause of the `JavaException`.

### Synchronized Action Block

As in Java, the synchronized action is useful for synchronizing the actions of multiple threads. The synchronized action block allows you to acquire the specified object's lock, then execute the action-block, then release the lock.

Format

synchronized ::= synchronized object-primary-expression action-block

Example

Example 2-45 changes the name of a `Person` object, adding old names to the nicknames, and synchronizes so that a concurrent reader of the Java object who is also synchronizing will see a consistent view of the `Person` (See Example 2-14 details on the `Person` bean).

Example 2-45 Synchronized Action

    import example.Person;  // this Java bean is defined in example J1
    function changeName(Person p, String first, String last) {
      synchronized(p) {
        java.util.Set s = p.getNicknames();
        s.add(p.getFirstName());
        s.add(p.getLastName());
        p.setFirstName(first);
        p.setLastName(last);
      }
      assert(p);
    }
    Person person = new Person("Elmer", "Fudd", new String[]{"Wabbit Wuver"});
    println(person.nicknames.toArray());
    changeName(person, "Bugs", "Bunny");
    println(person.nicknames.toArray());

### Return Action

The return action returns from the action block of a function or a rule.

A return action in a rule pops the ruleset stack, so that execution continues with the activations on the agenda that are from the ruleset that is currently at the top of the ruleset stack.

If rule execution was initiated with either the run or step functions, and a return action pops the last ruleset from the ruleset stack, then control returns to the caller of the `run` or step function.

If rule execution was initiated with the runUntilHalt function, then a return action will not pop the last ruleset from the ruleset stack. The last ruleset is popped with runUntilHalt when there are not any activations left. The Oracle Rules Engine then waits for mor activations to appear. When they do, it places the last ruleset on the ruleset stack before resuming ruleset firing.

Format

return ::= return [ return-value] ;

return-value ::= expression

If the function has a `returns` clause, then the return-value must be specified and it must be of the type specified by the `returns` clause.

Usage Notes

A return action in a rule or a `function` without a `returns` clause must not specify a return-value.

### Throw Action

Throw an exception, which must be a Java object that implements `java.lang.Throwable`. A thrown exception may be caught by a `catch` in a try action block.

Format

throw ::= throw throwable ;

throwable ::= object-primary-expression

### Assign Action

An assignment in RL Language, as in Java, is an expression that can appear as an action.

Format

assign ::= assignment-expression ;

assignment-expression ::= boolean-assignment

| numeric-assignment

| string-assignment

| object-assignment

| array-assignment

Example

Example 2-46 shows the use of the RL Language assignment expression. This prints "`6 5`".

Example 2-46 Assignment Expression

    clear;
    int i = 1;
    int j = 2;
    i += j += 3;
    println(i + " " + j);

### Increment or Decrement Expressions

Increment and decrement in RL Language, as in Java, are expressions that can appear as actions.

Format

incr-decr ::= incr-decr-expression ;

incr-decr-expression ::= ( ++ | -- ) numeric-target-expression | numeric-target-expression ( ++ | -- )

Examples

Example 2-47 shows the use of the RL Language decrement action. This example prints "`0`".

Example 2-47 Decrement Action

    clear;
    int i = 1;
    --i;
    println(i);

### Primary Actions

A primary action is a primary expression such as a function call, assert, or Java method call executed for its side-effects. For example, the `println` function is often used as a primary action.

Format

primary-action ::= primary-expression ;

## Built-in Functions

This section covers the following RL Language built-in functions:

assert, assertXPath, clearRule, clearRulesetStack, clearWatchRules, clearWatchActivations, clearWatchFacts, clearWatchFocus, clearWatchCompilations, clearWatchAll, getRulesetStack, getRuleSession, getStrategy, halt, id, object, println, popRuleset, pushRuleset, retract, reset, run, runUntilHalt, setRulesetStack, setStrategy, showActivations, showFacts, step, watchRules, watchActivations, watchFacts, watchFocus, watchCompilations

### assert

Adds a fact to working memory or updates a fact already in working memory based on the properties of the supplied object obj. If the supplied object obj is a Java instance, then properties are Java bean properties defined by an associated `BeanInfo` class or by the existence of getter and setter methods. If obj is an RL Language class instance, then the properties are the fields of the class.

Format

function assert(Object obj);

Usage Notes

The fact in working memory is a shadow of the supplied object obj, and this shadow contains a copy, clone, or reference to each property prop. If prop is a primitive type, then prop is copied to the shadow. If prop implements the Java `Cloneable` interface, then a clone, shallow copy, of prop is shadowed. Otherwise, only the reference to prop is shadowed. The more a shadow can copy its object's properties, the better a rule with references to several facts can be optimized.

Note that because `==` and `!=` when applied to an `Object` in RL Language always invokes the `Object` equals method, whether a shadow contains copies, clones, or references is transparent to the RL Language program.

Assert may affect the agenda. Rules whose conditions now return a fact set because of a new fact place activations on the agenda. Activations that test for non-existence of facts, using `!`, may be removed from the agenda. Updates to facts may affect the agenda. Activations whose rule conditions no longer match the changed facts are removed from the agenda. Rules whose conditions return a fact set because of the changed facts have activations placed on the agenda.

Assert should be used to update the fact in working memory if any part of the obj's state has been updated that could possibly have an effect on a rule condition, unless the obj is a Java bean that supports registering property change listeners, and all that is changed is the value of a bean property.

Examples

Example 2-48 prints, "Pavi has highest salary 65000.0" and Example 2-49 prints, "dept 10 has no employees!".

Example 2-48 Using Assert Function in the highestSalary Rule

    class Emp { String ename; double salary; }
                    rule highestSalary {
                         if (fact Emp hi && !(fact Emp e && e.salary > hi.salary))                         {
                               println(hi.ename + " has highest salary " + hi.salary);
                            }
                    }
                    Emp e1 = new Emp(ename: "Pavi", salary: 55000.00);
                    assert(e1); // put in working memory
                    Emp e2 = new Emp(ename: "Fred", salary: 60000.00);
                    assert(e2);        // put in working memory
                    e1.salary += 10000.00;    // Pavi is now the highest paid
                    assert(e1);       // MUST re-assert before allowing rules to fire
                    run();

Example 2-49 Using Assert Function in the emptyDept Rule

    import java.util.*;
    class Dept { int deptno; List emps = new ArrayList(); }
                 rule emptyDept {
                      if (fact Dept d && d.emps.isEmpty()) {
                             println("dept " + d.deptno + " has no employees!");
                            }
                    }
                    Dept d = new Dept(deptno: 10);
                    d.emps.add(e1);
                    assert(d);         // put in working memory with 1 employee
                    d.emps.remove(0);
                    assert(d);         // MUST re-assert before allowing rules to fire
                    run();

See Also

assertXPath, id, object, retract

### assertXPath

Add a tree of facts to working memory using the specified element as the root and an XML xpath-like expression to define the objects in the tree. The pkg is the Java package or RL Language ruleset that contains the classes of objects in the tree. All objects in the tree must be in the same package or ruleset.

In addition to asserting "element" and selected descendants, XLink facts are asserted that link parent and child objects. The classes of all objects in the tree must use the supports xpath (supports) clause of the RL class (rl-class-definition) or fact-class declaration.

Format

function assertXPath(String pkg, Object element, String xpath);

See Also

assert, id, object, retract

### clearRule

Clears the named rule from the rule session. Removes all of the rule's activations from the agenda.

Format

function clearRule(String name);

See Also

getRuleSession

### clearRulesetStack

Empties the ruleset stack.

Format

function clearRulesetStack();

See Also

getRulesetStack, getStrategy, popRuleset, pushRuleset, run, setStrategy

### clearWatchRules, clearWatchActivations, clearWatchFacts, clearWatchFocus, clearWatchCompilations, clearWatchAll

The clearWatch functions stop printing debug information.

Format

function clearWatchRules();

function clearWatchActivations();

function clearWatchFacts();

function clearWatchFocus();

function clearWatchCompilations();

function clearWatchAll();

See Also

watchRules, watchActivations, watchFacts, watchFocus, watchCompilations

### getRulesetStack

Returns the ruleset stack as an array of ruleset names.

Format

function getRulesetStack() returns String[];

Usage Notes

Returns: the ruleset stack as an array of ruleset names.

Entry 0, the top of the stack, is the focus ruleset. The focus ruleset is the ruleset whose activations are fired first by a subsequent run, runUntilHalt, or step function execution.

See Also

clearRulesetStack, getStrategy, popRuleset, pushRuleset, setRulesetStack, setStrategy

### getRuleSession

Returns a Java `RuleSession` object. An RL Language program could use this `RuleSession` to dynamically define new classes, rules, functions, or variables.

Format

function getRuleSession() returns RuleSession;

Example

    rule learn {
      if (fact f1 && …)
      {
        RuleSession rs = getRuleSession();
        rs.executeRuleset("rule newRule { if fact f1 && fact f2 && … { … } }");
      }
    }

See Also

clearRule

### getStrategy

Returns the current strategy. Table 2-6 shows the possible strategy values.

Format

function getStrategy() returns String;

See Also

clearRulesetStack, getRulesetStack, popRuleset, pushRuleset, setStrategy

### halt

The halt function halts execution of the currently firing rule, and returns control to the run, runUntilHalt, or step function that caused the halted rule to run. The agenda is left intact, so that a subsequent run, runUntilHalt, or step can be executed to resume rule firings.

The halt function has no effect if it is invoked outside the context of a run, runUntilHalt, or step function.

Format

function halt();

See Also

reset, run, runUntilHalt, step

### id

Return the fact id associated with the object obj. If obj is not associated with a fact, returns -1.

Format

function id(Object obj) returns int;

See Also

assert, object, retract

### object

Return the object associated with the given fact id. If there is no such fact id, returns null.

Format

function object(int factId) returns Object;

See Also

assert, id, retract

### println

Print the given value to the RuleSession output writer.

Format

function println(char c);

function println(char[] ca);

function println(int i);

function println(long l);

function println(float f);

function println(double d);

function println(boolean b);

function println(Object obj);

### popRuleset

If the stack is empty, `popRuleset` throws `RLRuntimeException`. If the stack is not empty, `popRuleset` pops the focus off the stack and returns it.

All entries are shifted down one position, and the new focus is the new top of stack, entry 0.

Entry 0, the top of the stack, is the focus ruleset. The focus ruleset is the ruleset whose activations are fired first by a subsequent run, runUntilHalt, or step function execution.

Format

function popRuleset() returns String;

Example 2-50 Using popRuleSet and Throwing RLRuntimeException

    clearRulesetStack();
    popRuleset();        // RLRuntimeException

See Also

clearRulesetStack, getRulesetStack, getStrategy, pushRuleset, setStrategy

### pushRuleset

Push the given ruleset onto the stack and make it the focus. It is an error to push a ruleset that is already the focus (`RLIllegalArgumentException` is thrown for this error).

Entry 0, the top of the stack, is the focus ruleset. The focus ruleset is the ruleset whose activations are fired first by a subsequent run, runUntilHalt, or step function execution.

Format

function pushRuleset(String focus);

Examples

Example 2-52 shows the RL Language using the `pushRuleset` and `popRuleset` functions.

Example 2-51 Using pushRuleSet - Throws RLIllegalArgumentException

    clearRulesetStack();
    pushRuleset("main");        // focus is "main"
    pushRuleset("main");        // RLIllegalArgumentException

Example 2-52 Using pushRuleSet - Throws RLIllegalArgumentException

    clearRulesetStack();
    pushRuleset("main");        // focus is "main"
    pushRuleset("main");        // RLIllegalArgumentException

Example 2-53 Using popRuleSet - Throws RLRuntimeException

    clearRulesetStack();
    popRuleset();        // RLRuntimeException

See Also

clearRulesetStack, getRulesetStack, getStrategy, popRuleset, setStrategy

### retract

Remove the fact associated with the object obj from working memory.

Format

function retract(Object obj);

Usage Notes

Retract may affect the agenda. Activations that depend on the retracted fact are removed from the agenda.

Note, rules that have conditions that test for non-existence of facts (using `!`) may place new activations on the agenda.

See Also

assert, id, object

### reset

Clears all facts from working memory, clears all activations from the agenda, and re-evaluates non-final global variable initialization expressions.

Format

function reset();

See Also

halt, run, runUntilHalt, step

### run

Fire rule activations on the agenda until:

- A rule action calls halt directly or indirectly. For example, when halt is called by a function called by a rule action.

- The agenda is empty.

- The ruleset stack is empty.

Format

function run() returns int;

function run(String rulesetName) returns int;

Usage Notes

If the argument, rulesetName is supplied, the named ruleset is pushed on the top of the ruleset stack before firing any rules.

If a null rulesetName is supplied, the ruleset stack is not modified before firing rules.

If no rulesetName is supplied and the default `main` ruleset is not on the ruleset stack, then the `main` ruleset is placed at the bottom of the ruleset stack before firing any rules.

Returns: `int`, the number of rules fired.

See Also

halt, reset, runUntilHalt, step

### runUntilHalt

This functions fires rule activations until halt is called. Unlike run and step, runUntilHalt does not return when the agenda is empty. Also, runUntilHalt does not pop the bottommost ruleset name from the ruleset stack. Instead, it waits for the agenda to contain activations.

Format

function runUntilHalt() returns int;

Usage Notes

The only way for activations to be added to the agenda while the main RuleSession thread is busy executing runUntilHalt is for a second thread to either:

1. Modify Java bean facts with `PropertyChangeListeners`.

2. Execute assert or `retract` functions.

Rules must be designed carefully when using runUntilHalt. For example, a rule that attempts to find a fact with the minimum value of a property will fire when the first instance of the fact is asserted, and then every time another instance is asserted with a lower valued property.

See Also

halt, reset, run, step

### setRulesetStack

Sets the ruleset stack to the given array of ruleset names.

Entry 0, the top of the stack, is the focus ruleset, which is the ruleset whose activations will be fired first by a subsequent run, runUntilHalt, or step function execution.

Format

function setRulesetStack(String[] rulesetStack

See Also

clearRulesetStack, getRulesetStack, getStrategy, popRuleset, pushRuleset, setStrategy

### setStrategy

Strategy specifies the order in which activations from the same ruleset and with the same priority are executed. Table 2-6 shows the valid strategy values.

Table 2-6 Strategy Values for setStrategy and getStrategy Functions

Strategy | Description
--- | ---
queue | Activations are fired in order from oldest to newest.
stack | Activations are fired in order from newest to oldest.

Format

function setStrategy(String strategy);

See Also

clearRulesetStack, getRulesetStack, getStrategy, popRuleset, pushRuleset

### showActivations

The show functions print rule session state to the output Writer. State that can be shown is: Activations all activations on the agenda

Format

function showActivations();

See Also

clearWatchRules, clearWatchActivations, clearWatchFacts, clearWatchFocus, clearWatchCompilations, clearWatchAll, showFacts, watchRules, watchActivations, watchFacts, watchFocus, watchCompilations

### showFacts

The show functions print rule session state to the output Writer. State that can be shown is: all facts in working memory.

Format

function showFacts();

See Also

clearWatchRules, clearWatchActivations, clearWatchFacts, clearWatchFocus, clearWatchCompilations, clearWatchAll, showActivations, watchRules, watchActivations, watchFacts, watchFocus, watchCompilations

### step

Fire rule activations on the agenda until:

- The specified number of rule activations, numRulesToFire have been fired.

- A rule action calls halt directly or indirectly. For example, by a function called by a rule action.

- The agenda is empty.

- The ruleset stack is empty.

Format

function step(int numRulesToFire) returns int;

function step(int numRulesToFire, String rulesetName) returns int;

Usage Notes

If no ruleset name is supplied and the `main` ruleset is not on the ruleset stack, then the `main` ruleset is placed at the bottom of the ruleset stack before firing any rules.

If a ruleset named, rulesetName, is supplied, the specified ruleset is pushed on the top of the ruleset stack before firing any rules. If a null ruleset name is supplied, the ruleset stack is not modified before firing rules.

Returns the integer number of rules fired.

See Also

halt, reset, run, runUntilHalt

### watchRules, watchActivations, watchFacts, watchFocus, watchCompilations

The watch functions turn on printing of information about important rule session events. The information is printed to the output Writer whenever the events occur. Use a clearWatch function to turn off printing.

Table 2-7 describes the available debugging information.

Table 2-7 Watch Functions Event Descriptions

Debug Watch | Rule Session Event Description
--- | ---
watch | Rule session event description
Rules | Information about rule firings (execution of activations)
Activations | Addition or removal of activations from the agenda
Facts | Assertion, retraction, or modification of facts in working memory
Focus | Pushing or popping of the ruleset stack. The top of the ruleset stack is called the focus ruleset, and all activations on the agenda from the focus ruleset will be fired before the focus is popped and the next ruleset on the stack becomes the focus.
Compilations | When a rule's conditions are added to the rete network, information about how the condition parts are shared with existing rules is printed. "=" indicates sharing. The order that rules are defined can affect sharing and thus can affect performance.
All | Includes information shown with watch Rules, watch Activations, watch Facts, watch Compilations and watch Focus.

Format

function watchRules();

function watchActivations();

function watchFacts();

function watchFocus();

function watchCompilations();

function watchAll();

See Also

clearWatchRules, clearWatchActivations, clearWatchFacts, clearWatchFocus, clearWatchCompilations, clearWatchAll, showActivations, showFacts