Add ExternalSystem to generated yaml
[aai/schema-service.git] / aai-schema-gen / src / main / java / org / onap / aai / schemagen / genxsd / OxmFileProcessor.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 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 package org.onap.aai.schemagen.genxsd;
21
22 import org.onap.aai.edges.EdgeIngestor;
23 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
24 import org.onap.aai.nodes.NodeIngestor;
25 import org.onap.aai.setup.SchemaVersion;
26 import org.onap.aai.setup.SchemaVersions;
27 import org.w3c.dom.*;
28 import org.xml.sax.InputSource;
29 import org.xml.sax.SAXException;
30
31 import javax.xml.XMLConstants;
32 import javax.xml.parsers.DocumentBuilder;
33 import javax.xml.parsers.DocumentBuilderFactory;
34 import javax.xml.parsers.ParserConfigurationException;
35 import java.io.File;
36 import java.io.FileNotFoundException;
37 import java.io.IOException;
38 import java.io.StringReader;
39 import java.util.*;
40
41 public abstract class OxmFileProcessor {
42
43     public static final String LINE_SEPARATOR = System.getProperty("line.separator");
44     public static final String DOUBLE_LINE_SEPARATOR =
45         System.getProperty("line.separator") + System.getProperty("line.separator");
46     protected static int annotationsStartVersion = 9; // minimum version to support annotations in
47                                                       // xsd
48     protected static int annotationsMinVersion = 6; // lower versions support annotations in xsd
49     protected static int swaggerSupportStartsVersion = 1; // minimum version to support swagger
50                                                           // documentation
51     protected static int swaggerDiffStartVersion = 1; // minimum version to support difference
52     protected static int swaggerMinBasepath = 6; // minimum version to support difference
53     static List<String> nodeFilter = createNodeFilter();
54     protected Set<String> namespaceFilter;
55     protected File oxmFile;
56     protected String xml;
57     protected SchemaVersion v;
58     protected Document doc = null;
59     protected String apiVersion = null;
60     protected SchemaVersions schemaVersions;
61     protected Map combinedJavaTypes;
62     protected String apiVersionFmt = null;
63     protected List<String> topLevelPaths = new ArrayList<String>();
64     protected HashMap<String, String> generatedJavaType = new HashMap<String, String>();
65     protected HashMap<String, String> appliedPaths = new HashMap<String, String>();
66     protected NodeList javaTypeNodes = null;
67     protected Map<String, String> javaTypeDefinitions = createJavaTypeDefinitions();
68     EdgeIngestor ei;
69     NodeIngestor ni;
70
71     public OxmFileProcessor(SchemaVersions schemaVersions, NodeIngestor ni, EdgeIngestor ei) {
72         this.schemaVersions = schemaVersions;
73         this.ni = ni;
74         this.ei = ei;
75     }
76
77     private static List<String> createNodeFilter() {
78         List<String> list = Arrays.asList("search", "actions", "aai-internal", "nodes");
79         return list;
80     }
81
82     private Map<String, String> createJavaTypeDefinitions() {
83         StringBuffer aaiInternal = new StringBuffer();
84         StringBuffer nodes = new StringBuffer();
85         Map<String, String> javaTypeDefinitions = new HashMap<String, String>();
86         // update to use platform portable line separator
87         aaiInternal.append("  aai-internal:" + LINE_SEPARATOR);
88         aaiInternal.append("    properties:" + LINE_SEPARATOR);
89         aaiInternal.append("      property-name:" + LINE_SEPARATOR);
90         aaiInternal.append("        type: string" + LINE_SEPARATOR);
91         aaiInternal.append("      property-value:" + LINE_SEPARATOR);
92         aaiInternal.append("        type: string" + LINE_SEPARATOR);
93         // javaTypeDefinitions.put("aai-internal", aaiInternal.toString());
94         nodes.append("  nodes:" + LINE_SEPARATOR);
95         nodes.append("    properties:" + LINE_SEPARATOR);
96         nodes.append("      inventory-item-data:" + LINE_SEPARATOR);
97         nodes.append("        type: array" + LINE_SEPARATOR);
98         nodes.append("        items:" + LINE_SEPARATOR);
99         nodes.append("          $ref: \"#/definitions/inventory-item-data\"" + LINE_SEPARATOR);
100         javaTypeDefinitions.put("nodes", nodes.toString());
101         return javaTypeDefinitions;
102     }
103
104     public void setOxmVersion(File oxmFile, SchemaVersion v) {
105         this.oxmFile = oxmFile;
106         this.v = v;
107     }
108
109     public void setXmlVersion(String xml, SchemaVersion v) {
110         this.xml = xml;
111         this.v = v;
112     }
113
114     public void setVersion(SchemaVersion v) {
115         this.oxmFile = null;
116         this.v = v;
117     }
118
119     public void setNodeIngestor(NodeIngestor ni) {
120         this.ni = ni;
121     }
122
123     public void setEdgeIngestor(EdgeIngestor ei) {
124         this.ei = ei;
125     }
126
127     public SchemaVersions getSchemaVersions() {
128         return schemaVersions;
129     }
130
131     public void setSchemaVersions(SchemaVersions schemaVersions) {
132         this.schemaVersions = schemaVersions;
133     }
134
135     protected void getTopLevelPaths(XSDElement elem) {
136         NodeList parentNodes;
137         Element parentElement;
138         NodeList xmlElementNodes;
139
140         parentNodes = elem.getElementsByTagName("java-attributes");
141         if (parentNodes.getLength() == 0) {
142             return;
143         }
144         parentElement = (Element) parentNodes.item(0);
145         xmlElementNodes = parentElement.getElementsByTagName("xml-element");
146         if (xmlElementNodes.getLength() <= 0) {
147             return;
148         }
149
150         XSDElement xmlElementElement;
151
152         for (int i = 0; i < xmlElementNodes.getLength(); ++i) {
153             xmlElementElement = new XSDElement((Element) xmlElementNodes.item(i));
154             if (!xmlElementElement.getParentNode().isSameNode(parentElement)) {
155                 continue;
156             }
157             String topLevel = xmlElementElement.getAttribute("type");
158             topLevel = topLevel.substring(topLevel.lastIndexOf('.') + 1);
159             if (!topLevelPaths.contains(topLevel)) {
160                 if ("Nodes".equals(topLevel) || "AaiInternal".equals(topLevel)) {
161                     continue;
162                 }
163                 topLevelPaths.add(topLevel);
164             }
165         }
166     }
167
168     protected boolean checkTopLevel(String topLevel, boolean ignoreActionsSearch) {
169         // when ignoreActionsSearch is set to true, with a topLevel that matches one of the values
170         // to ignore, the logic will handle those values, as if they are not at the top level.
171         // this was done when refactoring checks that may or may not include these top levels.
172         // Using this API allows new top levels to be added to the schema file and
173         // included in the generated yaml without changing this generation logic.
174         if (ignoreActionsSearch) {
175             if ("Actions".equals(topLevel) || "Search".equals(topLevel)) {
176                 return false;
177             }
178         }
179         if (topLevelPaths.contains(topLevel)) {
180             return true;
181         }
182         return false;
183     }
184
185     protected void init()
186         throws ParserConfigurationException, SAXException, IOException, EdgeRuleNotFoundException {
187         if (this.xml != null || this.oxmFile != null) {
188             createDocument();
189         }
190         if (this.doc == null) {
191             this.doc = ni.getSchema(v);
192         }
193         namespaceFilter = new HashSet<>();
194
195         NodeList bindingsNodes = doc.getElementsByTagName("xml-bindings");
196         Element bindingElement;
197         NodeList javaTypesNodes;
198         Element javaTypesElement;
199
200         if (bindingsNodes == null || bindingsNodes.getLength() == 0) {
201             throw new SAXException("OXM file error: missing <binding-nodes> in " + oxmFile);
202         }
203
204         bindingElement = (Element) bindingsNodes.item(0);
205         javaTypesNodes = bindingElement.getElementsByTagName("java-types");
206         if (javaTypesNodes.getLength() < 1) {
207             throw new SAXException(
208                 "OXM file error: missing <binding-nodes><java-types> in " + oxmFile);
209         }
210         javaTypesElement = (Element) javaTypesNodes.item(0);
211
212         javaTypeNodes = javaTypesElement.getElementsByTagName("java-type");
213         if (javaTypeNodes.getLength() < 1) {
214             throw new SAXException(
215                 "OXM file error: missing <binding-nodes><java-types><java-type> in " + oxmFile);
216         }
217     }
218
219     private void createDocument() throws ParserConfigurationException, SAXException, IOException {
220         DocumentBuilder dBuilder = null;
221         try {
222             DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
223             dbFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
224             dBuilder = dbFactory.newDocumentBuilder();
225         } catch (ParserConfigurationException e) {
226             throw e;
227         }
228         try {
229             if (xml == null) {
230                 doc = dBuilder.parse(oxmFile);
231             } else {
232                 InputSource isInput = new InputSource(new StringReader(xml));
233                 doc = dBuilder.parse(isInput);
234             }
235         } catch (SAXException e) {
236             throw e;
237         } catch (IOException e) {
238             throw e;
239         }
240         return;
241     }
242
243     public abstract String getDocumentHeader();
244
245     public abstract String process() throws ParserConfigurationException, SAXException, IOException,
246         FileNotFoundException, EdgeRuleNotFoundException;
247
248     public String getXMLRootElementName(Element javaTypeElement) {
249         String xmlRootElementName = null;
250         NamedNodeMap attributes;
251
252         NodeList valNodes = javaTypeElement.getElementsByTagName("xml-root-element");
253         Element valElement = (Element) valNodes.item(0);
254         attributes = valElement.getAttributes();
255         for (int i = 0; i < attributes.getLength(); ++i) {
256             Attr attr = (Attr) attributes.item(i);
257             String attrName = attr.getNodeName();
258
259             String attrValue = attr.getNodeValue();
260             if (attrName.equals("name")) {
261                 xmlRootElementName = attrValue;
262             }
263         }
264         return xmlRootElementName;
265     }
266
267     public String getXmlRootElementName(String javaTypeName) {
268         String attrName, attrValue;
269         Attr attr;
270         Element javaTypeElement;
271         for (int i = 0; i < javaTypeNodes.getLength(); ++i) {
272             javaTypeElement = (Element) javaTypeNodes.item(i);
273             NamedNodeMap attributes = javaTypeElement.getAttributes();
274             for (int j = 0; j < attributes.getLength(); ++j) {
275                 attr = (Attr) attributes.item(j);
276                 attrName = attr.getNodeName();
277                 attrValue = attr.getNodeValue();
278                 if (attrName.equals("name") && attrValue.equals(javaTypeName)) {
279                     NodeList valNodes = javaTypeElement.getElementsByTagName("xml-root-element");
280                     Element valElement = (Element) valNodes.item(0);
281                     attributes = valElement.getAttributes();
282                     for (int k = 0; k < attributes.getLength(); ++k) {
283                         attr = (Attr) attributes.item(k);
284                         attrName = attr.getNodeName();
285
286                         attrValue = attr.getNodeValue();
287                         if (attrName.equals("name")) {
288                             return (attrValue);
289                         }
290                     }
291                 }
292             }
293         }
294         return null;
295     }
296
297     public Map getCombinedJavaTypes() {
298         return combinedJavaTypes;
299     }
300
301     public void setCombinedJavaTypes(Map combinedJavaTypes) {
302         this.combinedJavaTypes = combinedJavaTypes;
303     }
304
305     public Element getJavaTypeElementSwagger(String javaTypeName) {
306
307         String attrName, attrValue;
308         Attr attr;
309         Element javaTypeElement;
310
311         List<Element> combineElementList = new ArrayList<Element>();
312         for (int i = 0; i < javaTypeNodes.getLength(); ++i) {
313             javaTypeElement = (Element) javaTypeNodes.item(i);
314             NamedNodeMap attributes = javaTypeElement.getAttributes();
315             for (int j = 0; j < attributes.getLength(); ++j) {
316                 attr = (Attr) attributes.item(j);
317                 attrName = attr.getNodeName();
318                 attrValue = attr.getNodeValue();
319                 if (attrName.equals("name") && attrValue.equals(javaTypeName)) {
320                     combineElementList.add(javaTypeElement);
321                 }
322             }
323         }
324         if (combineElementList.size() == 0) {
325             return (Element) null;
326         } else if (combineElementList.size() > 1) {
327             return combineElements(javaTypeName, combineElementList);
328         }
329         return combineElementList.get(0);
330     }
331
332     public boolean versionSupportsSwaggerDiff(String version) {
333         int ver = new Integer(version.substring(1)).intValue();
334         if (ver >= HTMLfromOXM.swaggerDiffStartVersion) {
335             return true;
336         }
337         return false;
338     }
339
340     public boolean versionSupportsBasePathProperty(String version) {
341         int ver = new Integer(version.substring(1)).intValue();
342         if (ver <= HTMLfromOXM.swaggerMinBasepath) {
343             return true;
344         }
345         return false;
346     }
347
348     protected void updateParentXmlElements(Element parentElement, NodeList moreXmlElementNodes) {
349         Element xmlElement;
350         NodeList childNodes;
351         Node childNode;
352
353         Node refChild = null;
354         // find childNode with attributes and no children, insert children before that node
355         childNodes = parentElement.getChildNodes();
356         if (childNodes == null || childNodes.getLength() == 0) {
357             // should not happen since the base parent was chosen if it had children
358             return;
359         }
360
361         for (int i = 0; i < childNodes.getLength(); ++i) {
362             refChild = childNodes.item(i);
363             if (refChild.hasAttributes() && !refChild.hasChildNodes()) {
364                 break;
365             }
366
367         }
368
369         for (int i = 0; i < moreXmlElementNodes.getLength(); ++i) {
370             xmlElement = (Element) moreXmlElementNodes.item(i);
371             childNode = xmlElement.cloneNode(true);
372             parentElement.insertBefore(childNode, refChild);
373         }
374     }
375
376     protected Node getXmlPropertiesNode(Element javaTypeElement) {
377         NodeList nl = javaTypeElement.getChildNodes();
378         Node child;
379         for (int i = 0; i < nl.getLength(); ++i) {
380             child = nl.item(i);
381             if ("xml-properties".equals(child.getNodeName())) {
382                 return child;
383             }
384         }
385         return null;
386     }
387
388     protected Node merge(NodeList nl, Node mergeNode) {
389         NamedNodeMap nnm = mergeNode.getAttributes();
390         Node childNode;
391         NamedNodeMap childNnm;
392
393         String mergeName = nnm.getNamedItem("name").getNodeValue();
394         String mergeValue = nnm.getNamedItem("value").getNodeValue();
395         String childName;
396         String childValue;
397         for (int j = 0; j < nl.getLength(); ++j) {
398             childNode = nl.item(j);
399             if ("xml-property".equals(childNode.getNodeName())) {
400                 childNnm = childNode.getAttributes();
401                 childName = childNnm.getNamedItem("name").getNodeValue();
402                 childValue = childNnm.getNamedItem("value").getNodeValue();
403                 if (childName.equals(mergeName)) {
404                     // attribute exists
405                     // keep, replace or update
406                     if (childValue.contains(mergeValue)) {
407                         return null;
408                     }
409                     if (mergeValue.contains(childValue)) {
410                         childNnm.getNamedItem("value").setTextContent(mergeValue);
411                         return null;
412                     }
413                     childNnm.getNamedItem("value").setTextContent(mergeValue + "," + childValue);
414                     return null;
415                 }
416             }
417         }
418         childNode = mergeNode.cloneNode(true);
419         return childNode;
420     }
421
422     protected void mergeXmlProperties(Node useChildProperties, NodeList propertiesToMerge) {
423         NodeList nl = useChildProperties.getChildNodes();
424         Node childNode;
425         Node newNode;
426         for (int i = 0; i < propertiesToMerge.getLength(); ++i) {
427             childNode = propertiesToMerge.item(i);
428             if ("xml-property".equals(childNode.getNodeName())) {
429                 newNode = merge(nl, childNode);
430                 if (newNode != null) {
431                     useChildProperties.appendChild(newNode);
432                 }
433             }
434
435         }
436     }
437
438     protected void combineXmlProperties(int useElement, List<Element> combineElementList) {
439         // add or update xml-properties to the referenced element from the combined list
440         Element javaTypeElement = combineElementList.get(useElement);
441         NodeList nl = javaTypeElement.getChildNodes();
442         Node useChildProperties = getXmlPropertiesNode(javaTypeElement);
443         int cloneChild = -1;
444         Node childProperties;
445         if (useChildProperties == null) {
446             // find xml-properties to clone
447             for (int i = 0; i < combineElementList.size(); ++i) {
448                 if (i == useElement) {
449                     continue;
450                 }
451                 childProperties = getXmlPropertiesNode(combineElementList.get(i));
452                 if (childProperties != null) {
453                     useChildProperties = childProperties.cloneNode(true);
454                     javaTypeElement.appendChild(useChildProperties);
455                     cloneChild = i;
456                 }
457             }
458         }
459         NodeList cnl;
460         // find other xml-properties
461         for (int i = 0; i < combineElementList.size(); ++i) {
462             if (i == useElement || (cloneChild >= 0 && i <= cloneChild)) {
463                 continue;
464             }
465             childProperties = getXmlPropertiesNode(combineElementList.get(i));
466             if (childProperties == null) {
467                 continue;
468             }
469             cnl = childProperties.getChildNodes();
470             mergeXmlProperties(useChildProperties, cnl);
471         }
472
473     }
474
475     protected Element combineElements(String javaTypeName, List<Element> combineElementList) {
476         Element javaTypeElement;
477         NodeList parentNodes;
478         Element parentElement = null;
479         NodeList xmlElementNodes;
480
481         int useElement = -1;
482         if (combinedJavaTypes.containsKey(javaTypeName)) {
483             return combineElementList.get((int) combinedJavaTypes.get(javaTypeName));
484         }
485         for (int i = 0; i < combineElementList.size(); ++i) {
486             javaTypeElement = combineElementList.get(i);
487             parentNodes = javaTypeElement.getElementsByTagName("java-attributes");
488             if (parentNodes.getLength() == 0) {
489                 continue;
490             }
491             parentElement = (Element) parentNodes.item(0);
492             xmlElementNodes = parentElement.getElementsByTagName("xml-element");
493             if (xmlElementNodes.getLength() <= 0) {
494                 continue;
495             }
496             useElement = i;
497             break;
498         }
499         boolean doCombineElements = true;
500         if (useElement < 0) {
501             useElement = 0;
502             doCombineElements = false;
503         } else if (useElement == combineElementList.size() - 1) {
504             doCombineElements = false;
505         }
506         if (doCombineElements) {
507             // get xml-element from other javaTypeElements
508             Element otherParentElement = null;
509             for (int i = 0; i < combineElementList.size(); ++i) {
510                 if (i == useElement) {
511                     continue;
512                 }
513                 javaTypeElement = combineElementList.get(i);
514                 parentNodes = javaTypeElement.getElementsByTagName("java-attributes");
515                 if (parentNodes.getLength() == 0) {
516                     continue;
517                 }
518                 otherParentElement = (Element) parentNodes.item(0);
519                 xmlElementNodes = otherParentElement.getElementsByTagName("xml-element");
520                 if (xmlElementNodes.getLength() <= 0) {
521                     continue;
522                 }
523                 // xml-element that are not present
524                 updateParentXmlElements(parentElement, xmlElementNodes);
525
526             }
527         }
528         // need to combine xml-properties
529         combineXmlProperties(useElement, combineElementList);
530         combinedJavaTypes.put(javaTypeName, useElement);
531         return combineElementList.get(useElement);
532     }
533 }