0b072b88d1a4088a5a46d71851877b091105dc0d
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / parsers / relationship / RelationshipToURI.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.parsers.relationship;
22
23 import com.att.eelf.configuration.EELFLogger;
24 import com.att.eelf.configuration.EELFManager;
25
26 import java.io.UnsupportedEncodingException;
27 import java.net.URI;
28 import java.net.URISyntaxException;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Optional;
33
34 import javax.ws.rs.core.UriBuilder;
35
36 import org.apache.tinkerpop.gremlin.structure.Direction;
37 import org.onap.aai.config.SpringContextAware;
38 import org.onap.aai.edges.EdgeIngestor;
39 import org.onap.aai.edges.EdgeRule;
40 import org.onap.aai.edges.EdgeRuleQuery;
41 import org.onap.aai.edges.enums.AAIDirection;
42 import org.onap.aai.edges.enums.EdgeType;
43 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
44 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
45 import org.onap.aai.exceptions.AAIException;
46 import org.onap.aai.introspection.*;
47 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
48 import org.onap.aai.parsers.exceptions.AAIIdentityMapParseException;
49 import org.onap.aai.parsers.exceptions.AmbiguousMapAAIException;
50 import org.onap.aai.parsers.uri.URIParser;
51 import org.onap.aai.schema.enums.ObjectMetadata;
52 import org.onap.aai.setup.SchemaVersions;
53 import org.springframework.context.ApplicationContext;
54
55 /**
56  * The Class RelationshipToURI.
57  */
58 public class RelationshipToURI {
59
60     private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(RelationshipToURI.class);
61
62     private Introspector relationship = null;
63
64     private Loader loader = null;
65
66     private ModelType modelType = null;
67
68     private EdgeIngestor edgeRules = null;
69
70     private URI uri = null;
71
72     private SchemaVersions schemaVersions;
73
74     /**
75      * Instantiates a new relationship to URI.
76      *
77      * @param loader the loader
78      * @param relationship the relationship
79      * @throws UnsupportedEncodingException the unsupported encoding exception
80      * @throws AAIException the AAI exception
81      */
82     public RelationshipToURI(Loader loader, Introspector relationship)
83             throws UnsupportedEncodingException, AAIException {
84         this.relationship = relationship;
85         this.modelType = relationship.getModelType();
86         this.loader = loader;
87         this.initEdgeIngestor();
88         this.parse();
89
90     }
91
92     protected void initEdgeIngestor() {
93         // TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
94         ApplicationContext ctx = SpringContextAware.getApplicationContext();
95         edgeRules = ctx.getBean(EdgeIngestor.class);
96         schemaVersions = (SchemaVersions) ctx.getBean("schemaVersions");
97     }
98
99     /**
100      * Parses the.
101      * 
102      * @throws
103      *
104      *         @throws UnsupportedEncodingException the unsupported encoding exception
105      * @throws AAIException the AAI exception
106      */
107     protected void parse() throws AAIException {
108         String relatedLink = (String) relationship.getValue("related-link");
109         Optional<URI> result;
110         try {
111             if (loader.getVersion().compareTo(schemaVersions.getRelatedLinkVersion()) >= 0) {
112                 result = processRelatedLink(relatedLink);
113                 if (!result.isPresent()) {
114                     result = processRelationshipData();
115                 }
116             } else {
117                 result = processRelationshipData();
118                 if (!result.isPresent()) {
119                     result = processRelatedLink(relatedLink);
120                 }
121             }
122             if (result.isPresent()) {
123                 this.uri = result.get();
124             } else {
125                 throw new AAIIdentityMapParseException("nothing to parse");
126             }
127         } catch (AAIException e) {
128             throw e;
129         } catch (Exception e) {
130             throw new AAIIdentityMapParseException("Could not parse relationship-list object: " + e.getMessage(), e);
131         }
132
133     }
134
135     private Optional<URI> processRelationshipData() throws AAIException, UnsupportedEncodingException {
136         Optional<URI> result = Optional.empty();
137         StringBuilder uriBuilder = new StringBuilder();
138         List<Object> data = (List<Object>) relationship.getValue("relationship-data");
139         Introspector wrapper;
140         String key;
141         String value;
142         String objectType;
143         String propertyName;
144         String topLevelType = null;
145         String[] split;
146         HashMap<String, Introspector> map = new HashMap<>();
147         for (Object datum : data) {
148             wrapper = IntrospectorFactory.newInstance(modelType, datum);
149             key = (String) wrapper.getValue("relationship-key");
150             value = (String) wrapper.getValue("relationship-value");
151             split = key.split("\\.");
152             if (split == null || split.length != 2) {
153                 throw new AAIIdentityMapParseException(
154                         "incorrect format for key must be of the form {node-type}.{property-name}");
155             }
156             // check node name ok
157             // check prop name ok
158             objectType = split[0];
159             propertyName = split[1];
160
161             try {
162                 Introspector wrappedObj = loader.introspectorFromName(objectType);
163
164                 if (!wrappedObj.hasProperty(propertyName)) {
165                     throw new AAIIdentityMapParseException("invalid property name in map: " + propertyName);
166                 }
167                 if (map.containsKey(objectType)) {
168                     wrappedObj = map.get(objectType);
169                 } else {
170                     map.put(objectType, wrappedObj);
171                 }
172                 if (wrappedObj.getValue(propertyName) == null) {
173                     wrappedObj.setValue(propertyName, value);
174                 } else {
175                     throw new AmbiguousMapAAIException(
176                             "cannot determine where key/value goes: " + propertyName + "/" + value);
177                 }
178
179                 if (wrappedObj.getMetadata(ObjectMetadata.NAMESPACE) != null) {
180                     if (topLevelType == null) {
181                         topLevelType = objectType;
182                     } else if (!topLevelType.equals(objectType)) {
183                         throw new AmbiguousMapAAIException(
184                                 "found two top level nodes of different types: " + topLevelType + " and " + objectType);
185                     }
186                 }
187             } catch (AAIUnknownObjectException e) {
188                 throw new AAIIdentityMapParseException("invalid object name in map: " + objectType, e);
189             }
190
191         }
192         if (!map.isEmpty()) {
193             String startType = (String) relationship.getValue("related-to");
194             List<String> nodeTypes = new ArrayList<>();
195             nodeTypes.addAll(map.keySet());
196
197             String displacedType;
198             for (int i = 0; i < nodeTypes.size(); i++) {
199                 if (nodeTypes.get(i).equals(startType)) {
200                     displacedType = nodeTypes.set(nodeTypes.size() - 1, startType);
201                     nodeTypes.set(i, displacedType);
202                     break;
203                 }
204             }
205             sortRelationships(nodeTypes, startType, 1);
206             int startTypeIndex = nodeTypes.indexOf(startType);
207             int topLevelIndex = 0;
208             if (topLevelType != null) {
209                 topLevelIndex = nodeTypes.indexOf(topLevelType);
210             }
211             // remove additional types not needed if they are there
212             List<String> nodeTypesSubList = nodeTypes;
213             if (topLevelIndex != 0) {
214                 nodeTypesSubList = nodeTypes.subList(topLevelIndex, startTypeIndex + 1);
215             }
216             for (String type : nodeTypesSubList) {
217                 uriBuilder.append(map.get(type).getURI());
218             }
219             if (!nodeTypesSubList.isEmpty()) {
220                 result = Optional.of(UriBuilder.fromPath(uriBuilder.toString()).build());
221             }
222         }
223         return result;
224     }
225
226     private Optional<URI> processRelatedLink(String relatedLink)
227             throws URISyntaxException, UnsupportedEncodingException, AAIIdentityMapParseException {
228         Optional<URI> result = Optional.empty();
229         if (relatedLink != null) {
230             URI resultUri = new URI(relatedLink);
231             String path = resultUri.toString();
232             resultUri = UriBuilder.fromPath(resultUri.getRawPath()).build();
233             URIParser uriParser = new URIParser(this.loader, resultUri);
234             try {
235                 uriParser.validate();
236             } catch (AAIException e) {
237                 throw new AAIIdentityMapParseException("related link is invalid: " + relatedLink, e);
238             }
239             result = Optional.of(resultUri);
240         }
241
242         return result;
243     }
244
245     /**
246      * Sort relationships.
247      *
248      * @param data the data
249      * @param startType the start type
250      * @param i the i
251      * @return true, if successful
252      * @throws AAIException
253      */
254     private boolean sortRelationships(List<String> data, String startType, int i) throws AAIException {
255
256         if (i == data.size()) {
257             return true;
258         }
259         int j;
260         String objectType;
261         String displacedObject;
262         EdgeRule rule;
263         Direction direction;
264         for (j = (data.size() - i) - 1; j >= 0; j--) {
265             objectType = data.get(j);
266             try {
267                 rule = edgeRules
268                         .getRule(new EdgeRuleQuery.Builder(startType, objectType).edgeType(EdgeType.TREE).build());
269                 direction = rule.getDirection();
270                 if (direction != null) {
271                     if ((rule.getContains().equals(AAIDirection.OUT.toString()) && direction.equals(Direction.IN))
272                             || (rule.getContains().equals(AAIDirection.IN.toString())
273                                     && direction.equals(Direction.OUT))) {
274                         displacedObject = data.set((data.size() - i) - 1, data.get(j));
275                         data.set(j, displacedObject);
276                         if (sortRelationships(data, objectType, i + 1)) {
277                             return true;
278                         } else {
279                             // continue to process
280                         }
281                     }
282                 }
283             } catch (AAIException | EdgeRuleNotFoundException | AmbiguousRuleChoiceException e) {
284                 // ignore exceptions generated
285                 continue;
286             }
287         }
288
289         return false;
290     }
291
292     /**
293      * Gets the uri.
294      *
295      * @return the uri
296      */
297     public URI getUri() {
298         return uri;
299     }
300
301 }