d9c544dba9ceb44d5546961c131c77ee18e41772
[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             dbFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
225             dbFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
226             dbFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
227             dbFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
228             dbFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
229             dBuilder = dbFactory.newDocumentBuilder();
230         } catch (ParserConfigurationException e) {
231             throw e;
232         }
233         try {
234             if (xml == null) {
235                 doc = dBuilder.parse(oxmFile);
236             } else {
237                 InputSource isInput = new InputSource(new StringReader(xml));
238                 doc = dBuilder.parse(isInput);
239             }
240         } catch (SAXException e) {
241             throw e;
242         } catch (IOException e) {
243             throw e;
244         }
245         return;
246     }
247
248     public abstract String getDocumentHeader();
249
250     public abstract String process() throws ParserConfigurationException, SAXException, IOException,
251         FileNotFoundException, EdgeRuleNotFoundException;
252
253     public String getXMLRootElementName(Element javaTypeElement) {
254         String xmlRootElementName = null;
255         NamedNodeMap attributes;
256
257         NodeList valNodes = javaTypeElement.getElementsByTagName("xml-root-element");
258         Element valElement = (Element) valNodes.item(0);
259         attributes = valElement.getAttributes();
260         for (int i = 0; i < attributes.getLength(); ++i) {
261             Attr attr = (Attr) attributes.item(i);
262             String attrName = attr.getNodeName();
263
264             String attrValue = attr.getNodeValue();
265             if (attrName.equals("name")) {
266                 xmlRootElementName = attrValue;
267             }
268         }
269         return xmlRootElementName;
270     }
271
272     public String getXmlRootElementName(String javaTypeName) {
273         String attrName, attrValue;
274         Attr attr;
275         Element javaTypeElement;
276         for (int i = 0; i < javaTypeNodes.getLength(); ++i) {
277             javaTypeElement = (Element) javaTypeNodes.item(i);
278             NamedNodeMap attributes = javaTypeElement.getAttributes();
279             for (int j = 0; j < attributes.getLength(); ++j) {
280                 attr = (Attr) attributes.item(j);
281                 attrName = attr.getNodeName();
282                 attrValue = attr.getNodeValue();
283                 if (attrName.equals("name") && attrValue.equals(javaTypeName)) {
284                     NodeList valNodes = javaTypeElement.getElementsByTagName("xml-root-element");
285                     Element valElement = (Element) valNodes.item(0);
286                     attributes = valElement.getAttributes();
287                     for (int k = 0; k < attributes.getLength(); ++k) {
288                         attr = (Attr) attributes.item(k);
289                         attrName = attr.getNodeName();
290
291                         attrValue = attr.getNodeValue();
292                         if (attrName.equals("name")) {
293                             return (attrValue);
294                         }
295                     }
296                 }
297             }
298         }
299         return null;
300     }
301
302     public Map getCombinedJavaTypes() {
303         return combinedJavaTypes;
304     }
305
306     public void setCombinedJavaTypes(Map combinedJavaTypes) {
307         this.combinedJavaTypes = combinedJavaTypes;
308     }
309
310     public Element getJavaTypeElementSwagger(String javaTypeName) {
311
312         String attrName, attrValue;
313         Attr attr;
314         Element javaTypeElement;
315
316         List<Element> combineElementList = new ArrayList<Element>();
317         for (int i = 0; i < javaTypeNodes.getLength(); ++i) {
318             javaTypeElement = (Element) javaTypeNodes.item(i);
319             NamedNodeMap attributes = javaTypeElement.getAttributes();
320             for (int j = 0; j < attributes.getLength(); ++j) {
321                 attr = (Attr) attributes.item(j);
322                 attrName = attr.getNodeName();
323                 attrValue = attr.getNodeValue();
324                 if (attrName.equals("name") && attrValue.equals(javaTypeName)) {
325                     combineElementList.add(javaTypeElement);
326                 }
327             }
328         }
329         if (combineElementList.size() == 0) {
330             return (Element) null;
331         } else if (combineElementList.size() > 1) {
332             return combineElements(javaTypeName, combineElementList);
333         }
334         return combineElementList.get(0);
335     }
336
337     public boolean versionSupportsSwaggerDiff(String version) {
338         int ver = new Integer(version.substring(1)).intValue();
339         if (ver >= HTMLfromOXM.swaggerDiffStartVersion) {
340             return true;
341         }
342         return false;
343     }
344
345     public boolean versionSupportsBasePathProperty(String version) {
346         int ver = new Integer(version.substring(1)).intValue();
347         if (ver <= HTMLfromOXM.swaggerMinBasepath) {
348             return true;
349         }
350         return false;
351     }
352
353     protected void updateParentXmlElements(Element parentElement, NodeList moreXmlElementNodes) {
354         Element xmlElement;
355         NodeList childNodes;
356         Node childNode;
357
358         Node refChild = null;
359         // find childNode with attributes and no children, insert children before that node
360         childNodes = parentElement.getChildNodes();
361         if (childNodes == null || childNodes.getLength() == 0) {
362             // should not happen since the base parent was chosen if it had children
363             return;
364         }
365
366         for (int i = 0; i < childNodes.getLength(); ++i) {
367             refChild = childNodes.item(i);
368             if (refChild.hasAttributes() && !refChild.hasChildNodes()) {
369                 break;
370             }
371
372         }
373
374         for (int i = 0; i < moreXmlElementNodes.getLength(); ++i) {
375             xmlElement = (Element) moreXmlElementNodes.item(i);
376             childNode = xmlElement.cloneNode(true);
377             parentElement.insertBefore(childNode, refChild);
378         }
379     }
380
381     protected Node getXmlPropertiesNode(Element javaTypeElement) {
382         NodeList nl = javaTypeElement.getChildNodes();
383         Node child;
384         for (int i = 0; i < nl.getLength(); ++i) {
385             child = nl.item(i);
386             if ("xml-properties".equals(child.getNodeName())) {
387                 return child;
388             }
389         }
390         return null;
391     }
392
393     protected Node merge(NodeList nl, Node mergeNode) {
394         NamedNodeMap nnm = mergeNode.getAttributes();
395         Node childNode;
396         NamedNodeMap childNnm;
397
398         String mergeName = nnm.getNamedItem("name").getNodeValue();
399         String mergeValue = nnm.getNamedItem("value").getNodeValue();
400         String childName;
401         String childValue;
402         for (int j = 0; j < nl.getLength(); ++j) {
403             childNode = nl.item(j);
404             if ("xml-property".equals(childNode.getNodeName())) {
405                 childNnm = childNode.getAttributes();
406                 childName = childNnm.getNamedItem("name").getNodeValue();
407                 childValue = childNnm.getNamedItem("value").getNodeValue();
408                 if (childName.equals(mergeName)) {
409                     // attribute exists
410                     // keep, replace or update
411                     if (childValue.contains(mergeValue)) {
412                         return null;
413                     }
414                     if (mergeValue.contains(childValue)) {
415                         childNnm.getNamedItem("value").setTextContent(mergeValue);
416                         return null;
417                     }
418                     childNnm.getNamedItem("value").setTextContent(mergeValue + "," + childValue);
419                     return null;
420                 }
421             }
422         }
423         childNode = mergeNode.cloneNode(true);
424         return childNode;
425     }
426
427     protected void mergeXmlProperties(Node useChildProperties, NodeList propertiesToMerge) {
428         NodeList nl = useChildProperties.getChildNodes();
429         Node childNode;
430         Node newNode;
431         for (int i = 0; i < propertiesToMerge.getLength(); ++i) {
432             childNode = propertiesToMerge.item(i);
433             if ("xml-property".equals(childNode.getNodeName())) {
434                 newNode = merge(nl, childNode);
435                 if (newNode != null) {
436                     useChildProperties.appendChild(newNode);
437                 }
438             }
439
440         }
441     }
442
443     protected void combineXmlProperties(int useElement, List<Element> combineElementList) {
444         // add or update xml-properties to the referenced element from the combined list
445         Element javaTypeElement = combineElementList.get(useElement);
446         NodeList nl = javaTypeElement.getChildNodes();
447         Node useChildProperties = getXmlPropertiesNode(javaTypeElement);
448         int cloneChild = -1;
449         Node childProperties;
450         if (useChildProperties == null) {
451             // find xml-properties to clone
452             for (int i = 0; i < combineElementList.size(); ++i) {
453                 if (i == useElement) {
454                     continue;
455                 }
456                 childProperties = getXmlPropertiesNode(combineElementList.get(i));
457                 if (childProperties != null) {
458                     useChildProperties = childProperties.cloneNode(true);
459                     javaTypeElement.appendChild(useChildProperties);
460                     cloneChild = i;
461                 }
462             }
463         }
464         NodeList cnl;
465         // find other xml-properties
466         for (int i = 0; i < combineElementList.size(); ++i) {
467             if (i == useElement || (cloneChild >= 0 && i <= cloneChild)) {
468                 continue;
469             }
470             childProperties = getXmlPropertiesNode(combineElementList.get(i));
471             if (childProperties == null) {
472                 continue;
473             }
474             cnl = childProperties.getChildNodes();
475             mergeXmlProperties(useChildProperties, cnl);
476         }
477
478     }
479
480     protected Element combineElements(String javaTypeName, List<Element> combineElementList) {
481         Element javaTypeElement;
482         NodeList parentNodes;
483         Element parentElement = null;
484         NodeList xmlElementNodes;
485
486         int useElement = -1;
487         if (combinedJavaTypes.containsKey(javaTypeName)) {
488             return combineElementList.get((int) combinedJavaTypes.get(javaTypeName));
489         }
490         for (int i = 0; i < combineElementList.size(); ++i) {
491             javaTypeElement = combineElementList.get(i);
492             parentNodes = javaTypeElement.getElementsByTagName("java-attributes");
493             if (parentNodes.getLength() == 0) {
494                 continue;
495             }
496             parentElement = (Element) parentNodes.item(0);
497             xmlElementNodes = parentElement.getElementsByTagName("xml-element");
498             if (xmlElementNodes.getLength() <= 0) {
499                 continue;
500             }
501             useElement = i;
502             break;
503         }
504         boolean doCombineElements = true;
505         if (useElement < 0) {
506             useElement = 0;
507             doCombineElements = false;
508         } else if (useElement == combineElementList.size() - 1) {
509             doCombineElements = false;
510         }
511         if (doCombineElements) {
512             // get xml-element from other javaTypeElements
513             Element otherParentElement = null;
514             for (int i = 0; i < combineElementList.size(); ++i) {
515                 if (i == useElement) {
516                     continue;
517                 }
518                 javaTypeElement = combineElementList.get(i);
519                 parentNodes = javaTypeElement.getElementsByTagName("java-attributes");
520                 if (parentNodes.getLength() == 0) {
521                     continue;
522                 }
523                 otherParentElement = (Element) parentNodes.item(0);
524                 xmlElementNodes = otherParentElement.getElementsByTagName("xml-element");
525                 if (xmlElementNodes.getLength() <= 0) {
526                     continue;
527                 }
528                 // xml-element that are not present
529                 updateParentXmlElements(parentElement, xmlElementNodes);
530
531             }
532         }
533         // need to combine xml-properties
534         combineXmlProperties(useElement, combineElementList);
535         combinedJavaTypes.put(javaTypeName, useElement);
536         return combineElementList.get(useElement);
537     }
538 }