Fix Sonar bugs in Dublin code
[aai/model-loader.git] / src / main / java / org / onap / aai / modelloader / util / GizmoTranslator.java
1 /**
2  * ============LICENSE_START==========================================
3  * org.onap.aai
4  * ===================================================================
5  * Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright (c) 2017-2019 European Software Marketing Ltd.
7  * ===================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END============================================
20  */
21
22 package org.onap.aai.modelloader.util;
23
24 import java.io.IOException;
25 import java.io.StringReader;
26 import java.util.HashSet;
27 import java.util.Map;
28 import java.util.Optional;
29 import java.util.Set;
30 import java.util.stream.Collectors;
31 import java.util.stream.IntStream;
32 import java.util.stream.Stream;
33 import javax.xml.parsers.DocumentBuilder;
34 import javax.xml.parsers.DocumentBuilderFactory;
35 import javax.xml.parsers.ParserConfigurationException;
36 import org.onap.aai.cl.api.Logger;
37 import org.onap.aai.cl.eelf.LoggerFactory;
38 import org.onap.aai.modelloader.gizmo.GizmoBulkPayload;
39 import org.onap.aai.modelloader.gizmo.GizmoEdge;
40 import org.onap.aai.modelloader.gizmo.GizmoEdgeOperation;
41 import org.onap.aai.modelloader.gizmo.GizmoVertex;
42 import org.onap.aai.modelloader.gizmo.GizmoVertexOperation;
43 import org.onap.aai.modelloader.service.ModelLoaderMsgs;
44 import org.w3c.dom.Document;
45 import org.w3c.dom.Element;
46 import org.w3c.dom.Node;
47 import org.w3c.dom.NodeList;
48 import org.xml.sax.InputSource;
49 import org.xml.sax.SAXException;
50
51 public class GizmoTranslator {
52
53     private enum NodeType {
54         VERTEX, ATTRIBUTE, CONTAINER, RELATIONSHIP_LIST, RELATIONSHIP, RELATED_TO, RELATIONSHIP_DATA, RELATIONSHIP_KEY, RELATIONSHIP_VALUE, MODEL_ELEMENT_VERTEX, NQ_ELEMENT_VERTEX, UNKNOWN
55     }
56
57     private static Logger logger = LoggerFactory.getInstance().getLogger(GizmoTranslator.class.getName());
58
59     public static String translate(String xmlPayload) throws ParserConfigurationException, SAXException, IOException {
60         logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Process XML model artifact: " + xmlPayload);
61
62         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
63         factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
64         DocumentBuilder builder = factory.newDocumentBuilder();
65         InputSource is = new InputSource(new StringReader(xmlPayload));
66         Document doc = builder.parse(is);
67
68         GizmoBulkPayload gizmoPayload = new GizmoBulkPayload();
69
70         processNode(doc.getDocumentElement(), null, null, gizmoPayload);
71
72         return gizmoPayload.toJson();
73     }
74
75     private static void processNode(Node node, Node parentNode, GizmoVertexOperation parentVertexOp,
76             GizmoBulkPayload gizmoPayload) {
77         if (!(node instanceof Element)) {
78             return;
79         }
80
81         Node newParent = null;
82         NodeType nodeType = getNodeType(node);
83
84         switch (nodeType) {
85             case VERTEX:
86             case MODEL_ELEMENT_VERTEX:
87             case NQ_ELEMENT_VERTEX:
88                 parentVertexOp = createGizmoVertexOp(node, GizmoBulkPayload.ADD_OP);
89                 gizmoPayload.addVertexOperation(parentVertexOp);
90                 if (parentNode != null) {
91                     gizmoPayload.addEdgeOperation(createGizmoEdgeOp(node, parentNode));
92                 }
93                 newParent = node;
94                 break;
95             case RELATIONSHIP:
96                 processRelationship((Element) node, parentVertexOp, gizmoPayload);
97                 newParent = parentNode;
98                 break;
99             default:
100                 newParent = parentNode;
101                 break;
102         }
103
104         NodeList childNodes = node.getChildNodes();
105         for (int ix = 0; ix < childNodes.getLength(); ix++) {
106             processNode(childNodes.item(ix), newParent, parentVertexOp, gizmoPayload);
107         }
108     }
109
110     private static void processRelationship(Element relationshipNode, GizmoVertexOperation sourceNode,
111             GizmoBulkPayload gizmoPayload) {
112         NodeList relatedToList = relationshipNode.getElementsByTagName("related-to");
113         if (relatedToList.getLength() != 1) {
114             logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, "Unable to resolve relationship");
115             return;
116         }
117
118         GizmoVertex targetVertex = new GizmoVertex();
119         targetVertex.setType(relatedToList.item(0).getTextContent().trim());
120
121         NodeList relationData = relationshipNode.getElementsByTagName("relationship-data");
122         for (int ix = 0; ix < relationData.getLength(); ix++) {
123             Element relationNode = (Element) relationData.item(ix);
124             NodeList keyList = relationNode.getElementsByTagName("relationship-key");
125             NodeList valueList = relationNode.getElementsByTagName("relationship-value");
126
127             if ((keyList.getLength() != 1) || (valueList.getLength() != 1)) {
128                 logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,
129                         "Unable to resolve relationship.  Missing key/value.");
130                 return;
131             }
132
133             String[] keyBits = keyList.item(0).getTextContent().trim().split("\\.");
134             String value = valueList.item(0).getTextContent().trim();
135
136             if (keyBits[0].equalsIgnoreCase(targetVertex.getType())) {
137                 targetVertex.setProperty(keyBits[1], value);
138             }
139         }
140
141         gizmoPayload.addVertexOperation(
142                 new GizmoVertexOperation(GizmoBulkPayload.EXISTS_OP, getVertexId(targetVertex), targetVertex));
143
144         GizmoEdge edge = new GizmoEdge();
145
146         edge.setSource("$" + getVertexId(sourceNode.getVertex()));
147         edge.setTarget("$" + getVertexId(targetVertex));
148
149         gizmoPayload.addEdgeOperation(
150                 new GizmoEdgeOperation(GizmoBulkPayload.ADD_OP, edge.getSource() + "_" + edge.getTarget(), edge));
151     }
152
153     private static GizmoEdgeOperation createGizmoEdgeOp(Node node, Node parentNode) {
154         GizmoEdge edge = new GizmoEdge();
155
156         edge.setSource("$" + getVertexId(createGizmoVertex(node)));
157         edge.setTarget("$" + getVertexId(createGizmoVertex(parentNode)));
158
159         return new GizmoEdgeOperation(GizmoBulkPayload.ADD_OP, edge.getSource() + "_" + edge.getTarget(), edge);
160     }
161
162     private static GizmoVertexOperation createGizmoVertexOp(Node node, String operationType) {
163         GizmoVertex vertex = createGizmoVertex(node);
164         return new GizmoVertexOperation(operationType, getVertexId(vertex), vertex);
165     }
166
167     private static String getVertexId(GizmoVertex vertex) {
168         StringBuilder sb = new StringBuilder();
169         sb.append(vertex.getType());
170         for (Map.Entry<String, String> entry : vertex.getProperties().entrySet()) {
171             sb.append("-" + entry.getValue());
172         }
173
174         return sb.toString();
175     }
176
177     private static GizmoVertex createGizmoVertex(Node node) {
178         GizmoVertex vertex = new GizmoVertex();
179         vertex.setType(node.getNodeName().trim());
180
181         NodeList childNodes = node.getChildNodes();
182
183         for (int ix = 0; ix < childNodes.getLength(); ix++) {
184             if (getNodeType(childNodes.item(ix)).equals(NodeType.ATTRIBUTE)) {
185                 vertex.setProperty(childNodes.item(ix).getNodeName().trim(),
186                         childNodes.item(ix).getTextContent().trim());
187             }
188         }
189
190         // Special case for model-element, where we need to generate an id field
191         if (getNodeType(node).equals(NodeType.MODEL_ELEMENT_VERTEX)) {
192             vertex.setProperty("model-element-uuid", generateModelElementId((Element) node));
193         }
194
195         // Special case for nq-element, where we need to generate an id field
196         if (getNodeType(node).equals(NodeType.NQ_ELEMENT_VERTEX)) {
197             vertex.setProperty("named-query-element-uuid", generateModelElementId((Element) node));
198         }
199
200         return vertex;
201     }
202
203     // Generate a unique hash to store as the id for this node
204     private static String generateModelElementId(Element node) {
205         // Get the parent model version / named query version
206         Optional<String> parentVersion = Optional.empty();
207
208         Node parentNode = node.getParentNode();
209         while (parentNode != null && !parentVersion.isPresent()) {
210             if (getNodeType(parentNode) == NodeType.VERTEX) {
211                 NodeList childNodes = ((Element) parentNode).getElementsByTagName("*");
212                 parentVersion = IntStream.range(0, childNodes.getLength()) //
213                         .mapToObj(childNodes::item) //
214                         .filter(child -> child.getNodeName().equalsIgnoreCase("named-query-uuid")
215                                 || child.getNodeName().equalsIgnoreCase("model-version-id")) //
216                         .map(child -> child.getTextContent().trim()) //
217                         .findFirst();
218             }
219             parentNode = parentNode.getParentNode();
220         }
221
222         Set<String> elemSet = new HashSet<>();
223         parentVersion.ifPresent(elemSet::add);
224
225         Set<NodeType> validNodeTypes = //
226                 Stream.of(NodeType.ATTRIBUTE, NodeType.RELATIONSHIP_KEY, NodeType.RELATIONSHIP_VALUE)
227                         .collect(Collectors.toSet());
228
229         NodeList childNodes = node.getElementsByTagName("*");
230         IntStream.range(0, childNodes.getLength()) //
231                 .mapToObj(childNodes::item) //
232                 .filter(child -> validNodeTypes.contains(getNodeType(child))) //
233                 .map(child -> child.getTextContent().trim()) //
234                 .forEachOrdered(elemSet::add);
235
236         return Integer.toString(elemSet.hashCode());
237     }
238
239     private static NodeType getNodeType(Node node) {
240         if (!(node instanceof Element)) {
241             return NodeType.UNKNOWN;
242         }
243
244         if (node.getNodeName().equalsIgnoreCase("relationship-list")) {
245             return NodeType.RELATIONSHIP_LIST;
246         }
247
248         if (node.getNodeName().equalsIgnoreCase("relationship")) {
249             return NodeType.RELATIONSHIP;
250         }
251
252         if (node.getNodeName().equalsIgnoreCase("relationship-data")) {
253             return NodeType.RELATIONSHIP_DATA;
254         }
255
256         if (node.getNodeName().equalsIgnoreCase("related-to")) {
257             return NodeType.RELATED_TO;
258         }
259
260         if (node.getNodeName().equalsIgnoreCase("relationship-key")) {
261             return NodeType.RELATIONSHIP_KEY;
262         }
263
264         if (node.getNodeName().equalsIgnoreCase("relationship-value")) {
265             return NodeType.RELATIONSHIP_VALUE;
266         }
267
268         if (node.getNodeName().equalsIgnoreCase("model-element")) {
269             return NodeType.MODEL_ELEMENT_VERTEX;
270         }
271
272         if (node.getNodeName().equalsIgnoreCase("named-query-element")) {
273             return NodeType.NQ_ELEMENT_VERTEX;
274         }
275
276         NodeList childNodes = node.getChildNodes();
277         int childElements = countChildElements(childNodes);
278
279         if ((childElements == 0) && (node.getTextContent() != null) && (!node.getTextContent().trim().isEmpty())) {
280             return NodeType.ATTRIBUTE;
281         }
282
283         for (int ix = 0; ix < childNodes.getLength(); ix++) {
284             if (getNodeType(childNodes.item(ix)) == NodeType.ATTRIBUTE) {
285                 return NodeType.VERTEX;
286             }
287         }
288
289         if (childElements > 0) {
290             return NodeType.CONTAINER;
291         }
292
293         return NodeType.UNKNOWN;
294     }
295
296     static int countChildElements(NodeList nodes) {
297         int count = 0;
298         for (int ix = 0; ix < nodes.getLength(); ix++) {
299             if (nodes.item(ix) instanceof Element) {
300                 count++;
301             }
302         }
303
304         return count;
305     }
306 }