1 package org.simantics.xml.sax.base;
3 import java.io.BufferedInputStream;
5 import java.io.FileInputStream;
6 import java.io.Serializable;
7 import java.util.ArrayDeque;
8 import java.util.ArrayList;
9 import java.util.Deque;
10 import java.util.HashMap;
11 import java.util.List;
14 import javax.xml.parsers.SAXParser;
15 import javax.xml.parsers.SAXParserFactory;
17 import org.eclipse.core.runtime.IStatus;
18 import org.eclipse.core.runtime.Status;
19 import org.simantics.db.Resource;
20 import org.simantics.db.WriteGraph;
21 import org.simantics.db.exception.DatabaseException;
22 import org.simantics.message.ILogger;
23 import org.xml.sax.Attributes;
24 import org.xml.sax.InputSource;
25 import org.xml.sax.SAXException;
26 import org.xml.sax.XMLReader;
27 import org.xml.sax.helpers.DefaultHandler;
29 public class XMLParser extends DefaultHandler implements Serializable {
31 public static String PLUGIN_ID = "org.simantics.xml.sax.base";
32 public static String XML_NAMESPACE_REF = "xmlns";
34 private static final long serialVersionUID = 7360740940824360338L;
35 private static final boolean debug = false;
37 private ILogger logger;
38 private WriteGraph graph;
39 private Resource root;
40 private String schemaURI;
41 private Deque<ParserElement> current = new ArrayDeque<ParserElement>();
42 private Map<String, XMLElementParser> parsers = new HashMap<String, XMLElementParser>();
43 private Map<Class<? extends XMLElementParser>, XMLElementParser> namedParsers = new HashMap<Class<? extends XMLElementParser>, XMLElementParser>();
44 private Map<String, XMLParser> subParsers = new HashMap<String, XMLParser>();
50 public WriteGraph getGraph() {
54 public void setGraph(WriteGraph graph) {
55 if (graph == this.graph)
58 for (XMLParser p : subParsers.values())
62 public void setLogger(ILogger logger) {
63 if (logger == this.logger)
66 for (XMLParser p : subParsers.values())
70 public String getSchemaURI() {
74 public void setSchemaURI(String schemaURI) {
75 this.schemaURI = schemaURI;
78 public void add(XMLElementParser parser) {
79 if (parser.getElementId() != null)
80 parsers.put(parser.getElementId(), parser);
81 namedParsers.put(parser.getClass(), parser);
84 public void add(XMLParser parser) {
85 subParsers.put(parser.getSchemaURI(), parser);
89 public XMLParser resolveDependencies() {
91 java.util.Map<String, XMLParser> map = new java.util.HashMap<>();
92 map.put(this.getSchemaURI(), this);
97 public void addDependencies(java.util.Map<String,XMLParser> map) {
101 private List<ParserElement> idReferenceElements = new ArrayList<ParserElement>();
103 private void loadElement(Deque<ParserElement> parents, ParserElement element) throws SAXException{
104 loadElement(parents, element, true);
106 private void loadElement(Deque<ParserElement> parents, ParserElement element, boolean checkParent) throws SAXException{
107 XMLElementParser parser = null;
108 ParserElement parent = null;
109 if (parents.size() > 0) {
110 // process a child element
111 parent = parents.peek();
112 // check for assigned subparser
113 if (checkParent && parent.getXMLParser() != null && parent.getXMLParser() != this) {
114 //element.setXMLParser(parent.getXMLParser());
115 element.setXMLParser(parent.getXMLParser());
116 parent.getXMLParser().loadElement(parents, element);
119 if (parent.getElementParser() instanceof XMLElementNamedChildParser) {
120 // use parent's named child parser if it is supported
121 Class<? extends XMLElementParser> parserClass = ((XMLElementNamedChildParser)parent.getElementParser()).getParser(parsers,parent,element);
122 if (parserClass != null) {
123 parser = namedParsers.get(parserClass);
124 if (parser == null) {
126 parser = parserClass.newInstance();
127 namedParsers.put(parserClass, parser);
128 } catch (IllegalAccessException | InstantiationException e) {
129 String err = "Error processing " + parserClass.getName() + " : element parsers must have accessible default constructor";
130 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
131 throw new SAXException(err, e);
135 // if (parser == null && ((XMLElementNamedChildParser)parent.getParser()).useGlobalRules())
136 // parser = parsers.get(element.qName);
138 // otherwise use globally configured element parser
139 parser = parsers.get(element.getLocalName());
142 // use globally configured element parser for a root element
143 parser = parsers.get(element.getLocalName());
146 if (parser != null) {
149 element.setData(parser.create(graph, element));
150 if (parser instanceof IDReferenceParser)
151 idReferenceElements.add(element);
152 element.setElementParser(parser);
153 } catch (DatabaseException e) {
154 throw new SAXException(e);
157 // check for schema reference attempt to locate subparser for it.
158 Attribute schemaRef = element.getAttribute(XML_NAMESPACE_REF);
159 String nsRef[] = element.getQName().split(":");
160 if (schemaRef != null && subParsers.containsKey(schemaRef.value)) {
161 XMLParser subParser = subParsers.get(schemaRef.value);
162 subParser.loadElement(parents, element);
163 element.setXMLParser(subParser);
164 } else if (nsRef.length == 2 && element.getNS(nsRef[0]) != null && subParsers.containsKey(element.getNS(nsRef[0]))) {
165 XMLParser subParser = subParsers.get(element.getNS(nsRef[0]));
166 subParser.loadElement(parents, element, false);
167 element.setXMLParser(subParser);
169 if (parent == null && parents.size() > 0)
170 parent = parents.peek();
173 err = "Unknown element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None");
175 err = "Unknown root element " + element.getLocalName() + ", cannot import the file";
176 throw new SAXException(err);
179 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
180 if (debug) System.err.println(err);
185 private Map<String, ParserElement> idMap = new HashMap<String, ParserElement>();
187 private void handleElement(Deque<ParserElement> parents, ParserElement element) throws SAXException{
188 XMLElementParser parser = element.getElementParser();
189 if (parser != null) {
191 parser.configure(graph, parents, element);
192 if (parents.size() > 0) {
193 ParserElement parent = parents.peek();
194 if (parent.getElementParser() != null) {
195 if (!parent.getElementParser().connectChild(graph, parent, element))
196 if (!parser.connectParent(graph, parent, element)) {
197 String err = "Did not connect element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None");
198 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
199 if (debug) System.err.println(err);
202 String err = "Did not connect element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None") + " was not imported";
203 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
204 if (debug) System.err.println(err);
207 String id = parser.getID();
209 ParserElement existing = idMap.put(id, element);
210 if (existing != null) {
211 // report error + use id priorities to select the kept element.
212 boolean useExt = existing.elementParser.idPriority() > element.elementParser.idPriority();
214 idMap.put(id, existing);
215 String err = "Duplicate XML element id: " + id + " for " + element.getLocalName() + " and " + existing.getLocalName() + ", using " + (useExt ? existing.getLocalName() : element.getLocalName());
216 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
217 if (debug) System.err.println(err);
220 } catch (DatabaseException e) {
221 throw new SAXException(e);
224 ParserElement parent = parents.peek();
225 if (parent != null) {
226 parser = parent.getElementParser();
227 if (parser != null && parser instanceof UnrecognizedElementParser) {
229 ((UnrecognizedElementParser)parser).configureChild(graph, parents, parent, element);
230 } catch (DatabaseException e) {
231 throw new SAXException(e);
237 element.createLists(graph);
238 } catch (DatabaseException e) {
239 throw new SAXException(e);
243 private void handleCharacters(ParserElement element, String string) throws SAXException{
244 XMLElementParser parser = element.getElementParser();
245 if (parser != null) {
247 parser.configure(graph, element, string);
248 } catch (DatabaseException e) {
249 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage()));
250 throw new SAXException(e);
257 public void done() throws SAXException{
259 for (ParserElement e : idReferenceElements) {
260 IDReferenceParser parser = (IDReferenceParser)parsers.get(e.getLocalName());
261 if (!parser.connectReferences(graph, e, idMap)) {
262 String err ="Could not resolve ID references for " + e.getLocalName() + " " + e.getUri();
263 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
264 if (debug) System.err.println(err);
267 } catch (DatabaseException e) {
268 throw new SAXException(e);
272 public Resource getRoot() {
276 StringBuilder charactersValue;
279 public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
280 ParserElement e = new ParserElement(current.peek(),uri,localName,name,attributes);
282 loadElement(current,e);
285 charactersValue = new StringBuilder();
289 public void endElement(String uri, String localName, String name)
290 throws SAXException {
291 ParserElement e = null;
292 if (!current.isEmpty()) {
296 handleCharacters(e, charactersValue.toString());
297 handleElement(current,e);
298 if (current.isEmpty()) {
304 charactersValue = new StringBuilder();
308 public void characters(char[] ch, int start, int length)
309 throws SAXException {
310 charactersValue.append(new String(ch,start,length));
311 // if (!current.isEmpty()) {
312 // String s = new String(ch,start,length);
313 // Element e = current.peek();
314 // handleCharacters(e, s);
319 public void parse(File file, ILogger logger) throws Exception {
321 SAXParserFactory spf = SAXParserFactory.newInstance();
322 SAXParser saxParser = spf.newSAXParser();
324 XMLReader reader = saxParser.getXMLReader();
325 reader.setContentHandler(this);
326 reader.parse(new InputSource(new BufferedInputStream(new FileInputStream(file))));