From d262e1c3f4671673a02ea402366a8c9b93fe4e53 Mon Sep 17 00:00:00 2001 From: danielhanrahan Date: Wed, 27 Nov 2024 15:38:41 +0000 Subject: [Cps Path Parser] Introduce Attribute axis Add grammar and tests for attribute-axis to match cps paths like: //books/@title which should return the titles of all books (a subsequent patch will implement the logic). The syntax is compatible with XPath standard. Issue-ID: CPS-2416 Signed-off-by: danielhanrahan Change-Id: I25164b23670147c504f0f0f6c0cc8ff15997f2a3 --- .../org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 | 4 +++- .../org/onap/cps/cpspath/parser/CpsPathBuilder.java | 7 +++++++ .../java/org/onap/cps/cpspath/parser/CpsPathQuery.java | 10 ++++++++++ .../onap/cps/cpspath/parser/CpsPathQuerySpec.groovy | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) (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 74b99feb33..bb6bfc3942 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 @@ -27,10 +27,12 @@ grammar CpsPath ; -cpsPath : ( prefix | descendant ) multipleLeafConditions? textFunctionCondition? containsFunctionCondition? ancestorAxis? EOF ; +cpsPath : ( prefix | descendant ) multipleLeafConditions? textFunctionCondition? containsFunctionCondition? ancestorAxis? attributeAxis? EOF ; slash : SLASH ; +attributeAxis : SLASH AT leafName ; + ancestorAxis : KW_ANCESTOR_AXIS_PREFIX ancestorPath ; ancestorPath : yangElement ( slash yangElement)* ; 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 b67d70847c..d0deb7defc 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 @@ -130,6 +130,13 @@ public class CpsPathBuilder extends CpsPathBaseListener { normalizedXpathBuilder.substring(startIndexOfAncestorSchemaNodeIdentifier)); } + @Override + public void exitAttributeAxis(final CpsPathParser.AttributeAxisContext ctx) { + final String attributeName = ctx.leafName().getText(); + normalizedXpathBuilder.append("/@").append(attributeName); + cpsPathQuery.setAttributeAxisAttributeName(attributeName); + } + @Override public void exitTextFunctionCondition(final TextFunctionConditionContext ctx) { cpsPathQuery.setTextFunctionConditionLeafName(ctx.leafName().getText()); 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 03602b64f6..3612ec57fb 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 @@ -40,6 +40,7 @@ public class CpsPathQuery { private String descendantName; private List leafConditions; private String ancestorSchemaNodeIdentifier = ""; + private String attributeAxisAttributeName = ""; private String textFunctionConditionLeafName; private String textFunctionConditionValue; private List booleanOperators; @@ -65,6 +66,15 @@ public class CpsPathQuery { return !(ancestorSchemaNodeIdentifier.isEmpty()); } + /** + * Has attribute axis been included in cpsPath. + * + * @return boolean value. + */ + public boolean hasAttributeAxis() { + return !(attributeAxisAttributeName.isEmpty()); + } + /** * Have leaf value conditions been included in cpsPath. * 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 a1bf28977a..b551080b40 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 @@ -34,6 +34,7 @@ class CpsPathQuerySpec extends Specification { then: 'the query has the correct default properties' assert result.cpsPathPrefixType == ABSOLUTE assert result.hasAncestorAxis() == false + assert result.hasAttributeAxis() == false assert result.hasLeafConditions() == false assert result.hasTextFunctionCondition() == false assert result.hasContainsFunctionCondition() == false @@ -107,6 +108,7 @@ class CpsPathQuerySpec extends Specification { '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" 'parent & child with multiple AND/OR combination' | '/parent/child[@key1=1 and @key2="abc" or @key="xyz"]/child2' || "/parent/child[@key1='1' and @key2='abc' or @key='xyz']/child2" 'parent & child with multiple OR/AND combination' | '/parent/child[@key1=1 or @key2="abc" and @key="xyz"]/child2' || "/parent/child[@key1='1' or @key2='abc' and @key='xyz']/child2" + 'attribute axis' | '/parent/child/@key' || '/parent/child/@key' } def 'Parse xpath to form the Normalized xpath containing #scenario.'() { @@ -254,4 +256,20 @@ class CpsPathQuerySpec extends Specification { '/test[@name2="value2" and @name1="value1"]' || 'name2' | 'name1' } + def 'Cps Path using attribute axis.'() { + when: 'the cps path is parsed' + def result = CpsPathQuery.createFrom(cpsPath) + then: 'the query has the correct properties for attribute axis' + assert result.hasAttributeAxis() == expectAttributeAxis + assert result.attributeAxisAttributeName == expectedAttributeName + where: 'the following data is used' + cpsPath || expectAttributeAxis | expectedAttributeName + '/test' || false | '' + '/test/@id' || true | 'id' + '/test[@id="id"]' || false | '' + '/test[@id="id"]/@id' || true | 'id' + '//test/ancestor::root' || false | '' + '//test/ancestor::root/@xyz' || true | 'xyz' + } + } -- cgit