99135962f82884e04d57295dd402bfe37282f8b4
[cps.git] / cps-path-parser / src / main / java / org / onap / cps / cpspath / parser / CpsPathBuilder.java
1 /*
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
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21
22 package org.onap.cps.cpspath.parser;
23
24 import static org.onap.cps.cpspath.parser.CpsPathPrefixType.DESCENDANT;
25
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;
37
38 public class CpsPathBuilder extends CpsPathBaseListener {
39
40     private static final String OPEN_BRACKET = "[";
41
42     private static final String CLOSE_BRACKET = "]";
43
44     private final CpsPathQuery cpsPathQuery = new CpsPathQuery();
45
46     private final List<CpsPathQuery.DataLeaf> leavesData = new ArrayList<>();
47
48     private final StringBuilder normalizedXpathBuilder = new StringBuilder();
49
50     private final StringBuilder normalizedAncestorPathBuilder = new StringBuilder();
51
52     private boolean processingAncestorAxis = false;
53
54     private final List<String> containerNames = new ArrayList<>();
55
56     private final List<String> booleanOperators = new ArrayList<>();
57
58     private final List<String> comparativeOperators = new ArrayList<>();
59
60     @Override
61     public void exitInvalidPostFix(final CpsPathParser.InvalidPostFixContext ctx) {
62         throw new PathParsingException(ctx.getText());
63     }
64
65     @Override
66     public void exitPrefix(final PrefixContext ctx) {
67         cpsPathQuery.setXpathPrefix(normalizedXpathBuilder.toString());
68     }
69
70     @Override
71     public void exitParent(final CpsPathParser.ParentContext ctx) {
72         cpsPathQuery.setNormalizedParentPath(normalizedXpathBuilder.toString());
73     }
74
75     @Override
76     public void exitIncorrectPrefix(final IncorrectPrefixContext ctx) {
77         throw new PathParsingException("CPS path can only start with one or two slashes (/)");
78     }
79
80     @Override
81     public void exitLeafCondition(final LeafConditionContext ctx) {
82         Object comparisonValue;
83         if (ctx.IntegerLiteral() != null) {
84             comparisonValue = Integer.valueOf(ctx.IntegerLiteral().getText());
85         } else if (ctx.StringLiteral() != null) {
86             final boolean wasWrappedInDoubleQuote = ctx.StringLiteral().getText().startsWith("\"");
87             comparisonValue = stripFirstAndLastCharacter(ctx.StringLiteral().getText());
88             if (wasWrappedInDoubleQuote) {
89                 comparisonValue = String.valueOf(comparisonValue).replace("'", "\\'");
90             }
91         } else {
92             throw new PathParsingException(
93                     "Unsupported comparison value encountered in expression" + ctx.getText());
94         }
95         leafContext(ctx.leafName(), comparisonValue);
96     }
97
98     @Override
99     public void exitBooleanOperators(final CpsPathParser.BooleanOperatorsContext ctx) {
100         booleanOperators.add(ctx.getText());
101     }
102
103     @Override
104     public void exitComparativeOperators(final CpsPathParser.ComparativeOperatorsContext ctx) {
105         comparativeOperators.add(ctx.getText());
106     }
107
108     @Override
109     public void exitDescendant(final DescendantContext ctx) {
110         cpsPathQuery.setCpsPathPrefixType(DESCENDANT);
111         cpsPathQuery.setDescendantName(normalizedXpathBuilder.substring(1));
112         normalizedXpathBuilder.insert(0, "/");
113     }
114
115     @Override
116     public void enterMultipleLeafConditions(final MultipleLeafConditionsContext ctx)  {
117         normalizedXpathBuilder.append(OPEN_BRACKET);
118         leavesData.clear();
119         booleanOperators.clear();
120         comparativeOperators.clear();
121     }
122
123     @Override
124     public void exitMultipleLeafConditions(final MultipleLeafConditionsContext ctx) {
125         normalizedXpathBuilder.append(CLOSE_BRACKET);
126         cpsPathQuery.setLeavesData(leavesData);
127     }
128
129     @Override
130     public void enterAncestorAxis(final AncestorAxisContext ctx) {
131         processingAncestorAxis = true;
132     }
133
134     @Override
135     public void exitAncestorAxis(final AncestorAxisContext ctx) {
136         cpsPathQuery.setAncestorSchemaNodeIdentifier(normalizedAncestorPathBuilder.substring(1));
137         processingAncestorAxis = false;
138     }
139
140     @Override
141     public void exitTextFunctionCondition(final TextFunctionConditionContext ctx) {
142         cpsPathQuery.setTextFunctionConditionLeafName(ctx.leafName().getText());
143         cpsPathQuery.setTextFunctionConditionValue(stripFirstAndLastCharacter(ctx.StringLiteral().getText()));
144     }
145
146     @Override
147     public void exitContainsFunctionCondition(final CpsPathParser.ContainsFunctionConditionContext ctx) {
148         cpsPathQuery.setContainsFunctionConditionLeafName(ctx.leafName().getText());
149         cpsPathQuery.setContainsFunctionConditionValue(stripFirstAndLastCharacter(ctx.StringLiteral().getText()));
150     }
151
152     @Override
153     public void enterListElementRef(final CpsPathParser.ListElementRefContext ctx) {
154         normalizedXpathBuilder.append(OPEN_BRACKET);
155         if (processingAncestorAxis) {
156             normalizedAncestorPathBuilder.append(OPEN_BRACKET);
157         }
158     }
159
160     @Override
161     public void exitListElementRef(final CpsPathParser.ListElementRefContext ctx) {
162         normalizedXpathBuilder.append(CLOSE_BRACKET);
163         if (processingAncestorAxis) {
164             normalizedAncestorPathBuilder.append(CLOSE_BRACKET);
165         }
166     }
167
168     CpsPathQuery build() {
169         cpsPathQuery.setNormalizedXpath(normalizedXpathBuilder.toString());
170         cpsPathQuery.setContainerNames(containerNames);
171         cpsPathQuery.setBooleanOperators(booleanOperators);
172         cpsPathQuery.setComparativeOperators(comparativeOperators);
173         return cpsPathQuery;
174     }
175
176     private static String stripFirstAndLastCharacter(final String wrappedString) {
177         return wrappedString.substring(1, wrappedString.length() - 1);
178     }
179
180     @Override
181     public void exitContainerName(final CpsPathParser.ContainerNameContext ctx) {
182         final String containerName = ctx.getText();
183         normalizedXpathBuilder.append("/")
184                 .append(containerName);
185         containerNames.add(containerName);
186         if (processingAncestorAxis) {
187             normalizedAncestorPathBuilder.append("/").append(containerName);
188         }
189     }
190
191     private void leafContext(final CpsPathParser.LeafNameContext ctx, final Object comparisonValue) {
192         leavesData.add(new CpsPathQuery.DataLeaf(ctx.getText(), comparisonValue));
193         appendCondition(normalizedXpathBuilder, ctx.getText(), comparisonValue);
194         if (processingAncestorAxis) {
195             appendCondition(normalizedAncestorPathBuilder, ctx.getText(), comparisonValue);
196         }
197     }
198
199     private void appendCondition(final StringBuilder currentNormalizedPathBuilder, final String name,
200                                  final Object value) {
201         final char lastCharacter = currentNormalizedPathBuilder.charAt(currentNormalizedPathBuilder.length() - 1);
202         final boolean isStartOfExpression = lastCharacter == '[';
203         if (!isStartOfExpression) {
204             currentNormalizedPathBuilder.append(" ").append(getLastElement(booleanOperators)).append(" ");
205         }
206         currentNormalizedPathBuilder.append("@")
207                                     .append(name)
208                                     .append(getLastElement(comparativeOperators))
209                                     .append("'")
210                                     .append(value)
211                                     .append("'");
212     }
213
214     private String getLastElement(final List<String> listOfStrings) {
215         return listOfStrings.get(listOfStrings.size() - 1);
216     }
217 }