Classes that inherit from
PlannerRule
form the base of the Cascades planning system. During the planning
process, rules are matched to a current
PlannerExpression
and then provide zero or more logically equivalent
expressions.
The rule matching occurs in two stages: first, the planner examines the
matcher
of the rule,
which is a
TypeMatcher
expression that expresses the operators that the rule applies to and the hierarchical
structure that they take on in the expression tree. If the rule matches the binding, its
onMatch(PlannerRuleCall)
method is called, with a parameter that provides the planner's
PlanContext
and access to the map of bindings,
among other things. This method can inspect the operators bound during the structural matching and implement other
logic.
The
onMatch()
method returns logically equivalent expressions to the planner by calling the
PlannerRuleCall.yield(ExpressionRef)
method on its rule call, with a new
MutableExpressionRef
. The
yield()
method can be called more than once, or zero times if no
alternative expressions are found.
A rule should not attempt to modify any of the bound objects that the rule call provides. Nearly all such objects are
immutable, and the mutable ones are hidden behind interfaces that do not expose mutation methods. In particular,
a rule should never cast an
ExpressionRef
to
MutableExpressionRef
, in an attempt to access it.
A
PlannerRule
should not store state between successive calls to
onMatch(PlannerRuleCall)
,
since it may be reused an arbitrary number of times and may be reinstantiated at any time. To simplify cleanup,
any state that needs to be shared within a rule call should be encapsulated in a helper object (such as a static
inner class) or documented carefully.