Author: mic...@jb... Date: 2006-01-22 23:20:12 -0500 (Sun, 22 Jan 2006) New Revision: 2174 Added: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/ trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/Chunk.java trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLExpressionCompiler.java trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLGrammar.java trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLMappingItem.java trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/TemplateContext.java trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/TemplateFactory.java trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/NLExpressionCompilerTest.java trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateContextTest.java trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateFactoryTest.java Removed: trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateTest.java Log: new template based parser/compiler Added: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/Chunk.java =================================================================== --- trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/Chunk.java 2006-01-23 01:24:32 UTC (rev 2173) +++ trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/Chunk.java 2006-01-23 04:20:12 UTC (rev 2174) @@ -0,0 +1,98 @@ +package org.drools.natural.template; + +import java.util.Map; + +import org.apache.commons.lang.StringUtils; + +/** + * This holds a linked list of chunks of natural language. + * A chunk is basically some text, which is delimited by "holes" in the template. + * + * eg: "this is {0} an {1} expression" + * Would have 5 chunks: "this is", "{0}", "an", "{1}" and "expression". + * + * Chunks also know how to parse themselves to work out the value. + * + * This is used by TemplatePopulateContext. + * This class is very recursive, to be prepated to be confused. + * + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + */ +class Chunk { + + String text; + Chunk next; + + //value if it is a hole, starts out as null. + String value; + + + Chunk(String text) { + this.text = text; + } + + + + /** + * This will build up a key to use to substitute the original string with. + * Can then swap it with the target text. + */ + void buildSubtitutionKey(StringBuffer buffer) { + if (isHole()) { + buffer.append(" " + value + " "); + } else { + buffer.append(text); + } + if (next != null) { + next.buildSubtitutionKey(buffer); + } + } + + boolean isHole() { + return text.startsWith("{"); + } + + void process(String expression) { + if (isHole()) { + //value = text until next next.text is found + if (next == null || next.text == null) { + value = expression.trim(); + } else { + value = StringUtils.substringBefore(expression, next.text).trim(); + } + + } else { + value = text; + } + if (next != null) { + next.process(StringUtils.substringAfter(expression, value)); + } + } + + void buildValueMap(Map map) { + if (this.isHole()) { + map.put(text, value); + } + if (next != null) { + next.buildValueMap(map); + } + } + + void addToEnd(Chunk chunk) { + if (next == null) { + next = chunk; + } else { + next.addToEnd(chunk); + } + } + + + /** recursively reset the values */ + public void clearValues() { + this.value = null; + if (this.next != null) { + next.clearValues(); + } + } + +} Property changes on: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/Chunk.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLExpressionCompiler.java =================================================================== --- trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLExpressionCompiler.java 2006-01-23 01:24:32 UTC (rev 2173) +++ trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLExpressionCompiler.java 2006-01-23 04:20:12 UTC (rev 2174) @@ -0,0 +1,45 @@ +package org.drools.natural.template; + +import java.util.Collection; +import java.util.Iterator; + +/** + * This is the utility class for compiling pseudo natural/DSL expression into the target + * language, via the supplied mappings. + * + * This version works off "string templates" rather then infix operators. + * + * Note that this is not particularly efficient for large grammars - IN THEORY ! + * However, I have tested it with grammars of 200 000 terms, and it took less then a second per expression, + * so its no slouch. This could be a problem for bulk compiling of large rulesets with thousands of conditions. + * + * In general, grammars of < 1000 items should be fine. The cost is a parse time cost for Drools, which can be done + * incrementally in a suitable environment ideally anyway. + * + * It will go through each item in the grammar, trying to apply it regardless of if it is needed. + * This may be improved by some early regex scanning, but most likely this will not really save much. + * + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + */ +public class NLExpressionCompiler { + + private TemplateFactory factory; + private Collection grammar; + + public NLExpressionCompiler(NLGrammar grammar) { + this.grammar = grammar.getMappings(); + this.factory = new TemplateFactory(); + } + + public String compile(String expression) { + String nl = expression; + for ( Iterator iter = grammar.iterator(); iter.hasNext(); ) { + NLMappingItem mapping = (NLMappingItem) iter.next(); + TemplateContext ctx = factory.buildContext(mapping.getNaturalTemplate()); + nl = ctx.processAllInstances(nl, mapping.getGrammarTemplate()); + } + return nl; + } + + +} Property changes on: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLExpressionCompiler.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLGrammar.java =================================================================== --- trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLGrammar.java 2006-01-23 01:24:32 UTC (rev 2173) +++ trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLGrammar.java 2006-01-23 04:20:12 UTC (rev 2174) @@ -0,0 +1,51 @@ +package org.drools.natural.template; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +/** This represents a simple grammar mapping. */ +public class NLGrammar + implements + Serializable { + + + private static final long serialVersionUID = 1L; + private List mappings = new ArrayList(); + + public NLGrammar() { + } + + + /** + * When loading from properties, the order in which they apprear in the props file + * is the priority. + * + * Which makes sense intuitively, the order you read them it the order in which they will be applied. + */ + public void loadFromProperties(Properties props) { + int i = 0; + for ( Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { + String key = (String) iter.next(); + String property = props.getProperty(key); + NLMappingItem item = new NLMappingItem(i, key, property); + addNLItem(item); + i++; + } + } + + public void addNLItem(NLMappingItem item) { + this.mappings.add(item); + } + + public Collection getMappings() { + Collections.sort(mappings); + return Collections.unmodifiableCollection(mappings); + } + + +} Property changes on: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLGrammar.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLMappingItem.java =================================================================== --- trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLMappingItem.java 2006-01-23 01:24:32 UTC (rev 2173) +++ trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLMappingItem.java 2006-01-23 04:20:12 UTC (rev 2174) @@ -0,0 +1,52 @@ +package org.drools.natural.template; + +import java.io.Serializable; + +/** + * This contains a single mapping from psuedo NL to a grammarTemplate. + * + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + * + */ +public class NLMappingItem + implements + Comparable, Serializable { + + + private static final long serialVersionUID = 7185580607729787497L; + + private int priority = 0; + + private String naturalTemplate; + private String grammarTemplate; + + public NLMappingItem(int priority, + String naturalTemplate, + String grammarTemplate) { + this.priority = priority; + this.naturalTemplate = naturalTemplate; + this.grammarTemplate = grammarTemplate; + } + + public String getNaturalTemplate() { + return naturalTemplate; + } + + public String getGrammarTemplate() { + return grammarTemplate; + } + + public int compareTo(Object arg) { + if ( arg instanceof NLMappingItem ) { + NLMappingItem item = (NLMappingItem) arg; + if ( item.priority == this.priority ) return 0; + if ( item.priority > this.priority ) return 1; + if ( item.priority < this.priority ) return -1; + return 0; + } + else { + return 0; + } + } + +} Property changes on: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/NLMappingItem.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/TemplateContext.java =================================================================== --- trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/TemplateContext.java 2006-01-23 01:24:32 UTC (rev 2173) +++ trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/TemplateContext.java 2006-01-23 04:20:12 UTC (rev 2174) @@ -0,0 +1,119 @@ +package org.drools.natural.template; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; + + +/** + * This class takes a linked list of Chunk objects, and will replace what the chunks represent + * in an nl string with a interpolated grammar template. + * The values are obtained by matching the chunks with the nl. + * + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + * This is an alternative approach to the infix parser. + */ +class TemplateContext { + + //the start of the linked list. + Chunk start; + + /** + * Ad a chunk from the dictionary expression. + * A chunk is a piece of nl, or a hole. + * nl & holes should not be mixed. + */ + TemplateContext addChunk(String chunkText) { + Chunk chunk = new Chunk(chunkText); + if (start == null) { + start = chunk; + } else { + start.addToEnd(chunk); + } + return this; + } + + /** + * This will parse the input nl expression, and build a map of values for the "holes" + * in the grammar expression. + * It does this by getting the Chunks of the grammar to parse themselves. + */ + void processNL(String nl, Map map) { + start.clearValues(); + start.process(nl); + start.buildValueMap(map); + } + + /** + * This builds a fragment of the nl expression which can be used + * to swap out a piece of the original with the target expression. + * + * The target expression is the "right hand side" of the grammar map. + */ + String getSubstitutionKey() { + StringBuffer buffer = new StringBuffer(); + start.buildSubtitutionKey(buffer); + return buffer.toString().trim(); //trim so we don't get any erroneous spaces to stop replacing. + } + + /** + * This will build the target string that you can use to substitute the original with. + * @param map The map of values to hole keys. + * @param grammar_r The grammar item which will have the values plugged into the "holes". + * @return The final expression ready for substitution. + */ + String populateTargetString(Map map, + String grammar_r) { + for ( Iterator iter = map.keySet().iterator(); iter.hasNext(); ) { + String key = (String) iter.next(); + grammar_r = StringUtils.replace(grammar_r, key, (String) map.get(key)); + } + return grammar_r; + } + + /** + * @param nl The natural language expression. + * @param subKey The part of the nl expression to be swapped out. + * @param target The chunk to be swapped in to the nl + * @return The nl with the chunk replaced with the target. + */ + String interpolate(String nl, String subKey, String target) { + return StringUtils.replace(nl, subKey, target); + } + + /** + * This does it all as one call. Requires that chunks have been setup. + * @param nl The nl expression to process. + * @param grammarTemplate The grammar expression that will be interpolated (with the values from the original chunks), + * and then inserted in to the nl. + * @return the NL with the populated grammarRHS replacing the original pattern (from the chunks). + */ + public String process(String nl, String grammarTemplate) { + Map values = new HashMap(); + this.processNL(nl, values); + String subKey = this.getSubstitutionKey(); + String target = this.populateTargetString(values, grammarTemplate); + return this.interpolate(nl, subKey, target); + } + + + /** + * Similar to process, but processes iteratively until there is + * no change in the output. This allows for stuff to be repeated in an NL expression. + */ + public String processAllInstances(String nl, String grammarTemplate) { + String result = nl; + while (true) { + String newResult = process(result, grammarTemplate); + if (newResult.equals(result)) { + break; + } + result = newResult; + } + return result; + } + + +} Property changes on: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/TemplateContext.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/TemplateFactory.java =================================================================== --- trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/TemplateFactory.java 2006-01-23 01:24:32 UTC (rev 2173) +++ trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/TemplateFactory.java 2006-01-23 04:20:12 UTC (rev 2174) @@ -0,0 +1,95 @@ +package org.drools.natural.template; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * This takes a grammar template (the left hand side of a grammar mapping table) + * and builds a TemplateContext for it. + * + * Uses a built in lexer. + * + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + */ +class TemplateFactory { + + /** + * This will lex the template string into chunks. + * @param template From the grammar. eg "{0} likes cheese" is a template. + * @return A template context ready to apply to a nl expression. + */ + public TemplateContext buildContext(String template) { + + TemplateContext ctx = new TemplateContext(); + + List chunkList = lexChunks(template); + for ( Iterator iter = chunkList.iterator(); iter.hasNext(); ) { + ctx.addChunk((String) iter.next()); + + } + return ctx; + } + + + List lexChunks(String grammarTemplate) { + ChunkLexer lexer = new ChunkLexer(); + return lexer.lex(grammarTemplate); + } + + + /** + * Lex out chunks. + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + */ + static class ChunkLexer { + + private List chunks = new ArrayList(); + + private StringBuffer buffer = new StringBuffer(); + + public List lex(String grammarTemplate) { + + char[] chars = grammarTemplate.toCharArray(); + + for ( int i = 0; i < chars.length; i++ ) { + switch ( chars[i] ) { + case '{' : + startHole(); + break; + case '}' : + endHole(); + break; + default : + buffer.append(chars[i]); + break; + } + } + String buf = this.buffer.toString(); + if (!buf.equals("")) addChunk( buf ); + return this.chunks; + + } + + private boolean addChunk(String buf) { + return this.chunks.add( buf.trim() ); + } + + private void endHole() { + String buf = this.buffer.toString(); + chunks.add("{" + buf + "}"); + this.buffer = new StringBuffer(); + } + + private void startHole() { + String buf = this.buffer.toString(); + if (!buf.equals("")) { + addChunk( buf ); + } + this.buffer = new StringBuffer(); + } + + + } + +} Property changes on: trunk/labs/jbossrules/drools-natural-dsl/src/main/java/org/drools/natural/template/TemplateFactory.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/NLExpressionCompilerTest.java =================================================================== --- trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/NLExpressionCompilerTest.java 2006-01-23 01:24:32 UTC (rev 2173) +++ trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/NLExpressionCompilerTest.java 2006-01-23 04:20:12 UTC (rev 2174) @@ -0,0 +1,64 @@ +package org.drools.natural.template; + +import java.util.Properties; + +import junit.framework.TestCase; + +public class NLExpressionCompilerTest extends TestCase { + + public void testIntegration() { + NLGrammar grammar = new NLGrammar(); + grammar.addNLItem(new NLMappingItem(0, "{0} likes cheese", "likesCheese({0})")); + + NLExpressionCompiler compiler = new NLExpressionCompiler(grammar); + String result = compiler.compile("bob likes cheese"); + + assertEquals("likesCheese(bob)", result); + + + //now lets use a properties + Properties props = new Properties(); + props.setProperty("{0} likes cheese", "likesCheese({0})"); + props.setProperty("the date between {0} and {1}", "dateCompare({0}, {1})"); + + grammar = new NLGrammar(); + grammar.loadFromProperties(props); + compiler = new NLExpressionCompiler(grammar); + result = compiler.compile("bob likes cheese"); + + assertEquals("likesCheese(bob)", result); + + result = compiler.compile("the date between bob and michael"); + assertEquals("dateCompare(bob, michael)", result); + + + } + + /** This is surprisingly fast. I didn't build it for speed. */ + public void testLargeGrammar() { + Properties props = new Properties(); + + for (int i = 0; i < 1000; i++ ) { + props.put("some {0} grammar" + i, "some mapping{0}"); + if (i == 42) { + props.put("{0} likes cheese", "{0}.likesCheese()"); + props.put("{0} is happy", "{0}.isHappy()"); + } + } + + + + NLGrammar grammar = new NLGrammar(); + grammar.loadFromProperties(props); + + NLExpressionCompiler compiler = new NLExpressionCompiler(grammar); + + long start = System.currentTimeMillis(); + String result = compiler.compile("michael likes cheese and michael is happy"); + long runtime = System.currentTimeMillis() - start; + System.out.println("Runtime for compile: " + runtime + "ms"); + assertEquals("michael.likesCheese() and michael.isHappy()", result); + + } + +} Property changes on: trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/NLExpressionCompilerTest.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateContextTest.java =================================================================== --- trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateContextTest.java 2006-01-23 01:24:32 UTC (rev 2173) +++ trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateContextTest.java 2006-01-23 04:20:12 UTC (rev 2174) @@ -0,0 +1,192 @@ +package org.drools.natural.template; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; + +import junit.framework.TestCase; + +public class TemplateContextTest extends TestCase { + + + public void testAllInOne() { + TemplateContext ctx = new TemplateContext(); + //chunks represent a lexed grammar "left hand side" + ctx.addChunk("baby on board") + .addChunk("{0}") + .addChunk("and") + .addChunk("{1}") + .addChunk("burt ward"); + String result = ctx.process("yeah this is an expression baby on board exp1 and exp2 burt ward end.", "something({0}, {1})"); + assertEquals("yeah this is an expression something(exp1, exp2) end.", result); + + + //and check that the iterative one is OK. + result = ctx.process("yeah this is an expression baby on board exp1 and exp2 burt ward end.", "something({0}, {1})"); + assertEquals("yeah this is an expression something(exp1, exp2) end.", result); + } + + public void testBuildStrings() { + + TemplateContext ctx = new TemplateContext(); + + //chunks represent a lexed grammar "left hand side" + ctx.addChunk("baby on board") + .addChunk("{0}") + .addChunk("and") + .addChunk("{1}") + .addChunk("burt ward"); + + //and this is the right hand side grammar mapping (no lexing required, simple hole filling !). + String grammar_r = "something({0}, {1})"; + + //and this is the full expression + String nl = "yeah this is an expression baby on board exp1 and exp2 burt ward end."; + + //match the pattern in nl, put the values in the map + HashMap map = new HashMap(); + ctx.processNL(nl, map); + + //now get the chunk of nl that will be replaced with the target later. + String subKey = ctx.getSubstitutionKey(); + assertEquals("baby on board exp1 and exp2 burt ward", subKey); + + String target = ctx.populateTargetString( map, grammar_r ); + assertEquals("something(exp1, exp2)", target); + + String result = ctx.interpolate(nl, subKey, target); + + assertEquals("yeah this is an expression something(exp1, exp2) end.", result); + + + + } + + + public void testMultipleReplacement() { + + TemplateContext ctx = new TemplateContext(); + + //chunks represent a lexed grammar "left hand side" + ctx.addChunk("{0}") + .addChunk("likes cheese"); + + String nl = "bob likes cheese and michael likes cheese conan likes cheese"; + String grammarTemplate = "{0}.likesCheese()"; + String expected = "bob.likesCheese() and michael.likesCheese() conan.likesCheese()"; + + + String result = ctx.processAllInstances(nl, grammarTemplate); + assertEquals(expected, result); + } + + + + + + public void testBasicExpression() { + + Chunk chunk1 = new Chunk("baby on board"); + Chunk chunk2 = new Chunk("{0}"); + Chunk chunk3 = new Chunk("and"); + Chunk chunk4 = new Chunk("{1}"); + Chunk chunk5 = new Chunk("burt ward"); + + chunk1.next = chunk2; + chunk2.next = chunk3; + chunk3.next = chunk4; + chunk4.next = chunk5; + + String nl = "yeah this is an expression baby on board exp1 and exp2 burt ward"; + chunk1.process(nl); + + HashMap map = new HashMap(); + chunk1.buildValueMap(map); + + assertEquals("exp1", map.get("{0}")); + assertEquals("exp2", map.get("{1}")); + + } + + public void testStartWith() { + + Chunk chunk1 = new Chunk("{0}"); + Chunk chunk2 = new Chunk("a thing"); + Chunk chunk3 = new Chunk("and"); + Chunk chunk4 = new Chunk("{1}"); + Chunk chunk5 = new Chunk("one more"); + + chunk1.next = chunk2; + chunk2.next = chunk3; + chunk3.next = chunk4; + chunk4.next = chunk5; + + String nl = "exp1 a thing and exp2 one more"; + chunk1.process(nl); + + HashMap map = new HashMap(); + chunk1.buildValueMap(map); + + assertEquals("exp1", map.get("{0}")); + assertEquals("exp2", map.get("{1}")); + + } + + + public void testEndWith() { + + Chunk chunk1 = new Chunk("blah blah blah"); + Chunk chunk2 = new Chunk("{1}"); + + chunk1.next = chunk2; + + String nl = "blah blah blah exp1"; + chunk1.process(nl); + + HashMap map = new HashMap(); + chunk1.buildValueMap(map); + + assertEquals("exp1", map.get("{1}")); + assertEquals(1, map.size()); + } + + public void testOneInTheMiddle() { + Chunk chunk1 = new Chunk("yeah "); + Chunk chunk2 = new Chunk("{abc}"); + Chunk chunk3 = new Chunk("one more"); + + chunk1.next = chunk2; + chunk2.next = chunk3; + + String nl = "yeah exp1 one more "; + chunk1.process(nl); + + HashMap map = new HashMap(); + chunk1.buildValueMap(map); + + assertEquals("exp1", map.get("{abc}")); + + } + + public void testNoTokens() { + Chunk chunk1 = new Chunk("yeah "); + + String nl = "yeah exp1 one more "; + chunk1.process(nl); + + HashMap map = new HashMap(); + chunk1.buildValueMap(map); + + assertEquals(0, map.size()); + } + + + + + + +} + + Property changes on: trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateContextTest.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateFactoryTest.java =================================================================== --- trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateFactoryTest.java 2006-01-23 01:24:32 UTC (rev 2173) +++ trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateFactoryTest.java 2006-01-23 04:20:12 UTC (rev 2174) @@ -0,0 +1,41 @@ +package org.drools.natural.template; + +import java.util.List; + +import junit.framework.TestCase; + +public class TemplateFactoryTest extends TestCase { + + public void testMake() { + TemplateFactory factory = new TemplateFactory(); + TemplateContext ctx = factory.buildContext("something {0} going {1} on."); + assertNotNull(ctx); + } + + public void testLex() { + TemplateFactory factory = new TemplateFactory(); + List list = factory.lexChunks("one chunk"); + assertEquals(1, list.size()); + assertEquals("one chunk", list.get(0)); + + + list = factory.lexChunks("three {0} chunks"); + assertEquals(3, list.size()); + + assertEquals("three", list.get(0)); + assertEquals("{0}", list.get(1)); + assertEquals("chunks", list.get(2)); + + + list = factory.lexChunks("{42} more {0} chunks"); + assertEquals(4, list.size()); + + assertEquals("{42}", list.get(0)); + assertEquals("more", list.get(1)); + assertEquals("{0}", list.get(2)); + assertEquals("chunks", list.get(3)); + + + } + +} Property changes on: trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateFactoryTest.java ___________________________________________________________________ Name: svn:eol-style + native Deleted: trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateTest.java =================================================================== --- trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateTest.java 2006-01-23 01:24:32 UTC (rev 2173) +++ trunk/labs/jbossrules/drools-natural-dsl/src/test/java/org/drools/natural/template/TemplateTest.java 2006-01-23 04:20:12 UTC (rev 2174) @@ -1,327 +0,0 @@ -package org.drools.natural.template; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import org.apache.commons.lang.StringUtils; - -import junit.framework.TestCase; - -public class TemplateTest extends TestCase { - - String grammar_l = "baby on board {0} and {1} burt ward"; - String grammar_r = "something({0}, {1})"; - String result = "yeah this is an expression something(exp1, exp2)"; - - - - /** - * Lets try it all together. - */ - public void testBuildStrings() { - - Context ctx = new Context(); - - //chunks represent a lexed grammar "left hand side" - ctx.addChunk("baby on board") - .addChunk("{0}") - .addChunk("and") - .addChunk("{1}") - .addChunk("burt ward"); - - //and this is the right hand side grammar mapping (no lexing required, simple hole filling !). - String grammar_r = "something({0}, {1})"; - - //and this is the full expression - String nl = "yeah this is an expression baby on board exp1 and exp2 burt ward end."; - - //match the pattern in nl, put the values in the map - HashMap map = new HashMap(); - ctx.processNL(nl, map); - - //now get the chunk of nl that will be replaced with the target later. - String subKey = ctx.getSubstitutionKey(); - assertEquals("baby on board exp1 and exp2 burt ward", subKey); - - String target = ctx.populateTargetString( map, grammar_r ); - assertEquals("something(exp1, exp2)", target); - - String result = ctx.replaceNlWithTarget(nl, subKey, target); - - assertEquals("yeah this is an expression something(exp1, exp2) end.", result); - - - - } - - public void testAllInOne() { - Context ctx = new Context(); - //chunks represent a lexed grammar "left hand side" - ctx.addChunk("baby on board") - .addChunk("{0}") - .addChunk("and") - .addChunk("{1}") - .addChunk("burt ward"); - String result = ctx.process("yeah this is an expression baby on board exp1 and exp2 burt ward end.", "something({0}, {1})"); - assertEquals("yeah this is an expression something(exp1, exp2) end.", result); - } - - - - - - public void testBasicExpression() { - - Chunk chunk1 = new Chunk("baby on board"); - Chunk chunk2 = new Chunk("{0}"); - Chunk chunk3 = new Chunk("and"); - Chunk chunk4 = new Chunk("{1}"); - Chunk chunk5 = new Chunk("burt ward"); - - chunk1.next = chunk2; - chunk2.next = chunk3; - chunk3.next = chunk4; - chunk4.next = chunk5; - - String nl = "yeah this is an expression baby on board exp1 and exp2 burt ward"; - chunk1.process(nl); - - HashMap map = new HashMap(); - chunk1.buildValueMap(map); - - assertEquals("exp1", map.get("{0}")); - assertEquals("exp2", map.get("{1}")); - - } - - public void testStartWith() { - - Chunk chunk1 = new Chunk("{0}"); - Chunk chunk2 = new Chunk("a thing"); - Chunk chunk3 = new Chunk("and"); - Chunk chunk4 = new Chunk("{1}"); - Chunk chunk5 = new Chunk("one more"); - - chunk1.next = chunk2; - chunk2.next = chunk3; - chunk3.next = chunk4; - chunk4.next = chunk5; - - String nl = "exp1 a thing and exp2 one more"; - chunk1.process(nl); - - HashMap map = new HashMap(); - chunk1.buildValueMap(map); - - assertEquals("exp1", map.get("{0}")); - assertEquals("exp2", map.get("{1}")); - - } - - - public void testEndWith() { - - Chunk chunk1 = new Chunk("blah blah blah"); - Chunk chunk2 = new Chunk("{1}"); - - chunk1.next = chunk2; - - String nl = "blah blah blah exp1"; - chunk1.process(nl); - - HashMap map = new HashMap(); - chunk1.buildValueMap(map); - - assertEquals("exp1", map.get("{1}")); - assertEquals(1, map.size()); - } - - public void testOneInTheMiddle() { - Chunk chunk1 = new Chunk("yeah "); - Chunk chunk2 = new Chunk("{abc}"); - Chunk chunk3 = new Chunk("one more"); - - chunk1.next = chunk2; - chunk2.next = chunk3; - - String nl = "yeah exp1 one more "; - chunk1.process(nl); - - HashMap map = new HashMap(); - chunk1.buildValueMap(map); - - assertEquals("exp1", map.get("{abc}")); - - } - - public void testNoTokens() { - Chunk chunk1 = new Chunk("yeah "); - - String nl = "yeah exp1 one more "; - chunk1.process(nl); - - HashMap map = new HashMap(); - chunk1.buildValueMap(map); - - assertEquals(0, map.size()); - } - - - static class Context { - - Chunk start; - - /** - * Ad a chunk from the dictionary expression. - * A chunk is a piece of nl, or a hole. - * nl & holes should not be mixed. - */ - Context addChunk(String chunkText) { - Chunk chunk = new Chunk(chunkText); - if (start == null) { - start = chunk; - } else { - start.addToEnd(chunk); - } - return this; - } - - /** - * This will parse the input nl expression, and build a map of values for the "holes" - * in the grammar expression. - * It does this by getting the Chunks of the grammar to parse themselves. - */ - void processNL(String nl, Map map) { - start.process(nl); - start.buildValueMap(map); - } - - /** - * This builds a fragment of the nl expression which can be used - * to swap out a piece of the original with the target expression. - * - * The target expression is the "right hand side" of the grammar map. - */ - String getSubstitutionKey() { - StringBuffer buffer = new StringBuffer(); - start.buildSubtitutionKey(buffer); - return buffer.toString(); - } - - /** - * This will build the target string that you can use to substitute the original with. - * @param map The map of values to hole keys. - * @param grammar_r The grammar item which will have the values plugged into the "holes". - * @return The final expression ready for substitution. - */ - String populateTargetString(Map map, - String grammar_r) { - for ( Iterator iter = map.keySet().iterator(); iter.hasNext(); ) { - String key = (String) iter.next(); - grammar_r = StringUtils.replace(grammar_r, key, (String) map.get(key)); - } - return grammar_r; - } - - /** - * @param nl The natural language expression. - * @param subKey The part of the nl expression to be swapped out. - * @param target The chunk to be swapped in to the nl - * @return The nl with the chunk replaced with the target. - */ - String replaceNlWithTarget(String nl, String subKey, String target) { - return StringUtils.replace(nl, subKey, target); - } - - /** - * This does it all as one call. Requires that chunks have been setup. - * @param nl The nl expression to process. - * @param grammarRHS The grammar expression that will be populated, and then inserted in to the nl. - * @return the NL with the populated grammarRHS replacing the original pattern (from the chunks). - */ - public String process(String nl, String grammarRHS) { - Map values = new HashMap(); - this.processNL(nl, values); - String subKey = this.getSubstitutionKey(); - String target = this.populateTargetString(values, grammarRHS); - return this.replaceNlWithTarget(nl, subKey, target); - - } - - - } - - static class Chunk { - String text; - Chunk next; - - //value if it is a hole - String value; - - - Chunk(String text) { - this.text = text; - } - - - - /** - * This will build up a key to use to substitute the original string with. - * Can then swap it with the target text. - */ - public void buildSubtitutionKey(StringBuffer buffer) { - if (isHole()) { - buffer.append(" " + value + " "); - } else { - buffer.append(text); - } - if (next != null) { - next.buildSubtitutionKey(buffer); - } - } - - boolean isHole() { - return text.startsWith("{"); - } - - void process(String expression) { - if (isHole()) { - //value = text until next next.text is found - if (next == null || next.text == null) { - value = expression.trim(); - } else { - value = StringUtils.substringBefore(expression, next.text).trim(); - } - - } else { - value = text; - } - if (next != null) { - next.process(StringUtils.substringAfterLast(expression, value)); - } - } - - void buildValueMap(Map map) { - if (this.isHole()) { - map.put(text, value); - } - if (next != null) { - next.buildValueMap(map); - } - } - - void addToEnd(Chunk chunk) { - if (next == null) { - next = chunk; - } else { - next.addToEnd(chunk); - } - } - - } - - -} - - |