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 { public static String PLUGIN_ID = "org.simantics.xml.sax.base"; public static String XML_NAMESPACE_REF = "xmlns"; private static final long serialVersionUID = 7360740940824360338L; private static final boolean debug = false; private ILogger logger; private WriteGraph graph; private Resource root; private String schemaURI; private Deque current = new ArrayDeque(); private Map parsers = new HashMap(); private Map, XMLElementParser> namedParsers = new HashMap, XMLElementParser>(); private Map subParsers = new HashMap(); public XMLParser() { } public WriteGraph getGraph() { return graph; } public void setGraph(WriteGraph graph) { if (graph == this.graph) return; this.graph = graph; for (XMLParser p : subParsers.values()) p.setGraph(graph); } public void setLogger(ILogger logger) { if (logger == this.logger) return; this.logger = logger; for (XMLParser p : subParsers.values()) p.setLogger(logger); } public String getSchemaURI() { return schemaURI; } public void setSchemaURI(String schemaURI) { this.schemaURI = schemaURI; } public void add(XMLElementParser parser) { if (parser.getElementId() != null) parsers.put(parser.getElementId(), parser); namedParsers.put(parser.getClass(), parser); } public void add(XMLParser parser) { subParsers.put(parser.getSchemaURI(), parser); } public XMLParser resolveDependencies() { java.util.Map map = new java.util.HashMap<>(); map.put(this.getSchemaURI(), this); addDependencies(map); return this; } public void addDependencies(java.util.Map map) { } private List idReferenceElements = new ArrayList(); private boolean loadElement(Deque parents, ParserElement element) throws SAXException{ return loadElement(parents, element, true); } private boolean loadElement(Deque parents, ParserElement element, boolean checkParent) throws SAXException{ XMLElementParser parser = null; ParserElement parent = null; if (parents.size() > 0) { // process a child element parent = parents.peek(); // check for assigned subparser if (checkParent && parent.getXMLParser() != null && parent.getXMLParser() != this) { //element.setXMLParser(parent.getXMLParser()); element.setXMLParser(parent.getXMLParser()); if (parent.getXMLParser().loadElement(parents, element)) return true; } if (parent.getElementParser() instanceof XMLElementNamedChildParser) { // use parent's named child parser if it is supported Class parserClass = ((XMLElementNamedChildParser)parent.getElementParser()).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 = "Error processing " + parserClass.getName() + " : 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.getLocalName()); } } else { // use globally configured element parser for a root element parser = parsers.get(element.getLocalName()); } if (parser != null) { try { element.setData(parser.create(graph, element)); if (parser instanceof IDReferenceParser) idReferenceElements.add(element); element.setElementParser(parser); return true; } catch (DatabaseException e) { throw new SAXException(e); } } else { // check for schema reference attempt to locate subparser for it. Attribute schemaRef = element.getAttribute(XML_NAMESPACE_REF); String nsRef[] = element.getQName().split(":"); if (schemaRef != null && subParsers.containsKey(schemaRef.value)) { XMLParser subParser = subParsers.get(schemaRef.value); boolean b = subParser.loadElement(parents, element); element.setXMLParser(subParser); return b; } else if (nsRef.length == 2 && element.getNS(nsRef[0]) != null && subParsers.containsKey(element.getNS(nsRef[0]))) { XMLParser subParser = subParsers.get(element.getNS(nsRef[0])); boolean b = subParser.loadElement(parents, element, false); element.setXMLParser(subParser); return b; } else { if (parent == null && parents.size() > 0) parent = parents.peek(); String err = null; if (parent != null) err = "Unknown element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None"); else { err = "Unknown root element " + element.getLocalName() + ", cannot import the file"; throw new SAXException(err); } logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err)); if (debug) System.err.println(err); return false; } } } private Map idMap = new HashMap(); private void handleElement(Deque parents, ParserElement element) throws SAXException{ XMLElementParser parser = element.getElementParser(); if (parser != null) { try { parser.configure(graph, parents, element); if (parents.size() > 0) { ParserElement parent = parents.peek(); if (parent.getElementParser() != null) { if (!parent.getElementParser().connectChild(graph, parent, element)) if (!parser.connectParent(graph, parent, element)) { String err = "Did not connect element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None"); logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err)); if (debug) System.err.println(err); } } else { String err = "Did not connect element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "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) { ParserElement existing = idMap.put(id, element); if (existing != null) { // report error + use id priorities to select the kept element. boolean useExt = existing.elementParser.idPriority() > element.elementParser.idPriority(); if (useExt) idMap.put(id, existing); String err = "Duplicate XML element id: " + id + " for " + element.getLocalName() + " and " + existing.getLocalName() + ", using " + (useExt ? existing.getLocalName() : element.getLocalName()); logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err)); if (debug) System.err.println(err); } } } catch (DatabaseException e) { throw new SAXException(e); } } else { ParserElement parent = parents.peek(); if (parent != null) { parser = parent.getElementParser(); if (parser != null && parser instanceof UnrecognizedElementParser) { try { ((UnrecognizedElementParser)parser).configureChild(graph, parents, parent, element); } catch (DatabaseException e) { throw new SAXException(e); } } } } try { element.createLists(graph); } catch (DatabaseException e) { throw new SAXException(e); } } private void handleCharacters(ParserElement element, String string) throws SAXException{ XMLElementParser parser = element.getElementParser(); 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); } } } public void done() throws SAXException{ try { for (ParserElement e : idReferenceElements) { IDReferenceParser parser = (IDReferenceParser)parsers.get(e.getLocalName()); if (!parser.connectReferences(graph, e, idMap)) { String err ="Could not resolve ID references for " + e.getLocalName() + " " + 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; } StringBuilder charactersValue; @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { ParserElement e = new ParserElement(current.peek(),uri,localName,name,attributes); loadElement(current,e); current.push(e); charactersValue = new StringBuilder(); } @Override public void endElement(String uri, String localName, String name) throws SAXException { ParserElement e = null; if (!current.isEmpty()) { e = current.pop(); } if (e != null) { handleCharacters(e, charactersValue.toString()); handleElement(current,e); if (current.isEmpty()) { root = e.getData(); } } charactersValue = new StringBuilder(); } @Override public void characters(char[] ch, int start, int length) throws SAXException { charactersValue.append(new String(ch,start,length)); // 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 { setLogger(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(); } public XMLElementParser getParser(String elementId) { return parsers.get(elementId); } public void setParser(String elementId, XMLElementParser p) { parsers.put(elementId, p); } }