package org.juxtasoftware.service.importer; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class XmlTemplateParser { private static final String CONFIG_SCHEMA = "templates.xsd"; private static final String XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; private Map<String, TemplateInfo> infoMap = new HashMap<String, TemplateInfo>(); /** * Helper method used to setup a schema aware, validating XML document builder * @return * @throws SAXException * @throws ParserConfigurationException */ private DocumentBuilder createDocBuilder() throws SAXException, ParserConfigurationException { // load the template schema SchemaFactory schemaFactory = SchemaFactory.newInstance(XML_SCHEMA); URL schemaUrl = ClassLoader.getSystemResource(CONFIG_SCHEMA); Schema schema = schemaFactory.newSchema(schemaUrl); // setup a docBuilder that will validate against the schema DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); factory.setNamespaceAware(true); factory.setSchema(schema); DocumentBuilder docBuilder = factory.newDocumentBuilder(); docBuilder.setErrorHandler(new TemplateErrorHander() ); return docBuilder; } public void parse( InputStream templateStream ) throws Exception { // setup the schema aware document builder DocumentBuilder docBuilder = createDocBuilder(); // parse the config into a doc. and walk the template nodes Document doc = docBuilder.parse( templateStream ); XPath xpath = XPathFactory.newInstance().newXPath(); XPathExpression expr = xpath.compile("//template"); XPathExpression tagExpr = xpath.compile("tag"); NodeList nodes = (NodeList)expr.evaluate(doc, XPathConstants.NODESET); for ( int i=0; i<nodes.getLength(); i++) { Node templateNode = nodes.item(i); String guid = templateNode.getAttributes().getNamedItem("guid").getTextContent(); String rootEle = templateNode.getAttributes().getNamedItem("rootTagName").getTextContent(); TemplateInfo info = new TemplateInfo(); // Walk all of its children and add behaviors to this template for each one NodeList tags = (NodeList)tagExpr.evaluate(templateNode, XPathConstants.NODESET); for (int t=0;t<tags.getLength();t++) { Node tag = tags.item(t); boolean tagNl = Boolean.parseBoolean(tag.getAttributes().getNamedItem("newLine").getTextContent()); String tagAct = tag.getAttributes().getNamedItem("action").getTextContent(); String tagName = tag.getAttributes().getNamedItem("name").getTextContent(); if ( tagNl ) { info.breaks.add(tagName); } if (tagAct.equalsIgnoreCase("exclude")) { info.excludes.add(tagName); } } // make sure biblio is excluded if ( rootEle.equals("juxta-document")) { info.excludes.add("bibliographic"); } this.infoMap.put(guid, info); } } public TemplateInfo findTemplateInfo( final String guid ) { return this.infoMap.get( guid ); } public List<TemplateInfo> getTemplates() { return new ArrayList<TemplateInfo>( this.infoMap.values()); } /** * Error handler for parsing template config. Ignore warnings and re-throw * any encountered errors */ private static class TemplateErrorHander implements org.xml.sax.ErrorHandler { public void error(SAXParseException exception) throws SAXException { throw exception; } public void fatalError(SAXParseException exception) throws SAXException { throw exception; } public void warning(SAXParseException exception) throws SAXException { // no-op } } /** * Class that collects all of the exclusion and linebreak * information from a juxta desktop parse template. * @author loufoster * */ public static class TemplateInfo { private Set<String> excludes = new HashSet<String>(); private Set<String> breaks = new HashSet<String>(); public Set<String> getLineBreaks() { return this.breaks; } public Set<String> getExcludes() { return this.excludes; } } }