gianugo 2003/07/13 05:33:53
Modified: src/scratchpad/src/org/apache/cocoon/generation XPathTraversableGenerator.java Log: Sterted refactoring to sync with XPathDirectoryGenerator. This version is still using addPath() since a planned feature (excluding from listing non matched resources) couldn't work the other way, but it will be taken into account shortly. From a user point of view, anyway, now using XDG or XTG should be absolutely transparent. Revision Changes Path 1.2 +96 -39 cocoon-2.1/src/scratchpad/src/org/apache/cocoon/generation/XPathTraversableGenerator.java Index: XPathTraversableGenerator.java =================================================================== RCS file: /home/cvs/cocoon-2.1/src/scratchpad/src/org/apache/cocoon/generation/XPathTraversableGenerator.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- XPathTraversableGenerator.java 10 Jul 2003 08:12:49 -0000 1.1 +++ XPathTraversableGenerator.java 13 Jul 2003 12:33:53 -0000 1.2 @@ -57,6 +57,7 @@ import java.util.HashMap; import java.util.Map; +import org.apache.avalon.framework.component.ComponentException; import org.apache.avalon.framework.component.ComponentManager; import org.apache.avalon.framework.parameters.Parameters; import org.apache.cocoon.ProcessingException; @@ -67,26 +68,41 @@ import org.apache.excalibur.xml.dom.DOMParser; import org.apache.excalibur.xml.xpath.PrefixResolver; import org.apache.excalibur.xml.xpath.XPathProcessor; +import org.apache.regexp.RE; +import org.apache.regexp.RESyntaxException; + import org.w3c.dom.Document; import org.w3c.dom.NodeList; + import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; /** - * Generates an XML directory listing performing XPath queries - * on XML files. It can be used both as a plain TraversableGenerator - * or, using an "xpointerinsh" syntax it will perform an XPath - * query on every XML resource. + * Generates an XML collection listing performing XPath queries on XML sources. + * It can be used both as a plain TraversableGenerator or, if an XPath is + * specified, it will perform an XPath query on every XML resource, where "xml + * resource" is, by default, any resource ending with ".xml", which can be + * overriden by setting the (regexp) pattern "xmlFiles as a sitemap parameter. + * + * The XPath can be specified in two ways: + * <ol> + * <li>By using an XPointerish syntax in the URL: everything following the + * pound sign (possiby preceding query + * string arguments) will be treated as the XPath; + * </li> + * <li>Specifying it as a sitemap parameter named "xpath" + * </ol> * * Sample usage: * * Sitemap: * <map:match pattern="documents/**"> * <map:generate type="xpathdirectory" - * src="docs/{1}#/article/title|/article/abstract" /> - * <map:serialize type="xml" /> - * </map:match> + * src=" docs/{1}#/article/title|/article/abstract" > + * < map:parameter name="xmlFiles" value="\.xml$"/> + * </map:generate> + * <map: serialize type="xml" /> </map:match> * * Request: * http://www.some.host/documents/test @@ -117,19 +133,26 @@ */ public class XPathTraversableGenerator extends TraversableGenerator { - /** Element <result> */ - protected static final String RESULT = "xpath"; - protected static final String QRESULT = PREFIX + ":" + RESULT; - protected static final String RESULT_DOCID_ATTR = "docid"; - protected static final String QUERY_ATTR = "query"; - - protected static final String CDATA = "CDATA"; - protected String XPathQuery = null; - protected XPathProcessor processor = null; - protected DOMParser parser; - protected Document doc; - private XPathPrefixResolver prefixResolver; - + /** Local name for the element that contains the included XML snippet. */ + protected static final String XPATH_NODE_NAME = "xpath"; + /** Attribute for the XPath query. */ + protected static final String QUERY_ATTR_NAME = "query"; + /** The document containing a successful XPath query */ + protected static final String RESULT_DOCID_ATTR = "docid"; + + /** The regular expression for the XML files pattern. */ + protected RE xmlRE = null; + /** The document that should be parsed and (partly) included. */ + protected Document doc = null; + /** The XPath. */ + protected String xpath = null; + /** The XPath processor. */ + protected XPathProcessor processor = null; + /** The parser for the XML snippets to be included. */ + protected DOMParser parser = null; + /** The prefix resolver for namespaced queries */ + protected XPathPrefixResolver prefixResolver; + public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException { super.setup(resolver, objectModel, src, par); @@ -138,18 +161,39 @@ if ((pointer = this.source.indexOf("#")) != -1) { int endpointer = this.source.indexOf('?'); if (endpointer != -1) { - this.XPathQuery = source.substring(pointer + 1, endpointer); + this.xpath = source.substring(pointer + 1, endpointer); } else { - this.XPathQuery = source.substring(pointer + 1); + this.xpath = source.substring(pointer + 1); } this.source = src.substring(0, pointer); - if (endpointer != -1) + if (endpointer != -1) { this.source += src.substring(endpointer); + } + + this.cacheKeyParList.add(this.xpath); if (this.getLogger().isDebugEnabled()) - this.getLogger().debug("Applying XPath: " + XPathQuery + this.getLogger().debug("Applying XPath: " + xpath + " to collection " + source); + } else { + this.xpath = par.getParameter("xpath", null); + this.cacheKeyParList.add(this.xpath); + this.getLogger().debug("Applying XPath: " + xpath + + " to collection " + source); } + String xmlFilesPattern = null; + try { + xmlFilesPattern = par.getParameter("xmlFiles", "\\.xml$"); + this.cacheKeyParList.add(xmlFilesPattern); + this.xmlRE = new RE(xmlFilesPattern); + if (this.getLogger().isDebugEnabled()) { + this.getLogger().debug("pattern for XML files: " + xmlFilesPattern); + } + } catch (RESyntaxException rese) { + throw new ProcessingException("Syntax error in regexp pattern '" + + xmlFilesPattern + "'", rese); + } + String[] params = par.getNames(); this.prefixResolver = new XPathPrefixResolver(); for (int i = 0; i < params.length; i++) { @@ -164,14 +208,10 @@ } } - public void compose(ComponentManager manager) { - try { + public void compose(ComponentManager manager) throws ComponentException { super.compose(manager); processor = (XPathProcessor)manager.lookup(XPathProcessor.ROLE); parser = (DOMParser)manager.lookup(DOMParser.ROLE); - } catch (Exception e) { - this.getLogger().error("Could not obtain a required component", e); - } } /** @@ -233,7 +273,7 @@ new Long(((TraversableSource)o2).getLastModified())); } }); - } else if (sort.equals("directory")) { + } else if (sort.equals("collection")) { Arrays.sort(contents.toArray(), new Comparator() { public int compare(Object o1, Object o2) { TraversableSource ts1 = (TraversableSource)o1; @@ -265,12 +305,29 @@ } else { if (isIncluded(path) && !isExcluded(path)) { startNode(RESOURCE_NODE_NAME, path); - if (path.getName().endsWith(".xml") && XPathQuery != null) + if (isXML(path) && xpath != null) performXPathQuery(path); endNode(RESOURCE_NODE_NAME); } } } + + /** + * Determines if a given TraversableSource shall be handled as XML. + * + * @param path the TraversableSource to check + * @return true if the given TraversableSource shall handled as XML, false + * otherwise. + */ + protected boolean isXML(TraversableSource path) { + return this.xmlRE.match(path.getName()); + } + + /** + * Performs an XPath query on the source. + * @param xmlFile the Source the XPath is performed on. + * @throws SAXException if something goes wrong while adding the XML snippet. + */ protected void performXPathQuery(TraversableSource in) throws SAXException { @@ -284,18 +341,18 @@ this.getLogger().error("Unable to resolve and parse document" + e); } if (doc != null) { - NodeList nl = processor.selectNodeList(doc.getDocumentElement(), XPathQuery, this.prefixResolver); + NodeList nl = processor.selectNodeList(doc.getDocumentElement(), xpath, this.prefixResolver); final String id = in.getName(); AttributesImpl attributes = new AttributesImpl(); attributes.addAttribute("", RESULT_DOCID_ATTR, RESULT_DOCID_ATTR, - CDATA, id); - attributes.addAttribute("", QUERY_ATTR, QUERY_ATTR, CDATA, - XPathQuery); - super.contentHandler.startElement(URI, RESULT, QRESULT, attributes); + " CDATA", id); + attributes.addAttribute("", QUERY_ATTR_NAME, QUERY_ATTR_NAME, "CDATA", + xpath); + super.contentHandler.startElement(URI, XPATH_NODE_NAME, PREFIX + ":" + XPATH_NODE_NAME, attributes); DOMStreamer ds = new DOMStreamer(super.xmlConsumer); for (int i = 0; i < nl.getLength(); i++) ds.stream(nl.item(i)); - super.contentHandler.endElement(URI, RESULT, QRESULT); + super.contentHandler.endElement(URI, XPATH_NODE_NAME, PREFIX + ":" + XPATH_NODE_NAME); } } @@ -305,7 +362,7 @@ */ public void recycle() { super.recycle(); - this.XPathQuery = null; + this.xpath = null; this.attributes = null; this.doc = null; }