cziegeler    2004/03/12 02:28:08

  Modified:    src/java/org/apache/cocoon/generation
                        XPathDirectoryGenerator.java
  Log:
  Syncing generators between 2.1 and 2.2 :(
  
  Revision  Changes    Path
  1.8       +251 -100  
cocoon-2.2/src/java/org/apache/cocoon/generation/XPathDirectoryGenerator.java
  
  Index: XPathDirectoryGenerator.java
  ===================================================================
  RCS file: 
/home/cvs/cocoon-2.2/src/java/org/apache/cocoon/generation/XPathDirectoryGenerator.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- XPathDirectoryGenerator.java      8 Mar 2004 14:02:44 -0000       1.7
  +++ XPathDirectoryGenerator.java      12 Mar 2004 10:28:08 -0000      1.8
  @@ -15,10 +15,14 @@
    */
   package org.apache.cocoon.generation;
   
  +import java.io.BufferedReader;
   import java.io.File;
   import java.io.IOException;
  +import java.io.InputStreamReader;
  +import java.util.HashMap;
   import java.util.Map;
   
  +import org.apache.avalon.framework.logger.Logger;
   import org.apache.avalon.framework.parameters.Parameters;
   import org.apache.avalon.framework.service.ServiceException;
   import org.apache.avalon.framework.service.ServiceManager;
  @@ -27,7 +31,9 @@
   import org.apache.cocoon.environment.SourceResolver;
   import org.apache.cocoon.xml.dom.DOMStreamer;
   import org.apache.excalibur.source.Source;
  +import org.apache.excalibur.source.SourceNotFoundException;
   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;
  @@ -37,132 +43,198 @@
   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 DirectoryGenerator or, by specifying a 
parameter
  - * <code>xpath</code>, it will perform an XPath query on every XML resource.
  - * Therefore an additional parameter <code>xmlFiles</code> can be set in the
  - * sitemap setting the regular expression pattern for determining if a file
  - * should be handled as XML file or not. The default value for this param is
  - * <code>\.xml$</code>, so that it matches all files ending 
<code>.xml</code>.
  - * <br>
  - * Sample usage:<br>
  - * <br>
  - * Sitemap:
  + * <p>
  + * Generates an XML directory listing performing XPath queries on XML files. 
It can be used both as a plain
  + * DirectoryGenerator or, by specifying a parameter <code>xpath</code>, it 
will perform an XPath query on every XML
  + * resource. A <code>nsmapping</code> parameter can be specified to point to 
a file containing lines to map prefixes
  + * to namespaces like this:
  + * </p>
  + * 
  + * <p>
  + * prefix=namespace-uri<br/> prefix2=namespace-uri-2
  + * </p>
  + * 
  + * <p>
  + * A parameter <code>nsmapping-reload</code> specifies if the 
prefix-2-namespace mapping file should be checked to be
  + * reloaded on each request to this generator if it was modified since the 
last time it was read.
  + * </p>
  + * 
  + * <p>
  + * An additional parameter <code>xmlFiles</code> can be set in the sitemap 
setting the regular expression pattern for
  + * determining if a file should be handled as XML file or not. The default 
value for this param is
  + * <code>\.xml$</code>, so that it  matches all files ending 
<code>.xml</code>.
  + * </p>
  + * 
  + * <p></p>
  + * <br>Sample usage: <br><br>Sitemap:
    * <pre>
  - * &lt;map:match pattern="documents/**"&gt;
  - *   &lt;map:generate type="xpathdirectory" src="docs/{1}"&gt;
  - *     &lt;map:parameter name="xpath" 
value="/article/title|/article/abstract"/&gt;
  - *     &lt;map:parameter name="xmlFiles" value="\.xml$"/&gt;
  - *   &lt;/map:generate&gt;
  - *   &lt;map:serialize type="xml" /&gt;
  - * &lt;/map:match&gt;
  + *  &lt;map:match pattern="documents/**"&gt; 
  + *   &lt;map:generate type="xpathdirectory" src="docs/{1}"&gt; 
  + *    &lt;map:parameter name="xpath" 
value="/article/title|/article/abstract"/&gt; 
  + *    &lt;map:parameter name="nsmapping" value="mapping.proeprties"/&gt; 
  + *    &lt;map:parameter name="nsmapping-reload" value="false"/&gt; 
  + *    &lt;map:parameter name="xmlFiles" value="\.xml$"/&gt; 
  + *   &lt;/map:generate&gt; 
  + *   &lt;map:serialize type="xml" /&gt; 
  + *  &lt;/map:match&gt;
    * </pre>
  - *
  - * <p>Request:<br>
  - *   http://www.some.host/documents/test</p>
  - *
  + * 
  + * <p>
  + * Request: <br>http://www.some.host/documents/test
  + * </p>
    * Result:
    * <pre>
  - * &lt;dir:directory
  - *   name="test" lastModified="1010400942000"
  - *   date="1/7/02 11:55 AM" requested="true"
  - *   xmlns:dir="http://apache.org/cocoon/directory/2.0"&gt;
  - *   &lt;dir:directory name="subdirectory" lastModified="1010400942000" 
date="1/7/02 11:55 AM"/&gt;
  - *   &lt;dir:file name="test.xml" lastModified="1011011579000" date="1/14/02 
1:32 PM"&gt;
  - *     &lt;dir:xpath query="/article/title"&gt;
  - *       &lt;title&gt;This is a test document&lt;/title&gt;
  - *       &lt;abstract&gt;
  - *         &lt;para&gt;Abstract of my test article&lt;/para&gt;
  - *       &lt;/abstract&gt;
  - *     &lt;/dir:xpath&gt;
  - *   &lt;/dir:file&gt;
  - *   &lt;dir:file name="test.gif" lastModified="1011011579000" date="1/14/02 
1:32 PM"/&gt;
  - * &lt;/dir:directory&gt;
  + *  &lt;dir:directory name="test" lastModified="1010400942000" date="1/7/02 
11:55 AM" requested="true" 
xmlns:dir="http://apache.org/cocoon/directory/2.0"&gt; 
  + *   &lt;dir:directory name="subdirectory" lastModified="1010400942000" 
date="1/7/02 11:55 AM"/&gt; 
  + *   &lt;dir:file name="test.xml" lastModified="1011011579000" date="1/14/02 
1:32 PM"&gt; 
  + *    &lt;dir:xpath query="/article/title"&gt; 
  + *     &lt;title&gt;This is a test document&lt;/title&gt; 
  + *      &lt;abstract&gt; 
  + *       &lt;para&gt;Abstract of my test article&lt;/para&gt; 
  + *      &lt;/abstract&gt; 
  + *     &lt;/dir:xpath&gt; 
  + *    &lt;/dir:file&gt; 
  + *   &lt;dir:file name="test.gif" lastModified="1011011579000" date="1/14/02 
1:32 PM"/&gt; 
  + *  &lt;/dir:directory&gt;
    * </pre>
    *
  + * @author <a href="mailto:[EMAIL PROTECTED]">Giacomo Pati</a>
    * @author <a href="mailto:[EMAIL PROTECTED]">Gianugo Rabellino</a>
    * @author <a href="mailto:[EMAIL PROTECTED]">J�rg Heinicke</a>
    * @version CVS $Id$
    */
  -public class XPathDirectoryGenerator extends DirectoryGenerator {
  -
  +public class XPathDirectoryGenerator
  +extends DirectoryGenerator {
       /** 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 regular expression for the XML files pattern. */
  -    protected RE xmlRE = null;
  +    /** All the mapping files lastmodified dates */
  +    protected static Map mappingFiles = new HashMap();
  +
  +    /** The parser for the XML snippets to be included. */
  +    protected DOMParser parser = null;
  +
       /** The document that should be parsed and (partly) included. */
       protected Document doc = null;
  +
  +    /** The PrefixResolver responsable for processing current request (if 
any). */
  +    protected PrefixResolver prefixResolver = null;
  +
  +    /** The regular expression for the XML files pattern. */
  +    protected RE xmlRE = 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;
   
  +    /**
  +     * Disposable
  +     */
  +    public void dispose() {
  +        if (this.manager != null) {
  +            this.manager.release(this.processor);
  +            this.manager.release(this.parser);
  +            this.processor = null;
  +            this.parser = null;
  +        }
  +
  +        super.dispose();
  +    }
  +
  +    /**
  +     * Recycle resources
  +     */
  +    public void recycle() {
  +        this.xpath = null;
  +        this.doc = null;
  +
  +        //this.parser = null;
  +        //this.processor = null;
  +        super.recycle();
  +    }
  +
  +    /**
  +     * Serviceable
  +     *
  +     * @param manager the ComponentManager
  +     *
  +     * @throws ServiceException in case a component could not be found
  +     */
  +    public void service(ServiceManager manager)
  +    throws ServiceException {
  +        super.service(manager);
  +        this.processor = (XPathProcessor)manager.lookup(XPathProcessor.ROLE);
  +        this.parser = (DOMParser)manager.lookup(DOMParser.ROLE);
  +    }
  +
  +    /**
  +     * Setup this sitemap component
  +     *
  +     * @param resolver the SourceResolver
  +     * @param objectModel The environmental object model
  +     * @param src the source attribute
  +     * @param par the parameters
  +     *
  +     * @throws ProcessingException if processing failes
  +     * @throws SAXException in case of XML related errors
  +     * @throws IOException in case of file related errors
  +     */
       public void setup(SourceResolver resolver, Map objectModel, String src, 
Parameters par)
  -            throws ProcessingException, SAXException, IOException {
  +    throws ProcessingException, SAXException, IOException {
           super.setup(resolver, objectModel, src, par);
  +
           // See if an XPath was specified
           this.xpath = par.getParameter("xpath", null);
           this.cacheKeyParList.add(this.xpath);
  +
           if (getLogger().isDebugEnabled()) {
  -            getLogger().debug("Applying XPath: " + this.xpath +
  -                              " to directory " + this.source);
  +            getLogger().debug("Applying XPath: " + this.xpath + " to 
directory " + this.source);
  +        }
  +
  +        final String mappings = par.getParameter("nsmapping", null);
  +
  +        if (null != mappings) {
  +            final boolean mapping_reload = 
par.getParameterAsBoolean("nsmapping-reload", false);
  +            final Source mappingSource = resolver.resolveURI(mappings);
  +            final String mappingKey = mappingSource.getURI();
  +            final MappingInfo mappingInfo = 
(MappingInfo)XPathDirectoryGenerator.mappingFiles.get(mappingKey);
  +
  +            if ((null == mappingInfo) || (mappingInfo.reload == false) ||
  +                (mappingInfo.mappingSource.getLastModified() < 
mappingSource.getLastModified())) {
  +                this.prefixResolver =
  +                    new 
MappingInfo(getLogger().getChildLogger("prefix-resolver"), mappingSource, 
mapping_reload);
  +                XPathDirectoryGenerator.mappingFiles.put(mappingKey, 
this.prefixResolver);
  +            } else {
  +                this.prefixResolver = mappingInfo;
  +            }
           }
  +
           String xmlFilesPattern = null;
  +
           try {
               xmlFilesPattern = par.getParameter("xmlFiles", "\\.xml$");
               this.cacheKeyParList.add(xmlFilesPattern);
               this.xmlRE = new RE(xmlFilesPattern);
  +
               if (getLogger().isDebugEnabled()) {
                   getLogger().debug("pattern for XML files: " + 
xmlFilesPattern);
               }
           } catch (RESyntaxException rese) {
  -            throw new ProcessingException("Syntax error in regexp pattern '"
  -                                          + xmlFilesPattern + "'", rese);
  -        }
  -    }
  -
  -    /**
  -     * Serviceable
  -     */
  -    public void service(ServiceManager manager) throws ServiceException {
  -        super.service(manager);
  -        this.processor = (XPathProcessor)manager.lookup(XPathProcessor.ROLE);
  -        this.parser = (DOMParser)manager.lookup(DOMParser.ROLE);
  -    }
  -
  -    /**
  -     * Disposable
  -     */
  -    public void dispose() {
  -        if ( this.manager != null ) {
  -            this.manager.release( this.processor );
  -            this.manager.release( this.parser );
  -            this.processor = null;
  -            this.parser = null;
  -        }
  -        super.dispose();
  -    }
  -    /**
  -     * Extends the startNode() method of the DirectoryGenerator by starting
  -     * a possible XPath query on a file.
  -     */
  -    protected void startNode(String nodeName, File path) throws SAXException 
{
  -        super.startNode(nodeName, path);
  -        if (this.xpath != null && path.isFile() && this.isXML(path)) {
  -            performXPathQuery(path);
  +            throw new ProcessingException("Syntax error in regexp pattern '" 
+ xmlFilesPattern + "'", rese);
           }
       }
   
       /**
        * Determines if a given File shall be handled as XML.
        *
  -     * @param path  the File to check
  -     * @return true  if the given File shall handled as XML, false otherwise.
  +     * @param path the File to check
  +     *
  +     * @return true if the given File shall handled as XML, false otherwise.
        */
       protected boolean isXML(File path) {
           return this.xmlRE.match(path.getName());
  @@ -170,49 +242,128 @@
   
       /**
        * Performs an XPath query on the file.
  -     * @param xmlFile  the File the XPath is performed on.
  -     * @throws SAXException  if something goes wrong while adding the XML 
snippet.
  +     *
  +     * @param xmlFile the File the XPath is performed on.
  +     *
  +     * @throws SAXException if something goes wrong while adding the XML 
snippet.
        */
  -    protected void performXPathQuery(File xmlFile) throws SAXException {
  +    protected void performXPathQuery(File xmlFile)
  +    throws SAXException {
           this.doc = null;
  +
           Source source = null;
  +
           try {
               source = resolver.resolveURI(xmlFile.toURL().toExternalForm());
               this.doc = 
this.parser.parseDocument(SourceUtil.getInputSource(source));
           } catch (SAXException e) {
  -            getLogger().error("Warning:" + xmlFile.getName() +
  -                              " is not a valid XML file. Ignoring.", e);
  +            getLogger().error("Warning:" + xmlFile.getName() + " is not a 
valid XML file. Ignoring.", e);
           } catch (ProcessingException e) {
  -            getLogger().error("Warning: Problem while reading the file " +
  -                              xmlFile.getName() + ". Ignoring.", e);
  +            getLogger().error("Warning: Problem while reading the file " + 
xmlFile.getName() + ". Ignoring.", e);
           } catch (IOException e) {
  -            getLogger().error("Warning: Problem while reading the file " +
  -                              xmlFile.getName() + ". Ignoring.", e);
  +            getLogger().error("Warning: Problem while reading the file " + 
xmlFile.getName() + ". Ignoring.", e);
           } finally {
               resolver.release(source);
           }
   
           if (doc != null) {
  -            NodeList nl = 
this.processor.selectNodeList(this.doc.getDocumentElement(), this.xpath);
  +            NodeList nl =
  +                (null == this.prefixResolver)
  +                ? 
this.processor.selectNodeList(this.doc.getDocumentElement(), this.xpath)
  +                : 
this.processor.selectNodeList(this.doc.getDocumentElement(), this.xpath, 
this.prefixResolver);
               AttributesImpl attributes = new AttributesImpl();
               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, XPATH_NODE_NAME, PREFIX + 
":" + XPATH_NODE_NAME);
           }
       }
   
       /**
  -     * Recycle resources
  +     * Extends the startNode() method of the DirectoryGenerator by starting 
a possible XPath query on a file.
  +     *
  +     * @param nodeName the node currently processing
  +     * @param path the file path
  +     *
  +     * @throws SAXException in case of errors
        */
  -    public void recycle() {
  -        this.xpath = null;
  -        this.doc = null;
  -        //this.parser = null;
  -        //this.processor = null;
  -        super.recycle();
  +    protected void startNode(String nodeName, File path)
  +    throws SAXException {
  +        super.startNode(nodeName, path);
  +
  +        if ((this.xpath != null) && path.isFile() && this.isXML(path)) {
  +            performXPathQuery(path);
  +        }
  +    }
  +
  +    /**
  +     * The MappingInfo class to reolve namespace prefixes to their namespace 
URI
  +     *
  +     * @author <a href="mailto:giacomo(at)apache.org">Giacomo Pati</a>
  +     * @version CVS $Id$
  +     */
  +    private static class MappingInfo
  +    implements PrefixResolver {
  +        /** The Source of the mapping file */
  +        public final Source mappingSource;
  +
  +        /** Whether to reload if mapping file has changed */
  +        public final boolean reload;
  +
  +        /** Our Logger */
  +        private final Logger logger;
  +
  +        /** Map of prefixes to namespaces */
  +        private final Map prefixMap;
  +
  +        /**
  +         * Creates a new MappingInfo object.
  +         *
  +         * @param logger DOCUMENT ME!
  +         * @param mappingSource The Source of the mapping file
  +         * @param reload Whether to reload if mapping file has changed
  +         *
  +         * @throws SourceNotFoundException In case the mentioned source is 
not there
  +         * @throws IOException in case the source could not be read
  +         */
  +        public MappingInfo(final Logger logger, final Source mappingSource, 
final boolean reload)
  +        throws SourceNotFoundException, IOException {
  +            this.logger = logger;
  +            this.mappingSource = mappingSource;
  +            this.reload = reload;
  +            prefixMap = new HashMap();
  +
  +            final BufferedReader br = new BufferedReader(new 
InputStreamReader(mappingSource.getInputStream()));
  +
  +            for (String line = br.readLine(); line != null; line = 
br.readLine()) {
  +                final int i = line.indexOf('=');
  +
  +                if (i > 0) {
  +                    final String prefix = line.substring(0, i);
  +                    final String namespace = line.substring(i + 1);
  +                    prefixMap.put(prefix, namespace);
  +                    logger.debug("added mapping: '" + prefix + "'='" + 
namespace + "'");
  +                }
  +            }
  +        }
  +
  +        /* (non-Javadoc)
  +         * @see 
org.apache.excalibur.xml.xpath.PrefixResolver#prefixToNamespace(java.lang.String)
  +         */
  +        public String prefixToNamespace(String prefix) {
  +            final String namespace = (String)this.prefixMap.get(prefix);
  +
  +            if (logger.isDebugEnabled()) {
  +                logger.debug("have to resolve prefix='" + prefix + ", found 
namespace='" + namespace + "'");
  +            }
  +
  +            return namespace;
  +        }
       }
   }
  
  
  

Reply via email to