From a6cc3aadf958fa3ca8a192cdcc6d490b9474e624 Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Wed, 11 May 2022 00:41:17 +0900 Subject: [PATCH] Enhance test/grammar coverage. function import, global, exists, not (#6) --- .../main/antlr4/org/drools/parser/DRLLexer.g4 | 2 + .../antlr4/org/drools/parser/DRLParser.g4 | 38 ++- .../org/drools/parser/DRLParserHelper.java | 26 ++ .../org/drools/parser/DRLParserWrapper.java | 5 + .../org/drools/parser/DRLVisitorImpl.java | 97 ++++++- .../java/org/drools/parser/StringUtils.java | 20 ++ .../org/drools/parser/MiscDRLParserTest.java | 237 +++++++++++++++++- .../resources/org/drools/parser/globals.drl | 28 +++ .../org/drools/parser/test_FunctionImport.drl | 27 ++ 9 files changed, 462 insertions(+), 18 deletions(-) create mode 100644 drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/StringUtils.java create mode 100644 drools-drl/drools-drl10-parser/src/test/resources/org/drools/parser/globals.drl create mode 100644 drools-drl/drools-drl10-parser/src/test/resources/org/drools/parser/test_FunctionImport.drl diff --git a/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLLexer.g4 b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLLexer.g4 index 732a0d3bcc9..0f4ee3cc48f 100644 --- a/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLLexer.g4 +++ b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLLexer.g4 @@ -32,8 +32,10 @@ WHEN : 'when'; THEN : 'then'; END : 'end'; +EXISTS : 'exists'; NOT : 'not'; IN : 'in'; +FROM : 'from'; SALIENCE : 'salience'; ENABLED : 'enabled'; diff --git a/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLParser.g4 b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLParser.g4 index 31bf8c80eed..2818590bd59 100644 --- a/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLParser.g4 +++ b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLParser.g4 @@ -33,7 +33,11 @@ lhsUnary : ( lhsExists namedConsequence? ) SEMI? ; */ -lhsUnary : lhsPatternBind ; +lhsUnary : ( + lhsExists + | lhsNot + | lhsPatternBind + ) ; lhsPatternBind : label? ( LPAREN lhsPattern (OR lhsPattern)* RPAREN | lhsPattern ) ; /* @@ -41,7 +45,7 @@ lhsPattern : xpathPrimary (OVER patternFilter)? | ( QUESTION? qualifiedIdentifier LPAREN positionalConstraints? constraints? RPAREN (OVER patternFilter)? (FROM patternSource)? ) ; */ -lhsPattern : QUESTION? objectType=qualifiedName LPAREN positionalConstraints? constraints? RPAREN ; +lhsPattern : QUESTION? objectType=qualifiedName LPAREN (positionalConstraints? constraints? ) RPAREN (FROM patternSource)? ; positionalConstraints : constraint (COMMA constraint)* SEMI ; constraints : constraint (COMMA constraint)* ; constraint : label? ( nestedConstraint | conditionalOrExpression ) ; @@ -60,6 +64,34 @@ drlExpression : conditionalExpression ( op=assignmentOperator right=drlExpressio conditionalExpression : left=conditionalOrExpression ternaryExpression? ; ternaryExpression : QUESTION ts=drlExpression COLON fs=drlExpression ; +/* + patternSource := FROM + ( fromAccumulate + | fromCollect + | fromEntryPoint + | fromWindow + | fromExpression ) +*/ +patternSource : fromExpression ; +fromExpression : conditionalOrExpression ; + +/* + lhsExists := EXISTS + ( (LEFT_PAREN (or_key|and_key))=> lhsOr // prevents '((' for prefixed and/or + | LEFT_PAREN lhsOr RIGHT_PAREN + | lhsPatternBind + ) +*/ +lhsExists : EXISTS lhsPatternBind ; +/* + lhsNot := NOT + ( (LEFT_PAREN (or_key|and_key))=> lhsOr // prevents '((' for prefixed and/or + | LEFT_PAREN lhsOr RIGHT_PAREN + | lhsPatternBind + ) +*/ +lhsNot : NOT lhsPatternBind ; + rhs : blockStatement+ ; stringId : ( IDENTIFIER | STRING_LITERAL ) ; @@ -76,7 +108,7 @@ drlAnnotation : AT name=qualifiedName drlArguments? ; attributes : attribute ( COMMA? attribute )* ; attribute : ( 'salience' DECIMAL_LITERAL ) - | ( 'enabled' | 'no-loop' | 'auto-focus' | 'lock-on-active' | 'refract' | 'direct' ) BOOLEAN? + | ( 'enabled' | 'no-loop' | 'auto-focus' | 'lock-on-active' | 'refract' | 'direct' ) BOOL_LITERAL? | ( 'agenda-group' | 'activation-group' | 'ruleflow-group' | 'date-effective' | 'date-expires' | 'dialect' ) STRING_LITERAL | 'calendars' STRING_LITERAL ( COMMA STRING_LITERAL )* | 'timer' ( DECIMAL_LITERAL | TEXT ) diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserHelper.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserHelper.java index 6c39a733235..feabbe39207 100644 --- a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserHelper.java +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserHelper.java @@ -3,8 +3,11 @@ import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.tree.ErrorNode; import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.TerminalNode; import org.drools.drl.ast.descr.PackageDescr; public class DRLParserHelper { @@ -46,4 +49,27 @@ else if (token.getLine() == row && start < col && stop >= col) } return null; } + + /** + * RuleContext.getText() connects all nodes including ErrorNode. This method appends texts only from valid nodes + */ + public static String getTextWithoutErrorNode(ParseTree tree) { + if (tree.getChildCount() == 0) { + return ""; + } + + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < tree.getChildCount(); i++) { + ParseTree child = tree.getChild(i); + if (!(child instanceof ErrorNode)) { + if (child instanceof TerminalNode) { + builder.append(child.getText()); + } else { + builder.append(getTextWithoutErrorNode(child)); + } + } + } + + return builder.toString(); + } } diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserWrapper.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserWrapper.java index 4b948cffca5..251c918190a 100644 --- a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserWrapper.java +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserWrapper.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.antlr.v4.runtime.tree.ParseTree; import org.drools.drl.ast.descr.PackageDescr; @@ -39,6 +40,10 @@ public List getErrors() { return errors; } + public List getErrorMessages() { + return errors.stream().map(DRLParserError::getMessage).collect(Collectors.toList()); + } + public boolean hasErrors() { return !errors.isEmpty(); } diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLVisitorImpl.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLVisitorImpl.java index 4c816406889..d93c707abe7 100644 --- a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLVisitorImpl.java +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLVisitorImpl.java @@ -1,18 +1,25 @@ package org.drools.parser; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.Objects; import java.util.stream.Collectors; +import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.drools.drl.ast.descr.*; +import static org.drools.parser.DRLParserHelper.getTextWithoutErrorNode; +import static org.drools.parser.StringUtils.safeStripStringDelimiters; + public class DRLVisitorImpl extends DRLParserBaseVisitor { private final PackageDescr packageDescr = new PackageDescr(); private RuleDescr currentRule; private PatternDescr currentPattern; + private Deque currentConstructStack = new ArrayDeque<>(); // e.g. whole LHS, not, exist @Override public Object visitCompilationUnit(DRLParser.CompilationUnitContext ctx) { @@ -21,7 +28,7 @@ public Object visitCompilationUnit(DRLParser.CompilationUnitContext ctx) { @Override public Object visitPackagedef(DRLParser.PackagedefContext ctx) { - packageDescr.setName(ctx.name.getText()); + packageDescr.setName(getTextWithoutErrorNode(ctx.name)); return super.visitPackagedef(ctx); } @@ -33,20 +40,32 @@ public Object visitUnitdef(DRLParser.UnitdefContext ctx) { @Override public Object visitGlobaldef(DRLParser.GlobaldefContext ctx) { - packageDescr.addGlobal(new GlobalDescr(ctx.IDENTIFIER().getText(), ctx.type().getText())); + GlobalDescr globalDescr = new GlobalDescr(ctx.IDENTIFIER().getText(), ctx.type().getText()); + populateStartEnd(globalDescr, ctx); + packageDescr.addGlobal(globalDescr); return super.visitGlobaldef(ctx); } @Override public Object visitImportdef(DRLParser.ImportdefContext ctx) { - String imp = ctx.qualifiedName().getText() + (ctx.MUL() != null ? ".*" : ""); - packageDescr.addImport(new ImportDescr(imp)); + String target = ctx.qualifiedName().getText() + (ctx.MUL() != null ? ".*" : ""); + if (ctx.FUNCTION() != null || ctx.STATIC() != null) { + FunctionImportDescr functionImportDescr = new FunctionImportDescr(); + functionImportDescr.setTarget(target); + populateStartEnd(functionImportDescr, ctx); + packageDescr.addFunctionImport(functionImportDescr); + } else { + ImportDescr importDescr = new ImportDescr(); + importDescr.setTarget(target); + populateStartEnd(importDescr, ctx); + packageDescr.addImport(importDescr); + } return super.visitImportdef(ctx); } @Override public Object visitRuledef(DRLParser.RuledefContext ctx) { - currentRule = new RuleDescr(ctx.name.getText()); + currentRule = new RuleDescr(safeStripStringDelimiters(ctx.name.getText())); currentRule.setConsequence(ctx.rhs().getText()); packageDescr.addRule(currentRule); @@ -55,14 +74,33 @@ public Object visitRuledef(DRLParser.RuledefContext ctx) { return result; } + @Override + public Object visitLhs(DRLParser.LhsContext ctx) { + currentConstructStack.push(currentRule.getLhs()); + try { + return super.visitLhs(ctx); + } finally { + currentConstructStack.pop(); + } + } + @Override public Object visitLhsPatternBind(DRLParser.LhsPatternBindContext ctx) { + // TODO: this logic will likely be split to visitLhsPattern if (ctx.lhsPattern().size() == 1) { - currentPattern = new PatternDescr(ctx.lhsPattern(0).objectType.getText()); + DRLParser.LhsPatternContext lhsPatternCtx = ctx.lhsPattern(0); + currentPattern = new PatternDescr(lhsPatternCtx.objectType.getText()); if (ctx.label() != null) { currentPattern.setIdentifier(ctx.label().IDENTIFIER().getText()); } - currentRule.getLhs().addDescr(currentPattern); + if (lhsPatternCtx.patternSource() != null) { + String expression = lhsPatternCtx.patternSource().getText(); + FromDescr from = new FromDescr(); + from.setDataSource(new MVELExprDescr(expression)); + from.setResource(currentPattern.getResource()); + currentPattern.setSource(from); + } + currentConstructStack.peek().addDescr(currentPattern); } Object result = super.visitLhsPatternBind(ctx); currentPattern = null; @@ -72,9 +110,11 @@ public Object visitLhsPatternBind(DRLParser.LhsPatternBindContext ctx) { @Override public Object visitConstraint(DRLParser.ConstraintContext ctx) { Object constraint = super.visitConstraint(ctx); - ExprConstraintDescr constr = new ExprConstraintDescr( constraint.toString() ); - constr.setType( ExprConstraintDescr.Type.NAMED ); - currentPattern.addConstraint( constr ); + if (constraint != null) { + ExprConstraintDescr constr = new ExprConstraintDescr(constraint.toString()); + constr.setType(ExprConstraintDescr.Type.NAMED); + currentPattern.addConstraint(constr); + } return null; } @@ -89,7 +129,11 @@ public Object visitExpression(DRLParser.ExpressionContext ctx) { @Override public Object visitIdentifier(DRLParser.IdentifierContext ctx) { - return ctx.IDENTIFIER().getText(); + if (ctx.IDENTIFIER() == null) { + return ""; + } else { + return ctx.IDENTIFIER().getText(); + } } @Override @@ -124,7 +168,38 @@ public Object visitAttribute(DRLParser.AttributeContext ctx) { return super.visitAttribute(ctx); } + @Override + public Object visitLhsExists(DRLParser.LhsExistsContext ctx) { + ExistsDescr existsDescr = new ExistsDescr(); + currentConstructStack.peek().addDescr(existsDescr); + currentConstructStack.push(existsDescr); + try { + return super.visitLhsExists(ctx); + } finally { + currentConstructStack.pop(); + } + } + + @Override + public Object visitLhsNot(DRLParser.LhsNotContext ctx) { + NotDescr notDescr = new NotDescr(); + currentConstructStack.peek().addDescr(notDescr); + currentConstructStack.push(notDescr); + try { + return super.visitLhsNot(ctx); + } finally { + currentConstructStack.pop(); + } + } + public PackageDescr getPackageDescr() { return packageDescr; } + + private void populateStartEnd(BaseDescr descr, ParserRuleContext ctx) { + descr.setStartCharacter(ctx.getStart().getStartIndex()); + // TODO: Current DRL6Parser adds +1 for EndCharacter but it doesn't look reasonable. At the moment, I don't add. Instead, I fix unit tests. + // I will revisit if this is the right approach. + descr.setEndCharacter(ctx.getStop().getStopIndex()); + } } diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/StringUtils.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/StringUtils.java new file mode 100644 index 00000000000..22f8adf4c8a --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/StringUtils.java @@ -0,0 +1,20 @@ +package org.drools.parser; + +/** + * will be merged in drools-util + */ +public class StringUtils { + + private StringUtils() { + } + + public static String safeStripStringDelimiters(String value) { + if (value != null) { + value = value.trim(); + if (value.length() >= 2 && value.startsWith("\"") && value.endsWith("\"")) { + value = value.substring(1, value.length() - 1); + } + } + return value; + } +} diff --git a/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/MiscDRLParserTest.java b/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/MiscDRLParserTest.java index 9a6b31f2b7f..caabed15927 100644 --- a/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/MiscDRLParserTest.java +++ b/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/MiscDRLParserTest.java @@ -1,7 +1,23 @@ package org.drools.parser; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + import junit.framework.TestCase; +import org.drools.drl.ast.descr.FromDescr; +import org.drools.drl.ast.descr.FunctionImportDescr; +import org.drools.drl.ast.descr.GlobalDescr; +import org.drools.drl.ast.descr.ImportDescr; +import org.drools.drl.ast.descr.NotDescr; import org.drools.drl.ast.descr.PackageDescr; +import org.drools.drl.ast.descr.PatternDescr; +import org.drools.drl.ast.descr.RuleDescr; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -24,6 +40,19 @@ protected void tearDown() throws Exception { super.tearDown(); } + private PackageDescr parseResource(final String filename) throws Exception { + Path path = Paths.get(getClass().getResource(filename).toURI()); + final StringBuilder sb = new StringBuilder(); + try (BufferedReader br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + for (String line; (line = br.readLine()) != null; ) { + sb.append(line + "\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return parser.parse(sb.toString()); + } + @Test public void testPackage() throws Exception { final String source = "package foo.bar.baz"; @@ -36,8 +65,7 @@ public void testPackageWithErrorNode() throws Exception { final String source = "package 12 foo.bar.baz"; final PackageDescr pkg = parser.parse(source); assertTrue(parser.hasErrors()); - // getText() combines an ErrorNode "12" so the result is different from DRL6Parser. - assertEquals("12foo.bar.baz", pkg.getName()); + assertEquals("foo.bar.baz", pkg.getName()); } @Test @@ -45,7 +73,208 @@ public void testPackageWithAllErrorNode() throws Exception { final String source = "package 12 12312 231"; final PackageDescr pkg = parser.parse(source); assertTrue(parser.hasErrors()); - // NPE inside DRLVisitorImpl.visitIdentifier(). So pkg is null. Different from DRL6Parser. - assertNull(pkg); + assertEquals("", pkg.getName()); + } + + @Test + public void testCompilationUnit() throws Exception { + final String source = "package foo; import com.foo.Bar; import com.foo.Baz;"; + PackageDescr pkg = parser.parse(source); + assertFalse(parser.getErrors().toString(), + parser.hasErrors()); + assertEquals("foo", + pkg.getName()); + assertEquals(2, + pkg.getImports().size()); + ImportDescr impdescr = pkg.getImports().get(0); + assertEquals("com.foo.Bar", + impdescr.getTarget()); + assertEquals(source.indexOf("import " + impdescr.getTarget()), + impdescr.getStartCharacter()); + assertEquals(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length(), + impdescr.getEndCharacter()); + + impdescr = pkg.getImports().get(1); + assertEquals("com.foo.Baz", + impdescr.getTarget()); + assertEquals(source.indexOf("import " + impdescr.getTarget()), + impdescr.getStartCharacter()); + assertEquals(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length(), + impdescr.getEndCharacter()); + } + + @Test + public void testFunctionImport() throws Exception { + final String source = "package foo\n" + + "import function java.lang.Math.max\n" + + "import function java.lang.Math.min;\n" + + "import foo.bar.*\n" + + "import baz.Baz"; + PackageDescr pkg = parser.parse(source); + assertFalse(parser.getErrors().toString(), + parser.hasErrors()); + assertEquals("foo", + pkg.getName()); + assertEquals(2, + pkg.getImports().size()); + ImportDescr impdescr = pkg.getImports().get(0); + assertEquals("foo.bar.*", + impdescr.getTarget()); + assertEquals(source.indexOf("import " + impdescr.getTarget()), + impdescr.getStartCharacter()); + + assertEquals(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length() - 1, + impdescr.getEndCharacter()); + + impdescr = pkg.getImports().get(1); + assertEquals("baz.Baz", + impdescr.getTarget()); + assertEquals(source.indexOf("import " + impdescr.getTarget()), + impdescr.getStartCharacter()); + assertEquals(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length() - 1, + impdescr.getEndCharacter()); + + assertEquals(2, + pkg.getFunctionImports().size()); + impdescr = pkg.getFunctionImports().get(0); + assertEquals("java.lang.Math.max", + impdescr.getTarget()); + assertEquals(source.indexOf("import function " + impdescr.getTarget()), + impdescr.getStartCharacter()); + assertEquals(source.indexOf("import function " + impdescr.getTarget()) + ("import function " + impdescr.getTarget()).length() - 1, + impdescr.getEndCharacter()); + + impdescr = pkg.getFunctionImports().get(1); + assertEquals("java.lang.Math.min", + impdescr.getTarget()); + assertEquals(source.indexOf("import function " + impdescr.getTarget()), + impdescr.getStartCharacter()); + assertEquals(source.indexOf("import function " + impdescr.getTarget()) + ("import function " + impdescr.getTarget()).length(), + impdescr.getEndCharacter()); + } + + @Test + public void testGlobalWithComplexType() throws Exception { + final String source = "package foo.bar.baz\n" + + "import com.foo.Bar\n" + + "global java.util.List> aList;\n" + + "global Integer aNumber"; + PackageDescr pkg = parser.parse(source); + assertFalse(parser.getErrors().toString(), + parser.hasErrors()); + assertEquals("foo.bar.baz", + pkg.getName()); + assertEquals(1, + pkg.getImports().size()); + + ImportDescr impdescr = pkg.getImports().get(0); + assertEquals("com.foo.Bar", + impdescr.getTarget()); + assertEquals(source.indexOf("import " + impdescr.getTarget()), + impdescr.getStartCharacter()); + assertEquals(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length() - 1, + impdescr.getEndCharacter()); + + assertEquals(2, + pkg.getGlobals().size()); + + GlobalDescr global = pkg.getGlobals().get(0); + assertEquals("java.util.List>", + global.getType()); + assertEquals("aList", + global.getIdentifier()); + assertEquals(source.indexOf("global " + global.getType()), + global.getStartCharacter()); + assertEquals(source.indexOf("global " + global.getType() + " " + global.getIdentifier()) + + ("global " + global.getType() + " " + global.getIdentifier()).length(), + global.getEndCharacter()); + + global = pkg.getGlobals().get(1); + assertEquals("Integer", + global.getType()); + assertEquals("aNumber", + global.getIdentifier()); + assertEquals(source.indexOf("global " + global.getType()), + global.getStartCharacter()); + assertEquals(source.indexOf("global " + global.getType() + " " + global.getIdentifier()) + + ("global " + global.getType() + " " + global.getIdentifier()).length() - 1, + global.getEndCharacter()); + } + + @Test + public void testGlobalWithOrWithoutSemi() throws Exception { + PackageDescr pack = parseResource("globals.drl"); + + assertEquals(1, + pack.getRules().size()); + + final RuleDescr rule = (RuleDescr) pack.getRules().get(0); + assertEquals(1, + rule.getLhs().getDescrs().size()); + + assertEquals(1, + pack.getImports().size()); + assertEquals(2, + pack.getGlobals().size()); + + final GlobalDescr foo = (GlobalDescr) pack.getGlobals().get(0); + assertEquals("java.lang.String", + foo.getType()); + assertEquals("foo", + foo.getIdentifier()); + final GlobalDescr bar = (GlobalDescr) pack.getGlobals().get(1); + assertEquals("java.lang.Integer", + bar.getType()); + assertEquals("bar", + bar.getIdentifier()); + } + + @Test + public void testFunctionImportWithNotExist() throws Exception { + PackageDescr pkg = (PackageDescr) parseResource("test_FunctionImport.drl"); + + assertEquals(2, + pkg.getFunctionImports().size()); + + assertEquals("abd.def.x", + ((FunctionImportDescr) pkg.getFunctionImports().get(0)).getTarget()); + assertFalse(((FunctionImportDescr) pkg.getFunctionImports().get(0)).getStartCharacter() == -1); + assertFalse(((FunctionImportDescr) pkg.getFunctionImports().get(0)).getEndCharacter() == -1); + assertEquals("qed.wah.*", + ((FunctionImportDescr) pkg.getFunctionImports().get(1)).getTarget()); + assertFalse(((FunctionImportDescr) pkg.getFunctionImports().get(1)).getStartCharacter() == -1); + assertFalse(((FunctionImportDescr) pkg.getFunctionImports().get(1)).getEndCharacter() == -1); + } + + @Test + public void testFromComplexAcessor() throws Exception { + String source = "rule \"Invalid customer id\" ruleflow-group \"validate\" lock-on-active true \n" + + " when \n" + + " o: Order( ) \n" + + " not( Customer( ) from customerService.getCustomer(o.getCustomerId()) ) \n" + + " then \n" + + " System.err.println(\"Invalid customer id found!\"); " + + "\n" + + " o.addError(\"Invalid customer id\"); \n" + + "end \n"; + PackageDescr pkg = parser.parse(source); + + assertFalse(parser.getErrorMessages().toString(), + parser.hasErrors()); + + RuleDescr rule = (RuleDescr) pkg.getRules().get(0); + assertEquals("Invalid customer id", + rule.getName()); + + assertEquals(2, + rule.getLhs().getDescrs().size()); + + NotDescr not = (NotDescr) rule.getLhs().getDescrs().get(1); + PatternDescr customer = (PatternDescr) not.getDescrs().get(0); + + assertEquals("Customer", + customer.getObjectType()); + assertEquals("customerService.getCustomer(o.getCustomerId())", + ((FromDescr) customer.getSource()).getDataSource().getText()); } } diff --git a/drools-drl/drools-drl10-parser/src/test/resources/org/drools/parser/globals.drl b/drools-drl/drools-drl10-parser/src/test/resources/org/drools/parser/globals.drl new file mode 100644 index 00000000000..bc352c77f4b --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/test/resources/org/drools/parser/globals.drl @@ -0,0 +1,28 @@ +/* + * Copyright 2015 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * 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.compiler.test; + +import org.drools.compiler.Cheese; + +global java.lang.String foo +global java.lang.Integer bar; + +rule baz + when + Cheese( ) + then + +end diff --git a/drools-drl/drools-drl10-parser/src/test/resources/org/drools/parser/test_FunctionImport.drl b/drools-drl/drools-drl10-parser/src/test/resources/org/drools/parser/test_FunctionImport.drl new file mode 100644 index 00000000000..a548a7b4c2c --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/test/resources/org/drools/parser/test_FunctionImport.drl @@ -0,0 +1,27 @@ +/* + * Copyright 2015 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * 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 HR1 + +import function abd.def.x +import function qed.wah.* + +rule simple_rule + when + not ( Cheese(type == "stilton") ) + exists ( Foo() ) + then + funky(); +end