/* * Copyright 2006 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.drools.rule.builder.dialect.java; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.drools.compiler.DescrBuildError; import org.drools.compiler.Dialect; import org.drools.core.util.ClassUtils; import org.drools.lang.descr.RuleDescr; import org.drools.rule.Declaration; import org.drools.rule.builder.ConsequenceBuilder; import org.drools.rule.builder.RuleBuildContext; import org.drools.rule.builder.dialect.java.parser.JavaBlockDescr; import org.drools.rule.builder.dialect.java.parser.JavaInterfacePointsDescr; import org.drools.rule.builder.dialect.java.parser.JavaModifyBlockDescr; import org.drools.rule.builder.dialect.java.parser.JavaBlockDescr.BlockType; import org.drools.rule.builder.dialect.mvel.MVELDialect; import org.drools.spi.PatternExtractor; import org.mvel2.compiler.ExecutableStatement; /** * @author etirelli * */ public class JavaConsequenceBuilder extends AbstractJavaRuleBuilder implements ConsequenceBuilder { private final Pattern lineBreakFinder = Pattern.compile( "\\r\\n|\\r|\\n" ); /* (non-Javadoc) * @see org.drools.semantics.java.builder.ConsequenceBuilder#buildConsequence(org.drools.semantics.java.builder.BuildContext, org.drools.semantics.java.builder.BuildUtils, org.drools.lang.descr.RuleDescr) */ public void build(final RuleBuildContext context, String consequenceName) { // pushing consequence LHS into the stack for variable resolution context.getBuildStack().push( context.getRule().getLhs() ); final String className = consequenceName + "Consequence"; final RuleDescr ruleDescr = context.getRuleDescr(); Map<String, Class< ? >> variables = context.getDeclarationResolver().getDeclarationClasses( context.getRule() ); Dialect.AnalysisResult analysis = context.getDialect().analyzeBlock( context, ruleDescr, (String) ruleDescr.getConsequence(), new Map[]{variables, context.getPackageBuilder().getGlobals()} ); if ( analysis == null ) { // not possible to get the analysis results return; } String fixedConsequence = this.fixBlockDescr( context, (JavaAnalysisResult) analysis, ( "default".equals( consequenceName ) ) ? (String) ruleDescr.getConsequence() : (String) ruleDescr.getNamedConsequences().get( consequenceName ) ); if ( fixedConsequence == null ) { // not possible to rewrite the modify blocks return; } fixedConsequence = ((JavaDialect) context.getDialect()).getKnowledgeHelperFixer().fix( fixedConsequence ); final List<String>[] usedIdentifiers = (List<String>[]) analysis.getBoundIdentifiers(); final Declaration[] declarations = new Declaration[usedIdentifiers[0].size()]; for ( int i = 0, size = usedIdentifiers[0].size(); i < size; i++ ) { declarations[i] = context.getDeclarationResolver().getDeclaration( context.getRule(), (String) usedIdentifiers[0].get( i ) ); } final Map<String, Object> map = createVariableContext( className, fixedConsequence, context, declarations, null, (String[]) usedIdentifiers[1].toArray( new String[usedIdentifiers[1].size()] ) ); map.put( "consequenceName", consequenceName ); // Must use the rule declarations, so we use the same order as used in the generated invoker final List list = Arrays.asList( context.getRule().getDeclarations() ); //final int[] indexes = new int[declarations.length]; final Integer[] indexes = new Integer[declarations.length]; final Boolean[] notPatterns = new Boolean[declarations.length]; for ( int i = 0, length = declarations.length; i < length; i++ ) { indexes[i] = new Integer( list.indexOf( declarations[i] ) ); notPatterns[i] = (declarations[i].getExtractor() instanceof PatternExtractor) ? Boolean.FALSE : Boolean.TRUE ; if ( (indexes[i]).intValue() == -1 ) { context.getErrors().add( new DescrBuildError( context.getParentDescr(), ruleDescr, null, "Internal Error : Unable to find declaration in list while generating the consequence invoker" ) ); } } map.put( "indexes", indexes ); map.put( "notPatterns", notPatterns ); generatTemplates( "consequenceMethod", "consequenceInvoker", context, className, map, context.getRule(), ruleDescr ); // popping Rule.getLHS() from the build stack context.getBuildStack().pop(); } protected String fixBlockDescr(final RuleBuildContext context, final JavaAnalysisResult analysis, final String originalCode) { MVELDialect mvel = (MVELDialect) context.getDialect( "mvel" ); // sorting exit points for correct order iteration List<JavaBlockDescr> blocks = analysis.getBlockDescrs(); Collections.sort( blocks, new Comparator<JavaBlockDescr>() { public int compare(JavaBlockDescr o1, JavaBlockDescr o2) { return o1.getStart() - o2.getStart(); } } ); StringBuilder consequence = new StringBuilder(); int lastAdded = 0; for ( JavaBlockDescr block : blocks ) { // adding chunk consequence.append( originalCode.substring( lastAdded, block.getStart() - 1 ) ); lastAdded = block.getEnd(); switch ( block.getType() ) { case MODIFY : rewriteModify( context, originalCode, mvel, consequence, (JavaModifyBlockDescr) block ); break; case ENTRY : case EXIT : case CHANNEL : rewriteInterfacePoint( context, originalCode, consequence, (JavaInterfacePointsDescr) block ); break; } } consequence.append( originalCode.substring( lastAdded ) ); return consequence.toString(); } @SuppressWarnings("unchecked") private void rewriteInterfacePoint(final RuleBuildContext context, final String originalCode, final StringBuilder consequence, final JavaInterfacePointsDescr ep) { // rewriting it for proper exitPoints access consequence.append( "drools.get" ); if ( ep.getType() == BlockType.EXIT ) { consequence.append( "ExitPoint( " ); } else if( ep.getType() == BlockType.ENTRY ) { consequence.append( "EntryPoint( " ); } else if( ep.getType() == BlockType.CHANNEL ) { consequence.append( "Channel( " ); } else { context.getErrors().add( new DescrBuildError( context.getParentDescr(), context.getRuleDescr(), ep, "Unable to rewrite code block: " + ep + "\n" ) ); return; } consequence.append( ep.getId() ); consequence.append( " )" ); // the following is a hack to preserve line breaks. String originalBlock = originalCode.substring( ep.getStart() - 1, ep.getEnd() ); int end = originalBlock.indexOf( "]" ); addLineBreaks( consequence, originalBlock.substring( 0, end ) ); } private void rewriteModify(final RuleBuildContext context, final String originalCode, MVELDialect mvel, StringBuilder consequence, JavaModifyBlockDescr d) { Map<String, Class< ? >> variables = context.getDeclarationResolver().getDeclarationClasses( context.getRule() ); Dialect.AnalysisResult mvelAnalysis = mvel.analyzeBlock( context, context.getRuleDescr(), mvel.getInterceptors(), d.getModifyExpression(), new Map[]{variables, context.getPackageBuilder().getGlobals()}, null ); final ExecutableStatement expr = (ExecutableStatement) mvel.compile( d.getModifyExpression(), mvelAnalysis, mvel.getInterceptors(), null, null, context ); Class ret = expr.getKnownEgressType(); if ( ret == null ) { // not possible to evaluate expression return value context.getErrors().add( new DescrBuildError( context.getParentDescr(), context.getRuleDescr(), originalCode, "Unable to determine the resulting type of the expression: " + d.getModifyExpression() + "\n" ) ); return; } if ( d.getEnd() <= 0 ) { // not correctly parse context.getErrors().add( new DescrBuildError( context.getParentDescr(), context.getRuleDescr(), originalCode, "Incorrect syntax for expression: " + d.getModifyExpression() + "\n" ) ); return; } String retString = ClassUtils.canonicalName( ret ); // adding modify expression consequence.append( "{ " ); consequence.append( retString ); consequence.append( " __obj__ = (" ); consequence.append( retString ); consequence.append( ") " ); consequence.append( d.getModifyExpression() ); consequence.append( "; " ); // the following is a hack to preserve line breaks. String originalBlock = originalCode.substring( d.getStart() - 1, d.getEnd() ); int end = originalBlock.indexOf( "{" ); if( end == -1 ){ // no block context.getErrors().add( new DescrBuildError( context.getParentDescr(), context.getRuleDescr(), null, "Block missing after modify" + d.getModifyExpression() + " ?\n" ) ); return; } addLineBreaks( consequence, originalBlock.substring( 0, end ) ); int start = end + 1; // adding each of the expressions: for ( String exprStr : d.getExpressions() ) { end = originalBlock.indexOf( exprStr, start ); addLineBreaks( consequence, originalBlock.substring( start, end ) ); consequence.append( "__obj__." ); consequence.append( exprStr ); consequence.append( "; " ); start = end + exprStr.length(); } // adding the modifyInsert call: addLineBreaks( consequence, originalBlock.substring( end ) ); consequence.append( "update( __obj__ ); }" ); } /** * @param consequence * @param chunk */ private void addLineBreaks(StringBuilder consequence, String chunk) { Matcher m = lineBreakFinder.matcher( chunk ); while ( m.find() ) { consequence.append( "\n" ); } } }