package org.simantics.xml.sax.base; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.Serializable; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.exception.DatabaseException; import org.simantics.message.ILogger; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; public class XMLParser extends DefaultHandler implements Serializable { private static final long serialVersionUID = 7360740940824360338L; private static final boolean debug = false; private ILogger logger; private Resource root; private Deque current = new ArrayDeque(); private Map parsers = new HashMap(); private WriteGraph graph; private Map, XMLElementParser> namedParsers = new HashMap, XMLElementParser>(); public XMLParser(WriteGraph graph) { this.graph = graph; } public void add(XMLElementParser parser) { if (parser.getElementId() != null) parsers.put(parser.getElementId(), parser); namedParsers.put(parser.getClass(), parser); } private List idReferenceElements = new ArrayList(); private void loadElement(Deque parents, Element element) throws SAXException{ XMLElementParser parser = null; Element parent = null; if (parents.size() > 0) { parent = parents.peek(); if (parent.getParser() instanceof XMLElementNamedChildParser) { // use parent's named child parser if it is supported Class parserClass = ((XMLElementNamedChildParser)parent.getParser()).getParser(parsers,parent,element); if (parserClass != null) { parser = namedParsers.get(parserClass); if (parser == null) { try { parser = parserClass.newInstance(); namedParsers.put(parserClass, parser); } catch (IllegalAccessException | InstantiationException e) { String err = "Element parsers must have accessible default constructor"; logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err)); throw new SAXException(err, e); } } } // if (parser == null && ((XMLElementNamedChildParser)parent.getParser()).useGlobalRules()) // parser = parsers.get(element.qName); } else { // otherwise use globally configured element parser parser = parsers.get(element.qName); } } else { // use globally configured element parser parser = parsers.get(element.qName); } if (parser != null) { try { element.setData(parser.create(graph, element)); if (parser instanceof IDReferenceParser) idReferenceElements.add(element); element.setParser(parser); } catch (DatabaseException e) { throw new SAXException(e); } } else { if (parent == null && parents.size() > 0) parent = parents.peek(); String err = "Unknown element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None"); logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err)); if (debug) System.err.println(err); } } private Map idMap = new HashMap(); private void handleElement(Deque parents, Element element) throws SAXException{ XMLElementParser parser = element.getParser(); if (parser != null) { try { parser.configure(graph, parents, element); if (parents.size() > 0) { Element parent = parents.peek(); if (parent.getParser() != null) { if (!parent.getParser().connectChild(graph, parent, element)) if (!parser.connectParent(graph, parent, element)) { String err = "Did not connect element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None"); logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err)); if (debug) System.err.println(err); } } else { String err = "Did not connect element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None") + " was not imported"; logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err)); if (debug) System.err.println(err); } } String id = parser.getID(); if (id != null) { Element existing = idMap.put(id, element); if (existing != null) { // report error + use id priorities to select the kept element. boolean useExt = existing.parser.idPriority() > element.parser.idPriority(); if (useExt) idMap.put(id, existing); String err = "Duplicate XML element id: " + id + " for " + element.getQName() + " and " + existing.getQName() + ", using " + (useExt ? existing.getQName() : element.getQName()); logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err)); if (debug) System.err.println(err); } } } catch (DatabaseException e) { throw new SAXException(e); } } else { Element parent = parents.peek(); if (parent != null) { parser = parent.getParser(); if (parser != null && parser instanceof UnrecognizedElementParser) { try { ((UnrecognizedElementParser)parser).configureChild(graph, parents, parent, element); } catch (DatabaseException e) { throw new SAXException(e); } } } } } private void handleCharacters(Element element, String string) throws SAXException{ XMLElementParser parser = element.getParser(); if (parser != null) { try { parser.configure(graph, element, string); } catch (DatabaseException e) { logger.log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage())); throw new SAXException(e); } } } private static String PLUGIN_ID = "org.simantics.xml.sax.base"; public void done() throws SAXException{ try { for (Element e : idReferenceElements) { IDReferenceParser parser = (IDReferenceParser)parsers.get(e.qName); if (!parser.connectReferences(graph, e, idMap)) { String err ="Could not resolve ID references for " + e.getQName() + " " + e.getUri(); logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err)); if (debug) System.err.println(err); } } } catch (DatabaseException e) { throw new SAXException(e); } } public Resource getRoot() { return root; } @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { Element e = new Element(uri,localName,name,attributes); loadElement(current,e); current.push(e); } @Override public void endElement(String uri, String localName, String name) throws SAXException { Element e = null; if (!current.isEmpty()) { e = current.pop(); } if (e != null) { handleElement(current,e); } if (current.isEmpty()) { root = e.getData(); } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (!current.isEmpty()) { String s = new String(ch,start,length); Element e = current.peek(); handleCharacters(e, s); } } public void parse(File file, ILogger logger) throws Exception { this.logger = logger; SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxParser = spf.newSAXParser(); XMLReader reader = saxParser.getXMLReader(); reader.setContentHandler(this); reader.parse(new InputSource(new BufferedInputStream(new FileInputStream(file)))); done(); } }