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