Enhancements for the aai-common library
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / introspection / IntrospectorWalker.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
21 package org.onap.aai.introspection;
22
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 import java.util.HashSet;
27 import java.util.LinkedHashSet;
28 import java.util.List;
29 import java.util.Set;
30
31 import org.onap.aai.exceptions.AAIException;
32 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
33 import org.onap.aai.logging.LogFormatTools;
34
35 public class IntrospectorWalker {
36
37     private static final Logger LOGGER = LoggerFactory.getLogger(IntrospectorWalker.class);
38
39     private Wanderer w = null;
40     private Set<String> blacklist = null;
41     private boolean preventCycles = false;
42     private final PropertyPredicate<Introspector, String> propVisibility;
43
44     /**
45      * Instantiates a new introspector walker.
46      *
47      * @param w the w
48      * @param llBuilder the ll builder
49      */
50     public IntrospectorWalker(Wanderer w) {
51         this.w = w;
52         this.blacklist = new HashSet<>();
53         this.propVisibility = null;
54     }
55
56     public IntrospectorWalker(Wanderer w, PropertyPredicate<Introspector, String> p) {
57         this.w = w;
58         this.blacklist = new HashSet<>();
59         this.propVisibility = p;
60     }
61
62     /**
63      * Sets the blacklist.
64      *
65      * @param list the new blacklist
66      */
67     public void setBlacklist(List<String> list) {
68         blacklist.addAll(list);
69     }
70
71     /**
72      * Prevent cycles.
73      *
74      * @param prevent the prevent
75      */
76     public void preventCycles(boolean prevent) {
77         this.preventCycles = prevent;
78     }
79
80     /**
81      * Walk.
82      *
83      * @param obj the obj
84      * @throws AAIException
85      */
86     public void walk(Introspector obj) throws AAIException {
87         Set<String> visited = new HashSet<>();
88
89         walk(obj, null, visited);
90     }
91
92     /**
93      * Walk.
94      *
95      * @param obj the obj
96      * @param parent the parent
97      * @throws AAIException
98      */
99     private void walk(Introspector obj, Introspector parent, Set<String> visited) throws AAIException {
100         boolean stopRecursion = false;
101         Set<String> localVisited = new HashSet<>();
102         localVisited.addAll(visited);
103         if (preventCycles) {
104             if (visited.contains(obj.getName())) {
105                 stopRecursion = true;
106             }
107             if (!obj.isContainer()) {
108                 localVisited.add(obj.getName()); // so we don't recurse while walking its children
109             }
110         }
111         Set<String> props;
112         // props must duplicate the result from getProperties because
113         // it is unmodifiable
114         if (this.propVisibility == null) {
115             props = new LinkedHashSet<>(obj.getProperties());
116         } else {
117             props = new LinkedHashSet<>(obj.getProperties(this.propVisibility));
118         }
119
120         w.processComplexObj(obj);
121         props.removeAll(blacklist);
122         if (!obj.isContainer()) {
123             parent = obj;
124         }
125         for (String prop : props) {
126
127             if (obj.isSimpleType(prop)) {
128
129                 w.processPrimitive(prop, obj);
130             } else if (obj.isListType(prop) && !stopRecursion) {
131
132                 List<Object> listReference = obj.getValue(prop);
133                 boolean isComplexType = obj.isComplexGenericType(prop);
134                 if (isComplexType) {
135                     List<Introspector> list = obj.getWrappedListValue(prop);
136                     try {
137                         Introspector child = obj.newIntrospectorInstanceOfNestedProperty(prop);
138                         w.modifyComplexList(list, listReference, parent, child);
139                         for (Object item : listReference) {
140                             child = IntrospectorFactory.newInstance(obj.getModelType(), item);
141                             walk(child, parent, localVisited);
142                         }
143                     } catch (AAIUnknownObjectException e) {
144                         LOGGER.warn("Skipping property " + prop + " (Unknown Object) " + LogFormatTools.getStackTop(e));
145                     }
146                 } else {
147                     w.processPrimitiveList(prop, obj);
148                 }
149                 if (listReference.size() == 0) {
150                     if (isComplexType) {
151                         try {
152                             Introspector child = obj.newIntrospectorInstanceOfNestedProperty(prop);
153                             int size = w.createComplexListSize(parent, child);
154                             for (int i = 0; i < size; i++) {
155                                 child = obj.newIntrospectorInstanceOfNestedProperty(prop);
156                                 walk(child, parent, localVisited);
157                                 listReference.add(child.getUnderlyingObject());
158                             }
159
160                             obj.setValue(prop, listReference);
161                         } catch (AAIUnknownObjectException e) {
162                             LOGGER.warn(
163                                     "Skipping property " + prop + " (Unknown Object) " + LogFormatTools.getStackTop(e));
164                         }
165                     } else if (!isComplexType) {
166                         w.processPrimitiveList(prop, obj);
167                     }
168                 }
169
170             } else if (obj.isComplexType(prop) && !stopRecursion) {
171                 Introspector child = null;
172                 if (obj.getValue(prop) != null) {
173                     child = IntrospectorFactory.newInstance(obj.getModelType(), obj.getValue(prop));
174                 } else {
175                     if (w.createComplexObjIfNull()) {
176                         try {
177                             child = obj.newIntrospectorInstanceOfProperty(prop);
178                             obj.setValue(prop, child.getUnderlyingObject());
179                         } catch (AAIUnknownObjectException e) {
180                             LOGGER.warn(
181                                     "Skipping property " + prop + " (Unknown Object) " + LogFormatTools.getStackTop(e));
182                         }
183                     }
184                 }
185                 if (child != null) {
186                     walk(child, obj, localVisited);
187                 }
188             }
189
190         }
191         /*
192          * if (preventCycles && !obj.isContainer()) {
193          * visited.remove(obj.getName()); //so we can see it down another path that isn't in danger of recursing over it
194          * }
195          */
196     }
197 }