This is an automated email from the ASF dual-hosted git repository.

tkobayas pushed a commit to branch dev-new-parser
in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git


The following commit(s) were added to refs/heads/dev-new-parser by this push:
     new 5f83f08f16 [incubator-kie-drools#5706] Grouped accessors for nested 
objects broken (#5790)
5f83f08f16 is described below

commit 5f83f08f169c0c4922e7bd842ae612c607f96dc8
Author: Toshiya Kobayashi <[email protected]>
AuthorDate: Tue Mar 19 11:39:52 2024 +0900

    [incubator-kie-drools#5706] Grouped accessors for nested objects broken 
(#5790)
    
    * [incubator-kie-drools#5706] Grouped accessors for nested objects broken
    
    * Update 
drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DRLVisitorImpl.java
    
    Co-authored-by: Jiří Locker <[email protected]>
    
    * Update 
drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/MiscDRLParserTest.java
    
    Co-authored-by: Jiří Locker <[email protected]>
    
    ---------
    
    Co-authored-by: Jiří Locker <[email protected]>
---
 .../drl/parser/antlr4/MiscDRLParserTest.java       | 34 ++++++++++
 .../org/drools/drl/parser/antlr4/DRLParser.g4      |  2 +-
 ...ringUtils.java => Antlr4ParserStringUtils.java} |  8 +--
 .../drools/drl/parser/antlr4/DRLVisitorImpl.java   | 47 +++++++++++---
 .../org/drools/drl/parser/lang/DRL6Parser.java     | 41 +-----------
 .../drools/drl/parser/util/ParserStringUtils.java  | 74 ++++++++++++++++++++++
 .../drl/parser/util/ParserStringUtilsTest.java     | 66 +++++++++++++++++++
 7 files changed, 220 insertions(+), 52 deletions(-)

diff --git 
a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/MiscDRLParserTest.java
 
b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/MiscDRLParserTest.java
index 0b394bbdd3..9e2d4f9d5b 100644
--- 
a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/MiscDRLParserTest.java
+++ 
b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/MiscDRLParserTest.java
@@ -3654,4 +3654,38 @@ class MiscDRLParserTest {
         ExprConstraintDescr constraintDescr = (ExprConstraintDescr) 
patternDescr.getConstraint().getDescrs().get(0);
         
assertThat(constraintDescr.toString()).isEqualToIgnoringWhitespace("$containsL 
: address!.city.contains(\"L\")");
     }
+
+    @Test
+    void groupedConstraints() {
+        final String text = "package org.drools\n" +
+                "rule R1\n" +
+                "when\n" +
+                "    $p : Person( address.(city.startsWith(\"I\") && 
city.length() == 5  ) )\n" +
+                "then\n" +
+                "end\n";
+        PackageDescr packageDescr = parser.parse(text);
+        RuleDescr ruleDescr = packageDescr.getRules().get(0);
+        PatternDescr patternDescr = (PatternDescr) 
ruleDescr.getLhs().getDescrs().get(0);
+        ExprConstraintDescr constraintDescr = (ExprConstraintDescr) 
patternDescr.getConstraint().getDescrs().get(0);
+        assertThat(constraintDescr.toString())
+                .as("prefix should be appended to each element")
+                .isEqualToIgnoringWhitespace("address.city.startsWith(\"I\") 
&& address.city.length() == 5");
+    }
+
+    @Test
+    void groupedConstraintsWithNullSafeDereferencing() {
+        final String text = "package org.drools\n" +
+                "rule R1\n" +
+                "when\n" +
+                "    $p : Person( address!.(city!.startsWith(\"I\") && 
city!.length() == 5  ) )\n" +
+                "then\n" +
+                "end\n";
+        PackageDescr packageDescr = parser.parse(text);
+        RuleDescr ruleDescr = packageDescr.getRules().get(0);
+        PatternDescr patternDescr = (PatternDescr) 
ruleDescr.getLhs().getDescrs().get(0);
+        ExprConstraintDescr constraintDescr = (ExprConstraintDescr) 
patternDescr.getConstraint().getDescrs().get(0);
+        assertThat(constraintDescr.toString())
+                .as("prefix should be appended to each element")
+                .isEqualToIgnoringWhitespace("address!.city!.startsWith(\"I\") 
&& address!.city!.length() == 5");
+    }
 }
diff --git 
a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLParser.g4
 
b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLParser.g4
index c358c66309..7523592f98 100644
--- 
a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLParser.g4
+++ 
b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLParser.g4
@@ -128,7 +128,7 @@ lhsPattern : QUESTION? objectType=drlQualifiedName LPAREN 
positionalConstraints?
 positionalConstraints : constraint (COMMA constraint)* SEMI ;
 constraints : constraint (COMMA constraint)* ;
 constraint : ( nestedConstraint | conditionalOrExpression ) ;
-nestedConstraint : ( IDENTIFIER ( DOT | HASH ) )* IDENTIFIER DOT LPAREN 
constraints RPAREN ;
+nestedConstraint : ( IDENTIFIER ( DOT | NULL_SAFE_DOT | HASH ) )* IDENTIFIER 
(DOT | NULL_SAFE_DOT ) LPAREN constraints RPAREN ;
 
 // TBD: constraint parsing could be delegated to DRL6ExpressionParser
 conditionalOrExpression : left=conditionalAndExpression (OR 
right=conditionalAndExpression)* ;
diff --git 
a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/ParserStringUtils.java
 
b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/Antlr4ParserStringUtils.java
similarity index 93%
rename from 
drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/ParserStringUtils.java
rename to 
drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/Antlr4ParserStringUtils.java
index 22ea2a7fa6..8c874cc3c1 100644
--- 
a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/ParserStringUtils.java
+++ 
b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/Antlr4ParserStringUtils.java
@@ -23,12 +23,12 @@ import org.antlr.v4.runtime.TokenStream;
 import org.antlr.v4.runtime.misc.Interval;
 
 /**
- * Collection of String utilities used by DRLParser.
- * This may be merged in drools-util
+ * Collection of String utilities used by antlr4 DRLParser.
  */
-public class ParserStringUtils {
+public class Antlr4ParserStringUtils {
 
-    private ParserStringUtils() {
+    private Antlr4ParserStringUtils() {
+        // Private constructor to prevent instantiation.
     }
 
     /**
diff --git 
a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DRLVisitorImpl.java
 
b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DRLVisitorImpl.java
index c37284fa51..487a09c3ea 100644
--- 
a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DRLVisitorImpl.java
+++ 
b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DRLVisitorImpl.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 
 import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.Token;
 import org.antlr.v4.runtime.TokenStream;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.RuleNode;
@@ -59,10 +60,11 @@ import org.drools.drl.ast.descr.UnitDescr;
 import org.drools.drl.ast.descr.WindowDeclarationDescr;
 
 import static 
org.drools.drl.parser.antlr4.DRLParserHelper.getTextWithoutErrorNode;
-import static 
org.drools.drl.parser.antlr4.ParserStringUtils.getTextPreservingWhitespace;
-import static 
org.drools.drl.parser.antlr4.ParserStringUtils.getTokenTextPreservingWhitespace;
-import static 
org.drools.drl.parser.antlr4.ParserStringUtils.safeStripStringDelimiters;
-import static org.drools.drl.parser.antlr4.ParserStringUtils.trimThen;
+import static 
org.drools.drl.parser.antlr4.Antlr4ParserStringUtils.getTextPreservingWhitespace;
+import static 
org.drools.drl.parser.antlr4.Antlr4ParserStringUtils.getTokenTextPreservingWhitespace;
+import static 
org.drools.drl.parser.antlr4.Antlr4ParserStringUtils.safeStripStringDelimiters;
+import static org.drools.drl.parser.antlr4.Antlr4ParserStringUtils.trimThen;
+import static org.drools.drl.parser.util.ParserStringUtils.appendPrefix;
 import static org.drools.util.StringUtils.unescapeJava;
 
 /**
@@ -533,7 +535,7 @@ public class DRLVisitorImpl extends 
DRLParserBaseVisitor<Object> {
         behaviorDescr.setType(ctx.DRL_WINDOW().getText());
         behaviorDescr.setSubType(ctx.IDENTIFIER().getText());
         List<DRLParser.DrlExpressionContext> drlExpressionContexts = 
ctx.expressionList().drlExpression();
-        List<String> parameters = 
drlExpressionContexts.stream().map(ParserStringUtils::getTextPreservingWhitespace).collect(Collectors.toList());
+        List<String> parameters = 
drlExpressionContexts.stream().map(Antlr4ParserStringUtils::getTextPreservingWhitespace).collect(Collectors.toList());
         behaviorDescr.setParameters(parameters);
         return behaviorDescr;
     }
@@ -620,17 +622,42 @@ public class DRLVisitorImpl extends 
DRLParserBaseVisitor<Object> {
     }
 
     /**
-     * Takes one constraint as String and create ExprConstraintDescr
+     * Takes one constraint as String and create ExprConstraintDescr.
+     * In case of nested constraint, it could be multiple ExprConstraintDescr 
objects.
      */
     @Override
-    public ExprConstraintDescr visitConstraint(DRLParser.ConstraintContext 
ctx) {
+    public List<ExprConstraintDescr> 
visitConstraint(DRLParser.ConstraintContext ctx) {
+        List<ExprConstraintDescr> descrList = new ArrayList<>();
+        if (ctx.nestedConstraint() != null) {
+            // nested constraint requires special string manipulation
+            return visitNestedConstraint(ctx.nestedConstraint());
+        }
+        // get a simple constraint as String
         String constraint = visitConstraintChildren(ctx);
         if (!constraint.isEmpty()) {
             ExprConstraintDescr constraintDescr = new 
ExprConstraintDescr(constraint);
             constraintDescr.setType(ExprConstraintDescr.Type.NAMED);
-            return constraintDescr;
+            descrList.add(constraintDescr);
+            return descrList;
         }
-        return null;
+        return descrList;
+    }
+
+    /**
+     * Append a prefix to nested constraints.
+     * For example,
+     *     address.(city.startsWith("I"), city.length() == 5)
+     * becomes
+     *     address.city.startsWith("I"), address.city.length() == 5
+     */
+    @Override
+    public List<ExprConstraintDescr> 
visitNestedConstraint(DRLParser.NestedConstraintContext ctx) {
+        Token prefixStartToken = ctx.start;
+        Token prefixEndToken = 
tokenStream.get(ctx.LPAREN().getSymbol().getTokenIndex() - 1);
+        String prefix = tokenStream.getText(prefixStartToken, prefixEndToken);
+        List<ExprConstraintDescr> exprConstraintDescr = 
visitConstraints(ctx.constraints());
+        exprConstraintDescr.forEach(d -> d.setText(appendPrefix(prefix, 
d.getText())));
+        return exprConstraintDescr;
     }
 
     @Override
@@ -797,6 +824,8 @@ public class DRLVisitorImpl extends 
DRLParserBaseVisitor<Object> {
             Object childResult = c.accept(this);
             if (childResult instanceof BaseDescr) {
                 aggregator.add((BaseDescr) childResult);
+            } else if (childResult instanceof List) {
+                aggregator.addAll((List<BaseDescr>) childResult);
             }
         }
         return aggregator;
diff --git 
a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/lang/DRL6Parser.java
 
b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/lang/DRL6Parser.java
index ebfec99223..f23b7063e2 100644
--- 
a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/lang/DRL6Parser.java
+++ 
b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/lang/DRL6Parser.java
@@ -85,6 +85,8 @@ import org.kie.internal.builder.conf.LanguageLevelOption;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.drools.drl.parser.util.ParserStringUtils.appendPrefix;
+
 public class DRL6Parser extends AbstractDRLParser implements DRLParser {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(DRL6Parser.class);
@@ -3778,44 +3780,7 @@ public class DRL6Parser extends AbstractDRLParser 
implements DRLParser {
 
     private String toExpression(String prefix, int first, int last) {
         String expr = input.toString(first, last);
-        if (prefix.length() == 0) {
-            return expr;
-        }
-        StringBuilder sb = new StringBuilder();
-        toOrExpression(sb, prefix, expr);
-        return sb.toString();
-    }
-
-    private void toOrExpression(StringBuilder sb, String prefix, String expr) {
-        int start = 0;
-        int end = expr.indexOf("||");
-        do {
-            if (start > 0) {
-                sb.append(" || ");
-            }
-            toAndExpression(sb, prefix, end > 0 ? expr.substring(start, end) : 
expr.substring(start));
-            start = end + 2;
-            end = expr.indexOf("||", start);
-        } while (start > 1);
-    }
-
-    private void toAndExpression(StringBuilder sb, String prefix, String expr) 
{
-        int start = 0;
-        int end = expr.indexOf("&&");
-        do {
-            if (start > 0) {
-                sb.append(" && ");
-            }
-            sb.append(toExpression(prefix, end > 0 ? expr.substring(start, 
end) : expr.substring(start)));
-            start = end + 2;
-            end = expr.indexOf("&&", start);
-        } while (start > 1);
-    }
-
-    private String toExpression(String prefix, String expr) {
-        expr = expr.trim();
-        int colonPos = expr.indexOf(":");
-        return colonPos < 0 ? prefix + expr : expr.substring(0, colonPos + 1) 
+ " " + prefix + expr.substring(colonPos + 1).trim();
+        return appendPrefix(prefix, expr);
     }
 
     private boolean speculateNestedConstraint() throws RecognitionException {
diff --git 
a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/util/ParserStringUtils.java
 
b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/util/ParserStringUtils.java
new file mode 100644
index 0000000000..bca3dca91f
--- /dev/null
+++ 
b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/util/ParserStringUtils.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.drl.parser.util;
+
+/**
+ * String utilities used by DRL Parser. Not dependent on ANTLR version.
+ */
+public class ParserStringUtils {
+
+    private ParserStringUtils() {
+        // Private constructor to prevent instantiation.
+    }
+
+    /**
+     * Append a prefix to a grouped constraint.
+     * Even if the constraint contains || and/or &&, append the prefix to each 
element.
+     */
+    public static String appendPrefix(String prefix, String expr) {
+        if (prefix.length() == 0) {
+            return expr;
+        }
+        StringBuilder sb = new StringBuilder();
+        appendPrefixToOrExpression(sb, prefix, expr);
+        return sb.toString();
+    }
+
+    private static void appendPrefixToOrExpression(StringBuilder sb, String 
prefix, String expr) {
+        int start = 0;
+        int end = expr.indexOf("||");
+        do {
+            if (start > 0) {
+                sb.append(" || ");
+            }
+            appendPrefixToAndExpression(sb, prefix, end > 0 ? 
expr.substring(start, end) : expr.substring(start));
+            start = end + 2;
+            end = expr.indexOf("||", start);
+        } while (start > 1);
+    }
+
+    private static void appendPrefixToAndExpression(StringBuilder sb, String 
prefix, String expr) {
+        int start = 0;
+        int end = expr.indexOf("&&");
+        do {
+            if (start > 0) {
+                sb.append(" && ");
+            }
+            sb.append(appendPrefixToExpression(prefix, end > 0 ? 
expr.substring(start, end) : expr.substring(start)));
+            start = end + 2;
+            end = expr.indexOf("&&", start);
+        } while (start > 1);
+    }
+
+    private static String appendPrefixToExpression(String prefix, String expr) 
{
+        expr = expr.trim();
+        int colonPos = expr.indexOf(":");
+        return colonPos < 0 ? prefix + expr : expr.substring(0, colonPos + 1) 
+ " " + prefix + expr.substring(colonPos + 1).trim();
+    }
+}
diff --git 
a/drools-drl/drools-drl-parser/src/test/java/org/drools/drl/parser/util/ParserStringUtilsTest.java
 
b/drools-drl/drools-drl-parser/src/test/java/org/drools/drl/parser/util/ParserStringUtilsTest.java
new file mode 100644
index 0000000000..9ef0e6edd5
--- /dev/null
+++ 
b/drools-drl/drools-drl-parser/src/test/java/org/drools/drl/parser/util/ParserStringUtilsTest.java
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.drl.parser.util;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ParserStringUtilsTest {
+
+    @Test
+    public void appendPrefix() {
+        String prefix = "prefix.";
+        String expr = "a == 1";
+        String result = ParserStringUtils.appendPrefix(prefix, expr);
+        assertThat(result).isEqualToIgnoringWhitespace("prefix.a == 1");
+    }
+
+    @Test
+    public void appendPrefixWithAnd() {
+        String prefix = "prefix.";
+        String expr = "a == 1 && b.equals(\"foo\")";
+        String result = ParserStringUtils.appendPrefix(prefix, expr);
+        assertThat(result).isEqualToIgnoringWhitespace("prefix.a == 1 && 
prefix.b.equals(\"foo\")");
+    }
+
+    @Test
+    public void appendPrefixWithOr() {
+        String prefix = "prefix.";
+        String expr = "a == 1 || b.equals(\"foo\")";
+        String result = ParserStringUtils.appendPrefix(prefix, expr);
+        assertThat(result).isEqualToIgnoringWhitespace("prefix.a == 1 || 
prefix.b.equals(\"foo\")");
+    }
+
+    @Test
+    public void appendPrefixWithANDOr() {
+        String prefix = "prefix.";
+        String expr = "a == 1 && b.equals(\"foo\") || c == 2 && 
d.equals(\"bar\")";
+        String result = ParserStringUtils.appendPrefix(prefix, expr);
+        assertThat(result).isEqualToIgnoringWhitespace("prefix.a == 1 && 
prefix.b.equals(\"foo\") || prefix.c == 2 && prefix.d.equals(\"bar\")");
+    }
+
+    @Test
+    public void appendPrefixWithBindingVariable() {
+        String prefix = "prefix.";
+        String expr = "$a : a == 1 && $b : b.equals(\"foo\")";
+        String result = ParserStringUtils.appendPrefix(prefix, expr);
+        assertThat(result).isEqualToIgnoringWhitespace("$a : prefix.a == 1 && 
$b : prefix.b.equals(\"foo\")");
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to