Developer Documentation

 

 

 

Developer Documentation

The Jalama application was designed using a modular architecture so that the functional parts of the system are isolated and so that a tech-savvy user could modify the system to work differently. Primarily, there are two parts of the Jalama system that were intended for modification: the XUL/XBL widgets that comprise the library of GUI widgets, and the logic that governs the assembly of a user interface from an input XML/XSD document.

Rules modification

The Jalama application relies on a rule-based system, Drools, for expressing the way XML files are translated into UI’s. The choice to use a rule-based system for storing this logic was based on the desire to externalize the logic from the compiled code so that it could easily be modified and extended in the future. The Drools engine uses an XML syntax for describing conditions and consequences for Java objects that are passed into the Drools engine. Within the Jalama architecture, there exists a Java object that roughly represents elements in an XML tree - including a link to both the element's DOM and the XSD for which the DOM conforms. During UI generation, Jalama descends the tree of the input XML and passes each element of the tree into the rules engine that sets UI-related attributes for each element. When the tree-traversal is complete then the UI basically aggregates the UI attributes set within by the rules engine into the final UI.

Developers may modify the business rules used in the UI generation and easily change the output UI’s in a consistent manner. Though the rules are implemented in an XML file, a basic understanding of the Java programming language is a prerequisite to rules modification. Moreover, an understanding of the Jalama object that is used by the rules engine is essential. Within the CVS tree, the rules files themselves exist in:

/jalama/lib/rules/drools/

Currently, within this directory reside two sets of business rules:

1] jalama_ui -- the business rules used to generate the generic data entry forms from xml or xsd documents.

2] dataset -- the business rules used to generate data entry forms for EML datasets.

Notice that there are 3 xml rules files for each of these rule-sets. The UI genereation scheme is basically a 3 step process corresponding to: 1] Pattern Matching - the assignment of widgets to elements of the XML/XSD, 2] the Attribute Population of the widgets with attributes (eg. names, labels, occurence restrictions), and 3] assembly of these widgets onto pages to be displayed to the application's user - this last step has not been fully developed as planned but is used by the system in a minimal fashion. Further detail of this system and how it works can be found here.

Throught the entire UI generation phase the Jalama object that is operated on by the rules engine is: XSElementDeclarationWrapper code, javadoc. This object basically represents the elements in the input XML/XSD document, and generally there is a one-to-one correspondence between the elements in the input and instances of the XSElementDeclarationWrapper. Here the term "element" potentially refers to both a DOM node as well as the XML Schema part that describes the DOM node. Basically, when developing this system we wanted a single Java object that could represent elements in the DOM as well as their definition and constraints stored in an XML Schema so that the UI generation system could gleen as much information as exists for the input through a single Java object. This way the UI generation system operates only on a single object - an object which can be used to obtain all the information related to the input data. What makes this object unique is the addition of the XML Schema info with the DOM node. The DOM API is a familiar structure to many programmers, yet the API for the XML Schema (PSVI) is a little more complicated and less common to programmers. Below are annotations on example modifications to the Jalama buisiness rules:

First Pass - Pattern Matching

The business rules engine takes each XSElementDeclarationWrapper in turn, and uses the "pattern matching" business rules to determine which UI component will be used to represent that element in the user interface. Each rules document can contain any number of business rules; a sample of a single pattern-matching rule is shown below:

<rule name="match_string">
  <parameter identifier="element">
    <java:class>edu.ucsb.nceas.jalama.generation.rulesengine.drools.XSElementDeclarationWrapper</java:class>
  </parameter>
  <java:condition>
    element.getType() == edu.ucsb.nceas.jalama.generation.XMLSchemaElementTypes.SIMPLE_STRING
  </java:condition>
  <java:consequence>
    <![CDATA[
      element.setWidgetType("textbox_bindings.xml#character_textbox");
      modifyObject(element);
      retractObject(element);
    ]]>
  </java:consequence>
</rule>

The tag: <parameter identifier="element"> takes an instance of the XSElementDeclarationWrapper class and assigns it to a variable named "element" that can be referenced by the code in the <java:condition> and <java:consequence> sections. The <java:condition> section is a test to determine whether this particular rule is fired (in this case, the rule will be fired if the element type is a String). The <java:consequence> section tells the rules engine what actions to perform if the <java:condition> is satisfied (ie if the rule is fired). In this case, the XBL UI component denoted by "textbox_bindings.xml#character_textbox" is assigned to represent this String value. The calls to modifyObject(element) and retractObject(element) are required by Drools.

