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