package hu.sztaki.ilab.longneck.bootstrap; import hu.sztaki.ilab.longneck.process.FileType; import hu.sztaki.ilab.longneck.process.SourceInfo; import java.io.IOException; import java.net.URL; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashMap; import java.util.Map; import javax.xml.parsers.*; import javax.xml.validation.Schema; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.ext.DefaultHandler2; /** * * @author Molnár Péter <molnarp@sztaki.mta.hu> */ public class XMLDocumentLoader implements ResourceLoaderAware { /** The Longneck namespace URI. */ public static final String NS = "urn:hu.sztaki.ilab.longneck:1.0"; public static final String NS_XSI = "http://www.w3.org/2001/XMLSchema-instance"; /** The schema loader to load the schema. */ private SchemaLoader schemaLoader; /** The SAX document loader factory. */ private ParserFactory parserFactory = null; /** Resource loader to load resources. */ private ResourceLoader resourceLoader; public Document getDocument(Resource r, FileType type) throws IOException, ParserConfigurationException, SAXException { if (parserFactory == null) { parserFactory = new ParserFactory(); } SAXParser parser = null; URL schemaUrl = resourceLoader.getResource(SchemaPath.forType(type)).getURL(); try { parser = parserFactory.getSAXParser(schemaUrl); } catch (ParserConfigurationException ex) { throw new RuntimeException(ex); } catch (SAXException ex) { throw new RuntimeException(ex); } catch (IOException ex) { throw new RuntimeException(ex); } DOMBuilder builder = new DOMBuilder(r.getURL().toExternalForm()); parser.parse(r.getInputStream(), builder); return builder.getDocument(); } public SchemaLoader getSchemaLoader() { return schemaLoader; } public void setSchemaLoader(SchemaLoader schemaLoader) { this.schemaLoader = schemaLoader; } @Override public void setResourceLoader(ResourceLoader rl) { this.resourceLoader = rl; } private class ParserFactory { private SAXParserFactory saxParserFactory; Map<String,SAXParser> saxParsers; public ParserFactory() { saxParserFactory = SAXParserFactory.newInstance(); saxParserFactory.setNamespaceAware(true); //saxParserFactory.setValidating(true); saxParsers = new HashMap<String,SAXParser>(); } public SAXParser getSAXParser(URL schemaUrl) throws ParserConfigurationException, SAXException, IOException { SAXParser saxParser; if (saxParsers.containsKey(schemaUrl.toString())) { saxParser = saxParsers.get(schemaUrl.toString()); } else { Schema schema; // Entity file parser schema = schemaLoader.getSchema(schemaUrl); saxParserFactory.setSchema(schema); saxParser = saxParserFactory.newSAXParser(); saxParsers.put(schemaUrl.toString(), saxParser); } return saxParser; } } private static class DOMBuilder extends DefaultHandler2 { /** The document, which is loaded. */ private final String documentUrl; /** The sequence counter for elements. */ private int sequenceCounter; /** The currently active container block id. */ private final Deque<String> blockStack = new ArrayDeque<String>(); /** The locator to query location in the XML document. */ private Locator locator; /** The document builder. */ private DocumentBuilder docBuilder; /** The currently built document. */ private Document document; /** The current node in the tree. */ private Node current; /** The prefix mapping. */ private Map<String,String> prefixMap = new HashMap<String,String>(); public DOMBuilder(String documentUrl) throws ParserConfigurationException { this.documentUrl = documentUrl; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); docBuilder = dbf.newDocumentBuilder(); } public Document getDocument() { return document; } @Override public void startDocument() throws SAXException { super.startDocument(); document = docBuilder.newDocument(); current = document; sequenceCounter = 0; } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); // Create element Element element = document.createElementNS(uri, qName); // Copy attributes for (int i = 0; i < attributes.getLength(); ++i) { // Fix fog bug http://jira.codehaus.org/browse/CASTOR-2813 if (NS_XSI.equals(attributes.getURI(i))) { continue; } element.setAttributeNS(attributes.getURI(i), attributes.getQName(i), attributes.getValue(i)); } // Save container block id, if available. if (NS.equals(uri) && "block".equals(localName)) { blockStack.addLast(String.format("%1$s:%2$s", attributes.getValue("id"), attributes.getValue("version"))); } // Create source info and add serialized form as attribute. SourceInfo pframe = new SourceInfo(documentUrl, (! blockStack.isEmpty()) ? blockStack.peekLast() : null, sequenceCounter, locator.getLineNumber(), locator.getColumnNumber()); element.setAttribute("source-info", pframe.toJSONString()); // Increase sequence counter. ++sequenceCounter; // Add and set current current.appendChild(element); current = element; } @Override public void endElement(String uri, String localName, String qName) throws SAXException { super.endElement(uri, localName, qName); current = current.getParentNode(); if (NS.equals(uri) && "block".equals(localName) && blockStack.size() > 0) { blockStack.removeLast(); } } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { super.startPrefixMapping(prefix, uri); prefixMap.put(uri, prefix); } @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); current.appendChild(document.createTextNode(String.copyValueOf(ch, start, length))); } @Override public void comment(char[] ch, int start, int length) throws SAXException { super.comment(ch, start, length); current.appendChild(document.createComment(String.copyValueOf(ch, start, length))); } @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { super.ignorableWhitespace(ch, start, length); current.appendChild(document.createTextNode(String.copyValueOf(ch, start, length))); } @Override public void processingInstruction(String target, String data) throws SAXException { super.processingInstruction(target, data); current.appendChild(document.createProcessingInstruction(target, data)); } @Override public void setDocumentLocator(Locator locator) { super.setDocumentLocator(locator); this.locator = locator; } @Override public void error(org.xml.sax.SAXParseException err) throws org.xml.sax.SAXParseException { System.out.println("** ERROR" + ", line " + err.getLineNumber() + ", uri " + err.getSystemId()); System.out.println(" " + err.getMessage()); throw err; } @Override public void warning(org.xml.sax.SAXParseException err) throws org.xml.sax.SAXParseException { System.out.println("** Warning" + ", line " + err.getLineNumber() + ", uri " + err.getSystemId()); System.out.println(" " + err.getMessage()); } } }