Interface ExpressionCompiler

All Superinterfaces:
AutoCloseable

public interface ExpressionCompiler extends AutoCloseable
Compiles the conditions and actions of one rule list, checks the names of the facts they run against, and creates the sessions they run with.

RulesEngine.load(java.util.List) calls the compile methods on one thread, and then, for an engine built with copiesAtLoad(n), newSession() and warmUp(Session) on the same thread. checkFactName(String), newSession() and convertOutput(Object, Class) are also called by runs of the rule list, possibly on many threads at once, so they must be thread-safe.

The engine closes the compiler once it no longer needs the rule list, after closing every session the compiler created: when a later load() has replaced the rule list and no run is still using it, when the engine is closed, or when the rule list fails to load.

Implemented by expression languages. A method added to this interface is a default method, so an existing language keeps compiling and working.

See Also:
  • Method Summary

    Modifier and Type
    Method
    Description
    default void
    Rejects the name of a fact that rules written in this language couldn't refer to, such as a keyword of the language.
    default void
    Releases what the compiler holds.
    Compiles an action.
    Compiles a condition.
    default @Nullable Object
    convertOutput(Object value, Class<?> target)
    Converts a value an action returned with ActionResult.set(java.util.Map) to the type of a setter of the output object, as this language understands its own values, such as a Double 3.0, which a language whose numbers are all doubles computes for 1 + 2, to the Integer 3 for a setter taking an int.
    default @Nullable Set<String>
    Returns the names of the facts the conditions and actions this compiler compiled can read, or null if it can't tell.
    Creates a session for one copy of the rule list: the state this language's conditions and actions change while they run.
    default void
    warmUp(Session session)
    Prepares a new session before any run uses it, such as by compiling this compiler's expressions into it, so that the runs that use it first don't pay for that.
  • Method Details

    • compileCondition

      CompiledCondition compileCondition(Expression source)
      Compiles a condition. A condition must evaluate to a Boolean, and can't change facts or declare variables; a compiler that can see such a write rejects the condition here.
      Parameters:
      source - The condition: the name of its rule, CONDITION, and its text, which isn't blank
      Returns:
      The compiled condition, which every run of the rule list shares. Keep what changes while it runs in a Session.
      Throws:
      InvalidExpressionException - if the condition breaks a rule the engine enforces, such as assigning to a fact, or has an error the language can point to, such as a syntax error; its issues say where. Anything else is reported as the cause of a RuleCompilationException too.
    • compileAction

      CompiledAction compileAction(Expression source)
      Compiles an action. When it runs, the action either changes the output object, ActionContext.output(), which a language may bind to a name such as "output", and returns ActionResult.done(), or returns ActionResult.set(java.util.Map) with the properties for the engine to set on the output object.
      Parameters:
      source - The action: the name of its rule, ACTION, and its text, which isn't blank
      Returns:
      The compiled action, which every run of the rule list shares. Keep what changes while it runs in a Session.
      Throws:
      InvalidExpressionException - if the action breaks a rule the engine enforces, or has an error the language can point to, such as a syntax error; its issues say where. Anything else is reported as the cause of a RuleCompilationException too.
    • newSession

      Session newSession()
      Creates a session for one copy of the rule list: the state this language's conditions and actions change while they run. Every condition and action this compiler compiled is given the session of the run that evaluates it.
      Returns:
      A new session, or Session.none() if the compiled expressions keep no state between runs and are safe to run on several threads at once. Throwing or returning null fails the run that needed the session with a RuleExecutionException.
    • warmUp

      default void warmUp(Session session) throws Exception
      Prepares a new session before any run uses it, such as by compiling this compiler's expressions into it, so that the runs that use it first don't pay for that. By default, does nothing: a session prepares itself the first time each expression runs, whatever that costs.

      RulesEngine.load(java.util.List) calls it on its own thread, once for each session it creates with newSession() for a copy made when the rules load (RulesEngineBuilder.copiesAtLoad(int)), after compiling every expression and before any run uses the session. It isn't called for Session.none(), or for a session created during a run, which prepares itself as it's used.

      Parameters:
      session - A session this compiler's newSession() returned, which no run has used
      Throws:
      Exception - if the session can't be prepared. It fails load() with a RuleCompilationException naming the language, and the rules loaded before stay loaded, except a fatal Error, which is rethrown unchanged.
    • checkFactName

      default void checkFactName(String name)
      Rejects the name of a fact that rules written in this language couldn't refer to, such as a keyword of the language. The engine has already rejected null and blank names, and the names its languages reserve (see ExpressionLanguage.reservedFactNames()). By default, every other name is accepted.

      The engine calls it for every fact of a run, and every declared fact, unless factNamesRead() returns a set: then only for the facts named in it.

      Parameters:
      name - The fact's name
      Throws:
      IllegalArgumentException - if rules can't refer to a fact with this name; run() throws it as is, unless a fatal Error is among its causes or their suppressed exceptions. Anything else this method throws is logged and thrown from run() as an IllegalArgumentException naming the fact and the language, except a fatal Error, thrown, among the causes of what this method throws or suppressed on one of them, which is rethrown unchanged.
    • factNamesRead

      default @Nullable Set<String> factNamesRead()
      Returns the names of the facts the conditions and actions this compiler compiled can read, or null if it can't tell. By default, null, and the engine calls checkFactName(String) for every fact, as a fact reaches every rule.

      When it returns a set, the engine calls checkFactName(String) only for the facts named in it: a fact that no rule of this compiler reads is never checked by this language, so a keyword of this language can still name a fact that only another language's rules read. It narrows nothing else: the names a language reserves (see ExpressionLanguage.reservedFactNames()) are still rejected for every fact. A name the set holds that is no fact's changes nothing, so a compiler that can tell only roughly returns more names, never fewer: a fact left out is one a rule may read under a name this language can't refer to, and no check says so. A compiler that can't tell for some expression, such as one that reads facts by a name it computes, returns null.

      RulesEngine.load(java.util.List) and RulesEngine.validate(java.util.List) call it once, on their own thread, after every rule of the list has compiled, and before they check the declared facts; not when a rule failed to compile. The engine keeps a copy of the set, so a later change to it changes nothing.

      Returns:
      The names, holding no null, or null if this compiler can't tell which facts its rules read. Throwing, or returning a set that holds null, fails load() with a RuleCompilationException naming the language, except a fatal Error, which is rethrown unchanged.
    • convertOutput

      default @Nullable Object convertOutput(Object value, Class<?> target)
      Converts a value an action returned with ActionResult.set(java.util.Map) to the type of a setter of the output object, as this language understands its own values, such as a Double 3.0, which a language whose numbers are all doubles computes for 1 + 2, to the Integer 3 for a setter taking an int. By default, returns the value unchanged: no conversion, so the writer sets only what it takes as it is.

      The default OutputWriter, OutputWriter.beansAndMaps(), calls it only when no setter of the property takes the value as it is, boxed primitives widened as Java widens them, and then for each type a setter takes, the most specific setter first, until one takes what this returns: a value the target accepts as it is, or one it accepts widened. A primitive target, such as int.class, takes its wrapper. Return the value unchanged for a target this language has no conversion for, such as a type it doesn't know: the writer tries the next setter, and fails the write as it does without a conversion, with the message that names the setters, when none takes the value. Throw an IllegalArgumentException naming the value and the target to refuse a conversion that would change the value, such as a fraction, NaN, an infinity or a number too large for an int: the writer still tries the property's other setters, and when none takes the value, it fails the write with the first refusal, which fails the rule naming it and the property. Anything else it throws fails the write at once. A writer of an application's own gets it as an OutputConverter, and may call it as it likes, or not at all. It's never called with null, which every setter taking a reference accepts.

      A language whose numbers are doubles converts a finite, whole Double within the target's range to an int, a short, a byte or their wrappers, and one whose magnitude is at most 253 to a long or a Long, where every whole double is exact, and refuses every other double. The engine calls it on the thread running the rule, possibly on many threads at once, so it must be thread-safe.

      Parameters:
      value - The value an action returned, not null
      target - The type of a setter's parameter, possibly primitive
      Returns:
      The converted value, which the target accepts, or value where this language has no conversion for the target. null is a value too, which the first setter taking a reference takes, so return it only to set the property to null, never for a conversion that isn't there.
      Throws:
      IllegalArgumentException - if this language refuses to convert the value to the target; its message should name the value and the target, as the rule's failure quotes it
    • close

      default void close()
      Releases what the compiler holds. The engine calls it once, after closing every session the compiler created. By default, does nothing. Anything it throws is logged at WARN, unless a run() or load() it started logged it already, and the engine still closes the other compilers. Only a fatal Error is then rethrown, unchanged, unless a fatal error of the call's own came first. An InterruptedException in the cause chain of what it throws, or suppressed there, puts back the interrupt status of the thread that closes it, which can be a run's thread, for example when a rule's action calls load() or close().
      Specified by:
      close in interface AutoCloseable