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