Add java 11 code support
[aai/schema-service.git] / aai-schema-service / src / main / java / org / onap / aai / schemaservice / nodeschema / NodeIngestor.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.schemaservice.nodeschema;
21
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24 import com.google.common.base.CaseFormat;
25 import com.google.common.collect.ArrayListMultimap;
26 import com.google.common.collect.Multimap;
27 import org.eclipse.persistence.jaxb.JAXBContextProperties;
28 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
29 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
30 import org.onap.aai.schemaservice.config.ConfigTranslator;
31 import org.springframework.beans.factory.annotation.Autowired;
32 import org.springframework.stereotype.Component;
33 import org.w3c.dom.Document;
34 import org.w3c.dom.Element;
35 import org.w3c.dom.Node;
36 import org.w3c.dom.NodeList;
37 import org.xml.sax.SAXException;
38
39 import javax.xml.XMLConstants;
40 import jakarta.xml.bind.JAXBException;
41 import javax.xml.parsers.DocumentBuilder;
42 import javax.xml.parsers.DocumentBuilderFactory;
43 import javax.xml.parsers.ParserConfigurationException;
44 import java.io.*;
45 import java.nio.charset.StandardCharsets;
46 import java.util.*;
47 import java.util.Map.Entry;
48 import java.util.regex.Matcher;
49 import java.util.regex.Pattern;
50
51 /**
52  * NodeIngestor - ingests A&AI OXM files per given config, serves DynamicJAXBContext per version
53  */
54 @Component
55 public class NodeIngestor {
56
57     private static final Logger LOGGER = LoggerFactory.getLogger(NodeIngestor.class);
58
59     private static final Pattern classNamePattern = Pattern.compile("\\.(v\\d+)\\.");
60     private Map<SchemaVersion, DynamicJAXBContext> versionContextMap = new TreeMap<>();
61     private Map<SchemaVersion, Set<String>> typesPerVersion = new TreeMap<>();
62     private Map<SchemaVersion, Document> schemaPerVersion = new TreeMap<>();
63     private ConfigTranslator translator;
64
65
66     @Autowired
67     /**
68      * Instantiates the NodeIngestor bean.
69      *
70      * @param translator - ConfigTranslator autowired in by Spring framework which
71      * contains the configuration information needed to ingest the desired files.
72      */
73     public NodeIngestor(ConfigTranslator translator) {
74         this.translator = translator;
75         Map<SchemaVersion, List<String>> filesToIngest = translator.getNodeFiles();
76
77         try {
78             for (Entry<SchemaVersion, List<String>> verFiles : filesToIngest.entrySet()) {
79                 SchemaVersion v = verFiles.getKey();
80                 List<String> files = verFiles.getValue();
81                 final DynamicJAXBContext ctx = ingest(files);
82                 versionContextMap.put(v, ctx);
83                 typesPerVersion.put(v, getAllNodeTypes(files));
84                 schemaPerVersion.put(v, createCombinedSchema(files, v));
85             }
86         } catch (JAXBException | ParserConfigurationException | SAXException | IOException e) {
87             throw new ExceptionInInitializerError(e);
88         }
89     }
90
91     /**
92      * Ingests the given OXM files into DynamicJAXBContext
93      *
94      * @param files - List<String> of full filenames (ie including the path) to be ingested
95      * @return DynamicJAXBContext including schema information from all given files
96      * @throws FileNotFoundException if an OXM file can't be found
97      * @throws JAXBException         if there's an error creating the DynamicJAXBContext
98      */
99     private DynamicJAXBContext ingest(List<String> files) throws FileNotFoundException, JAXBException {
100         List<InputStream> streams = new ArrayList<>();
101
102         for (String name : files) {
103             streams.add(new FileInputStream(new File(name)));
104         }
105
106         Map<String, Object> properties = new HashMap<>();
107         properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, streams);
108         return DynamicJAXBContextFactory.createContextFromOXM(this.getClass().getClassLoader(), properties);
109     }
110
111
112     private Set<String> getAllNodeTypes(List<String> files) throws ParserConfigurationException, SAXException, IOException {
113         Set<String> types = new HashSet<>();
114         final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
115         docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
116         final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
117
118         ArrayList<Node> javaTypes = new ArrayList<>();
119         for (String file : files) {
120             InputStream inputStream = new FileInputStream(file);
121
122             final Document doc = docBuilder.parse(inputStream);
123             final NodeList list = doc.getElementsByTagName("java-type");
124
125
126             for (int i = 0; i < list.getLength(); i++) {
127                 String type = list.item(i).getAttributes().getNamedItem("name").getNodeValue();
128                 javaTypes.add(list.item(i));
129                 types.add(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, type));
130             }
131         }
132
133         return types;
134     }
135
136     private Document createCombinedSchema(List<String> files, SchemaVersion v) throws ParserConfigurationException, SAXException, IOException {
137         final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
138         docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
139         final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
140         DocumentBuilder masterDocBuilder = docFactory.newDocumentBuilder();
141         Document combinedDoc = masterDocBuilder.parse(getShell(v));
142         NodeList masterList = combinedDoc.getElementsByTagName("java-types");
143         Node javaTypesContainer = masterList.getLength() == 0 ? combinedDoc.getDocumentElement() : masterList.item(0);
144
145         Multimap<String, Node> nodeMultimap = ArrayListMultimap.create();
146         LOGGER.debug("Started combining the schema from list of files {} for version {}", files, v);
147
148         for (String file : files) {
149             InputStream inputStream = new FileInputStream(file);
150
151             final Document doc = docBuilder.parse(inputStream);
152             final NodeList list = doc.getElementsByTagName("java-type");
153
154             for(int i = 0; i < list.getLength(); i++){
155                 Node curNode = list.item(i);
156                 String name = curNode.getAttributes().getNamedItem("name").getNodeValue();
157                 nodeMultimap.put(name, curNode);
158             }
159         }
160
161         Map<String, Collection<Node>> map = nodeMultimap.asMap();
162         createNode(combinedDoc, javaTypesContainer, map);
163
164         LOGGER.debug("Successfully merged all schema files for version {}", v);
165
166         return combinedDoc;
167     }
168
169     private void createNode(Document combinedDoc, Node javaTypesContainer, Map<String, Collection<Node>> map){
170
171         for (Entry<String, Collection<Node>> entry : map.entrySet()) {
172
173             List<Node> listOfNodes = (List<Node>)entry.getValue();
174             LOGGER.trace("NodeType {} Occurrences {}", entry.getKey(), listOfNodes.size());
175             Node copyOfFirstElement = null;
176             Node javaAttributeElement = null;
177
178             if(listOfNodes.size() > 1){
179                 for(int index = 0; index < listOfNodes.size(); index++){
180                     if(index == 0){
181                         Node currentNode = listOfNodes.get(index);
182                         copyOfFirstElement = combinedDoc.importNode(currentNode, true);
183                         if(copyOfFirstElement.getNodeType() == Node.ELEMENT_NODE){
184                             Element element = (Element) copyOfFirstElement;
185                             NodeList javaAttributesList = element.getElementsByTagName("java-attributes");
186                             for(int javaAttributeIndex = 0; javaAttributeIndex < javaAttributesList.getLength(); javaAttributeIndex++){
187                                 javaAttributeElement = javaAttributesList.item(javaAttributeIndex);
188                             }
189                         }
190                     } else {
191                         Node currentNode = listOfNodes.get(index);
192                         Node copyOfCurrentElement = combinedDoc.importNode(currentNode, true);
193                         if(copyOfCurrentElement.getNodeType() == Node.ELEMENT_NODE){
194                             Element element = (Element) copyOfCurrentElement;
195                             NodeList javaAttributesList = element.getElementsByTagName("java-attributes");
196                             for(int javaAttributeIndex = 0; javaAttributeIndex < javaAttributesList.getLength(); javaAttributeIndex++){
197                                 Node jaElement = javaAttributesList.item(javaAttributeIndex);
198                                 NodeList xmlElementList = jaElement.getChildNodes();
199                                 for(int xmlElementIndex = 0; xmlElementIndex < xmlElementList.getLength(); xmlElementIndex++){
200                                     if(javaAttributeElement != null){
201                                         Node curElem = xmlElementList.item(xmlElementIndex);
202                                         if(curElem != null){
203                                             javaAttributeElement.appendChild(curElem.cloneNode(true));
204                                         }
205                                     }
206                                 }
207                             }
208                         }
209
210                     }
211                 }
212                 javaTypesContainer.appendChild(copyOfFirstElement);
213             } else if(listOfNodes.size() == 1){
214                 javaTypesContainer.appendChild(combinedDoc.importNode(listOfNodes.get(0), true));
215             }
216         }
217     }
218
219     /**
220      * Gets the DynamicJAXBContext for the given version
221      *
222      * @param v
223      * @return DynamicJAXBContext
224      */
225     public DynamicJAXBContext getContextForVersion(SchemaVersion v) {
226         return versionContextMap.get(v);
227     }
228
229     /**
230      * Determines if the given version contains the given node type
231      *
232      * @param nodeType - node type to check, must be in lower hyphen form (ie "type-name")
233      * @param v        - schema version to check against
234      * @return
235      */
236     public boolean hasNodeType(String nodeType, SchemaVersion v) {
237         return typesPerVersion.get(v).contains(nodeType);
238     }
239
240     public Set<String> getObjectsInVersion(SchemaVersion v) {
241         return typesPerVersion.get(v);
242     }
243
244     /**
245      * Determines if the given version contains the given node type
246      *
247      * @param nodeType - node type to check, must be in lower hyphen form (ie "type-name")
248      * @param v
249      * @return
250      */
251     public Document getSchema(SchemaVersion v) {
252         return schemaPerVersion.get(v);
253     }
254
255     private InputStream getShell(SchemaVersion v) {
256         String source = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
257             "<xml-bindings xmlns=\"http://www.eclipse.org/eclipselink/xsds/persistence/oxm\" package-name=\"inventory.aai.onap.org." + v.toString().toLowerCase() + "\" xml-mapping-metadata-complete=\"true\">\n" +
258             "   <xml-schema element-form-default=\"QUALIFIED\">\n" +
259             "           <xml-ns namespace-uri=\"http://org.onap.aai.inventory/" + v.toString().toLowerCase() + "\" />\n" +
260             "   </xml-schema>\n" +
261             "   <java-types>\n" +
262             "   </java-types>\n" +
263             "</xml-bindings>";
264         return new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
265     }
266
267
268     public SchemaVersion getVersionFromClassName(String classname) {
269         Matcher m = classNamePattern.matcher(classname);
270         String version = null;
271         if (m.find()) {
272             version = m.group(1);
273             return new SchemaVersion(version);
274         } else {
275             return translator.getSchemaVersions().getDefaultVersion();
276         }
277     }
278 }
279