Created by Adriana Alexandru
This document is a guide to creating a NRL to Schematron generator and presents an end-to-end generator example and the reasoning behind it.
Resources for NRL can be found on the NRL Sourceforge page.
Looking at this very useful overview of how Schematron works might be a good start. Resources for Schematron can be found on the official website of Schematron and a Schematron tutorial is also available.
The Concordion official website offers a very comprehensive tutorial about how Concordion works.
The software programs used in this tutorial are Eclipse version 3.6.1 and Oxygen XML Editor.
The NRL parser and Concordion need to be installed before starting. Dependencies are managed with Maven.
Since the NRL parser is developed in Java this tutorial uses Java to build a NRL to Schematron generator example.
Oxygen XML Editor is used in order to visualise the XML schema model when creating the NRL rules examples that are mapped to Schematron rules. It is useful to build the Oxygen XML Editor project in the Eclipse project in order to easily manage all the files. Below two screenshots show how files were organised for this tutorial in Eclipse and Oxygen XML Editor.
Eclipse screenshot | Oxygen XML Editor screenshot |
---|---|
Note that the models, rules and test-data folders in the Eclipse navigation screenshot that are the same with the Oxygen XML Editor folders.
The src/com/m20/absolute/schematron/ast/xpath package contains the classes relevant to translating NRL language constraints to XPath. XPath is relevant because the assert element in Schematron contains an attribute test whose value is expresssed in XPath.
The src/com/m20/absolute/schematron/ast package contains the classes used to translate NRL rules into Schematron rules.
For the purpose of easy file management the folder in which Concordion outputs the result html file has been changed to test-output/com/m20/absolute/schematron and the file is called NRLSchematronGenerator.html. This file displays the Concordion test cases and whether they pass or fail and works in the same way as it is explained in the Concordion tutorial.
The testsrc/com/m20/absolute/schematron/css package contains the NRLSchematronGeneratorTest.java file which represents the Concordion fixture which is also explained in the same Concordion tutorial. The NRLSchematronGenerator.html within this package is the file in which the test cases are written.
The mapping examples displayed below use the XML Schema model displayed below in a visual manner as a screenshot via Oxygen XML Editor.
NRL works on the basis of types rather than elements. So when referring to Trade for example, Trade is a type, and same with Amount. Because the Trade type is used in this tutorial it is also expanded and displayed visually below. Please ignore the highlights for now.
XML schema model | Trade type expanded |
---|---|
The easiest way to learn how NRL is mapped to Schematron is to look at an example. Since NRL rules map to a model, the XML schema is used to create a XML file model on which the NRL rule will be created agaist.
The table displayed below shows how two NRL rules are mapped to Schematron.
Example files | NRL | Schematron | Comments |
---|---|---|---|
NRL rule Schematron rule |
Rule Set "Expressions" Context: Trade Validation Rule "r1" numberOfLegs = 6 |
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.ascc.net/xml/schematron"> <pattern name="Expressions"> <rule context="//trade" id="r1"> <assert test="numberOfLegs = 6" /> </rule> </pattern> </schema> |
|
NRL rule Schematron rule |
Context: Amount Validation Rule "r2" amount >= 10000 |
<rule context="//trade/leg/pay | //trade/leg/receive | //trade/leg/netCashFlow | //trade/totalValue" id="r2"> <assert test="amount >= 10000"/> </rule> |
In this example the NRL rule context is Amount because the rule constraint is evaluating the value of amount and the immediate upper type in the hierarchy for amount is Amount (note that, for example, an immediate upper node of amount in the hierarchy is the pay node in the model diagram which is of type Amount). It is highlited in the diagram that more nodes of type Amount exist: pay, receive, netCashFlow, totalValue. It is also highlited that a node of type Amount contains the nodes amount and currency. This means that in this example the rule is evaluating the value of amount in 4 places in the model since amount is contained by the type Amount and 4 nodes as highlighted have this type. This explains why the NRL context translated to Schematron context includes the paths to these nodes in XPath format as displayed in the Schematron rule. (Note: this expansion of types into paths would not be necessary if we were to use Schematron with XPath 2 - we decided to stick to version 1 for this tutorial) |
The NRL to Schematron rule translation is probably the most common translation that needs to be done and it suffices for the purpose of this tutorial. A NRL to Schematron generator might attempt to generate Schematron reports as well.
The diagram displayed below shows the entire generatiom implementation workflow: The input is NRL code. The NRL code is parsed by the NRL parser into a NRL AST. The NRL AST is translated into a Schematron AST. The Schematron AST is serialised by the Serialiser. The Serialiser produces the Schematron code which is the final output.
The beauty of using Concordion to build the generator is that the specifications are tied to the implementation thus and the system is documented and the code is neat.
A preferred way of starting to build the generator is by starting with writing the specifications, more exactly in this case, by writing the test cases. Here is a link to the final Concordion validated test cases, which are also displayed below, which will show exatcly what it is aimed to be obtained. All the implementation sits behind the green colours from these two tables.
Writing these test cases follow the same procedure described in the Concordion tutorial mentioned above. The test cases are written in a html file which is tied to the fixture as showed in the tutorial. Here is the html code for the NRLSchematronGenerator.html file used to build this generator example. There is no need to write all the possible test cases from the beginning, test cases can always be added later .
Starting by writing some test cases helps to narrow down implementation focus on the NRL constraints specified to be translated.
The first column from both tables represents the expression or rule correctly written in NRL. The second column represents the same concept but translated to Schematron and this is what the implementation is expected to produce. The third column is optional for specifying any comments if desired. Obviously, this structure can be changed to add more columns or to not use tables at all. This example uses tables for test cases to maximise the clarity of the translations.
The next step is to write the fixture file that will map the test cases to their implementations. The fixture documented java source code for this example of generator is available for download. Writing the fixture class is straightforward and the Concordion tutorial mentioned above explains how to write fixture classes.
Each test case written at Step 1 needs to be implemented correctly in order to be validated by Concordion and coloured in green, otherwise it is coloured in red. The implementation needs to be serialised to generate the same output as the one written in the second column of the html test cases for Schematron as expected generated output.
The first test case from the first table, for example, says that a number, for example number 6 in NRL is 6 in NRL and in Schematron is 6 too. A class that allows to express numbers needs to be created. The class needs to implement methods to set the value of the number an to retrieve it. Here is the java source code for Number.java class which validates the first test case.
Other test cases may require the use of a serialiser method to format the output as desired. It is recommended to keep the serialiser methods separate in a different class from the implementation. An example of such test case that requires a serialiser is 5 / 4.2 which is translated from NRL to Schematron as 6 div 4.2. The implementation is in the java class BinaryPredicate.java and the serialiser method is in the XPathSerialiser.java. The code is documented and comprehensive. The serialiser method relevant for this test case is the method that takes as parameter an instance of a BinaryPredicate.
The second table uses a different serialiser class, SchematronSerialiser.java for clarity.
All implementation code for the test cases introduced above in the two tables is available for download and is documented and comprehensive.
The last step is to write the generator itself. The NRL to Schematron generator class, NRLSchematronGenerator.java, makes use of all the classes written for implementing the test cases. This class can be continously expanded as translations as test cases are implemented. This class will run as a JUnit test as described in the Concordion tutorial.
Here is the link to download the entire project including the source code and this tutorial. If you are using eclipse, you can import this as an eclipse project. It requires the NRL parser, which you can find on the sourceforge page.
In conclusion: this document attempts to explain how to translate a part of the NRL language to Schematron. It is not supposed to be a full code generator, and therefore neither implements all of NRL, nor all of Schematron. Nevertheless, we hope this was useful to you as an example of working with the NRL API and what is involved in writing a code generator.
Let us know on the nrl-dev list!.