All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.drools.rule.builder.dialect.DialectUtil Maven / Gradle / Ivy

There is a newer version: 10.0.0
Show newest version
package org.drools.rule.builder.dialect;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.drools.commons.jci.readers.*;
import org.drools.compiler.BoundIdentifiers;
import org.drools.compiler.DescrBuildError;
import org.drools.core.util.ClassUtils;
import org.drools.lang.descr.BaseDescr;
import org.drools.rule.Declaration;
import org.drools.rule.builder.RuleBuildContext;
import org.drools.rule.builder.dialect.java.*;
import org.drools.rule.builder.dialect.java.parser.JavaBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaCatchBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaContainerBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaElseBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaFinalBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaForBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaIfBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaInterfacePointsDescr;
import org.drools.rule.builder.dialect.java.parser.JavaLocalDeclarationDescr;
import org.drools.rule.builder.dialect.java.parser.JavaLocalDeclarationDescr.IdentifierDescr;
import org.drools.rule.builder.dialect.java.parser.JavaModifyBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaThrowBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaTryBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaWhileBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaRetractBlockDescr;
import org.drools.rule.builder.dialect.java.parser.JavaUpdateBlockDescr;
import org.drools.rule.builder.dialect.mvel.MVELAnalysisResult;
import org.drools.rule.builder.dialect.mvel.MVELConsequenceBuilder;
import org.drools.rule.builder.dialect.mvel.MVELDialect;
import org.drools.spi.KnowledgeHelper;
import org.mvel2.CompileException;
import org.mvel2.Macro;
import org.mvel2.MacroProcessor;

public final class DialectUtil {

    private static final Pattern NON_ALPHA_REGEX = Pattern.compile("[ -/:-@\\[-`\\{-\\xff]");
    private static final Pattern LINE_BREAK_FINDER = Pattern.compile( "\\r\\n|\\r|\\n" );

