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) { this.graph = graph; for (XMLParser p : subParsers.values()) p.setGraph(graph); } 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); } private List idReferenceElements = new ArrayList(); private void loadElement(Deque parents, ParserElement element) throws SAXException{ XMLElementParser parser = null; ParserElement parent = null; if (parents.size() > 0) { parent = parents.peek(); // check for assigned subparser if (parent.getXMLParser() != null && parent.getXMLParser() != this) { element.setXMLParser(parent.getXMLParser()); parent.getXMLParser().loadElement(parents, element); return; } 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.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.setElementParser(parser); } 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); if (schemaRef != null && subParsers.containsKey(schemaRef.value)) { XMLParser subParser = subParsers.get(schemaRef.value); subParser.loadElement(parents, element); element.setXMLParser(subParser); } 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, 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.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) { 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.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 { 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); } } } } } 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.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; } StringBuilder charactersValue; @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { ParserElement e = new ParserElement(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 { 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(); } }