Update the license for 2017-2018 license
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / query / builder / GraphTraversalBuilder.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 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 package org.onap.aai.query.builder;
21
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Optional;
26 import java.util.Set;
27
28 import org.apache.tinkerpop.gremlin.process.traversal.P;
29 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
30 import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin;
31 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
32 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
33 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
34 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
35 import org.apache.tinkerpop.gremlin.structure.Direction;
36 import org.apache.tinkerpop.gremlin.structure.Edge;
37 import org.apache.tinkerpop.gremlin.structure.Vertex;
38 import org.onap.aai.db.props.AAIProperties;
39 import org.onap.aai.exceptions.AAIException;
40 import org.onap.aai.introspection.Introspector;
41 import org.onap.aai.introspection.Loader;
42 import org.onap.aai.schema.enums.ObjectMetadata;
43 import org.onap.aai.schema.enums.PropertyMetadata;
44 import org.onap.aai.serialization.db.EdgeRule;
45 import org.onap.aai.serialization.db.EdgeRules;
46 import org.onap.aai.serialization.db.EdgeType;
47 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
48
49 /**
50  * The Class GraphTraversalBuilder.
51  */
52 public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> {
53
54         protected GraphTraversal<Vertex, E> traversal = null;
55         protected Admin<Vertex, E> completeTraversal = null;
56         private EdgeRules edgeRules = EdgeRules.getInstance();
57         
58         protected int parentStepIndex = 0;
59         protected int containerStepIndex = 0;
60         protected int stepIndex = 0;
61         
62         /**
63          * Instantiates a new graph traversal builder.
64          *
65          * @param loader the loader
66          */
67         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) {
68                 super(loader, source);
69                 
70                 traversal = (GraphTraversal<Vertex, E>) __.<E>start();
71                 
72         }
73         
74         /**
75          * Instantiates a new graph traversal builder.
76          *
77          * @param loader the loader
78          */
79         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, EdgeRules edgeRules) {
80                 super(loader, source);
81                 this.edgeRules = edgeRules;
82                 traversal = (GraphTraversal<Vertex, E>) __.<E>start();
83                 
84         }
85         
86         /**
87          * Instantiates a new graph traversal builder.
88          *
89          * @param loader the loader
90          * @param start the start
91          */
92         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
93                 super(loader, source, start);
94                 
95                 traversal = (GraphTraversal<Vertex, E>) __.__(start);
96                 
97         }
98         
99         /**
100          * Instantiates a new graph traversal builder.
101          *
102          * @param loader the loader
103          * @param start the start
104          */
105         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start, EdgeRules edgeRules) {
106                 super(loader, source, start);
107                 this.edgeRules = edgeRules;
108                 traversal = (GraphTraversal<Vertex, E>) __.__(start);
109                 
110         }
111
112         /**
113          * @{inheritDoc}
114          */
115         @Override
116         public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
117                 
118                 // correct value call because the index is registered as an Integer
119                 traversal.has(key, this.correctObjectType(value));
120                 
121                 stepIndex++;
122                 return (QueryBuilder<Vertex>) this;
123         }
124         
125         /**
126          * @{inheritDoc}
127          */
128         @Override
129         public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
130                 
131                 //this is because the index is registered as an Integer
132                 List<Object> correctedValues = new ArrayList<>();
133                 for (Object item : values) {
134                         correctedValues.add(this.correctObjectType(item));
135                 }
136                 
137                 traversal.has(key, P.within(correctedValues));
138                 
139                 stepIndex++;
140                 return (QueryBuilder<Vertex>) this;
141         }
142         
143         /**
144          * @{inheritDoc}
145          */
146         @Override
147         public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
148                 
149                 // correct value call because the index is registered as an Integer
150                 traversal.has(key, P.neq(this.correctObjectType(value)));
151                 
152                 stepIndex++;
153                 return (QueryBuilder<Vertex>) this;
154         }
155         
156         /**
157          * @{inheritDoc}
158          */
159         @Override
160         public QueryBuilder<Vertex> getVerticesExcludeByProperty(final String key, final List<?> values) {
161                 
162                 //this is because the index is registered as an Integer
163                 List<Object> correctedValues = new ArrayList<>();
164                 for (Object item : values) {
165                         correctedValues.add(this.correctObjectType(item));
166                 }
167                 
168                 traversal.has(key, P.without(correctedValues));
169                 
170                 stepIndex++;
171                 return (QueryBuilder<Vertex>) this;
172         }
173
174         /**
175          * @{inheritDoc}
176          */
177         @Override
178         public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
179                 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
180                 stepIndex++;
181                 return (QueryBuilder<Vertex>) this;
182         }
183
184         /**
185          * @{inheritDoc}
186          */
187         @Override
188         public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
189                 
190                 for (Map.Entry<String, String> es : map.entrySet()) {
191                         traversal.has(es.getKey(), es.getValue());
192                         stepIndex++;
193                 }
194                 traversal.has(AAIProperties.NODE_TYPE, type);
195                 stepIndex++;
196                 return (QueryBuilder<Vertex>) this;
197         }
198
199         /**
200          * @{inheritDoc}
201          */
202         @Override
203         public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
204                 Set<String> keys = obj.getKeys();
205                 Object val;
206                 for (String key : keys) {
207                         val = obj.getValue(key);
208                         Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
209                         if (metadata.isPresent()) {
210                                 //use the db name for the field rather than the object model
211                                 key = metadata.get();
212                         }
213                         if (val != null) {
214                                 //this is because the index is registered as an Integer
215                                 if (val.getClass().equals(Long.class)) {
216                                         traversal.has(key,new Integer(val.toString()));
217                                 } else {
218                                         traversal.has(key, val);
219                                 }
220                                 stepIndex++;
221                         }
222                 }
223                 return (QueryBuilder<Vertex>) this;
224         }
225
226         @Override
227         public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
228                 this.createKeyQuery(obj);
229                 allPropertiesQuery(obj);
230                 this.createContainerQuery(obj);
231                 return (QueryBuilder<Vertex>) this;
232         }
233         
234         private void allPropertiesQuery(Introspector obj) {
235                 Set<String> props = obj.getProperties();
236                 Set<String> keys = obj.getKeys();
237                 Object val;
238                 for (String prop : props) {
239                         if (obj.isSimpleType(prop) && !keys.contains(prop)) {
240                                 val = obj.getValue(prop);
241                                 if (val != null) {
242                                         Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
243                                         if (metadata.isPresent()) {
244                                                 //use the db name for the field rather than the object model
245                                                 prop = metadata.get();
246                                         }
247                                         //this is because the index is registered as an Integer
248                                         if (val.getClass().equals(Long.class)) {
249                                                 traversal.has(prop,new Integer(val.toString()));
250                                         } else {
251                                                 traversal.has(prop, val);
252                                         }
253                                         stepIndex++;
254                                 }
255                         }
256                 }
257         }
258         
259         /**
260          * @{inheritDoc}
261          */
262         @Override
263         
264         public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
265                 String type = obj.getChildDBName();
266                 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
267                 if (abstractType != null) {
268                         String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
269                         traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
270                 } else {
271                         traversal.has(AAIProperties.NODE_TYPE, type);
272                 }
273                 stepIndex++;
274                 markContainer();
275                 return (QueryBuilder<Vertex>) this;
276         }
277
278         /**
279          * @throws NoEdgeRuleFoundException 
280          * @throws AAIException 
281          * @{inheritDoc}
282          */
283         @Override
284         public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException {
285                 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
286                 if ("true".equals(isAbstractType)) {
287                         markParentBoundary();
288                         traversal.union(handleAbstractEdge(type, parent, child));
289                         stepIndex++;
290                 } else {
291                         this.edgeQueryToVertex(type, parent, child, null);
292                 }
293                 return (QueryBuilder<Vertex>) this;
294                         
295         }
296
297         /**
298          *
299          * @{inheritDoc}
300          */
301         @Override
302         public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, List<String> labels) throws AAIException {
303                 this.edgeQueryToVertex(type, out, in, labels);
304                 return (QueryBuilder<Vertex>) this;
305         }
306
307
308         private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
309                 String childName = child.getDbName();
310                 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
311                 String[] inheritors = inheritorMetadata.split(",");
312                 List<Traversal<Vertex, Vertex>> unionTraversals = new ArrayList<>(inheritors.length);
313
314                 for (int i = 0; i < inheritors.length; i++) {
315                         String inheritor = inheritors[i];
316                         if (edgeRules.hasEdgeRule(inheritor, childName) || edgeRules.hasEdgeRule(childName, inheritor)) {
317                                 Map<String, EdgeRule> rules = edgeRules.getEdgeRules(type, inheritor, childName);
318                                 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
319                                 
320                                 final List<String> inLabels = new ArrayList<>();
321                                 final List<String> outLabels = new ArrayList<>();
322
323                                 rules.forEach((k,v) -> {
324                                         if (v.getDirection().equals(Direction.IN)) {
325                                                 inLabels.add(k);
326                                         } else {
327                                                 outLabels.add(k);
328                                         }
329                                 } );
330                 
331                                 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
332                                         innerTraversal.out(outLabels.toArray(new String[outLabels.size()]));
333                                 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
334                                         innerTraversal.in(inLabels.toArray(new String[inLabels.size()]));
335                                 } else {                                
336                                         innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()])));
337                                 }
338                                 
339                                 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
340                                 unionTraversals.add(innerTraversal);
341                         }
342                 }
343                 
344                 return unionTraversals.toArray(new Traversal[unionTraversals.size()]);
345         }
346
347         @Override
348         public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType, List<String> labels) throws AAIException {
349                 Introspector outObj = loader.introspectorFromName(outNodeType);
350                 Introspector inObj = loader.introspectorFromName(inNodeType);
351                 this.edgeQuery(type, outObj, inObj, labels);
352
353                 return (QueryBuilder<Edge>)this;
354         }
355
356
357         /**
358          * @{inheritDoc}
359          */
360         @Override
361         public QueryBuilder<E> union(QueryBuilder... builder) {
362                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
363                 for (int i = 0; i < builder.length; i++) {
364                         traversals[i] = (GraphTraversal<Vertex, Vertex>)builder[i].getQuery();
365                 }
366                 this.traversal.union(traversals);
367                 stepIndex++;
368                 
369                 return this;
370         }
371         
372         /**
373          * @{inheritDoc}
374          */
375         @Override
376         public QueryBuilder<E> where(QueryBuilder... builder) {
377                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
378                 for (int i = 0; i < builder.length; i++) {
379                         this.traversal.where((GraphTraversal<Vertex, Vertex>)builder[i].getQuery());
380                         stepIndex++;
381                 }
382                 
383                 return this;
384         }
385         
386         @Override
387         public QueryBuilder<E> store(String name) {
388                 
389                 this.traversal.store(name);
390                 stepIndex++;
391                 
392                 return this;
393         }
394         
395         @Override
396         public QueryBuilder<E> cap(String name) {
397                 this.traversal.cap(name);
398                 stepIndex++;
399                 
400                 return this;
401         }
402         
403         @Override
404         public QueryBuilder<E> unfold() {
405                 this.traversal.unfold();
406                 stepIndex++;
407                         
408                 return this;
409         }
410         
411         @Override
412         public QueryBuilder<E> dedup() {
413                 
414                 this.traversal.dedup();
415                 stepIndex++;
416                 
417                 return this;
418         }
419         
420         @Override
421         public QueryBuilder<E> emit() {
422                 
423                 this.traversal.emit();
424                 stepIndex++;
425                 
426                 return this;
427                 
428         }
429         
430         @Override
431         public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
432                 
433                 this.traversal.repeat((GraphTraversal<Vertex, E>)builder.getQuery());
434                 stepIndex++;
435
436                 return this;
437         }
438         
439         @Override
440         public QueryBuilder<E> until(QueryBuilder<E> builder) {
441                 this.traversal.until((GraphTraversal<Vertex,E>)builder.getQuery());
442                 stepIndex++;
443                 
444                 return this;
445         }
446         
447         @Override
448         public QueryBuilder<E> groupCount() {
449                 this.traversal.groupCount();
450                 stepIndex++;
451                 
452                 return this;
453         }
454         
455         @Override
456         public QueryBuilder<E> both() {
457                 this.traversal.both();
458                 stepIndex++;
459                 
460                 return this;
461         }
462         
463         @Override
464         public QueryBuilder<E> by(String name) {
465                 this.traversal.by(name);
466                 stepIndex++;
467                 
468                 return this;
469         }
470         
471         /**
472          * {@inheritDoc}
473          */
474         @Override
475         public QueryBuilder<E> simplePath(){
476                 this.traversal.simplePath();
477                 stepIndex++;
478                 return this;
479         }
480         
481         @Override
482         public QueryBuilder<Edge> outE() {
483                 this.traversal.outE();
484                 stepIndex++;
485                 return (QueryBuilder<Edge>)this;
486         }
487         
488         @Override
489         public QueryBuilder<Edge> inE() {
490                 this.traversal.inE();
491                 stepIndex++;
492                 return (QueryBuilder<Edge>)this;
493         }
494         
495         @Override
496         public QueryBuilder<Vertex> outV() {
497                 this.traversal.outV();
498                 stepIndex++;
499                 return (QueryBuilder<Vertex>)this;
500         }
501         
502         @Override
503         public QueryBuilder<Vertex> inV() {
504                 this.traversal.inV();
505                 stepIndex++;
506                 return (QueryBuilder<Vertex>)this;
507         }
508         
509         @Override
510         public QueryBuilder<E> as(String name) {
511                 this.traversal.as(name);
512                 
513                 stepIndex++;
514                 return this;
515         }
516         
517         @Override
518         public QueryBuilder<E> not(QueryBuilder<E> builder) {
519                 this.traversal.not(builder.getQuery());
520                 
521                 stepIndex++;
522                 return this;
523         }
524         
525         @Override
526         public QueryBuilder<E> select(String name) {
527                 this.traversal.select(name);
528                 
529                 stepIndex++;
530                 
531                 return this;
532         }
533         
534         /**
535          * Edge query.
536          *
537          * @param outObj the out type
538          * @param inObj the in type
539          * @throws NoEdgeRuleFoundException 
540          * @throws AAIException 
541          */
542         private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) throws AAIException {
543                 String outType = outObj.getDbName();
544                 String inType = inObj.getDbName();
545                 
546                 if (outObj.isContainer()) {
547                         outType = outObj.getChildDBName();
548                 }
549                 if (inObj.isContainer()) {
550                         inType = inObj.getChildDBName();
551                 }
552                 markParentBoundary();
553                 Map<String, EdgeRule> rules;
554                 if (labels == null) {
555                         rules = edgeRules.getEdgeRules(type, outType, inType);
556                 } else {
557                         rules = edgeRules.getEdgeRulesWithLabels(type, outType, inType, labels);
558                 }
559                 
560                 final List<String> inLabels = new ArrayList<>();
561                 final List<String> outLabels = new ArrayList<>();
562
563                 rules.forEach((k, edgeRule) -> {
564                         if (labels != null && !labels.contains(k)) {
565                                 return;
566                         } else {
567                                 if (edgeRule.getDirection().equals(Direction.IN)) {
568                                         inLabels.add(edgeRule.getLabel());
569                                 } else {
570                                         outLabels.add(edgeRule.getLabel());
571                                 }
572                         }
573                 });
574
575                 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
576                         traversal.out(outLabels.toArray(new String[outLabels.size()]));
577                 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
578                         traversal.in(inLabels.toArray(new String[inLabels.size()]));
579                 } else {                                
580                         traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()])));
581                 }
582                 
583                 stepIndex++;
584
585                 this.createContainerQuery(inObj);
586                 
587         }
588         
589         /**
590          * Edge query.
591          *
592          * @param outObj the out type
593          * @param inObj the in type
594          * @throws NoEdgeRuleFoundException 
595          * @throws AAIException 
596          */
597         private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) throws AAIException {
598                 String outType = outObj.getDbName();
599                 String inType = inObj.getDbName();
600                 
601                 if (outObj.isContainer()) {
602                         outType = outObj.getChildDBName();
603                 }
604                 if (inObj.isContainer()) {
605                         inType = inObj.getChildDBName();
606                 }
607                 
608                 markParentBoundary();
609                 Map<String, EdgeRule> rules;
610                 if (labels == null) {
611                         rules = edgeRules.getEdgeRules(type, outType, inType);
612                 } else {
613                         rules = edgeRules.getEdgeRulesWithLabels(type, outType, inType, labels);
614                 }
615                 
616                 final List<String> inLabels = new ArrayList<>();
617                 final List<String> outLabels = new ArrayList<>();
618
619                 rules.forEach((k, edgeRule) -> {
620                         if (labels != null && !labels.contains(k)) {
621                                 return;
622                         } else {
623                                 if (edgeRule.getDirection().equals(Direction.IN)) {
624                                         inLabels.add(edgeRule.getLabel());
625                                 } else {
626                                         outLabels.add(edgeRule.getLabel());
627                                 }
628                         }
629                 });
630
631                 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
632                         traversal.outE(outLabels.toArray(new String[outLabels.size()]));
633                 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
634                         traversal.inE(inLabels.toArray(new String[inLabels.size()]));
635                 } else {                                
636                         traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])), __.inE(inLabels.toArray(new String[inLabels.size()])));
637                 }
638         }
639         
640         @Override
641         public QueryBuilder<E> limit(long amount) {
642                 traversal.limit(amount);
643                 return this;
644         }
645
646         /**
647          * @{inheritDoc}
648          */
649         @Override
650         public <E2> E2 getQuery() {
651                 return (E2)this.traversal;
652         }
653         
654         /**
655          * @{inheritDoc}
656          */
657         @Override
658         public QueryBuilder<E> getParentQuery() {
659
660                 return cloneQueryAtStep(parentStepIndex);
661         }
662         
663         @Override
664         public QueryBuilder<E> getContainerQuery() {
665                 
666                 if (this.parentStepIndex == 0) {
667                         return removeQueryStepsBetween(0, containerStepIndex);
668                 } else {
669                         return cloneQueryAtStep(containerStepIndex);
670                 }
671         }
672         
673         /**
674          * @{inheritDoc}
675          */
676         @Override
677         public void markParentBoundary() {
678                 parentStepIndex = stepIndex;
679         }
680         
681         @Override
682         public void markContainer() {
683                 containerStepIndex = stepIndex;
684         }
685         
686         
687         /**
688          * @{inheritDoc}
689          */
690         @Override
691         public Vertex getStart() {
692                 return this.start;
693         }
694
695         protected int getParentStepIndex() {
696                 return parentStepIndex;
697         }
698
699         protected int getContainerStepIndex() {
700                 return containerStepIndex;
701         }
702
703         protected int getStepIndex() {
704                 return stepIndex;
705         }
706
707         protected abstract QueryBuilder<E> cloneQueryAtStep(int index);
708         /**
709          * end is exclusive
710          * 
711          * @param start
712          * @param end
713          * @return
714          */
715         protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
716         
717         private void executeQuery() {
718                 
719                 Admin admin;
720                 if (start != null) {
721                         this.completeTraversal = traversal.asAdmin();
722                 } else {
723                         admin = source.V().asAdmin();
724                         TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
725                         
726                         this.completeTraversal = (Admin<Vertex, E>) admin;
727
728                 }
729                 
730                 
731         }
732         
733         @Override
734         public boolean hasNext() {
735                 if (this.completeTraversal == null) {
736                         executeQuery();
737                 }
738                 
739                 return this.completeTraversal.hasNext();
740         }
741         
742         @Override
743         public E next() {
744                 if (this.completeTraversal == null) {
745                         executeQuery();
746                 }
747                 
748                 return this.completeTraversal.next();
749         }
750         
751         @Override
752         public List<E> toList() {
753                 if (this.completeTraversal == null) {
754                         executeQuery();
755                 }
756                 
757                 return this.completeTraversal.toList();
758         }
759
760 }