Here's a moderately complex example of using Jess rules to match on XML
elements. However, it requires a controversial 2-line hack in Jess to make
it work. What's interesting, is that the simple set of Jess rules below can
parse an XML file and match on selected element patterns -- without writing
a single line of Java!! I just created this example today, so I'm making no
claims about an optimal design.
On October 1st, the W3C voted to accept the XML Document Object Model (DOM)
as a standard interface for accessing XML document structures. It's rather
limited, but works pretty well for many applications. So far, the IBM XML
parser seems like the only one to support this final release of DOM, but
others will surely follow. The rules below are written about DOM objects,
so should work for any compliant XML parser.
HOWEVER, the DOM is specifed as a set of Java interfaces that are NOT Java
Beans. So, in order to write Jess rules about DOM objects, Jess must allow
'definstance' for non-beans. I hacked this change into
jess.reflect.ReflectFunctions.java
catch (NoSuchMethodException nsm)
{
// throw new ReteException("definstance",
// "Obj doesn't accept
PropertyChangeListeners",
// "");
return Funcall.TRUE(); // add this hack
}
As Ernest pointed out, "let the programmer beware" that if these non-beans
are modifed outside of Jess, the rules won't know about it. This is OK here
where all of the DOM instances are static.
OK, on to the example. If you follow the following URL to my Web site,
you'll get an XML file returned that represents the Jess example class
'jess.examples.pumps.Pump' as an XML document. This is a reasonably
complete representation according to the XML Metadata Interchange (XMI)
first draft. (A second draft was released last week, which I'm not yet
compliant with.)
http://www.ontogenics.com/repository/translator?source=java&target=xml&resou
rce=jess.examples.pumps.Pump
1. Save this XML document to a local file named 'Pump.xml'
2. Add the IBM XML parser to your Java class path
3. Batch load these rules from the Jess command line.
Jess> (batch xmldom.clp)
Flow is a read-write property
Name is a read-only property
TRUE
Jess>
As you'll see, these rules instantiate an XML parser, invoke it on the
Pump.xml file, traverse the XML document and definstance all Node objects
into the working memory, then use rules to pattern match to discover the
JavaBean design patterns. Finally, the rules write out which properties are
read-only and which are read-write. The entire parser instantiation and
traversal is written in 4 rules!!
You'll also notice that this example asserts over 400 facts into Jess as it
traverses the document structure.
For extra fun, I asserted the file as an instance of a java.io.File object
(which also requires the 2-line hack), then used rules to match on a File
object and access the File's java bean properties. Although, File is not a
full fledged JavaBean because it doesn't support PropertyChangeListener.
If you've read this far, and are still interested, try another example using
an XML representation of java.util.Vector. Follow this URL to get the XML file.
http://www.ontogenics.com/repository/translator?source=java&target=xml&resou
rce=java.util.Vector
Then, modify the final definstance in the rules to read this file. You
should get this output:
Jess> (batch xmldom.clp)
!! Design warning: elementData is a 'protected' attribute
!! Design warning: elementCount is a 'protected' attribute
!! Design warning: capacityIncrement is a 'protected' attribute
TRUE
Jess>
I added one rule that could be a simple object-oriented design heuristic:
Issue a warning if the class includes any 'protected' attributes. Vector
has three. Vector has no read-only or read-write JavaBean properties.
Have fun!
-------------------------------------------------------------------
(defclass File java.io.File)
(defclass Document org.w3c.dom.Document)
(defclass Element org.w3c.dom.Element)
(defclass Node org.w3c.dom.Node)
;;;(defglobal ?*NodeClass* = (call java.lang.Class forName "org.w3c.dom.Node"))
(deffacts init
(NodeClass (call java.lang.Class forName "org.w3c.dom.Node")))
(deftemplate JavaBean
(slot propertyName)
(slot readMethod)
(slot writeMethod))
(defrule ParseDocument
(File (path ?path) (OBJECT ?file))
(test (?path endsWith ".xml"))
=>
(bind ?parser (new com.ibm.xml.parser.Parser ?path))
(bind ?fs (new java.io.FileInputStream ?file))
(bind ?doc (?parser readStream ?fs))
(definstance Document ?doc)
)
(defrule GetDocumentRoot
(Document (documentElement ?root))
=>
(definstance Node ?root)
)
(defrule GetFirstChild
"If the first child is instanceof Node, assert into working memory"
(Node (firstChild ?node))
;;this should work, but Jess says that ?*NodeClass* is not bound...
;;(test (call ?*NodeClass* isInstance ?node))
(NodeClass ?cls)
(test (?cls isInstance ?node))
=>
(definstance Node ?node)
)
(defrule GetNextSibling
"Assert all of a Node's siblings into working memory"
(Node (nextSibling ?node))
(NodeClass ?cls)
(test (?cls isInstance ?node))
=>
(definstance Node ?node)
)
(defrule FindProtectedAttributes
(Node (nodeName "Attribute") (OBJECT ?attr))
(Node (nodeName "visibility") (parentNode ?attr) (OBJECT ?vis))
(test (eq (?vis getAttribute "XMI.value") "protected"))
(Node (parentNode ?attr) (nodeName "name") (OBJECT ?name))
(Node (parentNode ?name) (nodeValue ?nameValue))
=>
(printout t "!! Design warning: " ?nameValue " is a 'protected' attribute"
crlf)
)
(defrule BeanReadMethod
(Node (nodeName "Operation") (OBJECT ?op))
(Node (parentNode ?op) (nodeName "name") (OBJECT ?name))
(Node (parentNode ?name) (nodeValue ?nameValue))
(test (?nameValue startsWith "get"))
=>
(bind ?propName (?nameValue substring 3))
(assert (JavaBean (propertyName ?propName) (readMethod ?nameValue)))
)
(defrule BeanWriteMethod
(Node (nodeName "Operation") (OBJECT ?op))
(Node (parentNode ?op) (nodeName "name") (OBJECT ?name))
(Node (parentNode ?name) (nodeValue ?nameValue))
(test (?nameValue startsWith "set"))
?bean <- (JavaBean (writeMethod nil) (propertyName ?p&:(eq ?p (?nameValue
substring 3))))
=>
(bind ?propName (?nameValue substring 3))
(modify ?bean (writeMethod ?propName))
)
(defrule ReadonlyProperty
(declare (salience -100))
(JavaBean (propertyName ?prop) (readMethod ~nil) (writeMethod nil))
=>
(printout t ?prop " is a read-only property" crlf)
)
(defrule ReadWriteProperty
(declare (salience -100))
(JavaBean (propertyName ?prop) (readMethod ~nil) (writeMethod ~nil))
=>
(printout t ?prop " is a read-write property" crlf)
)
(definstance File (new java.io.File "Pump.xml"))
(reset)
(run)
---------------------------------------------------------------------
To unsubscribe, send the words 'unsubscribe jess-users [EMAIL PROTECTED]'
in the BODY of a message to [EMAIL PROTECTED], NOT to the
list. List problems? Notify [EMAIL PROTECTED]
---------------------------------------------------------------------