From c4621cbcb1f9aad97ba3389468546444d1e9f3dc Mon Sep 17 00:00:00 2001 From: Rudrangi Anupriya Date: Thu, 13 Apr 2023 22:49:06 +0530 Subject: Add OR operator to cps-path Issue-ID: CPS-1215 Change-Id: I91fdf5bddcc4fc12a8cf9dbce75f77c832c55871 Signed-off-by: Rudrangi Anupriya --- .../org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 | 8 ++- .../cpspath/parser/CpsPathBooleanOperatorType.java | 51 +++++++++++++++++ .../onap/cps/cpspath/parser/CpsPathBuilder.java | 35 +++++++++--- .../org/onap/cps/cpspath/parser/CpsPathQuery.java | 2 + .../cps/cpspath/parser/CpsPathQuerySpec.groovy | 64 ++++++++++++---------- 5 files changed, 121 insertions(+), 39 deletions(-) create mode 100644 cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBooleanOperatorType.java (limited to 'cps-path-parser/src') diff --git a/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 b/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 index db09b3c532..d4718111f6 100644 --- a/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 +++ b/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021-2022 Nordix Foundation + * Modifications Copyright (C) 2023 TechMahindra Ltd * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,14 +41,16 @@ yangElement : containerName listElementRef? ; containerName : QName ; -listElementRef : OB leafCondition ( KW_AND leafCondition)* CB ; +listElementRef : OB leafCondition ( booleanOperators leafCondition)* CB ; -multipleLeafConditions : OB leafCondition ( KW_AND leafCondition)* CB ; +multipleLeafConditions : OB leafCondition ( booleanOperators leafCondition)* CB ; leafCondition : AT leafName EQ ( IntegerLiteral | StringLiteral) ; leafName : QName ; +booleanOperators : ( KW_AND | KW_OR ) ; + invalidPostFix : (AT | CB | COLONCOLON | EQ ).+ ; /* @@ -68,6 +71,7 @@ SLASH : '/' ; KW_ANCESTOR : 'ancestor' ; KW_AND : 'and' ; KW_TEXT_FUNCTION: 'text()' ; +KW_OR : 'or' ; IntegerLiteral : FragDigits ; // Add below type definitions for leafvalue comparision in https://jira.onap.org/browse/CPS-440 diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBooleanOperatorType.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBooleanOperatorType.java new file mode 100644 index 0000000000..b2f1dddb19 --- /dev/null +++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBooleanOperatorType.java @@ -0,0 +1,51 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 TechMahindra Ltd + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.cpspath.parser; + +public enum CpsPathBooleanOperatorType { + AND("and"), + OR("or"); + + private final String operatorValue; + + CpsPathBooleanOperatorType(final String operatorValue) { + this.operatorValue = operatorValue; + } + + public String getValues() { + return this.operatorValue; + } + + /** + * Finds the value of the given enumeration. + * + * @param operatorValue value of the enum + * @return a booleanOperatorType + */ + public static CpsPathBooleanOperatorType fromString(final String operatorValue) { + for (final CpsPathBooleanOperatorType booleanOperatorType : CpsPathBooleanOperatorType.values()) { + if (booleanOperatorType.operatorValue.equalsIgnoreCase(operatorValue)) { + return booleanOperatorType; + } + } + return null; + } +} diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java index 3a9d70ebbc..4299d13081 100644 --- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java +++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021-2022 Nordix Foundation + * Modifications Copyright (C) 2023 TechMahindra Ltd * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,8 +25,10 @@ import static org.onap.cps.cpspath.parser.CpsPathPrefixType.DESCENDANT; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Queue; import org.onap.cps.cpspath.parser.antlr4.CpsPathBaseListener; import org.onap.cps.cpspath.parser.antlr4.CpsPathParser; import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.AncestorAxisContext; @@ -54,6 +57,10 @@ public class CpsPathBuilder extends CpsPathBaseListener { private List containerNames = new ArrayList<>(); + final List booleanOperators = new ArrayList<>(); + + final Queue booleanOperatorsQueue = new LinkedList<>(); + @Override public void exitInvalidPostFix(final CpsPathParser.InvalidPostFixContext ctx) { throw new PathParsingException(ctx.getText()); @@ -90,12 +97,22 @@ public class CpsPathBuilder extends CpsPathBaseListener { throw new PathParsingException("Unsupported comparison value encountered in expression" + ctx.getText()); } leavesData.put(ctx.leafName().getText(), comparisonValue); - appendCondition(normalizedXpathBuilder, ctx.leafName().getText(), comparisonValue); + final String booleanOperator = booleanOperatorsQueue.poll(); + appendCondition(normalizedXpathBuilder, ctx.leafName().getText(), booleanOperator, comparisonValue); if (processingAncestorAxis) { - appendCondition(normalizedAncestorPathBuilder, ctx.leafName().getText(), comparisonValue); + appendCondition(normalizedAncestorPathBuilder, ctx.leafName().getText(), booleanOperator, comparisonValue); } } + @Override + public void exitBooleanOperators(final CpsPathParser.BooleanOperatorsContext ctx) { + final CpsPathBooleanOperatorType cpsPathBooleanOperatorType = CpsPathBooleanOperatorType.fromString( + ctx.getText()); + booleanOperators.add(cpsPathBooleanOperatorType.getValues()); + booleanOperatorsQueue.add(cpsPathBooleanOperatorType.getValues()); + cpsPathQuery.setBooleanOperatorsType(booleanOperators); + } + @Override public void exitDescendant(final DescendantContext ctx) { cpsPathQuery.setCpsPathPrefixType(DESCENDANT); @@ -170,13 +187,13 @@ public class CpsPathBuilder extends CpsPathBaseListener { } private void appendCondition(final StringBuilder currentNormalizedPathBuilder, final String name, - final Object value) { + final String booleanOperator, final Object value) { final char lastCharacter = currentNormalizedPathBuilder.charAt(currentNormalizedPathBuilder.length() - 1); - currentNormalizedPathBuilder.append(lastCharacter == '[' ? "" : " and ") - .append("@") - .append(name) - .append("='") - .append(value) - .append("'"); + currentNormalizedPathBuilder.append(lastCharacter == '[' ? "" : " " + booleanOperator + " ") + .append("@") + .append(name) + .append("='") + .append(value) + .append("'"); } } diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java index 3985455263..2c96d91051 100644 --- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java +++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021-2023 Nordix Foundation + * Modifications Copyright (C) 2023 TechMahindra Ltd * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,6 +43,7 @@ public class CpsPathQuery { private String ancestorSchemaNodeIdentifier = ""; private String textFunctionConditionLeafName; private String textFunctionConditionValue; + private List booleanOperatorsType; /** * Returns a cps path query. diff --git a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy index b837a64fef..153dfbe9ed 100644 --- a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy +++ b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021-2022 Nordix Foundation + * Modifications Copyright (C) 2023 TechMahindra Ltd * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,19 +67,23 @@ class CpsPathQuerySpec extends Specification { then: 'the query has the right normalized xpath type' assert result.normalizedXpath == expectedNormalizedXPath where: 'the following data is used' - scenario | cpsPath || expectedNormalizedXPath - 'yang container' | '/cps-path' || '/cps-path' - 'descendant anywhere' | '//cps-path' || '//cps-path' - 'descendant with leaf condition' | '//cps-path[@key=1]' || "//cps-path[@key='1']" - 'descendant with leaf value and ancestor' | '//cps-path[@key=1]/ancestor:parent[@key=1]' || "//cps-path[@key='1']/ancestor:parent[@key='1']" - 'parent & child' | '/parent/child' || '/parent/child' - 'parent leaf of type Integer & child' | '/parent/child[@code=1]/child2' || "/parent/child[@code='1']/child2" - 'parent leaf with double quotes' | '/parent/child[@code="1"]/child2' || "/parent/child[@code='1']/child2" - 'parent leaf with double quotes inside single quotes' | '/parent/child[@code=\'"1"\']/child2' || "/parent/child[@code='\"1\"']/child2" - 'parent leaf with single quotes inside double quotes' | '/parent/child[@code="\'1\'"]/child2' || "/parent/child[@code='\\\'1\\\'']/child2" - 'leaf with single quotes inside double quotes' | '/parent/child[@code="\'1\'"]' || "/parent/child[@code='\\\'1\\\'']" - 'leaf with more than one attribute' | '/parent/child[@key1=1 and @key2="abc"]' || "/parent/child[@key1='1' and @key2='abc']" - 'parent & child with more than one attribute' | '/parent/child[@key1=1 and @key2="abc"]/child2' || "/parent/child[@key1='1' and @key2='abc']/child2" + scenario | cpsPath || expectedNormalizedXPath + 'yang container' | '/cps-path' || '/cps-path' + 'descendant anywhere' | '//cps-path' || '//cps-path' + 'descendant with leaf condition' | '//cps-path[@key=1]' || "//cps-path[@key='1']" + 'descendant with leaf value and ancestor' | '//cps-path[@key=1]/ancestor:parent[@key=1]' || "//cps-path[@key='1']/ancestor:parent[@key='1']" + 'parent & child' | '/parent/child' || '/parent/child' + 'parent leaf of type Integer & child' | '/parent/child[@code=1]/child2' || "/parent/child[@code='1']/child2" + 'parent leaf with double quotes' | '/parent/child[@code="1"]/child2' || "/parent/child[@code='1']/child2" + 'parent leaf with double quotes inside single quotes' | '/parent/child[@code=\'"1"\']/child2' || "/parent/child[@code='\"1\"']/child2" + 'parent leaf with single quotes inside double quotes' | '/parent/child[@code="\'1\'"]/child2' || "/parent/child[@code='\\\'1\\\'']/child2" + 'leaf with single quotes inside double quotes' | '/parent/child[@code="\'1\'"]' || "/parent/child[@code='\\\'1\\\'']" + 'leaf with more than one attribute' | '/parent/child[@key1=1 and @key2="abc"]' || "/parent/child[@key1='1' and @key2='abc']" + 'parent & child with more than one attribute' | '/parent/child[@key1=1 and @key2="abc"]/child2' || "/parent/child[@key1='1' and @key2='abc']/child2" + 'leaf with more than one attribute has OR operator' | '/parent/child[@key1=1 or @key2="abc"]' || "/parent/child[@key1='1' or @key2='abc']" + 'parent & child with more than one attribute has OR operator' | '/parent/child[@key1=1 or @key2="abc"]/child2' || "/parent/child[@key1='1' or @key2='abc']/child2" + 'parent & child with multiple AND operators' | '/parent/child[@key1=1 and @key2="abc" and @key="xyz"]/child2' || "/parent/child[@key1='1' and @key2='abc' and @key='xyz']/child2" + 'parent & child with multiple OR operators' | '/parent/child[@key1=1 or @key2="abc" or @key="xyz"]/child2' || "/parent/child[@key1='1' or @key2='abc' or @key='xyz']/child2" } def 'Parse xpath to form the Normalized xpath containing #scenario.'() { @@ -100,10 +105,14 @@ class CpsPathQuerySpec extends Specification { and: 'the right parameters are set' result.descendantName == "child" result.leavesData.size() == expectedNumberOfLeaves + result.booleanOperatorsType == expectedOperators where: 'the following data is used' - scenario | cpsPath || expectedNumberOfLeaves - 'one attribute' | '//child[@common-leaf-name-int=5]' || 1 - 'more than one attribute' | '//child[@int-leaf=5 and @leaf-name="leaf value"]' || 2 + scenario | cpsPath || expectedNumberOfLeaves || expectedOperators + 'one attribute' | '//child[@common-leaf-name-int=5]' || 1 || null + 'more than one attribute has AND operator' | '//child[@int-leaf=5 and @leaf-name="leaf value"]' || 2 || ['and'] + 'more than one attribute has OR operator' | '//child[@int-leaf=5 or @leaf-name="leaf value"]' || 2 || ['or'] + 'more than one attribute has combinations and/or operator' | '//child[@int-leaf=5 and @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2 || ['and', 'and'] + 'more than one attribute has combinations or/and operator' | '//child[@int-leaf=5 or @leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 2 || ['or', 'or'] } def 'Parse #scenario cps path with text function condition'() { @@ -133,18 +142,17 @@ class CpsPathQuerySpec extends Specification { then: 'a CpsPathException is thrown' thrown(PathParsingException) where: 'the following data is used' - scenario | cpsPath - 'no / at the start' | 'invalid-cps-path/child' - 'additional / after descendant option' | '///cps-path' - 'float value' | '/parent/child[@someFloat=5.0]' - 'unmatched quotes, double quote first ' | '/parent/child[@someString="value with unmatched quotes\']' - 'unmatched quotes, single quote first' | '/parent/child[@someString=\'value with unmatched quotes"]' - 'end with descendant and more than one attribute separated by "or"' | '//child[@int-leaf=5 or @leaf-name="leaf value"]' - 'missing attribute value' | '//child[@int-leaf=5 and @name]' - 'incomplete ancestor value' | '//books/ancestor::' - 'invalid list element with missing [' | '/parent-206/child-206/grand-child-206@key="A"]' - 'invalid list element with incorrect ]' | '/parent-206/child-206/grand-child-206]@key="A"]' - 'invalid list element with incorrect ::' | '/parent-206/child-206/grand-child-206::@key"A"]' + scenario | cpsPath + 'no / at the start' | 'invalid-cps-path/child' + 'additional / after descendant option' | '///cps-path' + 'float value' | '/parent/child[@someFloat=5.0]' + 'unmatched quotes, double quote first ' | '/parent/child[@someString="value with unmatched quotes\']' + 'unmatched quotes, single quote first' | '/parent/child[@someString=\'value with unmatched quotes"]' + 'missing attribute value' | '//child[@int-leaf=5 and @name]' + 'incomplete ancestor value' | '//books/ancestor::' + 'invalid list element with missing [' | '/parent-206/child-206/grand-child-206@key="A"]' + 'invalid list element with incorrect ]' | '/parent-206/child-206/grand-child-206]@key="A"]' + 'invalid list element with incorrect ::' | '/parent-206/child-206/grand-child-206::@key"A"]' } def 'Parse cps path using ancestor by schema node identifier with a #scenario.'() { -- cgit 1.2.3-korg