10. Introduction to Programming with Jess in Java
There are two main ways in which Java code can be used with Jess: Java can be used to extend Jess, and the Jess library can be used from Java. The material in this section is relevant to both of these endeavors. Refer to the API documentation for the complete story on these classes.Note: the code samples herein are necessarily not complete Java programs. In general, all excerpted code would need to appear inside a try block, inside a Java method, inside a Java class, to compile; and all Java source files are expected to include the "import jess.*;" declaration. Sometimes examples build on previous ones; this is usually clear from context. Such compound examples will need to be assembled into one method before compiling.
10.1. The jess.Rete class
The jess.Rete class is the rule engine itself. Each jess.Rete object has its own working memory, agenda, rules, etc. To embed Jess in a Java application, you'll simply need to create one or more jess.Rete objects and manipulate them appropriately. We'll cover this in more detail in the section on embedding Jess in Java applications. Here I will cover some general features of the jess.Rete class.10.1.1. Equivalents for common Jess functions
Several of the most commonly used Jess functions are wrappers for methods in the jess.Rete class. Examples are run(), run(int), reset(), clear(), assertFact(Fact), retract(Fact), retract(int), and halt(). You can call these from Java just as you would from Jess.10.1.2. Executing other Jess commands
You can use the Rete class's jess.Rete.eval(String) method to easily execute, from Java, any Jess function call or construct definition that can be represented as a parseable String. For example,import jess.*; public class ExSquare { public static void main(String[] unused) { try { Rete r = new Rete(); r.eval("(deffunction square (?n) (return (* ?n ?n)))"); Value v = r.eval("(square 3)"); // Prints '9' System.out.println(v.intValue(r.getGlobalContext())); } catch (JessException ex) { System.err.println(ex); } } } C:\> java ExSquare9
10.1.2.1. Optional commands
Note that when you create a Rete object from Java, it will already contain definitions for all of the functions that come with Jess. There are no longer any "optional" commands.10.1.3. The script library
Some of Jess's commands are defined in Jess language code, in the file jess/scriptlib.clp. Each Rete object will load this script library when it is created and again if (clear) is called. In previous versions of Jess you had to do this yourself; this is no longer necessary.10.1.4. Methods for adding, finding and listing constructs
The easiest (and still encouraged) way to define templates, defglobals, and other constructs is to use Jess language code and let Jess parse the textual definition. However, many of these constructs are represented by public classes in the Jess library, and if you wish, you can construct your own instances of these in Java code and add them to an engine explicitly. This is currently possible for most, but not all, Jess constructs. Right now the jess.Defrule class does not expose enough public methods to properly create one outside of the jess package. This is deliberate, as this API is likely to change again in the near future. For information about the classes mentioned here (jess.Deftemplate, jess.Defglobal, etc) see the API documentation.These jess.Rete methods let you add constructs to the engine:
- public void addDeffacts(Deffacts)
- public void addDefglobal(Defglobal)
- public void addDefrule(Defrule)
- public void addDeftemplate(Deftemplate)
- public void addUserfunction(Userfunction)
- public void addUserpackage(Userpackage)
These methods return individual constructs from within the engine, generally by name:
- public Defglobal findDefglobal(String)
- public Defrule findDefrule(String)
- public Deftemplate findDeftemplate(String)
- public Userfunction findUserfunction(String)
These methods return java.util.Iterators of various data structures in the engine:
- public Iterator listActivations()
- public Iterator listDeffacts()
- public Iterator listDefglobals()
- public Iterator listDefrules()
- public Iterator listDeftemplates()
- public Iterator listFacts()
- public Iterator listFunctions()
import jess.*; import java.util.*; public class ExFilter { public static void main(String[] argv) throws JessException { // Run a Jess program... Rete engine = new Rete(); engine.batch("somecode.clp"); // ... now retrieve only the facts in a module named RESULTS Iterator it = new FilteringIterator(engine.listFacts(), new Filter.ByModule("RESULTS")); } }
10.1.5. I/O Routers
Several Jess functions like printout, format, read, and readline take an I/O router name as an argument, while other functions like open return an I/O router name. An I/O router name is just a symbolic name for a Java java.io.Writer and/or a java.io.Reader. Each jess.Rete instance keeps separate tables of input routers and output routers, so that both a Reader and a Writer can be registered under the same name in each rule engine. When you call, for example, printout, Jess uses the first argument to look up the appropriate Writer in that table, and that's where the output will go.
The most commonly used router is t, which is used as Jess' standard input and output. Jess also has a built-in router named WSTDOUT for printing user messages internally -- for example, the Jess> prompt and the output of commands like facts and ppdefrule. The read and readline commands take input from the t router by default. Output from the watch function goes to the WSTDOUT router by default, but you can make it go to any other router using the jess.Rete.setWatchRouter(java.lang.String) method.
As startup, Jess's standard routers are connected to Java's standard streams, so that output goes to the command-line window. This is perfect for command-line programs, but of course not acceptable for GUI-based applications. To remedy this, Jess lets you connect the t router (or any other router) to any Java java.io.Reader and java.io.Writer objects you choose. In fact, you can not only redirect the built-in routers, but you can add routers of your own, in much the same way that the open command creates a new router that reads from a file.
These functions in the Rete class let you manipulate the router list:
- public void addInputRouter(String s, Reader is, boolean consoleLike)
- public void addOutputRouter(String s, Writer os)
- public Reader getInputMode(String s)
- public Reader getInputRouter(String s)
- public Writer getOutputRouter(String s)
- public void removeInputRouter(String s)
- public void removeOutputRouter(String s)
- public void setWatchRouter(String s)
The words "input" and "output" are from the perspective of the Jess library itself; i.e., Jess reads from input routers and writes to output routers.
Note that you can use the same name for an input router and an output router (the t router is like that.) Note also that although these functions accept and return generic Reader and Writer objects, Jess internally uses java.io.PrintWriter and java.io.BufferedReader. If you pass in other types, Jess will construct one of these preferred classes to "wrap" the object you pass in.
When Jess starts up, there are three output routers and one input router defined: the t router, which reads and writes from the standard input and output; the WSTDOUT router, which Jess uses for all prompts, diagnostic outputs, and other displays; and the WSTDERR router, which Jess uses to print stack traces and error messages. By default, t is connected to System.in and System.out, and both WSTDOUT and WSTDERR are connected to System.out (neither is connected to System.err.) You can reroute these inputs and outputs simply by changing the Readers and Writers they are attached to using the above functions. You can use any kind of streams you can dream up: network streams, file streams, etc.
The boolean argument consoleLike to the addInputRouter method specifies whether the stream should be treated like the standard input or like a file. The difference is that on console-like streams, a read call consumes an entire line of input, but only the first token is returned; while on file-like streams, only the characters that make up each token are consumed on any one call. That means, for instance, that a read followed by a readline will consume two lines of text from a console-like stream, but only one from a file-like stream, given that the first line is of non-zero length.
The jess.Rete class has two more handy router-related methods: getOutStream() and getErrStream(), both of which return a java.io.PrintWriter object. getOutStream() returns a stream that goes to the same place as the current setting of WSTDOUT; getErrStream() does the same for WSTDERR.
10.1.6. TextAreaWriter, JTextAreaWriter and TextReader
Jess ships with three utility classes that can be very useful when building GUIs for Jess: the jess.awt.TextAreaWriter, jess.swing.JTextAreaWriter and jess.awt.TextReader classes. All three can serve as adapters between Jess and graphical input/output widgets. The TextAreaWriter class is, as the name implies, a Java java.io.Writer that sends any data written to it to a java.awt.TextArea. This lets you place Jess's output in a scrolling window on your GUI. The jess.Console and jess.ConsoleApplet jess GUIs use these classes. To use TextAreaWriter simply call addOutputRouter(), passing in an instance of this class:import java.awt.TextArea; import jess.awt.*; import jess.*; public class ExTAW { public static void main(String[] unused) throws JessException { TextArea ta = new TextArea(20, 80); TextAreaWriter taw = new TextAreaWriter(ta); Rete r = new Rete(); r.addOutputRouter("t", taw); r.addOutputRouter("WSTDOUT", taw); r.addOutputRouter("WSTDERR", taw); // Do something interesting, then... System.exit(0); } } C:\> java ExTAW
10.2. The jess.JessException class
The jess.JessException exception type is the only kind of exception thrown by any functions in the Jess library. jess.JessException is rather complex, as exception classes go. An instance of this class can contain a wealth of information about an error that occurred in Jess. Besides the typical error message, a jess.JessException may be able to tell you the name of the routine in which the error occurred, the name of the Jess constructs that were on the exceution stack, the relevant text and line number of the executing Jess language program, and the Java exception that triggered the error (if any.) See the the API documentation for details. One of the most important pieces of advice for working with the Jess library is that in your catch clauses for JessException, display the exception object. Print it to System.out, or convert to a String and display it in a dialog box. The exceptions are there to help you by telling when something goes wrong; don't ignore them. Another important tip: the JessException class has a method getCause which returns non-null when a particular JessException is a wrapper for another kind of exception. For example, if you use the Jess function call to call a function that throws an exception, then call will throw a JessException, and calling JessException.getCause() will return the real exception that was thrown. Your JessException handlers should always check getCause(); if your handler simply displays a thrown exception, then it should display the return value of getCause(), too. getCause() replaces the now deprecated getNextException().10.3. The jess.Value class
The class jess.Value is probably the one you'll use the most in working with Jess. A Value is a self-describing data object. Every datum in Jess is contained in one. Once it is constructed, a Value's type and contents cannot be changed; it is immutable. Value supports a type() function, which returns one of these type constants (defined in the class jess.RU (RU = "Rete Utilities")):final public static int NONE = 0; ; an empty value (not NIL) final public static int SYMBOL = 1; ; a symbol final public static int STRING = 2; ; a string final public static int INTEGER = 4; ; an integer final public static int VARIABLE = 8; ; a variable final public static int FACT = 16; ; a jess.Fact object final public static int FLOAT = 32; ; a double float final public static int FUNCALL = 64; ; a function call final public static int LIST = 512; ; a list final public static int DESCRIPTOR = 1024; ; (internal use) final public static int JAVA_OBJECT = 2048; ; a Java object final public static int INTARRAY = 4096; ; (internal use) final public static int MULTIVARIABLE = 8192; ; a multifield final public static int SLOT = 16384; ; (internal use) final public static int MULTISLOT = 32768; ; (internal use) final public static int LONG = 65536; ; a Java long
public Value(Object o) throws JessException public Value(String s, int type) throws JessException public Value(Value v) public Value(ValueVector f, int type) throws JessException public Value(double d, int type) throws JessException public Value(int value, int type) throws JessException
public Object javaObjectValue(Context c) throws JessException public String stringValue(Context c) throws JessException public Fact factValue(Context c) throws JessException public Funcall funcallValue(Context c) throws JessException public ValueVector listValue(Context c) throws JessException public double floatValue(Context c) throws JessException public double numericValue(Context c) throws JessException public int intValue(Context c) throws JessException
10.3.1. The subclasses of jess.Value
jess.Value has a number of subclasses: jess.Variable, jess.FuncallValue, jess.FactIDValue, and jess.LongValue are the four of most interest to the reader. When you wish to create a value to represent a variable, a function call, a fact, or a Java long, you must use the appropriate subclass.Note to the design-minded: I could have use a Factory pattern here and hidden the subclasses from the programmer. Because of the many different Value constructors, and for performance reasons, I decided this wouldn't be worth the overhead.
10.3.1.1. The class jess.Variable
Use this subclass of Value when you want to create a Value that represents a Variable. The one constructor looks like this:public Variable(String s, int type) throws JessException
10.3.1.2. The class jess.FuncallValue
Use this subclass of Value when you want to create a Value that represents a function call (for example, when you are creating a jess.Funcall containing nested function calls.) The one constructor looks like this:public FuncallValue(Funcall f) throws JessException
10.3.1.3. The class jess.LongValue
Use this subclass of Value when you want to create a Value that represents a Java long. These are mostly used to pass to Java functions called via reflection. The one constructor looks likepublic LongValue(long l) throws JessException
10.3.1.4. The class jess.FactIDValue
Use this subclass of Value when you want to create a Value that represents a fact-id. The one constructor looks like this:public FactIDValue(Fact f) throws JessException
10.3.2. Value resolution
Some jess.Value objects may need to be resolved before use. To resolve a jess.Value means to interpret it in a particular context. jess.Value objects can represent both static values (symbols, numbers, strings) and dynamic ones (variables, function calls). It is the dynamic ones that obviously have to be interpreted in context. All the jess.Value member functions, like intValue(), that accept a jess.Context as an argument are self-resolving; that is, if a jess.Value object represents a function call, the call will be executed in the given jess.Context, and the intValue() method will be called on the result. Therefore, you often don't need to worry about resolution as it is done automatically. There are several cases where you will, however.- When interpreting arguments to a function written in Java. The parameters passed to a Java Userfunction may themselves represent function calls. It may be important, therefore, that these values be resolved only once, as these functions may have side-effects (I'm tempted to use the computer-science word: these functions may not be idempotent. Idempotent functions have no side-effects and thus may be called multiple times without harm.) You can accomplish this by calling one of the (x)Value() methods and storing the return value, using this return value instead of the parameter itseld. Alternatively, you may call resolveValue()and store the return value in a new jess.Value variable, using this value as the new parameter. Note that the type() method will return RU.VARIABLE for a jess.Value object that refers to a variable, regardless of the type of the value the variable is bound to. The resolved value will return the proper type. Note that arguments to deffunctions are resolved automatically, before your Jess language code runs.
- when returning a jess.Value object from a function written in Java. If you return one of a function's parameters from a Java Userfunction, be sure to return the return value of resolveValue(), not the parameter itself.
- When storing a jess.Value object. It is important that any values passed out of a particular execution context be resolved; for example, before storing a Value object in a Map, resolveValue() should always be called on both the key and object.
10.4. The jess.Context class
jess.Context represents an execution context for the evaluation of function calls and the resolution of variables. There are very few public member functions in this class, and only a few of general importance. You can use getVariable() and setvariableto get and change the value of a variable from Java code, respectively. The function getEngine() gives any Userfunction access to the Rete object in which it is executing. When a Userfunction is called, a jess.Context argument is passed in as the final argument. You should pass this jess.Context to any jess.Value.(x)Value() calls that you make.10.5. The jess.ValueVector class
The jess.ValueVector class is Jess's internal representation of a list, and therefore has a central role in programming with Jess in Java. The jess.ValueVector class itself is used to represent generic lists, while specialized subclasses are used as function calls (jess.Funcall), facts (jess.Fact), and templates (Deftemplate).
Working with ValueVector itself is simple. Its API is reminiscent of java.util.Vector. Like that class, it is a self-extending array: when new elements are added the ValueVector grows in size to accomodate them. Here is a bit of example Java code in which we create the Jess list (a b c). Note that the jess.ValueVector.add method has several overloaded forms that convert primitives into jess.Value objects. The overload used here automatically converts its argument into a Jess symbol.
import jess.*; public class ExABC { public static void main(String[] unused) throws JessException { ValueVector vv = new ValueVector(); vv.add("a"); vv.add("b"); vv.add("c"); // Prints "(a b c)" System.out.println(vv.toStringWithParens()); } } C:\> java ExABC(a b c)
The add() function returns the ValueVector object itself, so that add() calls can be chained together for convenience:
import jess.*; public class ExChain { public static void main(String[] unused) throws JessException { ValueVector vv = new ValueVector(); vv.add("a").add("b").add("c"); // Prints "(a b c)" System.out.println(vv.toStringWithParens()); } } C:\> java ExChain(a b c)
10.6. The jess.Funcall class
jess.Funcall is a specialized subclass of ValueVector that represents a Jess function call. It contains the name of the function, an internal pointer to the actual jess.Userfunction object containing the function code, and the arguments to pass to the function. You can call Jess functions using jess.Funcall if you prefer, rather than using jess.Rete.executeFunction(). This method has less overhead since there is no parsing to be done. This example calls Jess's "set-reset-globals" function:import jess.*; public class ExResetGlobals { public static void main(String[] unused) throws JessException { Rete r = new Rete(); Context c = r.getGlobalContext(); Funcall f = new Funcall("set-reset-globals", r); f.arg(Funcall.FALSE); Value result = f.execute(c); System.out.println(result); } } C:\> java ExResetGlobalsFALSE
10.7. The jess.Fact class
Another interesting subclass of ValueVector is jess.Fact, which, predictably, is how Jess represents facts. A Fact is stored as a list in which all the entries correspond to slots. The head or name of the fact is stored in a separate variable (available via the getName() method.) Once you assert a jess.Fact object, you no longer "own" it - it becomes part of the Rete object's internal data structures. As such, you must not change the values of any of the Fact's slots. If you retract the fact, the Fact object is released and you are free to alter it as you wish. Alternatively, you can use the jess.Rete.#modify(jess.Fact, java.lang.String, jess.Value) method to modify a fact.10.7.1. Constructing an Unordered Fact from Java
In the following example, we create a template and assert an unordered fact that uses it.import jess.*; public class ExPoint { public static void main(String[] unused) throws JessException { Rete r = new Rete(); r.eval("(deftemplate point \"A 2D point\" (slot x) (slot y))"); Fact f = new Fact("point", r); f.setSlotValue("x", new Value(37, RU.INTEGER)); f.setSlotValue("y", new Value(49, RU.INTEGER)); r.assertFact(f); r.eval("(facts)"); } } C:\> java ExPointf-0 (MAIN::point (x 37) (y 49)) For a total of 1 facts in module MAIN.
10.7.2. Constructing a Multislot from Java
In this example, the template has a multislot. In Java, a multislot is represented by a Value of type RU.LIST; the Value object contains a ValueVector containing the fields of the multislot.import jess.*; public class ExMulti { public static void main(String[] unused) throws JessException { Rete r = new Rete(); r.eval("(deftemplate vector \"A named vector\" (slot name) (multislot list))"); Fact f = new Fact("vector", r); f.setSlotValue("name", new Value("Groceries", RU.SYMBOL)); ValueVector vv = new ValueVector(); vv.add(new Value("String Beans", RU.STRING)); vv.add(new Value("Milk", RU.STRING)); vv.add(new Value("Bread", RU.STRING)); f.setSlotValue("list", new Value(vv, RU.LIST)); r.assertFact(f); r.eval("(facts)"); } } C:\> java ExMultif-0 (MAIN::vector (name Groceries) (list "String Beans" "Milk" "Bread")) For a total of 1 facts in module MAIN.
10.7.3. Constructing an Ordered Fact from Java
An ordered fact is actually represented as an unordered fact with a single slot: a multislot named __data. You don't need to create a template for an ordered fact: one will be created automatically if it doesn't already exist.import jess.*; public class ExOrdered { public static void main(String[] unused) throws JessException { Rete r = new Rete(); Fact f = new Fact("letters", r); ValueVector vv = new ValueVector(); vv.add("a").add("b").add("c"); f.setSlotValue("__data", new Value(vv, RU.LIST)); r.assertFact(f); r.eval("(facts)"); } } C:\> java ExOrderedf-0 (MAIN::letters a b c) For a total of 1 facts in module MAIN.
10.8. The jess.Deftemplate class
Yet another interesting subclass of ValueVector is jess.Deftemplate, the purpose of which should be obvious. Deftemplate has a fairly large interface which allows you to set and query the properties of a template's slots. This example is an alternative to the deftemplate command in the previous example.import jess.*; public class ExBuildDeftemplate { public static void main(String[] unused) throws JessException { Rete r = new Rete(); Deftemplate dt = new Deftemplate("point", "A 2D point", r); Value zero = new Value(0, RU.INTEGER); dt.addSlot("x", zero, "NUMBER"); dt.addSlot("y", zero, "NUMBER"); r.addDeftemplate(dt); // Now create and assert Fact } }
10.9. Parsing Jess code with jess.Jesp
You can parse Jess language code directly with the class jess.Jesp. Simply loading the contents of a file (or any other data source that can be supplied as a java.io.Reader is very easy:import jess.*; import java.io.*; public class ExReadInFile { public static void main(String[] unused) throws JessException, IOException { Rete engine = new Rete(); FileReader file = new FileReader("myfile.clp"); try { Jesp parser = new Jesp(file, engine); parser.parse(false); } finally { file.close(); } } }
import jess.*; import java.io.*; public class ExParseExpressions { public static void main(String[] unused) throws JessException, IOException { Rete engine = new Rete(); FileReader file = new FileReader("myfile.clp"); Context context = engine.getGlobalContext(); try { Jesp parser = new Jesp(file, engine); Object result = Funcall.TRUE; while (!result.equals(Funcall.EOF)) { result = parser.parseExpression(context, false); // Here you can use instanceof to determine what sort // of object "result" is, and process it however you want } } finally { file.close(); } } }
10.10. The jess.Token class
The jess.Token class is used to represent partial matches in the Rete network. You'll use it if you're writing an Accelerator (not documented here) or perhaps if you're working with queries. Only a few methods of jess.Token are public, and fewer are of use to the programmer. int size() tells you how many jess.Facts are in a given jess.Token. The most important method is Fact fact(int), which returns the jess.Fact objects that make up the partial match. Its argument is the zero-based index of the jess.Fact to retrieve, and must be between 0 and the return value of size(). Each Fact will correspond to one pattern on a rule or query LHS; dummy facts are inserted for not and test CEs.10.11. The jess.JessEvent and jess.JessListener classes
jess.JessEvent and jess.JessListener make up Jess's rendition of the standard Java event pattern. By implementing the JessListener interface, a class can register itself with a source of JessEvents, like the jess.Rete class. jess.Rete (potentially) fires events at all critical junctures during its execution: when rules fire, when a reset() or clear() call is made, when a fact is asserted or retracted, etc. JessEvent has a getType() method to tell you what sort of event you have been notified of; the type will be one of the constants in the JessEvent class. You can control which events a jess.Rete object will fire using the setEventMask() method. The argument is the result of logical-OR-ing together some of the constants in the jess.JessEvent class. By default, the event mask is 0 and no events are sent.As an example, let's suppose you'd like your program's graphical interface to display a running count of the number of facts on the fact-list, and the name of the last executed rule. You've provided a static method, MyGUI.displayCurrentRule(String ruleName), which you would like to have called when a rule fires. You've got a pair of methods MyGUI.incrementFactCount() and MyGUI.decrementFactCount() to keep track of facts. And you've got one more static method, MyGUI.clearDisplay(), to call when Jess is cleared or reset. To accomplish this, you simply need to write an event handler, install it, and set the event mask properly. Your event handler class might look like this.
import jess.*; public class ExMyEventHandler implements JessListener { public void eventHappened(JessEvent je) { int defaultMask = JessEvent.DEFRULE_FIRED | JessEvent.FACT | JessEvent.RESET | JessEvent.CLEAR; int type = je.getType(); switch (type) { case JessEvent.CLEAR: Rete engine = (Rete) je.getSource(); int mask = engine.getEventMask(); mask |= defaultMask; engine.setEventMask(mask); // MyGUI.clearDisplay(); break; case JessEvent.RESET: // MyGUI.clearDisplay(); break; case JessEvent.DEFRULE_FIRED: // MyGUI.displayCurrentRule( ((Activation) je.getObject()).getRule().getName()); break; case JessEvent.FACT | JessEvent.REMOVED: // MyGUI.decrementFactCount(); break; case JessEvent.FACT: // MyGUI.incrementFactCount(); break; default: // ignore } } }
import jess.*; public class ExMask { public static void main(String[] unused) throws JessException { Rete engine = new Rete(); engine.addJessListener(new ExMyEventHandler()); engine.setEventMask(engine.getEventMask() | JessEvent.DEFRULE_FIRED | JessEvent.CLEAR | JessEvent.FACT | JessEvent.RESET ); } }
10.11.1. Working with events from the Jess language
It's possible to work with the event classes from Jess language code as well. To write an event listener, you can use the jess.JessEventAdapter class. This class works rather like the jess.awt adapter classes do. Usage is best illustrated with an example. Let's say you want to print a message each time a new template is defined, and you want to do it from Jess code. Here it is:Jess> ;; make code briefer (import jess.*)
TRUE
Jess> ;; Here is the event-handling deffunction ;; It accepts one argument, a JessEvent (deffunction display-deftemplate-from-event (?evt) (if (eq (JessEvent.DEFTEMPLATE) (get ?evt type)) then (printout t "New deftemplate: " (call (call ?evt getObject) getName) crlf)))
TRUE
Jess> ;; Here we install the above function using a JessEventAdapter (call (engine) addJessListener (new JessEventAdapter display-deftemplate-from-event (engine)))
Jess> ;; Now we add DEFTEMPLATE to the event mask (set (engine) eventMask (bit-or (get (engine) eventMask) (JessEvent.DEFTEMPLATE)))
10.12. Setting and Reading Java Bean Properties
As mentioned previously, Java objects can be explicitly pattern-matched on the LHS of rules, but only to the extent that they are Java Beans. A Java Bean is really just a Java object that has a number of methods that obey a simple naming convention for Java Bean properties. A class has a Bean property if, for some string X and type T it has either or both of:- A method named getX which returns T and accepts no arguments; or, if T is boolean, named isX which accepts no arguments;
- A method named setX which returns void and accepts a single argument of type T.
Jess> (defglobal ?*frame* = (new java.awt.Frame "Frame Demo"))
TRUE
Jess> ;; Directly call 'isVisible', or... (printout t (call ?*frame* isVisible) crlf)
FALSE
Jess> ;; ... equivalently, query the Bean property (printout t (get ?*frame* visible) crlf)
FALSE
10.13. Formatting Jess Constructs
The class jess.PrettyPrinter can produce a formatted rendering of many Jess objects, including jess.Defrules, Deffunctions jess.Defquerys, etc -- anything that implements the jess.Visitable interface. jess.PrettyPrinter is very simple to use: you just create an instance, passing the object to be rendered as a constructor argument, and then call toString to get the formatted result.import jess.*; public class ExPretty { public static void main(String[] unused) throws JessException { Rete r = new Rete(); r.eval("(defrule myrule (A) => (printout t \"A\" crlf))"); Defrule dr = (Defrule) r.findDefrule("myrule"); System.out.println(new PrettyPrinter(dr)); } } C:\> java ExPretty(defrule MAIN::myrule (A) => (printout t "A" crlf))