package org.drools.rule.builder.dialect.mvel; import java.io.InputStreamReader; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Map.Entry; import junit.framework.Assert; import junit.framework.TestCase; import org.drools.Cheese; import org.drools.RuleBase; import org.drools.RuleBaseFactory; import org.drools.WorkingMemory; import org.drools.base.ClassObjectType; import org.drools.base.DefaultKnowledgeHelper; import org.drools.base.mvel.MVELConsequence; import org.drools.base.mvel.MVELDebugHandler; import org.drools.common.AgendaItem; import org.drools.common.InternalFactHandle; import org.drools.common.PropagationContextImpl; import org.drools.compiler.Dialect; import org.drools.compiler.DialectCompiletimeRegistry; import org.drools.compiler.DrlParser; import org.drools.compiler.DroolsParserException; import org.drools.compiler.PackageBuilder; import org.drools.compiler.PackageBuilderConfiguration; import org.drools.compiler.PackageRegistry; import org.drools.lang.descr.AttributeDescr; import org.drools.lang.descr.PackageDescr; import org.drools.lang.descr.RuleDescr; import org.drools.reteoo.LeftTuple; import org.drools.reteoo.MockLeftTupleSink; import org.drools.rule.Declaration; import org.drools.rule.GroupElement; import org.drools.rule.ImportDeclaration; import org.drools.rule.Package; import org.drools.rule.Pattern; import org.drools.rule.Rule; import org.drools.rule.builder.RuleBuildContext; import org.drools.rule.builder.RuleBuilder; import org.drools.rule.builder.dialect.java.JavaConsequenceBuilder; import org.drools.spi.CompiledInvoker; import org.drools.spi.Consequence; import org.drools.spi.ObjectType; import org.drools.spi.PatternExtractor; import org.mvel2.ParserContext; import org.mvel2.compiler.ExpressionCompiler; import org.mvel2.debug.DebugTools; public class MVELConsequenceBuilderTest extends TestCase { public void setUp() { } public void testSimpleExpression() throws Exception { PackageDescr pkgDescr = new PackageDescr( "pkg1" ); PackageBuilder pkgBuilder = new PackageBuilder(); pkgBuilder.addPackage( pkgDescr ); final Package pkg = pkgBuilder.getPackageRegistry( "pkg1" ).getPackage(); final RuleDescr ruleDescr = new RuleDescr( "rule 1" ); ruleDescr.setNamespace( "pkg1" ); ruleDescr.setConsequence( "modify (cheese) {price = 5 };\nretract (cheese)" ); final PackageBuilderConfiguration conf = pkgBuilder.getPackageBuilderConfiguration(); DialectCompiletimeRegistry dialectRegistry = pkgBuilder.getPackageRegistry( pkg.getName() ).getDialectCompiletimeRegistry(); MVELDialect mvelDialect = (MVELDialect) dialectRegistry.getDialect( "mvel" ); final InstrumentedBuildContent context = new InstrumentedBuildContent( pkgBuilder, ruleDescr, dialectRegistry, pkg, mvelDialect ); final InstrumentedDeclarationScopeResolver declarationResolver = new InstrumentedDeclarationScopeResolver(); final ObjectType cheeseObjeectType = new ClassObjectType( Cheese.class ); final Pattern pattern = new Pattern( 0, cheeseObjeectType, "cheese" ); final GroupElement subrule = new GroupElement( GroupElement.AND ); subrule.addChild( pattern ); final Map<String, Declaration> map = new HashMap<String, Declaration>(); map.put( "cheese", pattern.getDeclaration() ); declarationResolver.setDeclarations( map ); context.setDeclarationResolver( declarationResolver ); final MVELConsequenceBuilder builder = new MVELConsequenceBuilder(); builder.build( context, "default" ); RuleBase ruleBase = RuleBaseFactory.newRuleBase(); ruleBase.addPackage( pkg ); final WorkingMemory wm = ruleBase.newStatefulSession(); MockLeftTupleSink sink = new MockLeftTupleSink(); final Cheese cheddar = new Cheese( "cheddar", 10 ); final InternalFactHandle f0 = (InternalFactHandle) wm.insert( cheddar ); final LeftTuple tuple = new LeftTuple( f0, sink, true ); final AgendaItem item = new AgendaItem( 0, tuple, 10, new PropagationContextImpl( 1, 1, null, null, null ), context.getRule(), subrule ); final DefaultKnowledgeHelper kbHelper = new DefaultKnowledgeHelper( wm ); kbHelper.setActivation( item ); ((MVELConsequence) context.getRule().getConsequence()).compile( Thread.currentThread().getContextClassLoader() ); context.getRule().getConsequence().evaluate( kbHelper, wm ); assertEquals( 5, cheddar.getPrice() ); } public void testImperativeCodeError() throws Exception { final Package pkg = new Package( "pkg1" ); final RuleDescr ruleDescr = new RuleDescr( "rule 1" ); ruleDescr.setConsequence( "if (cheese.price == 10) { cheese.price = 5; }" ); Properties properties = new Properties(); properties.setProperty( "drools.dialect.default", "mvel" ); PackageBuilderConfiguration cfg1 = new PackageBuilderConfiguration( properties ); PackageBuilder pkgBuilder = new PackageBuilder( pkg, cfg1 ); final PackageBuilderConfiguration conf = pkgBuilder.getPackageBuilderConfiguration(); PackageRegistry pkgRegistry = pkgBuilder.getPackageRegistry( pkg.getName() ); DialectCompiletimeRegistry dialectRegistry = pkgBuilder.getPackageRegistry( pkg.getName() ).getDialectCompiletimeRegistry(); MVELDialect mvelDialect = (MVELDialect) dialectRegistry.getDialect( pkgRegistry.getDialect() ); final InstrumentedBuildContent context = new InstrumentedBuildContent( pkgBuilder, ruleDescr, dialectRegistry, pkg, mvelDialect ); final InstrumentedDeclarationScopeResolver declarationResolver = new InstrumentedDeclarationScopeResolver(); final ObjectType cheeseObjeectType = new ClassObjectType( Cheese.class ); final Pattern pattern = new Pattern( 0, cheeseObjeectType ); final PatternExtractor extractor = new PatternExtractor( cheeseObjeectType ); final Declaration declaration = new Declaration( "cheese", extractor, pattern ); final Map<String, Declaration> map = new HashMap<String, Declaration>(); map.put( "cheese", declaration ); declarationResolver.setDeclarations( map ); context.setDeclarationResolver( declarationResolver ); final MVELConsequenceBuilder builder = new MVELConsequenceBuilder(); builder.build( context, "default" ); final RuleBase ruleBase = RuleBaseFactory.newRuleBase(); final WorkingMemory wm = ruleBase.newStatefulSession(); final Cheese cheddar = new Cheese( "cheddar", 10 ); final InternalFactHandle f0 = (InternalFactHandle) wm.insert( cheddar ); final LeftTuple tuple = new LeftTuple( f0, null, true ); final AgendaItem item = new AgendaItem( 0, tuple, 10, null, context.getRule(), null ); final DefaultKnowledgeHelper kbHelper = new DefaultKnowledgeHelper( wm ); kbHelper.setActivation( item ); try { ((MVELConsequence) context.getRule().getConsequence()).compile( Thread.currentThread().getContextClassLoader() ); context.getRule().getConsequence().evaluate( kbHelper, wm ); fail( "should throw an exception, as 'if' is not allowed" ); } catch ( Exception e ) { } assertEquals( 10, cheddar.getPrice() ); } /** * Just like MVEL command line, we can allow expressions to span lines, with optional ";" * seperating expressions. If its needed a ";" can be thrown in, but if not, a new line is fine. * * However, when in the middle of unbalanced brackets, a new line means nothing. * * @throws Exception */ public void testLineSpanOptionalSemis() throws Exception { String simpleEx = "foo\nbar\nbaz"; MVELConsequenceBuilder cons = new MVELConsequenceBuilder(); assertEquals( "foo;\nbar;\nbaz", MVELConsequenceBuilder.delimitExpressions( simpleEx ) ); String ex = "foo (\n bar \n)\nbar;\nyeah;\nman\nbaby"; assertEquals( "foo (\n bar \n);\nbar;\nyeah;\nman;\nbaby", MVELConsequenceBuilder.delimitExpressions( ex ) ); ex = "foo {\n bar \n}\nbar; \nyeah;\nman\nbaby"; assertEquals( "foo {\n bar \n};\nbar; \nyeah;\nman;\nbaby", MVELConsequenceBuilder.delimitExpressions( ex ) ); ex = "foo [\n bar \n]\nbar; x\nyeah();\nman[42]\nbaby;ca chiga;\nend"; assertEquals( "foo [\n bar \n];\nbar; x;\nyeah();\nman[42];\nbaby;ca chiga;\nend", MVELConsequenceBuilder.delimitExpressions( ex ) ); ex = " \n\nfoo [\n bar \n]\n\n\nbar; x\n \nyeah();\nman[42]\nbaby;ca chiga;\nend"; assertEquals( " \n\nfoo [\n bar \n];\n\n\nbar; x;\n \nyeah();\nman[42];\nbaby;ca chiga;\nend", MVELConsequenceBuilder.delimitExpressions( ex ) ); ex = " retract(f1) // some comment\n retract(f2)\nend"; assertEquals( " retract(f1) ;// some comment\n retract(f2);\nend", MVELConsequenceBuilder.delimitExpressions( ex ) ); ex = " retract(f1 /* inline comment */) /* some\n comment\n*/ retract(f2)\nend"; assertEquals( " retract(f1 /* inline comment */) ;/* some\n comment\n*/ retract(f2);\nend", MVELConsequenceBuilder.delimitExpressions( ex ) ); } public void testMVELDebugSymbols() throws DroolsParserException { MVELDebugHandler.setDebugMode( true ); try { final DrlParser parser = new DrlParser(); final PackageDescr pkgDescr = parser.parse( new InputStreamReader( getClass().getResourceAsStream( "mvel_rule.drl" ) ) ); // just checking there is no parsing errors Assert.assertFalse( parser.getErrors().toString(), parser.hasErrors() ); final Package pkg = new Package( "org.drools" ); final RuleDescr ruleDescr = (RuleDescr) pkgDescr.getRules().get( 0 ); final RuleBuilder builder = new RuleBuilder(); final PackageBuilder pkgBuilder = new PackageBuilder( pkg ); final PackageBuilderConfiguration conf = pkgBuilder.getPackageBuilderConfiguration(); DialectCompiletimeRegistry dialectRegistry = pkgBuilder.getPackageRegistry( pkg.getName() ).getDialectCompiletimeRegistry(); Dialect dialect = dialectRegistry.getDialect( "mvel" ); RuleBuildContext context = new RuleBuildContext( pkgBuilder, ruleDescr, dialectRegistry, pkg, dialect ); builder.build( context ); Assert.assertTrue( context.getErrors().toString(), context.getErrors().isEmpty() ); final Rule rule = context.getRule(); MVELConsequence mvelCons = (MVELConsequence) rule.getConsequence(); mvelCons.compile( Thread.currentThread().getContextClassLoader() ); String s = DebugTools.decompile( mvelCons.getCompExpr() ); int fromIndex = 0; int count = 0; while ( (fromIndex = s.indexOf( "DEBUG_SYMBOL", fromIndex + 1 )) > -1 ) { count++; } assertEquals( 4, count ); } finally { MVELDebugHandler.setDebugMode( false ); } } public void testX() { String expr = "System.out.println( \"a1\" );\n" + "System.out.println( \"a2\" );\n" + "System.out.println( \"a3\" );\n" + "System.out.println( \"a4\" );\n"; ExpressionCompiler compiler = new ExpressionCompiler( expr ); ParserContext context = new ParserContext(); context.setDebugSymbols( true ); context.addImport( "System", System.class ); context.setStrictTypeEnforcement( true ); //context.setDebugSymbols( true ); context.setSourceFile( "mysource" ); Serializable compiledExpression = compiler.compile( context ); String s = DebugTools.decompile( compiledExpression ); System.out.println( "s " + s ); int fromIndex = 0; int count = 0; while ( (fromIndex = s.indexOf( "DEBUG_SYMBOL", fromIndex + 1 )) > -1 ) { count++; } assertEquals( 4, count ); } private RuleBuildContext context; private RuleDescr ruleDescr; private MVELConsequenceBuilder builder; private void setupTest(String consequence, Map<String, Object> namedConsequences) { builder = new MVELConsequenceBuilder(); Package pkg = new Package( "org.drools" ); pkg.addImport( new ImportDeclaration( "org.drools.Cheese" ) ); PackageBuilderConfiguration conf = new PackageBuilderConfiguration(); PackageBuilder pkgBuilder = new PackageBuilder( pkg, conf ); ruleDescr = new RuleDescr( "test consequence builder" ); ruleDescr.setConsequence( consequence ); ruleDescr.addAttribute( new AttributeDescr("dialect", "mvel") ); for ( Entry<String, Object> entry : namedConsequences.entrySet() ) { ruleDescr.getNamedConsequences().put( entry.getKey(), entry.getValue() ); } Rule rule = new Rule( ruleDescr.getName() ); rule.addPattern( new Pattern( 0, new ClassObjectType( Cheese.class ), "$cheese" ) ); PackageRegistry pkgRegistry = pkgBuilder.getPackageRegistry( pkg.getName() ); DialectCompiletimeRegistry reg = pkgBuilder.getPackageRegistry( pkg.getName() ).getDialectCompiletimeRegistry(); context = new RuleBuildContext( pkgBuilder, ruleDescr, reg, pkg, reg.getDialect( pkgRegistry.getDialect() ) ); context.getBuildStack().push( rule.getLhs() ); context.getDialect().getConsequenceBuilder().build( context, "default" ); for ( String name : namedConsequences.keySet() ) { context.getDialect().getConsequenceBuilder().build( context, name ); } context.getDialect().addRule( context ); pkgRegistry.getPackage().addRule( context.getRule() ); pkgBuilder.compileAll(); pkgBuilder.reloadAll(); } public void testDefaultConsequenceCompilation() { String consequence = " System.out.println(\"this is a test\");\n "; setupTest( consequence, new HashMap<String, Object>() ); assertNotNull( context.getRule().getConsequence() ); assertTrue( context.getRule().getNamedConsequences().isEmpty() ); assertTrue( context.getRule().getConsequence() instanceof MVELConsequence ); } public void testDefaultConsequenceWithSingleNamedConsequenceCompilation() { String defaultCon = " System.out.println(\"this is a test\");\n "; Map<String, Object> namedConsequences = new HashMap<String, Object>(); String name1 = " System.out.println(\"this is a test name1\");\n "; namedConsequences.put( "name1", name1 ); setupTest( defaultCon, namedConsequences); assertEquals( 1, context.getRule().getNamedConsequences().size() ); assertTrue( context.getRule().getConsequence() instanceof MVELConsequence ); assertTrue( context.getRule().getNamedConsequences().get( "name1" ) instanceof MVELConsequence ); assertNotSame( context.getRule().getConsequence(), context.getRule().getNamedConsequences().get( "name1" ) ); } public void testDefaultConsequenceWithMultipleNamedConsequenceCompilation() { String defaultCon = " System.out.println(\"this is a test\");\n "; Map<String, Object> namedConsequences = new HashMap<String, Object>(); String name1 = " System.out.println(\"this is a test name1\");\n "; namedConsequences.put( "name1", name1 ); String name2 = " System.out.println(\"this is a test name2\");\n "; namedConsequences.put( "name2", name2 ); setupTest( defaultCon, namedConsequences); assertEquals( 2, context.getRule().getNamedConsequences().size() ); assertTrue( context.getRule().getConsequence() instanceof MVELConsequence ); assertTrue( context.getRule().getNamedConsequences().get( "name1" ) instanceof MVELConsequence ); assertTrue( context.getRule().getNamedConsequences().get( "name2" ) instanceof MVELConsequence ); assertNotSame( context.getRule().getConsequence(), context.getRule().getNamedConsequences().get( "name1" ) ); assertNotSame( context.getRule().getConsequence(), context.getRule().getNamedConsequences().get( "name2" ) ); assertNotSame( context.getRule().getNamedConsequences().get( "name1"), context.getRule().getNamedConsequences().get( "name2" ) ); } }