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