cb08a251d7e180903d5d297e50c112842580a1c8
[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         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
67         factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
68         DocumentBuilder builder = factory.newDocumentBuilder();
69         InputSource is = new InputSource(new StringReader(xmlPayload));
70         Document doc = builder.parse(is);
71
72         GizmoBulkPayload gizmoPayload = new GizmoBulkPayload();
73
74         processNode(doc.getDocumentElement(), null, null, gizmoPayload);
75
76         return gizmoPayload.toJson();
77     }
78
79     private static void processNode(Node node, Node parentNode, GizmoVertexOperation parentVertexOp, GizmoBulkPayload gizmoPayload) {
80         if (!(node instanceof Element)) {
81             return;
82         }
83
84         Node newParent = null;
85         NodeType nodeType = getNodeType(node);
86
87         switch (nodeType) {
88         case VERTEX:
89         case MODEL_ELEMENT_VERTEX:
90         case NQ_ELEMENT_VERTEX:
91             parentVertexOp = createGizmoVertexOp(node, GizmoBulkPayload.ADD_OP);
92             gizmoPayload.addVertexOperation(parentVertexOp);
93             if (parentNode != null) {
94                 gizmoPayload.addEdgeOperation(createGizmoEdgeOp(node, parentNode));
95             }
96             newParent = node;
97             break;
98         case RELATIONSHIP:
99             processRelationship((Element)node, parentVertexOp, gizmoPayload);
100             newParent = parentNode;
101             break;
102         default:
103             newParent = parentNode;
104             break;
105         }
106
107         NodeList childNodes = node.getChildNodes();
108         for (int ix = 0; ix < childNodes.getLength(); ix++) {
109             processNode(childNodes.item(ix), newParent, parentVertexOp, gizmoPayload);
110         }
111     }
112
113     private static void processRelationship(Element relationshipNode, GizmoVertexOperation sourceNode, GizmoBulkPayload gizmoPayload) {
114         NodeList relatedToList = relationshipNode.getElementsByTagName("related-to");
115         if (relatedToList.getLength() != 1) {
116             logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, "Unable to resolve relationship");
117             return;
118         }
119
120         GizmoVertex targetVertex = new GizmoVertex();
121         targetVertex.setType(relatedToList.item(0).getTextContent().trim());
122
123         NodeList relationData = relationshipNode.getElementsByTagName("relationship-data");
124         for (int ix = 0; ix < relationData.getLength(); ix++) {
125             Element relationNode = (Element)relationData.item(ix);
126             NodeList keyList = relationNode.getElementsByTagName("relationship-key");
127             NodeList valueList = relationNode.getElementsByTagName("relationship-value");
128
129             if ( (keyList.getLength() != 1) || (valueList.getLength() != 1) ) {
130                 logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, "Unable to resolve relationship.  Missing key/value.");
131                 return;
132             }
133
134             String[] keyBits = keyList.item(0).getTextContent().trim().split("\\.");
135             String value = valueList.item(0).getTextContent().trim();
136
137             if (keyBits[0].equalsIgnoreCase(targetVertex.getType())) {
138                 targetVertex.setProperty(keyBits[1], value);
139             }
140         }
141
142         gizmoPayload.addVertexOperation(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(new GizmoEdgeOperation(GizmoBulkPayload.ADD_OP, edge.getSource() + "_" + edge.getTarget(), edge));
150     }
151
152     private static GizmoEdgeOperation createGizmoEdgeOp(Node node, Node parentNode) {
153         GizmoEdge edge = new GizmoEdge();
154
155         edge.setSource("$" + getVertexId(createGizmoVertex(node)));
156         edge.setTarget("$" + getVertexId(createGizmoVertex(parentNode)));
157
158         return new GizmoEdgeOperation(GizmoBulkPayload.ADD_OP, edge.getSource() + "_" + edge.getTarget(), edge);
159     }
160
161     private static GizmoVertexOperation createGizmoVertexOp(Node node, String operationType) {
162         GizmoVertex vertex = createGizmoVertex(node);
163         return new GizmoVertexOperation(operationType, getVertexId(vertex), vertex);
164     }
165
166     private static String getVertexId(GizmoVertex vertex) {
167         StringBuilder sb = new StringBuilder();
168         sb.append(vertex.getType());
169         for (Map.Entry<String, String> entry : vertex.getProperties().entrySet()) {
170             sb.append("-" + entry.getValue());
171         }
172
173         return sb.toString();
174     }
175
176     private static GizmoVertex createGizmoVertex(Node node) {
177         GizmoVertex vertex = new GizmoVertex();
178         vertex.setType(node.getNodeName().trim());
179
180         NodeList childNodes = node.getChildNodes();
181
182         for (int ix = 0; ix < childNodes.getLength(); ix++) {
183             if (getNodeType(childNodes.item(ix)).equals(NodeType.ATTRIBUTE)) {
184                 vertex.setProperty(childNodes.item(ix).getNodeName().trim(), childNodes.item(ix).getTextContent().trim());
185             }
186         }
187
188         // Special case for model-element, where we need to generate an id field
189         if (getNodeType(node).equals(NodeType.MODEL_ELEMENT_VERTEX)) {
190             vertex.setProperty("model-element-uuid", generateModelElementId((Element)node));
191         }
192
193         // Special case for nq-element, where we need to generate an id field
194         if (getNodeType(node).equals(NodeType.NQ_ELEMENT_VERTEX)) {
195             vertex.setProperty("named-query-element-uuid", generateModelElementId((Element)node));
196         }
197
198         return vertex;
199     }
200
201     // Generate a unique hash to store as the id for this node
202     private static String generateModelElementId(Element node) {
203         Set<String> elemSet = new HashSet<>();
204
205         NodeList childNodes = node.getElementsByTagName("*");
206         for (int ix = 0; ix < childNodes.getLength(); ix++) {
207             NodeType nt = getNodeType(childNodes.item(ix));
208             if ( nt.equals(NodeType.ATTRIBUTE) || nt.equals(NodeType.RELATIONSHIP_KEY) || nt.equals(NodeType.RELATIONSHIP_VALUE) ) {
209                 elemSet.add(childNodes.item(ix).getTextContent().trim());
210             }
211         }
212
213         return Integer.toString(elemSet.hashCode());
214     }
215
216     private static NodeType getNodeType(Node node) {
217         if (!(node instanceof Element)) {
218             return NodeType.UNKNOWN;
219         }
220
221         if (node.getNodeName().equalsIgnoreCase("relationship-list")) {
222             return NodeType.RELATIONSHIP_LIST;
223         }
224
225         if (node.getNodeName().equalsIgnoreCase("relationship")) {
226             return NodeType.RELATIONSHIP;
227         }
228
229         if (node.getNodeName().equalsIgnoreCase("relationship-data")) {
230             return NodeType.RELATIONSHIP_DATA;
231         }
232
233         if (node.getNodeName().equalsIgnoreCase("related-to")) {
234             return NodeType.RELATED_TO;
235         }
236
237         if (node.getNodeName().equalsIgnoreCase("relationship-key")) {
238             return NodeType.RELATIONSHIP_KEY;
239         }
240
241         if (node.getNodeName().equalsIgnoreCase("relationship-value")) {
242             return NodeType.RELATIONSHIP_VALUE;
243         }
244
245         if (node.getNodeName().equalsIgnoreCase("model-element")) {
246             return NodeType.MODEL_ELEMENT_VERTEX;
247         }
248
249         if (node.getNodeName().equalsIgnoreCase("named-query-element")) {
250             return NodeType.NQ_ELEMENT_VERTEX;
251         }
252
253         NodeList childNodes = node.getChildNodes();
254         int childElements = countChildElements(childNodes);
255
256         if ( (childElements == 0) && (node.getTextContent() != null) && (!node.getTextContent().trim().isEmpty()) ) {
257             return NodeType.ATTRIBUTE;
258         }
259
260         for (int ix = 0; ix < childNodes.getLength(); ix++) {
261             if (getNodeType(childNodes.item(ix)) == NodeType.ATTRIBUTE) {
262                 return NodeType.VERTEX;
263             }
264         }
265
266         if (childElements > 0) {
267             return NodeType.CONTAINER;
268         }
269
270         return NodeType.UNKNOWN;
271     }
272
273     static int countChildElements(NodeList nodes) {
274         int count = 0;
275         for (int ix = 0; ix < nodes.getLength(); ix++) {
276             if (nodes.item(ix) instanceof Element) {
277                 count++;
278             }
279         }
280
281         return count;
282     }
283 }