Update license files, sonar plugin and fix tests
[aai/aai-common.git] / aai-core / src / main / java / org / openecomp / aai / query / builder / GraphTraversalBuilder.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * org.openecomp.aai
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.aai.query.builder;
22
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Optional;
28 import java.util.Set;
29
30 import org.apache.tinkerpop.gremlin.process.traversal.P;
31 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
32 import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin;
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.dsl.graph.__;
36 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
37 import org.apache.tinkerpop.gremlin.structure.Direction;
38 import org.apache.tinkerpop.gremlin.structure.Vertex;
39
40 import org.openecomp.aai.db.props.AAIProperties;
41 import org.openecomp.aai.exceptions.AAIException;
42 import org.openecomp.aai.introspection.Introspector;
43 import org.openecomp.aai.introspection.Loader;
44 import org.openecomp.aai.schema.enums.ObjectMetadata;
45 import org.openecomp.aai.schema.enums.PropertyMetadata;
46 import org.openecomp.aai.serialization.db.EdgeRule;
47 import org.openecomp.aai.serialization.db.EdgeRules;
48 import org.openecomp.aai.serialization.db.EdgeType;
49 import org.openecomp.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
50
51 /**
52  * The Class GraphTraversalBuilder.
53  */
54 public abstract class GraphTraversalBuilder extends QueryBuilder {
55
56         protected GraphTraversal<Vertex, Vertex> traversal = null;
57         protected Admin<Vertex, Vertex> completeTraversal = null;
58         private EdgeRules edgeRules = EdgeRules.getInstance();
59         
60         protected int parentStepIndex = 0;
61         protected int containerStepIndex = 0;
62         protected int stepIndex = 0;
63         
64         /**
65          * Instantiates a new graph traversal builder.
66          *
67          * @param loader the loader
68          */
69         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) {
70                 super(loader, source);
71                 
72                 traversal = __.start();
73                 
74         }
75         
76         /**
77          * Instantiates a new graph traversal builder.
78          *
79          * @param loader the loader
80          * @param start the start
81          */
82         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
83                 super(loader, source, start);
84                 
85                 traversal = __.start();
86                 
87         }
88
89         /**
90          * @{inheritDoc}
91          */
92         @Override
93         public QueryBuilder getVerticesByIndexedProperty(String key, Object value) {
94         
95                 return this.getVerticesByProperty(key, value);
96         }
97
98         /**
99          * @{inheritDoc}
100          */
101         @Override
102         public QueryBuilder getVerticesByIndexedProperty(String key, List<?> values) {
103                 return this.getVerticesByProperty(key, values);
104         }
105         
106         /**
107          * @{inheritDoc}
108          */
109         @Override
110         public QueryBuilder getVerticesByProperty(String key, Object value) {
111                 
112                 //this is because the index is registered as an Integer
113                 value = this.correctObjectType(value);
114                 
115                 traversal.has(key, value);
116                 
117                 stepIndex++;
118                 return this;
119         }
120         
121         /**
122          * @{inheritDoc}
123          */
124         @Override
125         public QueryBuilder getVerticesByProperty(final String key, final List<?> values) {
126                 
127                 //this is because the index is registered as an Integer
128                 List<Object> correctedValues = new ArrayList<>();
129                 for (Object item : values) {
130                         correctedValues.add(this.correctObjectType(item));
131                 }
132                 
133                 traversal.has(key, P.within(correctedValues));
134                 
135                 stepIndex++;
136                 return this;
137         }
138
139         /**
140          * @{inheritDoc}
141          */
142         @Override
143         public QueryBuilder getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
144                 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
145                 stepIndex++;
146                 return this;
147         }
148
149         /**
150          * @{inheritDoc}
151          */
152         @Override
153         public QueryBuilder getTypedVerticesByMap(String type, LinkedHashMap<String, String> map) {
154                 
155                 for (String key : map.keySet()) {
156                         traversal.has(key, map.get(key));
157                         stepIndex++;
158                 }
159                 traversal.has(AAIProperties.NODE_TYPE, type);
160                 stepIndex++;
161                 return this;
162         }
163
164         /**
165          * @{inheritDoc}
166          */
167         @Override
168         public QueryBuilder createDBQuery(Introspector obj) {
169                 this.createKeyQuery(obj);
170                 this.createContainerQuery(obj);
171                 return this;
172         }
173
174         /**
175          * @{inheritDoc}
176          */
177         @Override
178         public QueryBuilder createKeyQuery(Introspector obj) {
179                 Set<String> keys = obj.getKeys();
180                 Object val;
181                 for (String key : keys) {
182                         val = obj.getValue(key);
183                         Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
184                         if (metadata.isPresent()) {
185                                 //use the db name for the field rather than the object model
186                                 key = metadata.get();
187                         }
188                         if (val != null) {
189                                 //this is because the index is registered as an Integer
190                                 if (val.getClass().equals(Long.class)) {
191                                         traversal.has(key,new Integer(val.toString()));
192                                 } else {
193                                         traversal.has(key, val);
194                                 }
195                                 stepIndex++;
196                         }
197                 }
198                 return this;
199         }
200
201         @Override
202         public QueryBuilder exactMatchQuery(Introspector obj) {
203                 this.createKeyQuery(obj);
204                 allPropertiesQuery(obj);
205                 this.createContainerQuery(obj);
206                 return this;
207         }
208         
209         private void allPropertiesQuery(Introspector obj) {
210                 Set<String> props = obj.getProperties();
211                 Set<String> keys = obj.getKeys();
212                 Object val;
213                 for (String prop : props) {
214                         if (obj.isSimpleType(prop) && !keys.contains(prop)) {
215                                 val = obj.getValue(prop);
216                                 if (val != null) {
217                                         Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
218                                         if (metadata.isPresent()) {
219                                                 //use the db name for the field rather than the object model
220                                                 prop = metadata.get();
221                                         }
222                                         //this is because the index is registered as an Integer
223                                         if (val != null && val.getClass().equals(Long.class)) {
224                                                 traversal.has(prop,new Integer(val.toString()));
225                                         } else {
226                                                 traversal.has(prop, val);
227                                         }
228                                         stepIndex++;
229                                 }
230                         }
231                 }
232         }
233         
234         /**
235          * @{inheritDoc}
236          */
237         @Override
238         
239         public QueryBuilder createContainerQuery(Introspector obj) {
240                 String type = obj.getChildDBName();
241                 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
242                 if (abstractType != null) {
243                         String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
244                         traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
245                 } else {
246                         traversal.has(AAIProperties.NODE_TYPE, type);
247                 }
248                 stepIndex++;
249                 markContainer();
250                 return this;
251         }
252
253         /**
254          * @throws NoEdgeRuleFoundException 
255          * @throws AAIException 
256          * @{inheritDoc}
257          */
258         @Override
259         public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
260                 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
261                 if ("true".equals(isAbstractType)) {
262                         markParentBoundary();
263                         traversal.union(handleAbstractEdge(type, parent, child));
264                         stepIndex += 1;
265                 } else {
266                         this.edgeQuery(type, parent, child);
267                 }
268                 return this;
269                         
270         }
271         
272         private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
273                 String childName = child.getDbName();
274                 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
275                 String[] inheritors = inheritorMetadata.split(",");
276                 Traversal<Vertex, Vertex>[] unionTraversals = new Traversal[inheritors.length];
277                 int traversalIndex = 0;
278                 for (int i = 0; i < inheritors.length; i++) {
279                         String inheritor = inheritors[i];
280                         if (edgeRules.hasEdgeRule(inheritor, childName) || edgeRules.hasEdgeRule(childName, inheritor)) {
281                                 EdgeRule rule = edgeRules.getEdgeRule(type, inheritor, childName);
282                                 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
283                                 if (rule.getDirection().equals(Direction.OUT)) {
284                                         innerTraversal.out(rule.getLabel());
285                                 } else {
286                                         innerTraversal.in(rule.getLabel());
287                                 }
288                                 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
289                                 unionTraversals[traversalIndex] = innerTraversal;
290                                 traversalIndex++;
291                         }
292                 }
293                 if (traversalIndex < inheritors.length) {
294                         Traversal<Vertex, Vertex>[] temp = Arrays.copyOfRange(unionTraversals, 0, traversalIndex);
295                         unionTraversals = temp;
296                 }
297                 return unionTraversals;
298         }
299         /**
300          * @throws NoEdgeRuleFoundException 
301          * @throws AAIException 
302          * @{inheritDoc}
303          */
304         @Override
305         public QueryBuilder createEdgeTraversal(EdgeType type, Vertex parent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
306                 
307                 String nodeType = parent.<String>property(AAIProperties.NODE_TYPE).orElse(null);
308                 Introspector parentObj = loader.introspectorFromName(nodeType);
309                 this.edgeQuery(type, parentObj, child);
310                 return this;
311                         
312         }
313         
314         /**
315          * @{inheritDoc}
316          */
317         @Override
318         public QueryBuilder union(QueryBuilder... builder) {
319                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
320                 for (int i = 0; i < builder.length; i++) {
321                         traversals[i] = (GraphTraversal<Vertex, Vertex>)builder[i].getQuery();
322                 }
323                 this.traversal.union(traversals);
324                 stepIndex++;
325                 
326                 return this;
327         }
328         
329         /**
330          * @{inheritDoc}
331          */
332         @Override
333         public QueryBuilder where(QueryBuilder... builder) {
334                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
335                 for (int i = 0; i < builder.length; i++) {
336                         this.traversal.where((GraphTraversal<Vertex, Vertex>)builder[i].getQuery());
337                         stepIndex++;
338                 }
339                 
340                 return this;
341         }
342         
343         /**
344          * Edge query.
345          *
346          * @param outType the out type
347          * @param inType the in type
348          * @throws NoEdgeRuleFoundException 
349          * @throws AAIException 
350          */
351         private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj) throws AAIException, NoEdgeRuleFoundException {
352                 String outType = outObj.getDbName();
353                 String inType = inObj.getDbName();
354                 
355                 if (outObj.isContainer()) {
356                         outType = outObj.getChildDBName();
357                 }
358                 if (inObj.isContainer()) {
359                         inType = inObj.getChildDBName();
360                 }
361                 markParentBoundary();
362                 EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType);
363                 if (rule.getDirection().equals(Direction.OUT)) {
364                         traversal.out(rule.getLabel());
365                 } else {
366                         traversal.in(rule.getLabel());
367                 }
368                 stepIndex++;
369                 this.createContainerQuery(inObj);
370         }
371         
372         @Override
373         public QueryBuilder limit(long amount) {
374                 traversal.limit(amount);
375                 return this;
376         }
377
378         /**
379          * @{inheritDoc}
380          */
381         @Override
382         public <T> T getQuery() {
383                 return (T)this.traversal;
384         }
385         
386         /**
387          * @{inheritDoc}
388          */
389         @Override
390         public QueryBuilder getParentQuery() {
391
392                 return cloneQueryAtStep(parentStepIndex);
393         }
394         
395         @Override
396         public QueryBuilder getContainerQuery() {
397                 
398                 if (this.parentStepIndex == 0) {
399                         return removeQueryStepsBetween(0, containerStepIndex);
400                 } else {
401                         return cloneQueryAtStep(containerStepIndex);
402                 }
403         }
404         
405         /**
406          * @{inheritDoc}
407          */
408         @Override
409         public void markParentBoundary() {
410                 parentStepIndex = stepIndex;
411         }
412         
413         @Override
414         public void markContainer() {
415                 containerStepIndex = stepIndex;
416         }
417         
418         
419         /**
420          * @{inheritDoc}
421          */
422         @Override
423         public Vertex getStart() {
424                 return this.start;
425         }
426
427         protected int getParentStepIndex() {
428                 return parentStepIndex;
429         }
430
431         protected int getContainerStepIndex() {
432                 return containerStepIndex;
433         }
434
435         protected int getStepIndex() {
436                 return stepIndex;
437         }
438
439         protected abstract QueryBuilder cloneQueryAtStep(int index);
440         /**
441          * end is exclusive
442          * 
443          * @param start
444          * @param end
445          * @return
446          */
447         protected abstract QueryBuilder removeQueryStepsBetween(int start, int end);
448         
449         private void executeQuery() {
450                 
451                 Admin<Vertex, Vertex> admin;
452                 if (start != null) {
453                         admin = source.V(start).asAdmin();
454                 } else {
455                         admin = source.V().asAdmin();
456
457                 }
458                 
459                 TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
460                 
461                 this.completeTraversal = admin;
462         }
463         
464         @Override
465         public boolean hasNext() {
466                 if (this.completeTraversal == null) {
467                         executeQuery();
468                 }
469                 
470                 return this.completeTraversal.hasNext();
471         }
472         
473         @Override
474         public Vertex next() {
475                 if (this.completeTraversal == null) {
476                         executeQuery();
477                 }
478                 
479                 return this.completeTraversal.next();
480         }
481         
482         @Override
483         public List<Vertex> toList() {
484                 if (this.completeTraversal == null) {
485                         executeQuery();
486                 }
487                 
488                 return this.completeTraversal.toList();
489         }
490
491 }