[jboss-svn-commits] JBL Code SVN: r26331 - in labs/jbossrules/trunk: drools-templates/src/main/java/org/drools/template and 2 other directories.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Fri May 1 11:34:55 EDT 2009
Author: laune
Date: 2009-05-01 11:34:54 -0400 (Fri, 01 May 2009)
New Revision: 26331
Added:
labs/jbossrules/trunk/drools-docs/drools-docs-expert/src/main/docbook/en-US/Chapter-Authoring/Section-Templates.xml
labs/jbossrules/trunk/drools-templates/src/main/java/org/drools/template/ObjectDataCompiler.java
labs/jbossrules/trunk/drools-templates/src/main/java/org/drools/template/objects/
labs/jbossrules/trunk/drools-templates/src/main/java/org/drools/template/objects/ObjectDataProvider.java
Modified:
labs/jbossrules/trunk/drools-docs/drools-docs-expert/src/main/docbook/en-US/Chapter-Authoring/Chapter-Authoring.xml
labs/jbossrules/trunk/drools-templates/src/test/java/org/drools/template/DataProviderCompilerIntegrationTest.java
Log:
template compiling from beans & maps
Modified: labs/jbossrules/trunk/drools-docs/drools-docs-expert/src/main/docbook/en-US/Chapter-Authoring/Chapter-Authoring.xml
===================================================================
--- labs/jbossrules/trunk/drools-docs/drools-docs-expert/src/main/docbook/en-US/Chapter-Authoring/Chapter-Authoring.xml 2009-05-01 09:06:05 UTC (rev 26330)
+++ labs/jbossrules/trunk/drools-docs/drools-docs-expert/src/main/docbook/en-US/Chapter-Authoring/Chapter-Authoring.xml 2009-05-01 15:34:54 UTC (rev 26331)
@@ -9,5 +9,6 @@
<title>Authoring</title>
<xi:include href="Section-Decision_Tables.xml" />
+ <xi:include href="Section-Templates.xml" />
</chapter>
Added: labs/jbossrules/trunk/drools-docs/drools-docs-expert/src/main/docbook/en-US/Chapter-Authoring/Section-Templates.xml
===================================================================
--- labs/jbossrules/trunk/drools-docs/drools-docs-expert/src/main/docbook/en-US/Chapter-Authoring/Section-Templates.xml (rev 0)
+++ labs/jbossrules/trunk/drools-docs/drools-docs-expert/src/main/docbook/en-US/Chapter-Authoring/Section-Templates.xml 2009-05-01 15:34:54 UTC (rev 26331)
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="UTF-8"?>
+ <section version="5.0" xml:base="../../" xmlns="http://docbook.org/ns/docbook"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:m="http://www.w3.org/1998/Math/MathML"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:db="http://docbook.org/ns/docbook">
+
+ <title>Templates</title>
+
+ <para>If you discover that you have a group of rules following
+ the same arrangement of patterns, constraints and actions on
+ the RHS, differing only in constants or names for objects or
+ fields, you might think of employing Drool's rule template feature
+ for generating the actual rules. You would write a
+ <emphasis>rule template</emphasis> file, containing the
+ textual skeleton of your rule and use the Drools template
+ compiler in combination with a collection of objects providing
+ the actual values for the "flesh" of the rules for their
+ instantiation.</para>
+
+ <para>The mechanism is very similar to what a macro processor
+ does. The major advantage proffered by template expansion is
+ that it's nicely integrated in the overall handling of
+ Knowledge Resources.</para>
+
+ <section>
+ <title>The Rule Template File</title>
+
+ <para>A rule template file begins with a header defining the
+ placeholders, or <emphasis>formal template parameters</emphasis>
+ for the strings that are to be inserted during instantiation.
+ After the first line, which invariably contains <kw>template header</kw>,
+ you should write a number of lines, each of which contains a single
+ parameter name.</para>
+
+ <example>
+ <title>Rule template file: template header</title>
+
+ <programlisting>
+<emphasis role="bold">template header</emphasis>
+<emphasis>parameter-name-1</emphasis>
+...
+<emphasis>parameter-name-n</emphasis>
+...</programlisting>
+ </example>
+
+ <para>The template header is followed by the text that is to
+ be replicated and interpolated with the actual parameters. It
+ may begin with a <kw>package</kw> statement, followed by some
+ additional lines. These
+ may be sectioned into one or more templates, each of them
+ between a pair of matching <kw>template</kw> and
+ <kw>end template</kw> statements. The <kw>template</kw> takes
+ an argument, which puts a name to the template. The name
+ can be a simple unquoted name or an arbitrary string enclosed
+ in double quotes. The template text between these lines may
+ contain one or more rules, constituting the "raw material"
+ for the expansion.</para>
+
+ <example>
+ <title>Rule template file: templates</title>
+
+ <programlisting>
+<emphasis role="bold">template header</emphasis>
+<emphasis role="bold"><emphasis>parameter-name-1</emphasis></emphasis>
+...
+<emphasis>parameter-name-n</emphasis>
+<emphasis role="bold">package ...</emphasis> # optional
+<emphasis>header text</emphasis> # optional
+<emphasis role="bold">template</emphasis> <emphasis>template-name</emphasis>
+...
+// template text
+...
+<emphasis role="bold">end template</emphasis>
+...</programlisting>
+ </example>
+
+ <para>The resulting text will begin with the package line and the
+ header text following it, if present. Then, each template text
+ will be expanded individually, yielding one set of rules for each
+ of the actual parameter sets. Therefore, the structure of the
+ template sections affect the order of the generated rules, since
+ the generator iterates over the sections and then over the
+ set of actual parameters.</para>
+
+ <para>Any interpolation takes place between a pair of <kw>template</kw>
+ and <kw>end template</kw> statements, when this template is
+ expanded. The template text is scanned for occurrences of
+ <emphasis>parameter expansions</emphasis> written according to:
+ <programlisting>
+...<emphasis role="bold">@{<emphasis>parameter-name</emphasis>}</emphasis>...</programlisting>
+ The name between '@{' and '}' should be one of the parameter names
+ defined in the template header.</para>
+
+ <para>An important parameter is available without having to be
+ included in the data source providing the actual values. The
+ parameter substitution
+ <programlisting>
+...<emphasis role="bold">@{row.rowNumber}</emphasis>...</programlisting>
+ expands to the integers 0, 1, 2, etc., providing a unique distinction
+ for the instantiation. You would use this as part of each rule
+ name, because, without this precaution, there would be duplicate
+ rule names. (You are, of course, free to use your own identification
+ included as an extra parameter.)</para>
+
+ </section>
+
+ <section>
+ <title>Expanding a Template</title>
+
+ <para>To expand a template, you must prepare a data source. This can
+ be a spreadsheet, as explained in the previous section. Here, we'll
+ concentrate on expansion driven by Java objects. There are two
+ straightforward ways of supplying values for a fixed set of names:
+ Java objects, in the JavaBeans style, and Maps. Both of them can
+ be arranged in a <code>Collection</code>, whose elements will be
+ processed during the expansion, resulting in an instantiation
+ for each element.</para>
+
+ <section>
+ <title>Instantiation from Java Objects</title>
+
+ <para>You may use a Java object that provides getter methods
+ corresponding to all of the parameter names of your template
+ file. If, for instance, you have defined a header
+ <programlisting>
+template header
+type
+limit
+word</programlisting>
+ the following Java class could be used:
+ <programlisting>
+public class ParamSet {
+ //...
+ public ParamSet( String t, int l, boolean w ) {
+ //...
+ }
+ public String getType(){...}
+ public int getLimit(){...}
+ public boolean isWord(){...}
+}</programlisting>
+ Although interpolation is pure text manipulation, the actual values
+ supplied may be of any type, just as long as this type provides a
+ reasonable <code>toString()</code> method. (For simple types, the
+ eponymous static method of the related class from <code>java.lang</code>
+ is used.)</para>
+
+ <para>Assuming that we have created a <code>Collection<ParamSet></code>
+ for a template file <code>template.drl</code>, we can now proceed to
+ request its expansion.</para>
+
+ <programlisting>
+Collection<ParamSet> paramSets = new ArrayList<ParamSet>();
+// populate paramSets
+paramSets.add( new ParamSet( "Foo", 42, true ) );
+paramSets.add( new ParamSet( "Bar", 13, false ) );
+ObjectDataCompiler converter = new ObjectDataCompiler();
+InputStream templateStream =
+ this.getClass().getResourceAsStream( "template.drl" );
+String drl = converter.compile( objs, templateStream );</programlisting>
+
+ The resulting string contains the expanded rules text. You could
+ write it to a file and proceed as usual, but it's also possible to
+ feed this to a <code>KnowledgeBuilder</code> and continue with the
+ resulting Knowledge Packages.
+
+ <programlisting>
+KnowledgeBase kBase = KnowledgeBaseFactory.newKnowledgeBase();
+KnowledgeBuilder kBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
+Reader rdr = new StringReader( drl );
+kBuilder.add( ResourceFactory.newReaderResource( rdr ), ResourceType.DRL );
+if( kBuilder.hasErrors() ){
+ // ...
+ throw new IllegalStateException( "DRL errors" );
+}
+kBase.addKnowledgePackages( kBuilder.getKnowledgePackages() );</programlisting>
+ </section>
+
+ <section>
+ <title>Instantiation from Maps</title>
+
+ <para>A <code>Map</code> that provides the values for substituting
+ template parameters should have a (string) key set matching all of
+ the parameter names. Again, values could be from any class, as long
+ as they provide a good <code>toString()</code> method. The expansion
+ would use the same approach, just differing in the way the
+ map collection is composed.</para>
+
+ <programlisting>
+Collection<Map<String,Object>> paramMaps = new ArrayList<Map<String,Object>>();
+// populate paramMaps
+ObjectDataCompiler converter = new ObjectDataCompiler();
+InputStream templateStream =
+ this.getClass().getResourceAsStream( "template.drl" );
+String drl = converter.compile( objs, templateStream );</programlisting>
+ </section>
+ </section>
+ </section>
Added: labs/jbossrules/trunk/drools-templates/src/main/java/org/drools/template/ObjectDataCompiler.java
===================================================================
--- labs/jbossrules/trunk/drools-templates/src/main/java/org/drools/template/ObjectDataCompiler.java (rev 0)
+++ labs/jbossrules/trunk/drools-templates/src/main/java/org/drools/template/ObjectDataCompiler.java 2009-05-01 15:34:54 UTC (rev 26331)
@@ -0,0 +1,47 @@
+package org.drools.template;
+
+import java.io.InputStream;
+import java.util.Collection;
+
+import org.drools.template.parser.DefaultTemplateContainer;
+import org.drools.template.parser.TemplateContainer;
+import org.drools.template.parser.TemplateDataListener;
+
+import org.drools.template.objects.ObjectDataProvider;
+
+/**
+ * This class provides additional methods for invoking the template
+ * compiler, taking the actual parameters from maps or objects.
+ *
+ * @author Wolfgang Laun
+ *
+ */
+public class ObjectDataCompiler extends DataProviderCompiler {
+
+ /**
+ * Compile templates, substituting from a collection of maps or objects
+ * into the given template.
+ * @param objs the collection of maps or objects
+ * @param template the template resource pathname
+ * @return the expanded rules as a string
+ */
+ public String compile(final Collection<?> objs, final String template) {
+ final InputStream templateStream = this.getClass().getResourceAsStream( template );
+ return compile( objs, templateStream );
+ }
+
+ /**
+ * Compile templates, substituting from a collection of maps or objects
+ * into the given template.
+ * @param objs objs the collection of maps or objects
+ * @param templateStream the template as a stream
+ * @return the expanded rules as a string
+ */
+ public String compile(final Collection<?> objs,
+ final InputStream templateStream) {
+ TemplateContainer tc = new DefaultTemplateContainer( templateStream );
+ closeStream( templateStream );
+ return compile( new ObjectDataProvider( tc, objs ),
+ new TemplateDataListener( tc ) );
+ }
+}
Added: labs/jbossrules/trunk/drools-templates/src/main/java/org/drools/template/objects/ObjectDataProvider.java
===================================================================
--- labs/jbossrules/trunk/drools-templates/src/main/java/org/drools/template/objects/ObjectDataProvider.java (rev 0)
+++ labs/jbossrules/trunk/drools-templates/src/main/java/org/drools/template/objects/ObjectDataProvider.java 2009-05-01 15:34:54 UTC (rev 26331)
@@ -0,0 +1,118 @@
+package org.drools.template.objects;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.drools.template.DataProvider;
+import org.drools.template.parser.Column;
+import org.drools.template.parser.TemplateContainer;
+
+/**
+ * An object of this class acts as a DataProvider for rule templates.
+ * It may be created with a collection of maps or objects. The template's
+ * column names are used as keys or Bean-stype accessors to obtain the
+ * actual parameters for the substitution.
+ *
+ * @author Wolfgang Laun
+ */
+public class ObjectDataProvider implements DataProvider {
+
+ private Column[] columns;
+ private Iterator<?> iter;
+
+ /**
+ * Constructor from a template container
+ * @param tcont a template container
+ * @param objs a collection of maps or objects
+ */
+ public ObjectDataProvider( TemplateContainer tcont, Collection<?> objs ) {
+ columns = tcont.getColumns();
+ iter = objs.iterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ private Method getMethod( Class<?> clazz, String field ){
+ String Field = Character.toUpperCase( field.charAt( 0 ) ) + field.substring( 1 );
+ String getter = "get" + Field;
+ try {
+ return clazz.getMethod( getter );
+ } catch (SecurityException e) {
+ return null;
+ } catch (NoSuchMethodException e) {
+ }
+ getter = "is" + Field;
+ try {
+ return clazz.getMethod( getter );
+ } catch (SecurityException e) {
+ return null;
+ } catch (NoSuchMethodException e) {
+ }
+ getter = field;
+ try {
+ return clazz.getMethod( getter );
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private Field getField( Class<?> clazz, String field ){
+ try {
+ return clazz.getField( field );
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private String[] nextFromMap( Object obj ) {
+ @SuppressWarnings( "unchecked" )
+ Map<String,Object> map = (Map)obj;
+ String[] values = new String[columns.length];
+ for( int iCol = 0; iCol < columns.length; iCol++ ){
+ Object target = map.get( columns[iCol].getName() );
+ values[iCol] = target == null ? null : target.toString();
+ }
+ return values;
+ }
+
+ private String[] nextFromObject( Object obj ) {
+ Class<?> clazz = obj.getClass();
+ String[] values = new String[columns.length];
+ for( int iCol = 0; iCol < columns.length; iCol++ ){
+ String fieldName = columns[iCol].getName();
+ Object val = null;
+ try {
+ Method method = getMethod( clazz, fieldName );
+ if( method != null ){
+ val = method.invoke( obj );
+ } else {
+ Field field = getField( clazz, fieldName );
+ if( field != null ){
+ val = field.get( obj );
+ }
+ }
+ } catch (Exception e) {
+ // leave it null
+ }
+ values[iCol] = val == null ? null : val.toString();
+ }
+ return values;
+ }
+
+ @Override
+ public String[] next() {
+ Object obj = iter.next();
+ if( obj instanceof Map ){
+ return nextFromMap( obj );
+ } else {
+ return nextFromObject( obj );
+ }
+ }
+}
Modified: labs/jbossrules/trunk/drools-templates/src/test/java/org/drools/template/DataProviderCompilerIntegrationTest.java
===================================================================
--- labs/jbossrules/trunk/drools-templates/src/test/java/org/drools/template/DataProviderCompilerIntegrationTest.java 2009-05-01 09:06:05 UTC (rev 26330)
+++ labs/jbossrules/trunk/drools-templates/src/test/java/org/drools/template/DataProviderCompilerIntegrationTest.java 2009-05-01 15:34:54 UTC (rev 26331)
@@ -1,9 +1,17 @@
package org.drools.template;
+import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import org.drools.template.parser.Column;
+import org.drools.template.parser.DefaultTemplateContainer;
+import org.drools.template.parser.TemplateContainer;
+
import junit.framework.TestCase;
public class DataProviderCompilerIntegrationTest extends TestCase {
@@ -35,10 +43,7 @@
EXPECTED_RULES.append( rule0_a ).append( rule0_b ).append( rule0_then );
}
- private class TestDataProvider
- implements
- DataProvider {
-
+ private class TestDataProvider implements DataProvider {
private Iterator<String[]> iterator;
TestDataProvider(List<String[]> rows) {
@@ -52,94 +57,154 @@
public String[] next() {
return iterator.next();
}
+ }
+ private ArrayList<String[]> rows = new ArrayList<String[]>();
+
+ public void setUp(){
+ rows.add( new String[]{ "1",
+ "STANDARD",
+ "FLAT",
+ null,
+ "SBLC",
+ "ISS",
+ "Commission",
+ "Party 1",
+ "USD",
+ null,
+ "750",
+ "dummy"} );
+ rows.add( new String[]{ "15",
+ "STANDARD",
+ "FLAT",
+ "Entity Branch 1",
+ "SBLC",
+ "ISS",
+ "Commission",
+ null,
+ "YEN",
+ null,
+ "1600",
+ "dummy" } );
+ rows.add( new String[]{ "12",
+ "STANDARD",
+ "FLAT",
+ null,
+ "SBLC",
+ "ISS",
+ "Postage",
+ null,
+ "YEN",
+ null,
+ "40",
+ "dummy" } );
+ rows.add( new String[]{ "62",
+ "STANDARD",
+ "FLAT",
+ null,
+ "SBLC",
+ "ISS",
+ "Telex",
+ null,
+ "YEN",
+ "< 30000",
+ "45",
+ "dummy" } );
}
-
+
public void testCompiler() throws Exception {
- ArrayList<String[]> rows = new ArrayList<String[]>();
- rows.add( createRow( "1",
- "STANDARD",
- "FLAT",
- null,
- "SBLC",
- "ISS",
- "Commission",
- "Party 1",
- "USD",
- null,
- "750",
- "dummy") );
- rows.add( createRow( "15",
- "STANDARD",
- "FLAT",
- "Entity Branch 1",
- "SBLC",
- "ISS",
- "Commission",
- null,
- "YEN",
- null,
- "1600",
- "dummy" ) );
- rows.add( createRow( "12",
- "STANDARD",
- "FLAT",
- null,
- "SBLC",
- "ISS",
- "Postage",
- null,
- "YEN",
- null,
- "40",
- "dummy" ) );
- rows.add( createRow( "62",
- "STANDARD",
- "FLAT",
- null,
- "SBLC",
- "ISS",
- "Telex",
- null,
- "YEN",
- "< 30000",
- "45",
- "dummy" ) );
TestDataProvider tdp = new TestDataProvider( rows );
final DataProviderCompiler converter = new DataProviderCompiler();
final String drl = converter.compile( tdp,
"/templates/rule_template_1.drl" );
System.out.println( drl );
assertEquals( EXPECTED_RULES.toString(), drl );
+ }
+ public void testCompilerMaps() throws Exception {
+ Collection<Map<String,Object>> maps = new ArrayList<Map<String,Object>>();
+ final ObjectDataCompiler converter = new ObjectDataCompiler();
+ InputStream templateStream =
+ this.getClass().getResourceAsStream( "/templates/rule_template_1.drl" );
+ TemplateContainer tc = new DefaultTemplateContainer( templateStream );
+ Column[] columns = tc.getColumns();
+
+ for( String[] row: rows ){
+ Map<String,Object> map = new HashMap<String,Object>();
+ for( int icol = 0; icol < columns.length; icol++ ){
+ Object value = row[icol];
+ if( value != null ){
+ map.put( columns[icol].getName(), value );
+ }
+ }
+ maps.add( map );
+ }
+ templateStream =
+ this.getClass().getResourceAsStream( "/templates/rule_template_1.drl" );
+ final String drl = converter.compile( maps, templateStream );
+ System.out.println( drl );
+ assertEquals( EXPECTED_RULES.toString(), drl );
}
- private String[] createRow(String cell1,
- String cell2,
- String cell3,
- String cell4,
- String cell5,
- String cell6,
- String cell7,
- String cell8,
- String cell9,
- String cell10,
- String cell11,
- String cell12) {
- String[] row = new String[12];
- row[0] = cell1;
- row[1] = cell2;
- row[2] = cell3;
- row[3] = cell4;
- row[4] = cell5;
- row[5] = cell6;
- row[6] = cell7;
- row[7] = cell8;
- row[8] = cell9;
- row[9] = cell10;
- row[10] = cell11;
- row[11] = cell12;
- return row;
+ public static class OBJ {
+ private final String FEE_SCHEDULE_ID;
+ private final String FEE_SCHEDULE_TYPE;
+ private final String FEE_MODE_TYPE;
+ private final String ENTITY_BRANCH;
+ private final String PRODUCT_TYPE;
+ private final String ACTIVITY_TYPE;
+ public final String FEE_TYPE;
+ public final String OWNING_PARTY;
+ public final String CCY;
+ public final String LC_AMOUNT;
+ public final String AMOUNT;
+ OBJ( String[] vals ){
+ FEE_SCHEDULE_ID = vals[0];
+ FEE_SCHEDULE_TYPE = vals[1];
+ FEE_MODE_TYPE = vals[2];
+ ENTITY_BRANCH = vals[3];
+ PRODUCT_TYPE = vals[4];
+ ACTIVITY_TYPE = vals[5];
+ FEE_TYPE = vals[6];
+ OWNING_PARTY = vals[7];
+ CCY = vals[8];
+ LC_AMOUNT = vals[9];
+ AMOUNT = vals[10];
+ }
+ public String getFEE_SCHEDULE_ID() {
+ return FEE_SCHEDULE_ID;
+ }
+ public String getFEE_SCHEDULE_TYPE() {
+ return FEE_SCHEDULE_TYPE;
+ }
+ public String getFEE_MODE_TYPE() {
+ return FEE_MODE_TYPE;
+ }
+ public String ENTITY_BRANCH() {
+ return ENTITY_BRANCH;
+ }
+ public String PRODUCT_TYPE() {
+ return PRODUCT_TYPE;
+ }
+ public String ACTIVITY_TYPE() {
+ return ACTIVITY_TYPE;
+ }
}
+ public void testCompilerObjs() throws Exception {
+ Collection<Object> objs = new ArrayList<Object>();
+ final ObjectDataCompiler converter = new ObjectDataCompiler();
+ final InputStream templateStream =
+ this.getClass().getResourceAsStream( "/templates/rule_template_1.drl" );
+
+ for( String[] row: rows ){
+ OBJ obj = new OBJ( row );
+ objs.add( obj );
+ }
+ final String drl = converter.compile( objs, templateStream );
+ System.out.println( drl );
+ assertEquals( EXPECTED_RULES.toString(), drl );
+ }
+
+
}
More information about the jboss-svn-commits
mailing list