Newer
Older
/*
* _____________________________________________________________________________
*
* INAF - OATS National Institute for Astrophysics - Astronomical Observatory of
* Trieste INAF - IA2 Italian Center for Astronomical Archives
* _____________________________________________________________________________
*
* Copyright (C) 2017 Istituto Nazionale di Astrofisica
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License Version 3 as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package it.inaf.ia2.tsm.model;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.JAXB;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
/**
* Class used to parse TASMAN XML configuration files used to defining schemata
* structure and convert them into instances of {@link SchemaModel}.
*
* @author Sonia Zorba {@literal <zorba at oats.inaf.it>}
*/
public class XMLModelsLoader {
private static final Logger LOG = LoggerFactory.getLogger(XMLModelsLoader.class);
private final String[] xmlModelFileNames;
private final File[] xmlModelExternalFiles;
// key: doc version
private final Map<String, Document> documents;
private final Map<String, String> inheritanceGraph;
private final Map<Integer, List<Document>> inheritanceLevels;
public XMLModelsLoader(String[] xmlModelFileNames) {
this(xmlModelFileNames, new File[]{});
}
public XMLModelsLoader(String[] xmlModelFileNames, File[] xmlModelExternalFiles) {
this.xmlModelFileNames = xmlModelFileNames;
this.xmlModelExternalFiles = xmlModelExternalFiles;
this.documents = new HashMap<>();
inheritanceGraph = new HashMap<>();
inheritanceLevels = new HashMap<>();
}
/**
* Loads the XML files and obtains schemata models.
*
* @return a {@code Map} having versions as keys and {@link SchemaModel}s
* as values.
*/
public Map<String, SchemaModel> load() {
return load(XMLModelsLoader.class.getClassLoader());
}
/**
* This variant is used for testing (specifying a {@code ClassLoader} it is
* possible to use test resource files instead of real application files).
*/
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
public Map<String, SchemaModel> load(ClassLoader classLoader) {
try {
loadDocumentsMap(classLoader);
// It is necessary to apply inheritance in specific level order. (Example:
// if v2 extends from v1 and v1 extends from v0 it is necessary to merge v1
// with v0 first and then merge the result with v2).
// So this inheritance data structures are built.
buildInheritanceGraph();
buildInheritanceLevels();
for (int i = 0; i < inheritanceLevels.size(); i++) {
for (Document doc : inheritanceLevels.get(i)) {
applyInheritance(doc, null);
}
}
return getModelsMap();
} catch (IOException | ParserConfigurationException | SAXException e) {
throw new ModelLoadingException(e);
}
}
private void loadDocumentsMap(ClassLoader classLoader) throws IOException, SAXException, ParserConfigurationException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
for (String xmlModelFileName : xmlModelFileNames) {
try (InputStream in = classLoader.getResourceAsStream(xmlModelFileName)) {
loadFile(builder, in);
}
}
for (File xmlModelExternalFile : xmlModelExternalFiles) {
try (FileInputStream fis = new FileInputStream(xmlModelExternalFile)) {
loadFile(builder, fis);
private void loadFile(DocumentBuilder builder, InputStream in) throws IOException, SAXException {
Document doc = builder.parse(in);
Element root = doc.getDocumentElement();
String version = root.getAttribute("version");
// TODO: XML Model validation
// Documents loaded in a single XMLModelsLoader instance must
// have different versions.
assert documents.get(version) == null;
documents.put(version, doc);
}
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
private void buildInheritanceGraph() {
for (Document document : documents.values()) {
Element root = document.getDocumentElement();
String version = root.getAttribute("version");
String extendsFrom = root.getAttribute("extends");
if (extendsFrom == null || extendsFrom.isEmpty()) {
extendsFrom = null;
}
inheritanceGraph.put(version, extendsFrom);
}
}
private void buildInheritanceLevels() {
for (Document document : documents.values()) {
String version = document.getDocumentElement().getAttribute("version");
int level = getInheritanceLevel(version, 0);
List<Document> levelDocs = inheritanceLevels.get(level);
if (levelDocs == null) {
levelDocs = new ArrayList<>();
inheritanceLevels.put(level, levelDocs);
}
levelDocs.add(document);
}
}
private int getInheritanceLevel(String version, int count) {
String inheritsFrom = inheritanceGraph.get(version);
if (inheritsFrom == null) {
return count;
} else {
return getInheritanceLevel(inheritsFrom, count + 1);
}
}
private void applyInheritance(Document doc, Set<String> applied) throws ParserConfigurationException {
String version = doc.getDocumentElement().getAttribute("version");
String inheritFrom = inheritanceGraph.get(version);
if (inheritFrom != null) {
if (applied == null) {
applied = new HashSet<>();
}
if (!applied.contains(inheritFrom)) {
Document inheritedDocument = documents.get(inheritFrom);
XMLMerger merger = new XMLMerger(inheritedDocument, doc);
Document merged = merger.getMergedDocument();
documents.put(version, merged);
applied.add(inheritFrom);
applyInheritance(merged, applied);
}
}
}
/**
* Public exposed only for testing.
*/
public Map<String, Document> getDocuments() {
return documents;
}
private Map<String, SchemaModel> getModelsMap() {
Map<String, SchemaModel> models = new HashMap<>();
for (Map.Entry<String, Document> entry : documents.entrySet()) {
DOMSource source = new DOMSource(entry.getValue().getDocumentElement());
SchemaModel model = JAXB.unmarshal(source, SchemaModel.class);
models.put(entry.getKey(), model);
}
return models;
}
}