Merge "Release 1.14.0 maven artifact"
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / query / builder / TraversalQuery.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright © 2024 DEUTSCHE TELEKOM AG.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *    http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.aai.query.builder;
24
25 import java.io.UnsupportedEncodingException;
26 import java.net.URI;
27 import java.util.List;
28 import java.util.stream.Collectors;
29
30 import javax.ws.rs.core.MultivaluedMap;
31
32 import org.apache.tinkerpop.gremlin.process.traversal.Step;
33 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
35 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep;
36 import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
37 import org.apache.tinkerpop.gremlin.structure.Vertex;
38 import org.onap.aai.exceptions.AAIException;
39 import org.onap.aai.introspection.Introspector;
40 import org.onap.aai.introspection.Loader;
41 import org.onap.aai.parsers.query.QueryParser;
42 import org.onap.aai.parsers.query.TraversalStrategy;
43
44 /**
45  * The Class TraversalQuery.
46  */
47 public class TraversalQuery<E> extends GraphTraversalBuilder<E> {
48
49     /**
50      * Instantiates a new traversal query.
51      *
52      * @param loader the loader
53      */
54
55     public TraversalQuery(Loader loader, GraphTraversalSource source) {
56         super(loader, source);
57         this.factory = new TraversalStrategy(this.loader, this);
58     }
59
60     public TraversalQuery(Loader loader, GraphTraversalSource source, GraphTraversal<Vertex, E> traversal) {
61         super(loader, source, traversal);
62         this.factory = new TraversalStrategy(this.loader, this);
63     }
64
65     /**
66      * Instantiates a new traversal query.
67      *
68      * @param loader the loader
69      * @param start the start
70      */
71     public TraversalQuery(Loader loader, GraphTraversalSource source, Vertex start) {
72         super(loader, source, start);
73         this.factory = new TraversalStrategy(this.loader, this);
74     }
75
76     protected TraversalQuery(GraphTraversal<Vertex, E> traversal, Loader loader, GraphTraversalSource source,
77             GraphTraversalBuilder<E> gtb) {
78         super(loader, source);
79         this.traversal = traversal;
80         this.stepIndex = gtb.getStepIndex();
81         this.parentStepIndex = gtb.getParentStepIndex();
82         this.containerStepIndex = gtb.getContainerStepIndex();
83         this.factory = new TraversalStrategy(this.loader, this);
84         this.start = gtb.getStart();
85     }
86
87     /**
88      * @{inheritDoc}
89      */
90     @Override
91     public QueryParser createQueryFromURI(URI uri) throws UnsupportedEncodingException, AAIException {
92         return factory.buildURIParser(uri);
93     }
94
95     /**
96      * @{inheritDoc}
97      */
98     @Override
99     public QueryParser createQueryFromRelationship(Introspector relationship)
100             throws UnsupportedEncodingException, AAIException {
101         return factory.buildRelationshipParser(relationship);
102     }
103
104     /**
105      * @{inheritDoc}
106      */
107     @Override
108     public QueryParser createQueryFromURI(URI uri, MultivaluedMap<String, String> queryParams)
109             throws UnsupportedEncodingException, AAIException {
110         return factory.buildURIParser(uri, queryParams);
111     }
112
113     /**
114      * @{inheritDoc}
115      */
116     @Override
117     public QueryParser createQueryFromObjectName(String objName) {
118         return factory.buildObjectNameParser(objName);
119     }
120
121     /**
122      * @{inheritDoc}
123      */
124     @Override
125     public QueryBuilder<E> newInstance(Vertex start) {
126         return new TraversalQuery<>(loader, source, start);
127     }
128
129     /**
130      * @{inheritDoc}
131      */
132     @Override
133     public QueryBuilder<E> newInstance() {
134         return new TraversalQuery<>(loader, source);
135     }
136
137     @Override
138     public QueryBuilder<E> fold() {
139         return this;
140     }
141
142     @Override
143     public QueryBuilder<E> id() {
144         return this;
145     }
146
147     @Override
148     protected QueryBuilder<E> cloneQueryAtStep(int index) {
149         GraphTraversal.Admin<Vertex, E> cloneAdmin = getCloneAdmin(index);
150         return new TraversalQuery<>(cloneAdmin, loader, source, this);
151     }
152
153     protected GraphTraversal.Admin<Vertex, E> getCloneAdmin(int index) {
154         int idx = index;
155
156         if (idx == 0) {
157             idx = stepIndex;
158         }
159
160         GraphTraversal<Vertex, E> clone = this.traversal.asAdmin().clone();
161         GraphTraversal.Admin<Vertex, E> cloneAdmin = clone.asAdmin();
162         List<Step> steps = cloneAdmin.getSteps();
163
164         for (int i = steps.size() - 1; i >= idx; i--) {
165             cloneAdmin.removeStep(i);
166         }
167         return cloneAdmin;
168     }
169
170     @Override
171     protected QueryBuilder<E> removeQueryStepsBetween(int start, int end) {
172         if(start > 0) {
173             throw new IllegalArgumentException("A start index > 0 is currently not supported");
174         }
175         GraphTraversal<Vertex, E> clone = this.traversal.asAdmin().clone();
176         GraphTraversal.Admin<Vertex, E> cloneAdmin = clone.asAdmin();
177
178         List<Step> steps = cloneAdmin.getSteps();
179
180         // TODO: Use containerAdjusted start index to support start > 0
181         // TraversalQueryTest#removeQueryStepsBetweenTest27 can guide the implementation
182         int containerAdjusted = start > 0
183             ? getContainerAdjustedStart(cloneAdmin, steps, start)
184             : start;
185         for (int i = start; i < end - 1; i++) {
186             Step step = steps.get(start);
187             if (step instanceof HasStep) {
188                 List<HasContainer> hasContainers = ((HasStep) step).getHasContainers();
189                 int hasContainerSize = hasContainers.size();
190                 boolean isEndWithinHasContainer = isEndWithinHasContainer(end, i, hasContainers);
191                 if (isEndWithinHasContainer) {
192                     int splitPosition = end - i - 1;
193                     splitHasContainerAtPosition(cloneAdmin, hasContainers, start, splitPosition);
194                     i += (hasContainerSize - splitPosition);
195                 } else {
196                     cloneAdmin.removeStep(start);
197                     i += (hasContainerSize - 1);
198                 }
199             } else {
200                 cloneAdmin.removeStep(start);
201             }
202         }
203         return new TraversalQuery<>(cloneAdmin, loader, source, this);
204     }
205
206     private boolean isEndWithinHasContainer(int end, int i, List<HasContainer> hasContainers) {
207         return (i + hasContainers.size()) >= end - 1;
208     }
209
210     /**
211      * Since the has-step inlining that was introduced with tinkerpop version 3.2.4,
212      * a Step can be a HasContainer that can contain multiple steps.
213      * The start index needs to be adjusted to account for this fact
214      * @param cloneAdmin
215      * @param steps
216      * @param start
217      * @return
218      */
219     private int getContainerAdjustedStart(GraphTraversal.Admin<Vertex, E> cloneAdmin, List<Step> steps, int start) {
220         int adjustedIndex = start;
221         for (int i = 0; i < start; i++) {
222             Step step = steps.get(i);
223             if (step instanceof HasStep) {
224                 if(isEndWithinHasContainer(adjustedIndex, i, ((HasStep) step).getHasContainers())){
225                     adjustedIndex -= 1;
226                 }
227                 adjustedIndex -= ((HasStep) step).getHasContainers().size();
228             }
229         }
230         return adjustedIndex;
231     }
232
233     /**
234      * Split the hasContainer at the provided position and append everything
235      * after it to the step Array
236      * @param cloneAdmin
237      * @param hasContainers
238      * @param splitPosition
239      */
240     private void splitHasContainerAtPosition(GraphTraversal.Admin<Vertex, E> cloneAdmin,
241             List<HasContainer> hasContainers, int start, int splitPosition) {
242         List<HasContainer> newContainers = hasContainers.stream()
243                 .skip(splitPosition)
244                 .collect(Collectors.toList());
245         int replaceIndex = start;
246         cloneAdmin.removeStep(replaceIndex);
247         for (HasContainer hasContainer : newContainers) {
248             cloneAdmin.addStep(replaceIndex, new HasStep<>(cloneAdmin, hasContainer));
249             replaceIndex++;
250         }
251     }
252
253 }