2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2023 Nordix Foundation
4 * Modifications Copyright (C) 2023 TechMahindra Ltd
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.cps.cpspath.parser;
24 import static org.onap.cps.cpspath.parser.CpsPathPrefixType.DESCENDANT;
26 import java.util.ArrayList;
27 import java.util.List;
28 import org.onap.cps.cpspath.parser.antlr4.CpsPathBaseListener;
29 import org.onap.cps.cpspath.parser.antlr4.CpsPathParser;
30 import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.AncestorAxisContext;
31 import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.DescendantContext;
32 import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.IncorrectPrefixContext;
33 import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.LeafConditionContext;
34 import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.MultipleLeafConditionsContext;
35 import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.PrefixContext;
36 import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.TextFunctionConditionContext;
38 public class CpsPathBuilder extends CpsPathBaseListener {
40 private static final String OPEN_BRACKET = "[";
42 private static final String CLOSE_BRACKET = "]";
44 private final CpsPathQuery cpsPathQuery = new CpsPathQuery();
46 private final List<CpsPathQuery.DataLeaf> leavesData = new ArrayList<>();
48 private final StringBuilder normalizedXpathBuilder = new StringBuilder();
50 private final StringBuilder normalizedAncestorPathBuilder = new StringBuilder();
52 private boolean processingAncestorAxis = false;
54 private final List<String> containerNames = new ArrayList<>();
56 private final List<String> booleanOperators = new ArrayList<>();
58 private final List<String> comparativeOperators = new ArrayList<>();
61 public void exitInvalidPostFix(final CpsPathParser.InvalidPostFixContext ctx) {
62 throw new PathParsingException(ctx.getText());
66 public void exitPrefix(final PrefixContext ctx) {
67 cpsPathQuery.setXpathPrefix(normalizedXpathBuilder.toString());
71 public void exitParent(final CpsPathParser.ParentContext ctx) {
72 cpsPathQuery.setNormalizedParentPath(normalizedXpathBuilder.toString());
76 public void exitIncorrectPrefix(final IncorrectPrefixContext ctx) {
77 throw new PathParsingException("CPS path can only start with one or two slashes (/)");
81 public void exitLeafCondition(final LeafConditionContext ctx) {
82 final Object comparisonValue;
83 if (ctx.IntegerLiteral() != null) {
84 comparisonValue = Integer.valueOf(ctx.IntegerLiteral().getText());
85 } else if (ctx.StringLiteral() != null) {
86 comparisonValue = unwrapQuotedString(ctx.StringLiteral().getText());
88 throw new PathParsingException("Unsupported comparison value encountered in expression" + ctx.getText());
90 leafContext(ctx.leafName(), comparisonValue);
94 public void exitBooleanOperators(final CpsPathParser.BooleanOperatorsContext ctx) {
95 booleanOperators.add(ctx.getText());
99 public void exitComparativeOperators(final CpsPathParser.ComparativeOperatorsContext ctx) {
100 comparativeOperators.add(ctx.getText());
104 public void exitDescendant(final DescendantContext ctx) {
105 cpsPathQuery.setCpsPathPrefixType(DESCENDANT);
106 cpsPathQuery.setDescendantName(normalizedXpathBuilder.substring(1));
107 normalizedXpathBuilder.insert(0, "/");
111 public void enterMultipleLeafConditions(final MultipleLeafConditionsContext ctx) {
112 normalizedXpathBuilder.append(OPEN_BRACKET);
114 booleanOperators.clear();
115 comparativeOperators.clear();
119 public void exitMultipleLeafConditions(final MultipleLeafConditionsContext ctx) {
120 normalizedXpathBuilder.append(CLOSE_BRACKET);
121 cpsPathQuery.setLeavesData(leavesData);
125 public void enterAncestorAxis(final AncestorAxisContext ctx) {
126 processingAncestorAxis = true;
130 public void exitAncestorAxis(final AncestorAxisContext ctx) {
131 cpsPathQuery.setAncestorSchemaNodeIdentifier(normalizedAncestorPathBuilder.substring(1));
132 processingAncestorAxis = false;
136 public void exitTextFunctionCondition(final TextFunctionConditionContext ctx) {
137 cpsPathQuery.setTextFunctionConditionLeafName(ctx.leafName().getText());
138 cpsPathQuery.setTextFunctionConditionValue(unwrapQuotedString(ctx.StringLiteral().getText()));
142 public void exitContainsFunctionCondition(final CpsPathParser.ContainsFunctionConditionContext ctx) {
143 cpsPathQuery.setContainsFunctionConditionLeafName(ctx.leafName().getText());
144 cpsPathQuery.setContainsFunctionConditionValue(unwrapQuotedString(ctx.StringLiteral().getText()));
148 public void enterListElementRef(final CpsPathParser.ListElementRefContext ctx) {
149 normalizedXpathBuilder.append(OPEN_BRACKET);
150 if (processingAncestorAxis) {
151 normalizedAncestorPathBuilder.append(OPEN_BRACKET);
156 public void exitListElementRef(final CpsPathParser.ListElementRefContext ctx) {
157 normalizedXpathBuilder.append(CLOSE_BRACKET);
158 if (processingAncestorAxis) {
159 normalizedAncestorPathBuilder.append(CLOSE_BRACKET);
163 CpsPathQuery build() {
164 cpsPathQuery.setNormalizedXpath(normalizedXpathBuilder.toString());
165 cpsPathQuery.setContainerNames(containerNames);
166 cpsPathQuery.setBooleanOperators(booleanOperators);
167 cpsPathQuery.setComparativeOperators(comparativeOperators);
172 public void exitContainerName(final CpsPathParser.ContainerNameContext ctx) {
173 final String containerName = ctx.getText();
174 normalizedXpathBuilder.append("/")
175 .append(containerName);
176 containerNames.add(containerName);
177 if (processingAncestorAxis) {
178 normalizedAncestorPathBuilder.append("/").append(containerName);
182 private void leafContext(final CpsPathParser.LeafNameContext ctx, final Object comparisonValue) {
183 leavesData.add(new CpsPathQuery.DataLeaf(ctx.getText(), comparisonValue));
184 appendCondition(normalizedXpathBuilder, ctx.getText(), comparisonValue);
185 if (processingAncestorAxis) {
186 appendCondition(normalizedAncestorPathBuilder, ctx.getText(), comparisonValue);
190 private void appendCondition(final StringBuilder currentNormalizedPathBuilder, final String name,
191 final Object value) {
192 final char lastCharacter = currentNormalizedPathBuilder.charAt(currentNormalizedPathBuilder.length() - 1);
193 final boolean isStartOfExpression = lastCharacter == '[';
194 if (!isStartOfExpression) {
195 currentNormalizedPathBuilder.append(" ").append(getLastElement(booleanOperators)).append(" ");
197 currentNormalizedPathBuilder.append("@")
199 .append(getLastElement(comparativeOperators))
201 .append(value.toString().replace("'", "''"))
205 private static String getLastElement(final List<String> listOfStrings) {
206 return listOfStrings.get(listOfStrings.size() - 1);
209 private static String unwrapQuotedString(final String wrappedString) {
210 final boolean wasWrappedInSingleQuote = wrappedString.startsWith("'");
211 final String value = stripFirstAndLastCharacter(wrappedString);
212 if (wasWrappedInSingleQuote) {
213 return value.replace("''", "'");
215 return value.replace("\"\"", "\"");
219 private static String stripFirstAndLastCharacter(final String wrappedString) {
220 return wrappedString.substring(1, wrappedString.length() - 1);