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