    /**
     * Takes a given name and makes sure that its legal and doesn't already exist. If the file exists it increases counter appender untill it is unique.
     * 

* * @param packageName * @param name * @param ext * @return */ public static String getUniqueLegalName(final String packageName, final String name, final String ext, final String prefix, final ResourceReader src) { // replaces all non alphanumeric or $ chars with _ final String newName = prefix + "_" + NON_ALPHA_REGEX.matcher(name).replaceAll("_"); final String fileName = packageName.replace('.', '/') + "/" + newName; if (src == null || !src.isAvailable(fileName + "." + ext)) return newName; // make sure the class name does not exist, if it does increase the counter int counter = -1; while (true) { counter++; final String actualName = fileName + "_" + counter + "." + ext; //MVEL:test null to Fix failing test on org.drools.rule.builder.dialect.mvel.MVELConsequenceBuilderTest.testImperativeCodeError() if (!src.isAvailable(actualName)) break; } // we have duplicate file names so append counter return newName + "_" + counter; } public static String fixBlockDescr(final RuleBuildContext context, final JavaAnalysisResult analysis, Map decls) { // This is a list of all the non container blocks, which initially are in tree form. List blocks = buildBlockDescrs(new ArrayList(), analysis.getBlockDescrs()); return fixBlockDescr(context, analysis, decls, blocks); } public static String fixBlockDescr(final RuleBuildContext context, final JavaAnalysisResult analysis, Map decls, List blocks) { MVELDialect mvel = (MVELDialect) context.getDialect("mvel"); String originalCode = analysis.getAnalyzedExpr(); BoundIdentifiers bindings = analysis.getBoundIdentifiers(); // sorting exit points for correct order iteration Collections.sort(blocks, new Comparator() { public int compare(JavaBlockDescr o1, JavaBlockDescr o2) { return o1.getStart() - o2.getStart(); } }); StringBuilder consequence = new StringBuilder(); int lastAdded = 0; for (JavaBlockDescr block : blocks) { if (block.getEnd() == 0 || block.getEnd() > originalCode.length() ) { // do nothing, it was incorrectly parsed, but this error should be picked up else where continue; } // adding chunk consequence.append(originalCode.substring(lastAdded, block.getStart() - 1)); lastAdded = block.getEnd(); switch (block.getType()) { case MODIFY: case UPDATE: case RETRACT: rewriteDescr(context, originalCode, mvel, consequence, (JavaBlockDescr) block, bindings, decls); break; case ENTRY: case EXIT: case CHANNEL: rewriteInterfacePoint(context, originalCode, consequence, (JavaInterfacePointsDescr) block); break; default: consequence.append(originalCode.substring(block.getStart() - 1, lastAdded)); } } consequence.append(originalCode.substring(lastAdded)); return consequence.toString(); } private static List buildBlockDescrs(List descrs, JavaContainerBlockDescr parentBlock) { for (JavaBlockDescr block : parentBlock.getJavaBlockDescrs()) { if (block instanceof JavaContainerBlockDescr) { buildBlockDescrs(descrs, (JavaContainerBlockDescr) block); } else { descrs.add(block); } } return descrs; } /** * This code is not currently used, it's commented out in method caller. This is because we couldn't * get this to work and will have to wait until MVEL supports genercs (mdp). * * @param context * @param descrs * @param parentBlock * @param originalCode * @param bindings * @param parentVars * @param offset */ public static void setContainerBlockInputs(RuleBuildContext context, List descrs, JavaContainerBlockDescr parentBlock, String originalCode, BoundIdentifiers bindings, Map> parentVars, int offset) { StringBuilder consequence = new StringBuilder(); int lastAdded = 0; // strip blocks, so we can analyse this block with MVEL for (JavaBlockDescr block : parentBlock.getJavaBlockDescrs()) { if (block.getEnd() == 0) { // do nothing, it was incorrectly parsed, but this error should be picked up else where continue; } if (block.getType() == JavaBlockDescr.BlockType.TRY) { // adding previous chunk up to the start of this block consequence.append(originalCode.substring(lastAdded, block.getStart() - 1 - offset)); JavaTryBlockDescr tryDescr = (JavaTryBlockDescr) block; if (tryDescr.getFinal() != null) { lastAdded = tryDescr.getFinal().getEnd() - offset; } else { lastAdded = tryDescr.getCatches().get(tryDescr.getCatches().size() - 1).getEnd() - offset; } stripTryDescr(context, originalCode, consequence, (JavaTryBlockDescr) block, offset); } else if (block.getType() == JavaBlockDescr.BlockType.THROW) { // adding previous chunk up to the start of this block consequence.append(originalCode.substring(lastAdded, block.getStart() - 1 - offset)); JavaThrowBlockDescr throwBlock = (JavaThrowBlockDescr) block; addWhiteSpaces(originalCode, consequence, throwBlock.getStart() - offset, throwBlock.getTextStart() - offset); consequence.append(originalCode.substring(throwBlock.getTextStart() - offset - 1, throwBlock.getEnd() - 1 - offset) + ";"); lastAdded = throwBlock.getEnd() - offset; } else if (block.getType() == JavaBlockDescr.BlockType.IF) { // adding previous chunk up to the start of this block consequence.append(originalCode.substring(lastAdded, block.getStart() - 1 - offset)); JavaIfBlockDescr ifDescr = (JavaIfBlockDescr) block; lastAdded = ifDescr.getEnd() - offset; stripBlockDescr(context, originalCode, consequence, ifDescr, offset); } else if (block.getType() == JavaBlockDescr.BlockType.ELSE) { // adding previous chunk up to the start of this block consequence.append(originalCode.substring(lastAdded, block.getStart() - 1 - offset)); JavaElseBlockDescr elseDescr = (JavaElseBlockDescr) block; lastAdded = elseDescr.getEnd() - offset; stripBlockDescr(context, originalCode, consequence, elseDescr, offset); } else if (block.getType() == JavaBlockDescr.BlockType.WHILE) { // adding previous chunk up to the start of this block consequence.append(originalCode.substring(lastAdded, block.getStart() - 1 - offset)); JavaWhileBlockDescr whileDescr = (JavaWhileBlockDescr) block; lastAdded = whileDescr.getEnd() - offset; stripBlockDescr(context, originalCode, consequence, whileDescr, offset); } else if (block.getType() == JavaBlockDescr.BlockType.FOR) { // adding previous chunk up to the start of this block consequence.append(originalCode.substring(lastAdded, block.getStart() - 1 - offset)); JavaForBlockDescr forDescr = (JavaForBlockDescr) block; lastAdded = forDescr.getEnd() - offset; stripBlockDescr(context, originalCode, consequence, forDescr, offset); } } consequence.append(originalCode.substring(lastAdded)); // We need to do this as MVEL doesn't recognise "modify" MacroProcessor macroProcessor = new MacroProcessor(); Map macros = new HashMap(MVELConsequenceBuilder.macros); macros.put("modify", new Macro() { public String doMacro() { return "with "; } }); macroProcessor.setMacros(macros); String mvelCode = macroProcessor.parse(consequence.toString()); Map> inputs = (Map>) (Map) getInputs(context, mvelCode, bindings, parentVars); inputs.putAll(parentVars); parentBlock.setInputs(inputs); // now go depth, set inputs for each nested container // set inputs for current container blocks to be rewritten for (JavaBlockDescr block : parentBlock.getJavaBlockDescrs()) { if (block.getType() == JavaBlockDescr.BlockType.TRY) { JavaTryBlockDescr tryBlock = (JavaTryBlockDescr) block; setContainerBlockInputs(context, descrs, tryBlock, originalCode.substring(tryBlock.getTextStart() - offset, tryBlock.getEnd() - 1 - offset), bindings, inputs, tryBlock.getTextStart()); for (JavaCatchBlockDescr catchBlock : tryBlock.getCatches()) { setContainerBlockInputs(context, descrs, catchBlock, catchBlock.getClause() + "=null;" + originalCode.substring(catchBlock.getTextStart() - offset, catchBlock.getEnd() - 1 - offset), bindings, inputs, tryBlock.getTextStart()); } if (tryBlock.getFinal() != null) { JavaFinalBlockDescr finalBlock = (JavaFinalBlockDescr) tryBlock.getFinal(); setContainerBlockInputs(context, descrs, finalBlock, originalCode.substring(finalBlock.getTextStart() - offset, finalBlock.getEnd() - 1 - offset), bindings, inputs, tryBlock.getTextStart()); } } else if (block.getType() == JavaBlockDescr.BlockType.IF) { JavaIfBlockDescr ifBlock = (JavaIfBlockDescr) block; int adjustBlock = (originalCode.charAt(ifBlock.getTextStart() - offset - 1) == '{') ? 0 : 1; setContainerBlockInputs(context, descrs, ifBlock, originalCode.substring(ifBlock.getTextStart() - offset + adjustBlock, ifBlock.getEnd() - 1 - offset - adjustBlock), bindings, inputs, ifBlock.getTextStart()); } else if (block.getType() == JavaBlockDescr.BlockType.ELSE) { JavaElseBlockDescr elseBlock = (JavaElseBlockDescr) block; int adjustBlock = (originalCode.charAt(elseBlock.getTextStart() - offset - 1) == '{') ? 0 : 1; setContainerBlockInputs(context, descrs, elseBlock, originalCode.substring(elseBlock.getTextStart() - offset + adjustBlock, elseBlock.getEnd() - 1 - offset - adjustBlock), bindings, inputs, elseBlock.getTextStart()); } else if (block.getType() == JavaBlockDescr.BlockType.WHILE) { JavaWhileBlockDescr whileBlock = (JavaWhileBlockDescr) block; int adjustBlock = (originalCode.charAt(whileBlock.getTextStart() - offset - 1) == '{') ? 0 : 1; setContainerBlockInputs(context, descrs, whileBlock, originalCode.substring(whileBlock.getTextStart() - offset + adjustBlock, whileBlock.getEnd() - 1 - offset - adjustBlock), bindings, inputs, whileBlock.getTextStart()); } else if (block.getType() == JavaBlockDescr.BlockType.FOR) { JavaForBlockDescr forBlock = (JavaForBlockDescr) block; int adjustBlock = (originalCode.charAt(forBlock.getTextStart() - offset - 1) == '{') ? 0 : 1; setContainerBlockInputs(context, descrs, forBlock, originalCode.substring(forBlock.getStartParen() - offset, forBlock.getInitEnd() - offset) + originalCode.substring(forBlock.getTextStart() - offset + adjustBlock, forBlock.getEnd() - 1 - offset - adjustBlock), bindings, inputs, forBlock.getTextStart() - (forBlock.getInitEnd() - forBlock.getStartParen())); } else { block.setInputs(inputs); // each block to be rewritten now knows it's own variables descrs.add(block); } } } private static Map> getInputs(final RuleBuildContext context, String code, BoundIdentifiers bindings, Map> parentVars) { MVELDialect mvel = (MVELDialect) context.getDialect("mvel"); MVELAnalysisResult mvelAnalysis = null; try { mvelAnalysis = (MVELAnalysisResult) mvel.analyzeBlock(context, context.getRuleDescr(), null, code, bindings, parentVars, "drools", KnowledgeHelper.class); } catch (Exception e) { // swallow this as the error will be reported else where } return (mvelAnalysis != null) ? mvelAnalysis.getMvelVariables() : new HashMap>(); } private static void addWhiteSpaces(String original, StringBuilder consequence, int start, int end) { for (int i = start; i < end; i++) { switch (original.charAt(i)) { case '\n': case '\r': case '\t': case ' ': consequence.append(original.charAt(i)); break; default: consequence.append(" "); } } } private static void stripTryDescr(RuleBuildContext context, String originalCode, StringBuilder consequence, JavaTryBlockDescr block, int offset) { addWhiteSpaces(originalCode, consequence, consequence.length(), block.getTextStart() - offset); addWhiteSpaces(originalCode, consequence, consequence.length(), block.getEnd() - offset); for (JavaCatchBlockDescr catchBlock : block.getCatches()) { addWhiteSpaces(originalCode, consequence, consequence.length(), catchBlock.getTextStart() - offset); addWhiteSpaces(originalCode, consequence, consequence.length(), catchBlock.getEnd() - offset); } if (block.getFinal() != null) { addWhiteSpaces(originalCode, consequence, consequence.length(), block.getFinal().getTextStart() - offset); addWhiteSpaces(originalCode, consequence, consequence.length(), block.getFinal().getEnd() - offset); } } private static void stripBlockDescr(RuleBuildContext context, String originalCode, StringBuilder consequence, JavaBlockDescr block, int offset) { addWhiteSpaces(originalCode, consequence, consequence.length(), block.getEnd() - offset); } private static void stripElseDescr(RuleBuildContext context, String originalCode, StringBuilder consequence, JavaElseBlockDescr block, int offset) { addWhiteSpaces(originalCode, consequence, consequence.length(), block.getEnd() - offset); } @SuppressWarnings("unchecked") private static 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() == JavaBlockDescr.BlockType.EXIT) { consequence.append("ExitPoint( "); } else if (ep.getType() == JavaBlockDescr.BlockType.ENTRY) { consequence.append("EntryPoint( "); } else if (ep.getType() == JavaBlockDescr.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 static boolean rewriteDescr(final RuleBuildContext context, final String originalCode, final MVELDialect mvel, final StringBuilder consequence, final JavaBlockDescr d, final BoundIdentifiers bindings, final Map decls) { if ( d.getEnd() == 0 ) { // do nothing, it was incorrectly parsed, but this error should be picked up else where return false; } boolean typeSafety = context.isTypesafe(); context.setTypesafe( false ); // we have to analyse in dynamic mode for now, as we cannot safely determine all input vars Map> localTypes = d.getInputs(); if( d.getInScopeLocalVars() != null && ! d.getInScopeLocalVars().isEmpty() ) { localTypes = new HashMap>( d.getInputs() != null ? d.getInputs() : Collections.EMPTY_MAP ); for( JavaLocalDeclarationDescr local : d.getInScopeLocalVars() ) { // these are variables declared in the code itself that are in the scope for this expression try { Class type = context.getDialect( "java" ).getPackageRegistry().getTypeResolver().resolveType( local.getType() ); for( IdentifierDescr id : local.getIdentifiers() ) { localTypes.put( id.getIdentifier(), type ); } } catch ( ClassNotFoundException e ) { context.getErrors().add( new DescrBuildError( context.getRuleDescr(), context.getParentDescr(), null, "Unable to resolve type " + local.getType() + ":\n" + e.getMessage() ) ); } } } MVELAnalysisResult mvelAnalysis = ( MVELAnalysisResult ) mvel.analyzeBlock( context, context.getRuleDescr(), mvel.getInterceptors(), d.getTargetExpression(), bindings, localTypes, "drools", KnowledgeHelper.class); context.setTypesafe( typeSafety ); if ( mvelAnalysis == null ) { // something bad happened, issue already logged in errors return false; } Class ret = mvelAnalysis.getReturnType(); 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.getTargetExpression() + "\n" ) ); return false; } // adding modify expression String retString = ClassUtils.canonicalName( ret ); String declrString; if (d.getTargetExpression().charAt( 0 ) == '(' ) { declrString = d.getTargetExpression().substring( 1,d.getTargetExpression().length() -1 ).trim(); } else { declrString = d.getTargetExpression(); } String obj = declrString; Declaration declr = decls.get( declrString ); consequence.append( "{ " ); if ( declr == null ) { obj = "__obj__"; consequence.append( retString ); consequence.append( " " ); consequence.append( obj); consequence.append( " = " ); consequence.append( d.getTargetExpression() ); consequence.append( "; " ); } if ( declr == null || declr.isInternalFact() ) { consequence.append( "org.drools.FactHandle " ); consequence.append( obj ); consequence.append( "__Handle2__ = drools.getFactHandle(" ); consequence.append( obj ); consequence.append( ");" ); } // the following is a hack to preserve line breaks. String originalBlock = originalCode.substring( d.getStart() - 1, d.getEnd() ); if ( d instanceof JavaModifyBlockDescr ) { rewriteModifyDescr( context, d, originalBlock, consequence, declr, obj ); } else if ( d instanceof JavaUpdateBlockDescr ) { rewriteUpdateDescr( d, originalBlock, consequence, declr, obj ); } else if ( d instanceof JavaRetractBlockDescr ) { rewriteRetractDescr( d, originalBlock, consequence, declr, obj ); } return declr != null; } private static boolean rewriteModifyDescr(final RuleBuildContext context, JavaBlockDescr d, String originalBlock, StringBuilder consequence, Declaration declr, String obj) { int end = originalBlock.indexOf("{"); if (end == -1) { // no block context.getErrors().add(new DescrBuildError(context.getParentDescr(), context.getRuleDescr(), null, "Block missing after modify" + d.getTargetExpression() + " ?\n")); return false; } addLineBreaks(consequence, originalBlock.substring(0, end)); int start = end + 1; // adding each of the expressions: for (String exprStr : ((JavaModifyBlockDescr) 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)); if (declr != null && !declr.isInternalFact()) { consequence.append("drools.update( " + obj + "__Handle__ ); }"); } else { consequence.append("drools.update( " + obj + "__Handle2__ ); }"); } return declr != null; } private static boolean rewriteUpdateDescr(JavaBlockDescr d, String originalBlock, StringBuilder consequence, Declaration declr, String obj) { if (declr != null && !declr.isInternalFact()) { consequence.append("drools.update( " + obj + "__Handle__ ); }"); } else { consequence.append("drools.update( " + obj + "__Handle2__ ); }"); } return declr != null; } private static boolean rewriteRetractDescr(JavaBlockDescr d, String originalBlock, StringBuilder consequence, Declaration declr, String obj) { if (declr != null && !declr.isInternalFact()) { consequence.append("drools.retract( " + obj + "__Handle__ ); }"); } else { consequence.append("drools.retract( " + obj + "__Handle2__ ); }"); } return declr != null; } /** * @param consequence * @param chunk */ private static void addLineBreaks(StringBuilder consequence, String chunk) { Matcher m = LINE_BREAK_FINDER.matcher(chunk); while (m.find()) { consequence.append("\n"); } } public static void copyErrorLocation(Exception e, BaseDescr descr) { if (e instanceof CompileException) { CompileException compileException = (CompileException)e; compileException.setLineNumber(descr.getLine()); compileException.setColumn(descr.getColumn()); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy