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