fix xsd generation when obj crosses oxm files
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / util / genxsd / HTMLfromOXM.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.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28
29 import javax.xml.parsers.ParserConfigurationException;
30
31 import org.apache.commons.lang.StringUtils;
32 import org.onap.aai.config.SpringContextAware;
33 import org.onap.aai.edges.EdgeIngestor;
34 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
35 import org.onap.aai.exceptions.AAIException;
36 import org.onap.aai.nodes.NodeIngestor;
37 import org.onap.aai.setup.SchemaVersion;
38 import org.onap.aai.setup.SchemaVersions;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 import org.w3c.dom.Attr;
43 import org.w3c.dom.Element;
44 import org.w3c.dom.NamedNodeMap;
45 import org.w3c.dom.Node;
46 import org.w3c.dom.NodeList;
47 import org.xml.sax.SAXException;
48
49 import com.google.common.base.CaseFormat;
50
51 public class HTMLfromOXM extends OxmFileProcessor {
52
53         private static final Logger logger = LoggerFactory.getLogger("HTMLfromOXM.class");
54         public static final String LINE_SEPARATOR = System.getProperty("line.separator");
55
56         private String maxOccurs;
57         
58         public HTMLfromOXM(String maxOccurs, SchemaVersions schemaVersions, NodeIngestor ni, EdgeIngestor ei ){
59                 super(schemaVersions, ni,ei);
60                 this.maxOccurs = maxOccurs;
61         }
62         public void setOxmVersion(File oxmFile, SchemaVersion v) {
63                 super.setOxmVersion(oxmFile, v);
64                 this.v = v;
65         }
66         public void setXmlVersion(String xml, SchemaVersion v) {
67                 super.setXmlVersion(xml, v);
68                 this.v = v;
69         }
70         public void setVersion(SchemaVersion v) {
71                 super.setVersion(v);
72                 this.v = v;
73         }
74         
75         
76         @Override
77         public String getDocumentHeader() {
78                 StringBuffer sb = new StringBuffer();
79                 logger.trace("processing starts");
80                 sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n");
81                 String namespace = "org.onap";
82                 if (v.compareTo(getSchemaVersions().getNamespaceChangeVersion()) < 0 ) {
83                         namespace = "org.openecomp";
84                 }
85                 if ( versionUsesAnnotations(v.toString()) ) {
86                         sb.append("<xs:schema elementFormDefault=\"qualified\" version=\"1.0\" targetNamespace=\"http://" + namespace + ".aai.inventory/" 
87                                 + v.toString() + "\" xmlns:tns=\"http://" + namespace + ".aai.inventory/" + v.toString() + "\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
88                                                 + "\n"
89                                                 + "xmlns:jaxb=\"http://java.sun.com/xml/ns/jaxb\"\r\n" + 
90                                                 "    jaxb:version=\"2.1\"\r\n" + 
91                                                 "    xmlns:annox=\"http://annox.dev.java.net\"\r\n" + 
92                                                 "    jaxb:extensionBindingPrefixes=\"annox\">\n\n");
93                 } else {
94                         sb.append("<xs:schema elementFormDefault=\"qualified\" version=\"1.0\" targetNamespace=\"http://" + namespace + ".aai.inventory/" 
95                                         + v.toString() + "\" xmlns:tns=\"http://" + namespace + ".aai.inventory/" + v.toString() + "\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n\n");
96                 }
97                 return sb.toString();
98         }
99         
100
101         @Override
102         public String process() throws ParserConfigurationException, SAXException, IOException, AAIException, FileNotFoundException, EdgeRuleNotFoundException {
103                 StringBuilder sb = new StringBuilder();
104                 try {
105                         init();
106                 } catch(Exception e) {
107                         logger.error( "Error initializing " + this.getClass());
108                         throw e;
109                 }
110                 sb.append(getDocumentHeader());
111                 StringBuilder sbInventory = new StringBuilder();
112                 Element elem;
113                 String javaTypeName;
114                 combinedJavaTypes = new HashMap();
115                 for ( int i = 0; i < javaTypeNodes.getLength(); ++ i ) {
116                         elem = (Element)javaTypeNodes.item(i);
117                         javaTypeName = elem.getAttribute("name");
118                         if ( !"Inventory".equals(javaTypeName ) ) {
119                                 if ( generatedJavaType.containsKey(javaTypeName) ) {
120                                         continue;
121                                 }
122                                 // will combine all matching java-types
123                                 elem = getJavaTypeElement(javaTypeName,false );
124                         }
125                         XSDElement javaTypeElement = new XSDElement(elem, maxOccurs);
126                         //javaTypeName = javaTypeElement.name();
127                         if ( javaTypeName == null ) {
128                                 String msg = "Invalid OXM file: <java-type> has no name attribute in " + oxmFile; 
129                                 logger.error(msg);
130                                 throw new AAIException(msg);
131                         }
132                         if ("Nodes".equals(javaTypeName)) {
133                                 logger.debug("skipping Nodes entry (temporary feature)");
134                                 continue;
135                         }
136                         logger.debug(getXmlRootElementName(javaTypeName)+" vs "+ javaTypeName+":"+generatedJavaType.containsKey(getXmlRootElementName(javaTypeName)));
137
138                         if ( !"Inventory".equals(javaTypeName)) {
139                                 generatedJavaType.put(javaTypeName, null);
140                         }
141                         sb.append(processJavaTypeElement( javaTypeName, javaTypeElement, sbInventory ));
142                 }
143                 sb.append(sbInventory);
144                 sb.append("      </xs:sequence>\n");
145                 sb.append("    </xs:complexType>\n");
146                 sb.append("  </xs:element>\n");                 
147                 sb.append("</xs:schema>\n");
148                 StringBuilder invalidSb = new StringBuilder();
149                 return sb.toString();
150         }
151         
152         protected boolean isValidName( String name ) {
153                 if ( name == null || name.length() == 0 ) {
154                         return false;
155                 }
156                 String pattern = "^[a-z0-9-]*$";
157                 return name.matches(pattern);
158         }
159
160         public String processJavaTypeElement( String javaTypeName, Element javaType_Element, StringBuilder sbInventory) {
161                 String xmlRootElementName = getXMLRootElementName(javaType_Element);
162
163                 NodeList parentNodes = javaType_Element.getElementsByTagName("java-attributes");
164                 StringBuffer sb = new StringBuffer();
165                 if ( parentNodes.getLength() == 0 ) {
166                         logger.trace( "no java-attributes for java-type " + javaTypeName);
167                         return "";
168                 }
169         
170                 Element parentElement = (Element)parentNodes.item(0);
171                 NodeList xmlElementNodes = parentElement.getElementsByTagName("xml-element");
172                 // support for multiple inventory elements across oxm files
173                 boolean processingInventory = false;
174                 boolean hasPreviousInventory = false;
175                 if ( "inventory".equals(xmlRootElementName) && sbInventory != null ) {
176                         processingInventory = true;
177                         if ( sbInventory.toString().contains("xs:complexType") ) {
178                                 hasPreviousInventory = true;
179                         }
180                 }
181                 
182                 StringBuffer sb1 = new StringBuffer();
183                 if ( xmlElementNodes.getLength() > 0 ) {
184
185                         if ( !processingInventory || !hasPreviousInventory ) {
186                                 sb1.append("  <xs:element name=\"" + xmlRootElementName + "\">\n");
187                                 sb1.append("    <xs:complexType>\n");
188
189                                 XSDElement javaTypeElement = new XSDElement(javaType_Element, maxOccurs);
190                                 logger.debug("XSDElement name: "+javaTypeElement.name());
191                                 if(versionUsesAnnotations(v.toString())) {
192                                         sb1.append(javaTypeElement.getHTMLAnnotation("class", "      "));
193                                 }
194                                 sb1.append("      <xs:sequence>\n");
195                         }
196                         Element javatypeElement;
197                         for ( int i = 0; i < xmlElementNodes.getLength(); ++i ) {
198                         
199                                 XSDElement xmlElementElement = new XSDElement((Element)xmlElementNodes.item(i), maxOccurs);
200
201 //                              String elementName = xmlElementElement.getAttribute("name");
202                                 String elementType = xmlElementElement.getAttribute("type");
203                                 //No simple types; only AAI custom types
204                                 String addType = elementType.contains("." + v.toString() + ".") ? elementType.substring(elementType.lastIndexOf('.')+1) : null;
205                         if ( elementType.contains("." + v.toString() + ".") && !generatedJavaType.containsKey(addType) ) {
206                                 generatedJavaType.put(addType, elementType);
207                                 javatypeElement = getJavaTypeElement(addType, processingInventory);
208
209                                 sb.append(processJavaTypeElement( addType, javatypeElement, null ));    
210                         }
211                         if ("Nodes".equals(addType)) {
212                                 logger.trace("Skipping nodes, temporary testing");
213                                 continue;
214                         }
215                         //assembles the basic <element> 
216                         sb1.append(xmlElementElement.getHTMLElement(v, versionUsesAnnotations(v.toString()), this));
217                         }
218                         if ( !processingInventory ) {
219                                 sb1.append("      </xs:sequence>\n");
220                                 sb1.append("    </xs:complexType>\n");
221                                 sb1.append("  </xs:element>\n");
222                         }
223                 }
224         
225                 if ( xmlElementNodes.getLength() < 1 ) {
226                         sb.append("  <xs:element name=\"" + xmlRootElementName + "\">\n");
227                         sb.append("    <xs:complexType>\n");
228                         sb.append("      <xs:sequence/>\n");
229                         sb.append("    </xs:complexType>\n");
230                         sb.append("  </xs:element>\n");
231                         generatedJavaType.put(javaTypeName, null);
232                         return sb.toString();                   
233                 }
234                 if ( processingInventory && sbInventory != null ) {
235                         sbInventory.append(sb1);
236                 } else {
237                         sb.append( sb1 );
238                 }
239                 return sb.toString();
240         }
241         
242         private Element getJavaTypeElement( String javaTypeName, boolean processingInventory )
243         {               
244                 String attrName, attrValue;
245                 Attr attr;
246                 Element javaTypeElement;
247                 
248                 List<Element> combineElementList = new ArrayList<Element>();
249                 for ( int i = 0; i < javaTypeNodes.getLength(); ++ i ) {
250                         javaTypeElement = (Element) javaTypeNodes.item(i);
251                         NamedNodeMap attributes = javaTypeElement.getAttributes();
252                         for ( int j = 0; j < attributes.getLength(); ++j ) {
253                     attr = (Attr) attributes.item(j);
254                     attrName = attr.getNodeName();
255                     attrValue = attr.getNodeValue();
256                     if ( attrName.equals("name") && attrValue.equals(javaTypeName)) {
257                         if ( processingInventory ) {
258                                 return javaTypeElement;
259                         } else {
260                                 combineElementList.add(javaTypeElement);
261                         }
262                     }
263                         }
264                 }
265                 if ( combineElementList.size() == 0 ) {
266                         logger.error( "oxm file format error, missing java-type " + javaTypeName);
267                         return (Element) null;
268                 } else if ( combineElementList.size() > 1 ) {
269                         // need to combine java-attributes
270                         return combineElements( javaTypeName, combineElementList);
271                 }
272                 return combineElementList.get(0);
273                 
274         }
275
276         private boolean versionUsesAnnotations( String version) {
277                 int ver = new Integer(version.substring(1)).intValue();
278                 if ( ver >= HTMLfromOXM.annotationsStartVersion ) {
279                         return true;
280                 }
281                 if ( ver <= HTMLfromOXM.annotationsMinVersion ) {
282                         return true;
283                 }
284                 return false;
285         }
286 }