2 * ============LICENSE_START=======================================================
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
23 package org.onap.aai.query.builder;
25 import java.io.UnsupportedEncodingException;
27 import java.util.List;
28 import java.util.stream.Collectors;
30 import javax.ws.rs.core.MultivaluedMap;
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;
45 * The Class TraversalQuery.
47 public class TraversalQuery<E> extends GraphTraversalBuilder<E> {
50 * Instantiates a new traversal query.
52 * @param loader the loader
55 public TraversalQuery(Loader loader, GraphTraversalSource source) {
56 super(loader, source);
57 this.factory = new TraversalStrategy(this.loader, this);
60 public TraversalQuery(Loader loader, GraphTraversalSource source, GraphTraversal<Vertex, E> traversal) {
61 super(loader, source, traversal);
62 this.factory = new TraversalStrategy(this.loader, this);
66 * Instantiates a new traversal query.
68 * @param loader the loader
69 * @param start the start
71 public TraversalQuery(Loader loader, GraphTraversalSource source, Vertex start) {
72 super(loader, source, start);
73 this.factory = new TraversalStrategy(this.loader, this);
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();
91 public QueryParser createQueryFromURI(URI uri) throws UnsupportedEncodingException, AAIException {
92 return factory.buildURIParser(uri);
99 public QueryParser createQueryFromRelationship(Introspector relationship)
100 throws UnsupportedEncodingException, AAIException {
101 return factory.buildRelationshipParser(relationship);
108 public QueryParser createQueryFromURI(URI uri, MultivaluedMap<String, String> queryParams)
109 throws UnsupportedEncodingException, AAIException {
110 return factory.buildURIParser(uri, queryParams);
117 public QueryParser createQueryFromObjectName(String objName) {
118 return factory.buildObjectNameParser(objName);
125 public QueryBuilder<E> newInstance(Vertex start) {
126 return new TraversalQuery<>(loader, source, start);
133 public QueryBuilder<E> newInstance() {
134 return new TraversalQuery<>(loader, source);
138 public QueryBuilder<E> fold() {
143 public QueryBuilder<E> id() {
148 protected QueryBuilder<E> cloneQueryAtStep(int index) {
149 GraphTraversal.Admin<Vertex, E> cloneAdmin = getCloneAdmin(index);
150 return new TraversalQuery<>(cloneAdmin, loader, source, this);
153 protected GraphTraversal.Admin<Vertex, E> getCloneAdmin(int index) {
160 GraphTraversal<Vertex, E> clone = this.traversal.asAdmin().clone();
161 GraphTraversal.Admin<Vertex, E> cloneAdmin = clone.asAdmin();
162 List<Step> steps = cloneAdmin.getSteps();
164 for (int i = steps.size() - 1; i >= idx; i--) {
165 cloneAdmin.removeStep(i);
171 protected QueryBuilder<E> removeQueryStepsBetween(int start, int end) {
173 throw new IllegalArgumentException("A start index > 0 is currently not supported");
175 GraphTraversal<Vertex, E> clone = this.traversal.asAdmin().clone();
176 GraphTraversal.Admin<Vertex, E> cloneAdmin = clone.asAdmin();
178 List<Step> steps = cloneAdmin.getSteps();
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)
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);
196 cloneAdmin.removeStep(start);
197 i += (hasContainerSize - 1);
200 cloneAdmin.removeStep(start);
203 return new TraversalQuery<>(cloneAdmin, loader, source, this);
206 private boolean isEndWithinHasContainer(int end, int i, List<HasContainer> hasContainers) {
207 return (i + hasContainers.size()) >= end - 1;
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
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())){
227 adjustedIndex -= ((HasStep) step).getHasContainers().size();
230 return adjustedIndex;
234 * Split the hasContainer at the provided position and append everything
235 * after it to the step Array
237 * @param hasContainers
238 * @param splitPosition
240 private void splitHasContainerAtPosition(GraphTraversal.Admin<Vertex, E> cloneAdmin,
241 List<HasContainer> hasContainers, int start, int splitPosition) {
242 List<HasContainer> newContainers = hasContainers.stream()
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));