Fix bug in gixmo interface
[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 Â© 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright Â© 2017-2018 Amdocs
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 package org.onap.aai.modelloader.util;
22
23 import java.io.IOException;
24 import java.io.StringReader;
25 import java.util.HashSet;
26 import java.util.Map;
27 import java.util.Set;
28 import javax.xml.parsers.DocumentBuilder;
29 import javax.xml.parsers.DocumentBuilderFactory;
30 import javax.xml.parsers.ParserConfigurationException;
31 import org.onap.aai.cl.api.Logger;
32 import org.onap.aai.cl.eelf.LoggerFactory;
33 import org.onap.aai.modelloader.gizmo.GizmoBulkPayload;
34 import org.onap.aai.modelloader.gizmo.GizmoEdge;
35 import org.onap.aai.modelloader.gizmo.GizmoEdgeOperation;
36 import org.onap.aai.modelloader.gizmo.GizmoVertex;
37 import org.onap.aai.modelloader.gizmo.GizmoVertexOperation;
38 import org.onap.aai.modelloader.service.ModelLoaderMsgs;
39 import org.w3c.dom.Document;
40 import org.w3c.dom.Element;
41 import org.w3c.dom.Node;
42 import org.w3c.dom.NodeList;
43 import org.xml.sax.InputSource;
44 import org.xml.sax.SAXException;
45
46 public class GizmoTranslator {
47
48     private enum NodeType {
49         VERTEX,
50         ATTRIBUTE,
51         CONTAINER,
52         RELATIONSHIP_LIST,
53         RELATIONSHIP,
54         RELATED_TO,
55         RELATIONSHIP_DATA,
56         RELATIONSHIP_KEY,
57         RELATIONSHIP_VALUE,
58         MODEL_ELEMENT_VERTEX,
59         NQ_ELEMENT_VERTEX,
60         UNKNOWN
61     }
62
63     private static Logger logger = LoggerFactory.getInstance().getLogger(GizmoTranslator.class.getName());
64
65     public static String translate(String xmlPayload) throws ParserConfigurationException, SAXException, IOException {
66         logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Process XML model artifact: " + xmlPayload);
67         
68         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
69         factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
70         DocumentBuilder builder = factory.newDocumentBuilder();
71         InputSource is = new InputSource(new StringReader(xmlPayload));
72         Document doc = builder.parse(is);
73
74         GizmoBulkPayload gizmoPayload = new GizmoBulkPayload();
75
76         processNode(doc.getDocumentElement(), null, null, gizmoPayload);
77
78         return gizmoPayload.toJson();
79     }
80
81     private static void processNode(Node node, Node parentNode, GizmoVertexOperation parentVertexOp, GizmoBulkPayload gizmoPayload) {
82         if (!(node instanceof Element)) {
83             return;
84         }
85
86         Node newParent = null;
87         NodeType nodeType = getNodeType(node);
88
89         switch (nodeType) {
90         case VERTEX:
91         case MODEL_ELEMENT_VERTEX:
92         case NQ_ELEMENT_VERTEX:
93             parentVertexOp = createGizmoVertexOp(node, GizmoBulkPayload.ADD_OP);
94             gizmoPayload.addVertexOperation(parentVertexOp);
95             if (parentNode != null) {
96                 gizmoPayload.addEdgeOperation(createGizmoEdgeOp(node, parentNode));
97             }
98             newParent = node;
99             break;
100         case RELATIONSHIP:
101             processRelationship((Element)node, parentVertexOp, gizmoPayload);
102             newParent = parentNode;
103             break;
104         default:
105             newParent = parentNode;
106             break;
107         }
108
109         NodeList childNodes = node.getChildNodes();
110         for (int ix = 0; ix < childNodes.getLength(); ix++) {
111             processNode(childNodes.item(ix), newParent, parentVertexOp, gizmoPayload);
112         }
113     }
114
115     private static void processRelationship(Element relationshipNode, GizmoVertexOperation sourceNode, GizmoBulkPayload gizmoPayload) {
116         NodeList relatedToList = relationshipNode.getElementsByTagName("related-to");
117         if (relatedToList.getLength() != 1) {
118             logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, "Unable to resolve relationship");
119             return;
120         }
121
122         GizmoVertex targetVertex = new GizmoVertex();
123         targetVertex.setType(relatedToList.item(0).getTextContent().trim());
124
125         NodeList relationData = relationshipNode.getElementsByTagName("relationship-data");
126         for (int ix = 0; ix < relationData.getLength(); ix++) {
127             Element relationNode = (Element)relationData.item(ix);
128             NodeList keyList = relationNode.getElementsByTagName("relationship-key");
129             NodeList valueList = relationNode.getElementsByTagName("relationship-value");
130
131             if ( (keyList.getLength() != 1) || (valueList.getLength() != 1) ) {
132                 logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, "Unable to resolve relationship.  Missing key/value.");
133                 return;
134             }
135
136             String[] keyBits = keyList.item(0).getTextContent().trim().split("\\.");
137             String value = valueList.item(0).getTextContent().trim();
138
139             if (keyBits[0].equalsIgnoreCase(targetVertex.getType())) {
140                 targetVertex.setProperty(keyBits[1], value);
141             }
142         }
143
144         gizmoPayload.addVertexOperation(new GizmoVertexOperation(GizmoBulkPayload.EXISTS_OP, getVertexId(targetVertex), targetVertex));
145
146         GizmoEdge edge = new GizmoEdge();
147
148         edge.setSource("$" + getVertexId(sourceNode.getVertex()));
149         edge.setTarget("$" + getVertexId(targetVertex));
150
151         gizmoPayload.addEdgeOperation(new GizmoEdgeOperation(GizmoBulkPayload.ADD_OP, edge.getSource() + "_" + edge.getTarget(), edge));
152     }
153
154     private static GizmoEdgeOperation createGizmoEdgeOp(Node node, Node parentNode) {
155         GizmoEdge edge = new GizmoEdge();
156
157         edge.setSource("$" + getVertexId(createGizmoVertex(node)));
158         edge.setTarget("$" + getVertexId(createGizmoVertex(parentNode)));
159
160         return new GizmoEdgeOperation(GizmoBulkPayload.ADD_OP, edge.getSource() + "_" + edge.getTarget(), edge);
161     }
162
163     private static GizmoVertexOperation createGizmoVertexOp(Node node, String operationType) {
164         GizmoVertex vertex = createGizmoVertex(node);
165         return new GizmoVertexOperation(operationType, getVertexId(vertex), vertex);
166     }
167
168     private static String getVertexId(GizmoVertex vertex) {
169         StringBuilder sb = new StringBuilder();
170         sb.append(vertex.getType());
171         for (Map.Entry<String, String> entry : vertex.getProperties().entrySet()) {
172             sb.append("-" + entry.getValue());
173         }
174
175         return sb.toString();
176     }
177
178     private static GizmoVertex createGizmoVertex(Node node) {
179         GizmoVertex vertex = new GizmoVertex();
180         vertex.setType(node.getNodeName().trim());
181
182         NodeList childNodes = node.getChildNodes();
183
184         for (int ix = 0; ix < childNodes.getLength(); ix++) {
185             if (getNodeType(childNodes.item(ix)).equals(NodeType.ATTRIBUTE)) {
186                 vertex.setProperty(childNodes.item(ix).getNodeName().trim(), 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         Set<String> elemSet = new HashSet<>();
206
207         // Get the parent model version / named query version
208         String parentVersion = null;
209         Node parentNode = node.getParentNode();
210         while ( (parentNode != null) && (parentVersion == null) ) {
211             if (getNodeType(parentNode).equals(NodeType.VERTEX)) {
212                 NodeList childNodes = ((Element)parentNode).getElementsByTagName("*");
213                 for (int ix = 0; ix < childNodes.getLength(); ix++) {
214                     if (childNodes.item(ix).getNodeName().equalsIgnoreCase("named-query-uuid") || 
215                             childNodes.item(ix).getNodeName().equalsIgnoreCase("model-version-id")) {
216                         parentVersion = childNodes.item(ix).getTextContent().trim();
217                         break;
218                     }
219                 }
220             }
221             
222             parentNode = parentNode.getParentNode();
223         }
224         
225         if (parentVersion != null) {
226             elemSet.add(parentVersion);
227         }
228         
229         NodeList childNodes = node.getElementsByTagName("*");
230         for (int ix = 0; ix < childNodes.getLength(); ix++) {
231             NodeType nt = getNodeType(childNodes.item(ix));
232             if ( nt.equals(NodeType.ATTRIBUTE) || nt.equals(NodeType.RELATIONSHIP_KEY) || nt.equals(NodeType.RELATIONSHIP_VALUE) ) {
233                 elemSet.add(childNodes.item(ix).getTextContent().trim());
234             }
235         }
236
237         return Integer.toString(elemSet.hashCode());
238     }
239
240     private static NodeType getNodeType(Node node) {
241         if (!(node instanceof Element)) {
242             return NodeType.UNKNOWN;
243         }
244
245         if (node.getNodeName().equalsIgnoreCase("relationship-list")) {
246             return NodeType.RELATIONSHIP_LIST;
247         }
248
249         if (node.getNodeName().equalsIgnoreCase("relationship")) {
250             return NodeType.RELATIONSHIP;
251         }
252
253         if (node.getNodeName().equalsIgnoreCase("relationship-data")) {
254             return NodeType.RELATIONSHIP_DATA;
255         }
256
257         if (node.getNodeName().equalsIgnoreCase("related-to")) {
258             return NodeType.RELATED_TO;
259         }
260
261         if (node.getNodeName().equalsIgnoreCase("relationship-key")) {
262             return NodeType.RELATIONSHIP_KEY;
263         }
264
265         if (node.getNodeName().equalsIgnoreCase("relationship-value")) {
266             return NodeType.RELATIONSHIP_VALUE;
267         }
268
269         if (node.getNodeName().equalsIgnoreCase("model-element")) {
270             return NodeType.MODEL_ELEMENT_VERTEX;
271         }
272
273         if (node.getNodeName().equalsIgnoreCase("named-query-element")) {
274             return NodeType.NQ_ELEMENT_VERTEX;
275         }
276
277         NodeList childNodes = node.getChildNodes();
278         int childElements = countChildElements(childNodes);
279
280         if ( (childElements == 0) && (node.getTextContent() != null) && (!node.getTextContent().trim().isEmpty()) ) {
281             return NodeType.ATTRIBUTE;
282         }
283
284         for (int ix = 0; ix < childNodes.getLength(); ix++) {
285             if (getNodeType(childNodes.item(ix)) == NodeType.ATTRIBUTE) {
286                 return NodeType.VERTEX;
287             }
288         }
289
290         if (childElements > 0) {
291             return NodeType.CONTAINER;
292         }
293
294         return NodeType.UNKNOWN;
295     }
296
297     static int countChildElements(NodeList nodes) {
298         int count = 0;
299         for (int ix = 0; ix < nodes.getLength(); ix++) {
300             if (nodes.item(ix) instanceof Element) {
301                 count++;
302             }
303         }
304
305         return count;
306     }
307 }