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 boolean loadElement(Deque<ParserElement> parents, ParserElement element) throws SAXException{
104 return loadElement(parents, element, true);
106 private boolean 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 if (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);
154 } catch (DatabaseException e) {
155 throw new SAXException(e);
158 // check for schema reference attempt to locate subparser for it.
159 Attribute schemaRef = element.getAttribute(XML_NAMESPACE_REF);
160 String nsRef[] = element.getQName().split(":");
161 if (schemaRef != null && subParsers.containsKey(schemaRef.value)) {
162 XMLParser subParser = subParsers.get(schemaRef.value);
163 boolean b = subParser.loadElement(parents, element);
164 element.setXMLParser(subParser);
166 } else if (nsRef.length == 2 && element.getNS(nsRef[0]) != null && subParsers.containsKey(element.getNS(nsRef[0]))) {
167 XMLParser subParser = subParsers.get(element.getNS(nsRef[0]));
168 boolean b = subParser.loadElement(parents, element, false);
169 element.setXMLParser(subParser);
172 if (parent == null && parents.size() > 0)
173 parent = parents.peek();
176 err = "Unknown element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None");
178 err = "Unknown root element " + element.getLocalName() + ", cannot import the file";
179 throw new SAXException(err);
182 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
183 if (debug) System.err.println(err);
189 private Map<String, ParserElement> idMap = new HashMap<String, ParserElement>();
191 private void handleElement(Deque<ParserElement> parents, ParserElement element) throws SAXException{
192 XMLElementParser parser = element.getElementParser();
193 if (parser != null) {
195 parser.configure(graph, parents, element);
196 if (parents.size() > 0) {
197 ParserElement parent = parents.peek();
198 if (parent.getElementParser() != null) {
199 if (!parent.getElementParser().connectChild(graph, parent, element))
200 if (!parser.connectParent(graph, parent, element)) {
201 String err = "Did not connect element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None");
202 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
203 if (debug) System.err.println(err);
206 String err = "Did not connect element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None") + " was not imported";
207 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
208 if (debug) System.err.println(err);
211 String id = parser.getID();
213 ParserElement existing = idMap.put(id, element);
214 if (existing != null) {
215 // report error + use id priorities to select the kept element.
216 boolean useExt = existing.elementParser.idPriority() > element.elementParser.idPriority();
218 idMap.put(id, existing);
219 String err = "Duplicate XML element id: " + id + " for " + element.getLocalName() + " and " + existing.getLocalName() + ", using " + (useExt ? existing.getLocalName() : element.getLocalName());
220 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
221 if (debug) System.err.println(err);
224 } catch (DatabaseException e) {
225 throw new SAXException(e);
228 ParserElement parent = parents.peek();
229 if (parent != null) {
230 parser = parent.getElementParser();
231 if (parser != null && parser instanceof UnrecognizedElementParser) {
233 ((UnrecognizedElementParser)parser).configureChild(graph, parents, parent, element);
234 } catch (DatabaseException e) {
235 throw new SAXException(e);
241 element.createLists(graph);
242 } catch (DatabaseException e) {
243 throw new SAXException(e);
247 private void handleCharacters(ParserElement element, String string) throws SAXException{
248 XMLElementParser parser = element.getElementParser();
249 if (parser != null) {
251 parser.configure(graph, element, string);
252 } catch (DatabaseException e) {
253 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage()));
254 throw new SAXException(e);
261 public void done() throws SAXException{
263 for (ParserElement e : idReferenceElements) {
264 IDReferenceParser parser = (IDReferenceParser)parsers.get(e.getLocalName());
265 if (!parser.connectReferences(graph, e, idMap)) {
266 String err ="Could not resolve ID references for " + e.getLocalName() + " " + e.getUri();
267 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
268 if (debug) System.err.println(err);
271 } catch (DatabaseException e) {
272 throw new SAXException(e);
276 public Resource getRoot() {
280 StringBuilder charactersValue;
283 public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
284 ParserElement e = new ParserElement(current.peek(),uri,localName,name,attributes);
286 loadElement(current,e);
289 charactersValue = new StringBuilder();
293 public void endElement(String uri, String localName, String name)
294 throws SAXException {
295 ParserElement e = null;
296 if (!current.isEmpty()) {
300 handleCharacters(e, charactersValue.toString());
301 handleElement(current,e);
302 if (current.isEmpty()) {
308 charactersValue = new StringBuilder();
312 public void characters(char[] ch, int start, int length)
313 throws SAXException {
314 charactersValue.append(new String(ch,start,length));
315 // if (!current.isEmpty()) {
316 // String s = new String(ch,start,length);
317 // Element e = current.peek();
318 // handleCharacters(e, s);
323 public void parse(File file, ILogger logger) throws Exception {
325 SAXParserFactory spf = SAXParserFactory.newInstance();
326 SAXParser saxParser = spf.newSAXParser();
328 XMLReader reader = saxParser.getXMLReader();
329 reader.setContentHandler(this);
330 reader.parse(new InputSource(new BufferedInputStream(new FileInputStream(file))));
334 public XMLElementParser getParser(String elementId) {
335 return parsers.get(elementId);
338 public void setParser(String elementId, XMLElementParser p) {
339 parsers.put(elementId, p);