[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&lt;ParamSet&gt;</code>
+        for a template file <code>template.drl</code>, we can now proceed to
+        request its expansion.</para>
+
+        <programlisting>
+Collection&lt;ParamSet&gt; paramSets = new ArrayList&lt;ParamSet&gt;();
+// 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&lt;Map&lt;String,Object&gt;&gt; paramMaps = new ArrayList&lt;Map&lt;String,Object&gt;&gt;();
+// 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