diff options
Diffstat (limited to 'cps-path-parser')
7 files changed, 108 insertions, 111 deletions
diff --git a/cps-path-parser/pom.xml b/cps-path-parser/pom.xml index 633ec6bb80..57b1e6b247 100644 --- a/cps-path-parser/pom.xml +++ b/cps-path-parser/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>3.5.1-SNAPSHOT</version> + <version>3.5.5-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> 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 3aef120fed..74b99feb33 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,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2023 TechMahindra Ltd * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,23 +27,23 @@ grammar CpsPath ; -cpsPath : ( prefix | descendant | incorrectPrefix ) multipleLeafConditions? textFunctionCondition? containsFunctionCondition? ancestorAxis? invalidPostFix?; +cpsPath : ( prefix | descendant ) multipleLeafConditions? textFunctionCondition? containsFunctionCondition? ancestorAxis? EOF ; -ancestorAxis : SLASH KW_ANCESTOR COLONCOLON ancestorPath ; +slash : SLASH ; -ancestorPath : yangElement ( SLASH yangElement)* ; +ancestorAxis : KW_ANCESTOR_AXIS_PREFIX ancestorPath ; -textFunctionCondition : SLASH leafName OB KW_TEXT_FUNCTION EQ StringLiteral CB ; +ancestorPath : yangElement ( slash yangElement)* ; -containsFunctionCondition : OB KW_CONTAINS_FUNCTION OP AT leafName COMMA StringLiteral CP CB ; +textFunctionCondition : slash leafName OB KW_TEXT_FUNCTION EQ StringLiteral CB ; -parent : ( SLASH yangElement)* ; +containsFunctionCondition : OB KW_CONTAINS_FUNCTION OP AT leafName COMMA StringLiteral CP CB ; -prefix : parent SLASH containerName ; +parent : ( slash yangElement)* ; -descendant : SLASH prefix ; +prefix : parent slash containerName ; -incorrectPrefix : SLASH SLASH SLASH+ ; +descendant : slash prefix ; yangElement : containerName listElementRef? ; @@ -61,8 +61,6 @@ booleanOperators : ( KW_AND | KW_OR ) ; comparativeOperators : ( EQ | GT | LT | GE | LE ) ; -invalidPostFix : (AT | CB | COLONCOLON | comparativeOperators ).+ ; - /* * Lexer Rules * Most of the lexer rules below are inspired by @@ -89,10 +87,11 @@ KW_ANCESTOR : 'ancestor' ; KW_AND : 'and' ; KW_TEXT_FUNCTION: 'text()' ; KW_OR : 'or' ; -KW_CONTAINS_FUNCTION: 'contains' ; +KW_CONTAINS_FUNCTION : 'contains' ; +KW_ANCESTOR_AXIS_PREFIX : SLASH KW_ANCESTOR COLONCOLON ; IntegerLiteral : FragDigits ; -// Add below type definitions for leafvalue comparision in https://jira.onap.org/browse/CPS-440 +// Add below type definitions for leafvalue comparision in https://lf-onap.atlassian.net/CPS-440 DecimalLiteral : ('.' FragDigits) | (FragDigits '.' [0-9]*) ; DoubleLiteral : (('.' FragDigits) | (FragDigits ('.' [0-9]*)?)) [eE] [+-]? FragDigits ; StringLiteral : '"' (~["] | FragEscapeQuot)* '"' | '\'' (~['] | FragEscapeApos)* '\'' ; 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 de261e64b3..ed7dbecc18 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,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2023 TechMahindra Ltd * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,7 +29,6 @@ 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; import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.DescendantContext; -import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.IncorrectPrefixContext; import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.LeafConditionContext; import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.MultipleLeafConditionsContext; import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.PrefixContext; @@ -37,17 +36,19 @@ import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.TextFunctionConditionCon public class CpsPathBuilder extends CpsPathBaseListener { + private static final String NO_PARENT_PATH = ""; + private static final String OPEN_BRACKET = "["; private static final String CLOSE_BRACKET = "]"; private final CpsPathQuery cpsPathQuery = new CpsPathQuery(); - private final List<CpsPathQuery.DataLeaf> leavesData = new ArrayList<>(); + private final List<CpsPathQuery.LeafCondition> leafConditions = new ArrayList<>(); private final StringBuilder normalizedXpathBuilder = new StringBuilder(); - private final StringBuilder normalizedAncestorPathBuilder = new StringBuilder(); + private int startIndexOfAncestorSchemaNodeIdentifier = 0; private boolean processingAncestorAxis = false; @@ -55,11 +56,9 @@ public class CpsPathBuilder extends CpsPathBaseListener { private final List<String> booleanOperators = new ArrayList<>(); - private final List<String> comparativeOperators = new ArrayList<>(); - @Override - public void exitInvalidPostFix(final CpsPathParser.InvalidPostFixContext ctx) { - throw new PathParsingException(ctx.getText()); + public void exitSlash(final CpsPathParser.SlashContext ctx) { + normalizedXpathBuilder.append("/"); } @Override @@ -69,16 +68,19 @@ public class CpsPathBuilder extends CpsPathBaseListener { @Override public void exitParent(final CpsPathParser.ParentContext ctx) { - cpsPathQuery.setNormalizedParentPath(normalizedXpathBuilder.toString()); - } - - @Override - public void exitIncorrectPrefix(final IncorrectPrefixContext ctx) { - throw new PathParsingException("CPS path can only start with one or two slashes (/)"); + final String normalizedParentPath; + if (normalizedXpathBuilder.toString().equals("/")) { + normalizedParentPath = NO_PARENT_PATH; + } else { + normalizedParentPath = normalizedXpathBuilder.toString(); + } + cpsPathQuery.setNormalizedParentPath(normalizedParentPath); } @Override public void exitLeafCondition(final LeafConditionContext ctx) { + final String leafName = ctx.leafName().getText(); + final String operator = ctx.comparativeOperators().getText(); final Object comparisonValue; if (ctx.IntegerLiteral() != null) { comparisonValue = Integer.valueOf(ctx.IntegerLiteral().getText()); @@ -87,7 +89,7 @@ public class CpsPathBuilder extends CpsPathBaseListener { } else { throw new PathParsingException("Unsupported comparison value encountered in expression" + ctx.getText()); } - leafContext(ctx.leafName(), comparisonValue); + leafContext(leafName, operator, comparisonValue); } @Override @@ -96,40 +98,36 @@ public class CpsPathBuilder extends CpsPathBaseListener { } @Override - public void exitComparativeOperators(final CpsPathParser.ComparativeOperatorsContext ctx) { - comparativeOperators.add(ctx.getText()); - } - - @Override public void exitDescendant(final DescendantContext ctx) { cpsPathQuery.setCpsPathPrefixType(DESCENDANT); - cpsPathQuery.setDescendantName(normalizedXpathBuilder.substring(1)); - normalizedXpathBuilder.insert(0, "/"); + cpsPathQuery.setDescendantName(normalizedXpathBuilder.substring(2)); } @Override public void enterMultipleLeafConditions(final MultipleLeafConditionsContext ctx) { normalizedXpathBuilder.append(OPEN_BRACKET); - leavesData.clear(); + leafConditions.clear(); booleanOperators.clear(); - comparativeOperators.clear(); } @Override public void exitMultipleLeafConditions(final MultipleLeafConditionsContext ctx) { normalizedXpathBuilder.append(CLOSE_BRACKET); - cpsPathQuery.setLeavesData(leavesData); + cpsPathQuery.setLeafConditions(leafConditions); } @Override public void enterAncestorAxis(final AncestorAxisContext ctx) { processingAncestorAxis = true; + normalizedXpathBuilder.append("/ancestor::"); + startIndexOfAncestorSchemaNodeIdentifier = normalizedXpathBuilder.length(); } @Override public void exitAncestorAxis(final AncestorAxisContext ctx) { - cpsPathQuery.setAncestorSchemaNodeIdentifier(normalizedAncestorPathBuilder.substring(1)); processingAncestorAxis = false; + cpsPathQuery.setAncestorSchemaNodeIdentifier( + normalizedXpathBuilder.substring(startIndexOfAncestorSchemaNodeIdentifier)); } @Override @@ -147,48 +145,36 @@ public class CpsPathBuilder extends CpsPathBaseListener { @Override public void enterListElementRef(final CpsPathParser.ListElementRefContext ctx) { normalizedXpathBuilder.append(OPEN_BRACKET); - if (processingAncestorAxis) { - normalizedAncestorPathBuilder.append(OPEN_BRACKET); - } } @Override public void exitListElementRef(final CpsPathParser.ListElementRefContext ctx) { normalizedXpathBuilder.append(CLOSE_BRACKET); - if (processingAncestorAxis) { - normalizedAncestorPathBuilder.append(CLOSE_BRACKET); - } } CpsPathQuery build() { cpsPathQuery.setNormalizedXpath(normalizedXpathBuilder.toString()); cpsPathQuery.setContainerNames(containerNames); cpsPathQuery.setBooleanOperators(booleanOperators); - cpsPathQuery.setComparativeOperators(comparativeOperators); return cpsPathQuery; } @Override public void exitContainerName(final CpsPathParser.ContainerNameContext ctx) { final String containerName = ctx.getText(); - normalizedXpathBuilder.append("/") - .append(containerName); - containerNames.add(containerName); - if (processingAncestorAxis) { - normalizedAncestorPathBuilder.append("/").append(containerName); + normalizedXpathBuilder.append(containerName); + if (!processingAncestorAxis) { + containerNames.add(containerName); } } - private void leafContext(final CpsPathParser.LeafNameContext ctx, final Object comparisonValue) { - leavesData.add(new CpsPathQuery.DataLeaf(ctx.getText(), comparisonValue)); - appendCondition(normalizedXpathBuilder, ctx.getText(), comparisonValue); - if (processingAncestorAxis) { - appendCondition(normalizedAncestorPathBuilder, ctx.getText(), comparisonValue); - } + private void leafContext(final String leafName, final String operator, final Object comparisonValue) { + leafConditions.add(new CpsPathQuery.LeafCondition(leafName, operator, comparisonValue)); + appendCondition(normalizedXpathBuilder, leafName, operator, comparisonValue); } private void appendCondition(final StringBuilder currentNormalizedPathBuilder, final String name, - final Object value) { + final String operator, final Object value) { final char lastCharacter = currentNormalizedPathBuilder.charAt(currentNormalizedPathBuilder.length() - 1); final boolean isStartOfExpression = lastCharacter == '['; if (!isStartOfExpression) { @@ -196,7 +182,7 @@ public class CpsPathBuilder extends CpsPathBaseListener { } currentNormalizedPathBuilder.append("@") .append(name) - .append(getLastElement(comparativeOperators)) + .append(operator) .append("'") .append(value.toString().replace("'", "''")) .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 f98df05a28..03602b64f6 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,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2023 TechMahindra Ltd * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,8 +25,6 @@ import static org.onap.cps.cpspath.parser.CpsPathPrefixType.ABSOLUTE; import java.util.List; import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; @@ -40,12 +38,11 @@ public class CpsPathQuery { private List<String> containerNames; private CpsPathPrefixType cpsPathPrefixType = ABSOLUTE; private String descendantName; - private List<DataLeaf> leavesData; + private List<LeafCondition> leafConditions; private String ancestorSchemaNodeIdentifier = ""; private String textFunctionConditionLeafName; private String textFunctionConditionValue; private List<String> booleanOperators; - private List<String> comparativeOperators; private String containsFunctionConditionLeafName; private String containsFunctionConditionValue; @@ -74,7 +71,7 @@ public class CpsPathQuery { * @return boolean value. */ public boolean hasLeafConditions() { - return leavesData != null; + return leafConditions != null; } /** @@ -104,11 +101,6 @@ public class CpsPathQuery { return cpsPathPrefixType == ABSOLUTE && hasLeafConditions(); } - @Getter - @EqualsAndHashCode - @AllArgsConstructor - public static class DataLeaf { - private final String name; - private final Object value; - } + public record LeafCondition(String name, String operator, Object value) { } + } diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java index bde9b0638f..4ede0d9c90 100644 --- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java +++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-2024 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,12 +59,10 @@ public class CpsPathUtil { return getCpsPathBuilder(xpathSource).build().getNormalizedParentPath(); } - public static String[] getXpathNodeIdSequence(final String xpathSource) { - final List<String> containerNames = getCpsPathBuilder(xpathSource).build().getContainerNames(); - return containerNames.toArray(new String[containerNames.size()]); + public static List<String> getXpathNodeIdSequence(final String xpathSource) { + return getCpsPathBuilder(xpathSource).build().getContainerNames(); } - /** * Returns boolean indicating xpath is an absolute path to a list element. * 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 ae7ee598ab..16430d2fa5 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,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2023 TechMahindra Ltd * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -48,8 +48,8 @@ class CpsPathQuerySpec extends Specification { and: 'the right query parameters are set' assert result.xpathPrefix == expectedXpathPrefix assert result.hasLeafConditions() - assert result.leavesData[0].name == expectedLeafName - assert result.leavesData[0].value == expectedLeafValue + assert result.leafConditions[0].name() == expectedLeafName + assert result.leafConditions[0].value() == expectedLeafValue where: 'the following data is used' scenario | cpsPath || expectedXpathPrefix | expectedLeafName | expectedLeafValue 'leaf of type String' | '/parent/child[@common-leaf-name="common-leaf-value"]' || '/parent/child' | 'common-leaf-name' | 'common-leaf-value' @@ -91,13 +91,14 @@ class CpsPathQuerySpec extends Specification { 'descendant with leaf condition has "<" operator' | '//cps-path[@key<10]' || "//cps-path[@key<'10']" 'descendant with leaf condition has ">=" operator' | '//cps-path[@key>=8]' || "//cps-path[@key>='8']" 'descendant with leaf condition has "<=" operator' | '//cps-path[@key<=12]' || "//cps-path[@key<='12']" - 'descendant with leaf value and ancestor' | '//cps-path[@key=1]/ancestor:parent[@key=1]' || "//cps-path[@key='1']/ancestor:parent[@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 single quotes inside single quotes' | "/parent/child[@code='I''m quoted']" || "/parent/child[@code='I''m quoted']" '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']" @@ -123,11 +124,11 @@ class CpsPathQuerySpec extends Specification { when: 'the given cps path is parsed' def result = CpsPathQuery.createFrom(cpsPath) then: 'the expected number of leaves are returned' - result.leavesData.size() == expectedNumberOfLeaves + result.leafConditions.size() == expectedNumberOfLeaves and: 'the given operator(s) returns in the correct order' result.booleanOperators == expectedOperators and: 'the given comparativeOperator(s) returns in the correct order' - result.comparativeOperators == expectedComparativeOperator + result.leafConditions.operator == expectedComparativeOperator where: 'the following data is used' cpsPath || expectedNumberOfLeaves || expectedOperators || expectedComparativeOperator '/parent[@code=1]/child[@common-leaf-name-int=5]' || 1 || [] || ['='] @@ -234,19 +235,19 @@ class CpsPathQuerySpec extends Specification { when: 'the given cps path is parsed using multiple conditions on same leaf' def result = CpsPathQuery.createFrom('/test[@same-name="value1" or @same-name="value2"]') then: 'two leaves are present with correct values' - assert result.leavesData.size() == 2 - assert result.leavesData[0].name == "same-name" - assert result.leavesData[0].value == "value1" - assert result.leavesData[1].name == "same-name" - assert result.leavesData[1].value == "value2" + assert result.leafConditions.size() == 2 + assert result.leafConditions[0].name == "same-name" + assert result.leafConditions[0].value == "value1" + assert result.leafConditions[1].name == "same-name" + assert result.leafConditions[1].value == "value2" } def 'Ordering of data leaves is preserved.'() { when: 'the given cps path is parsed' def result = CpsPathQuery.createFrom(cpsPath) then: 'the order of the data leaves is preserved' - assert result.leavesData[0].name == expectedFirstLeafName - assert result.leavesData[1].name == expectedSecondLeafName + assert result.leafConditions[0].name == expectedFirstLeafName + assert result.leafConditions[1].name == expectedSecondLeafName where: 'the following data is used' cpsPath || expectedFirstLeafName | expectedSecondLeafName '/test[@name1="value1" and @name2="value2"]' || 'name1' | 'name2' diff --git a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy index d62f337b75..29bb3c7b58 100644 --- a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy +++ b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-2024 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,21 +36,40 @@ class CpsPathUtilSpec extends Specification { 'single quotes' | "/parent/child[@common-leaf-name='123']" } - def 'Normalized parent xpaths'() { - when: 'a given xpath with #scenario is parsed' - def result = CpsPathUtil.getNormalizedParentXpath(xpath) + def 'Normalized parent paths of absolute paths'() { + when: 'a given cps path is parsed' + def result = CpsPathUtil.getNormalizedParentXpath(cpsPath) then: 'the result is the expected parent path' assert result == expectedParentPath - where: 'the following xpaths are used' - scenario | xpath || expectedParentPath - 'no child' | '/parent' || '' - 'child and parent' | '/parent/child' || '/parent' - 'grand child' | '/parent/child/grandChild' || '/parent/child' - 'parent & top is list element' | '/parent[@id=1]/child' || "/parent[@id='1']" - 'parent is list element' | '/parent/child[@id=1]/grandChild' || "/parent/child[@id='1']" - 'parent is list element with /' | "/parent/child[@id='a/b']/grandChild" || "/parent/child[@id='a/b']" - 'parent is list element with [' | "/parent/child[@id='a[b']/grandChild" || "/parent/child[@id='a[b']" - 'parent is list element using "' | '/parent/child[@id="x"]/grandChild' || "/parent/child[@id='x']" + where: 'the following absolute cps paths are used' + cpsPath || expectedParentPath + '/parent' || '' + '/parent/child' || '/parent' + '/parent/child/grandChild' || '/parent/child' + '/parent[@id=1]/child' || "/parent[@id='1']" + '/parent/child[@id=1]/grandChild' || "/parent/child[@id='1']" + '/parent/child/grandChild[@id="x"]' || "/parent/child" + '/parent/ancestor::grandparent' || '' + '/parent/child/ancestor::grandparent' || '/parent' + '/parent/child/name[text()="value"]' || '/parent' + } + + def 'Normalized parent paths of descendant paths'() { + when: 'a given cps path is parsed' + def result = CpsPathUtil.getNormalizedParentXpath(cpsPath) + then: 'the result is the expected parent path' + assert result == expectedParentPath + where: 'the following descendant cps paths are used' + cpsPath || expectedParentPath + '//parent' || '' + '//parent/child' || '//parent' + '//parent/child/grandChild' || '//parent/child' + '//parent[@id=1]/child' || "//parent[@id='1']" + '//parent/child[@id=1]/grandChild' || "//parent/child[@id='1']" + '//parent/child/grandChild[@id="x"]' || "//parent/child" + '//parent/ancestor::grandparent' || '' + '//parent/child/ancestor::grandparent' || '//parent' + '//parent/child/name[text()="value"]' || '//parent' } def 'Get node ID sequence for given xpath'() { @@ -67,17 +86,19 @@ class CpsPathUtilSpec extends Specification { 'parent is list element' | '/parent/child[@id=1]/grandChild' || ["parent","child","grandChild"] 'parent is list element with /' | "/parent/child[@id='a/b']/grandChild" || ["parent","child","grandChild"] 'parent is list element with [' | "/parent/child[@id='a[b']/grandChild" || ["parent","child","grandChild"] + 'does not include ancestor node' | '/parent/child/ancestor::grandparent' || ["parent","child"] } def 'Recognizing (absolute) xpaths to List elements'() { expect: 'check for list returns the correct values' assert CpsPathUtil.isPathToListElement(xpath) == expectList where: 'the following xpaths are used' - xpath || expectList - '/parent[@id=1]' || true - '/parent[@id=1]/child' || false - '/parent/child[@id=1]' || true - '//child[@id=1]' || false + xpath || expectList + '/parent[@id=1]' || true + '/parent[@id=1]/child' || false + '/parent/child[@id=1]' || true + '//child[@id=1]' || false + '/parent/ancestor::grandparent[@id=1]' || false } def 'Parsing Exception'() { |