Configurable option to load models via Gizmo
[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
29 import javax.xml.parsers.DocumentBuilder;
30 import javax.xml.parsers.DocumentBuilderFactory;
31 import javax.xml.parsers.ParserConfigurationException;
32
33 import org.onap.aai.cl.api.Logger;
34 import org.onap.aai.cl.eelf.LoggerFactory;
35 import org.onap.aai.modelloader.gizmo.GizmoBulkPayload;
36 import org.onap.aai.modelloader.gizmo.GizmoEdge;
37 import org.onap.aai.modelloader.gizmo.GizmoEdgeOperation;
38 import org.onap.aai.modelloader.gizmo.GizmoVertex;
39 import org.onap.aai.modelloader.gizmo.GizmoVertexOperation;
40 import org.onap.aai.modelloader.service.ModelLoaderMsgs;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.Element;
43 import org.w3c.dom.Node;
44 import org.w3c.dom.NodeList;
45 import org.xml.sax.InputSource;
46 import org.xml.sax.SAXException;
47
48 public class GizmoTranslator {
49     
50     private enum NodeType {
51         VERTEX,
52         ATTRIBUTE,
53         CONTAINER,
54         RELATIONSHIP_LIST,
55         RELATIONSHIP,
56         RELATED_TO,
57         RELATIONSHIP_DATA,
58         RELATIONSHIP_KEY,
59         RELATIONSHIP_VALUE,
60         MODEL_ELEMENT_VERTEX,
61         NQ_ELEMENT_VERTEX,
62         UNKNOWN
63     }
64     
65     private static Logger logger = LoggerFactory.getInstance().getLogger(GizmoTranslator.class.getName());
66     
67     public static String translate(String xmlPayload) throws ParserConfigurationException, SAXException, IOException {
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         GizmoEdgeOperation edgeOp = new GizmoEdgeOperation(GizmoBulkPayload.ADD_OP, edge.getSource() + "_" + edge.getTarget(), edge);
161         
162         return edgeOp;
163     }
164
165     private static GizmoVertexOperation createGizmoVertexOp(Node node, String operationType) {
166         GizmoVertex vertex = createGizmoVertex(node);  
167         GizmoVertexOperation addOp = new GizmoVertexOperation(operationType, getVertexId(vertex), vertex);
168         return addOp;
169     }
170
171     private static String getVertexId(GizmoVertex vertex) {
172         StringBuilder sb = new StringBuilder();
173         sb.append(vertex.getType());
174         for (Map.Entry<String, String> entry : vertex.getProperties().entrySet()) {
175             sb.append("-" + entry.getValue());
176         }
177         
178         return sb.toString();
179     }
180
181     private static GizmoVertex createGizmoVertex(Node node) {
182         GizmoVertex vertex = new GizmoVertex();
183         vertex.setType(node.getNodeName().trim());
184
185         NodeList childNodes = node.getChildNodes();
186
187         for (int ix = 0; ix < childNodes.getLength(); ix++) {
188             if (getNodeType(childNodes.item(ix)).equals(NodeType.ATTRIBUTE)) {
189                 vertex.setProperty(childNodes.item(ix).getNodeName().trim(), childNodes.item(ix).getTextContent().trim());
190             }
191         }
192         
193         // Special case for model-element, where we need to generate an id field
194         if (getNodeType(node).equals(NodeType.MODEL_ELEMENT_VERTEX)) {
195             vertex.setProperty("model-element-uuid", generateModelElementId((Element)node));
196         }
197         
198         // Special case for nq-element, where we need to generate an id field
199         if (getNodeType(node).equals(NodeType.NQ_ELEMENT_VERTEX)) {
200             vertex.setProperty("named-query-element-uuid", generateModelElementId((Element)node));
201         }
202
203         return vertex;
204     }
205     
206     // Generate a unique hash to store as the id for this node
207     private static String generateModelElementId(Element node) {
208         Set<String> elemSet = new HashSet<String>();
209         
210         NodeList childNodes = node.getElementsByTagName("*");
211         for (int ix = 0; ix < childNodes.getLength(); ix++) {
212             NodeType nt = getNodeType(childNodes.item(ix));
213             if ( nt.equals(NodeType.ATTRIBUTE) || nt.equals(NodeType.RELATIONSHIP_KEY) || nt.equals(NodeType.RELATIONSHIP_VALUE) ) {
214                 elemSet.add(childNodes.item(ix).getTextContent().trim());
215             }
216         }
217                 
218         return Integer.toString(elemSet.hashCode());
219     }
220
221     private static NodeType getNodeType(Node node) {
222         if (!(node instanceof Element)) {
223             return NodeType.UNKNOWN;
224         }
225         
226         if (node.getNodeName().equalsIgnoreCase("relationship-list")) {
227             return NodeType.RELATIONSHIP_LIST;
228         }
229         
230         if (node.getNodeName().equalsIgnoreCase("relationship")) {
231             return NodeType.RELATIONSHIP;
232         }
233         
234         if (node.getNodeName().equalsIgnoreCase("relationship-data")) {
235             return NodeType.RELATIONSHIP_DATA;
236         }
237         
238         if (node.getNodeName().equalsIgnoreCase("related-to")) {
239             return NodeType.RELATED_TO;
240         }
241         
242         if (node.getNodeName().equalsIgnoreCase("relationship-key")) {
243             return NodeType.RELATIONSHIP_KEY;
244         }
245         
246         if (node.getNodeName().equalsIgnoreCase("relationship-value")) {
247             return NodeType.RELATIONSHIP_VALUE;
248         }
249         
250         if (node.getNodeName().equalsIgnoreCase("model-element")) {
251             return NodeType.MODEL_ELEMENT_VERTEX;
252         }
253         
254         if (node.getNodeName().equalsIgnoreCase("named-query-element")) {
255             return NodeType.NQ_ELEMENT_VERTEX;
256         }
257         
258         NodeList childNodes = node.getChildNodes();
259         int childElements = countChildElements(childNodes);
260         
261         if ( (childElements == 0) && (node.getTextContent() != null) && (!node.getTextContent().trim().isEmpty()) ) {
262             return NodeType.ATTRIBUTE;
263         }
264         
265         for (int ix = 0; ix < childNodes.getLength(); ix++) {
266             if (getNodeType(childNodes.item(ix)) == NodeType.ATTRIBUTE) {
267                 return NodeType.VERTEX;
268             }
269         }
270         
271         if (childElements > 0) {
272             return NodeType.CONTAINER;
273         }
274         
275         return NodeType.UNKNOWN;
276     }
277     
278     static int countChildElements(NodeList nodes) {
279         int count = 0;
280         for (int ix = 0; ix < nodes.getLength(); ix++) {
281             if (nodes.item(ix) instanceof Element) {
282                 count++;
283             }
284         }
285         
286         return count;   
287     }
288 }