There are a number of XBL UI components, so lets change the above code so that instead of a textbox we make the input element an integer textbox based on the condition that the element type is an integer - the following code would produce this change:

<rule name="match_string">
  <parameter identifier="element">
    <java:class>edu.ucsb.nceas.jalama.generation.rulesengine.drools.XSElementDeclarationWrapper</java:class>
  </parameter>
  <java:condition>
    element.getType() == edu.ucsb.nceas.jalama.generation.XMLSchemaElementTypes.SIMPLE_INTEGER
</java:condition>
  <java:consequence>
    <![CDATA[
      element.setWidgetType("textbox_bindings.xml#integer_textbox");
      modifyObject(element);
      retractObject(element);
    ]]>
  </java:consequence>
</rule>

 

Second Pass - Attribute Population

During the second pass, the "population" business rules add details that are specific to the input schema and document, such as labels, enumerations for drop-down lists, default values etc. These details are added by setting attribute values for the XBL bindings. For example, here is a code excerpt from the <java:consequence> section of a population rule for EML:

//try to get attributeLabel, if it exists...
String label = element.getDOMTextNodeValueAtRelXPath("attributeLabel[1]");
//...if not, use attributeName...
if (label==null || label.trim().length()<1) {
label = element.getDOMTextNodeValueAtRelXPath("attributeName[1]");
}
xbl.setLabel(label);

It checks the DOM for the XML instance document (that was originally passed along with the Schema), to see if an element named attributeLabel exists; if so, it uses the element's value as the label; if not, it uses the contents of the (required) attributeName element as a label.

The code below will set the lable to the value of an element "attibuteValue" if the label doesn't exist - assuming that the attributeValue element DOES exist:

//try to get attributeLabel, if it exists...
String label = element.getDOMTextNodeValueAtRelXPath("attributeLabel[1]");
//...if not, use attributeName...
if (label==null || label.trim().length()<1) {
label = element.getDOMTextNodeValueAtRelXPath("attributeName[1]");
} else {
String value = element.getDOMTextNodeValueAtRelXPath("attributeValue[1]"); if (value==null || value.trim().length()<1) { xbl.setLabel(value); } else { xbl.setLabel(value); } }
Third Pass - Layout

As mentioned above, the layout system's implementation is somewhat minimal. Below is a typical rule set used for the layout step:

<rule name="match_sequence_complex_element">
<parameter identifier="element"> <java:class>edu.ucsb.nceas.jalama.generation.rulesengine.drools.XSElementDeclarationWrapper</java:css>
</parameter>
<java:condition>element != null</java:condition>
<java:condition>element.getXBLBinding() != null</java:condition>
<java:consequence>
<![CDATA[

element.log("################# in jalama_ui_layout #################");
int totHeight = element.getTotalWidgetHeight();
element.log("################# getTotalWidgetHeight returned: "+totHeight+" #################");

element.assignPageNumber();

modifyObject(element);
retractObject(element);
]]>
</java:consequence>
</rule> </rule-set>

Notice that there are some Layout-specific attribute functionality in the XSElementDeclarationWrapper

 
XBL API

In addition to being able to modify the way the Jalama UI generation produces UI’s interested parties may extend the pool of UI widgets from which the UI generation system may draw from. The Jalama rendering system uses widgets that conform to the API described in this file. This API basically introduces structures in the XBL code that dictate an element's attributes, cardinality and relationship to other elements in the DOM. Writing these XBL widgets can be tricky – they use the Mozilla Extensible Binding Language (XBL) syntax and JavaScript and since neither of these is compiled, debugging can be frustrating. There are some good books on the subject, namely Essential XUL Programming by Vaughn Bullard, et al (Wiley), Creating Applications with Mozilla by David Boswell, et al (O'Reilly), and Rapid Application Development With Mozilla by Nigel McFarlane (Prentice Hall). Also, there are some good XUL/XBL tutorials online, which go through the creation of custom widgets in a step-by-step manner.

Each XBL <binding> used by Jalama is basically an XML document that contains 2 major sections, <content> and <implementation>:

<binding id="character_textbox">
  <content>
		<!-- ***  XUL code goes here - this defines the visible part of this UI component;  *** --> 
   <!-- ***  in this case, it's a text box that will accept typed characters as input  *** -->
  </content>
  <implementation>
		 <!-- ***  JavaScript code goes here, wrapped inside xml tags such as <attribute> and <method>  *** --> 
    <!-- ***  this defines the behavior of this UI component; validation of input values,          *** -->
    <!-- ***  population with default values etc                                                   *** -->
  </implementation>
</binding>