KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > generation > XPathDirectoryGenerator


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.generation;
17
18 import java.io.BufferedReader JavaDoc;
19 import java.io.File JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStreamReader JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Map JavaDoc;
24
25 import org.apache.avalon.framework.logger.Logger;
26 import org.apache.avalon.framework.parameters.Parameters;
27 import org.apache.avalon.framework.service.ServiceException;
28 import org.apache.avalon.framework.service.ServiceManager;
29 import org.apache.cocoon.ProcessingException;
30
31 import org.apache.cocoon.components.source.SourceUtil;
32
33 import org.apache.cocoon.environment.SourceResolver;
34
35 import org.apache.cocoon.xml.dom.DOMStreamer;
36
37 import org.apache.excalibur.source.Source;
38 import org.apache.excalibur.source.SourceNotFoundException;
39
40 import org.apache.excalibur.xml.dom.DOMParser;
41 import org.apache.excalibur.xml.xpath.PrefixResolver;
42 import org.apache.excalibur.xml.xpath.XPathProcessor;
43
44 import org.apache.regexp.RE;
45 import org.apache.regexp.RESyntaxException;
46
47 import org.w3c.dom.Document JavaDoc;
48 import org.w3c.dom.NodeList JavaDoc;
49
50 import org.xml.sax.SAXException JavaDoc;
51
52 import org.xml.sax.helpers.AttributesImpl JavaDoc;
53
54
55 /**
56  * @cocoon.sitemap.component.documentation
57  * Generates an XML directory listing performing XPath queries on XML files. It can be used both as a plain
58  * DirectoryGenerator or, by specifying a parameter <code>xpath</code>, it will perform an XPath query on every XML
59  * resource.
60  *
61  * @cocoon.sitemap.component.name xpathdirectory
62  * @cocoon.sitemap.component.label content
63  * @cocoon.sitemap.component.documentation.caching
64  * Uses the last modification date of the directory and the contained documents
65  * @cocoon.sitemap.component.logger sitemap.generator.xpathdirectory
66  *
67  *
68  * <p>
69  * Generates an XML directory listing performing XPath queries on XML files. It can be used both as a plain
70  * DirectoryGenerator or, by specifying a parameter <code>xpath</code>, it will perform an XPath query on every XML
71  * resource. A <code>nsmapping</code> parameter can be specified to point to a file containing lines to map prefixes
72  * to namespaces like this:
73  * </p>
74  *
75  * <p>
76  * prefix=namespace-uri<br/> prefix2=namespace-uri-2
77  * </p>
78  *
79  * <p>
80  * A parameter <code>nsmapping-reload</code> specifies if the prefix-2-namespace mapping file should be checked to be
81  * reloaded on each request to this generator if it was modified since the last time it was read.
82  * </p>
83  *
84  * <p>
85  * An additional parameter <code>xmlFiles</code> can be set in the sitemap setting the regular expression pattern for
86  * determining if a file should be handled as XML file or not. The default value for this param is
87  * <code>\.xml$</code>, so that it matches all files ending <code>.xml</code>.
88  * </p>
89  *
90  * <p></p>
91  * <br>Sample usage: <br><br>Sitemap:
92  * <pre>
93  * &lt;map:match pattern="documents/**"&gt;
94  * &lt;map:generate type="xpathdirectory" SRC="docs/{1}"&gt;
95  * &lt;map:parameter name="xpath" value="/article/title|/article/abstract"/&gt;
96  * &lt;map:parameter name="nsmapping" value="mapping.proeprties"/&gt;
97  * &lt;map:parameter name="nsmapping-reload" value="false"/&gt;
98  * &lt;map:parameter name="xmlFiles" value="\.xml$"/&gt;
99  * &lt;/map:generate&gt;
100  * &lt;map:serialize type="xml" /&gt;
101  * &lt;/map:match&gt;
102  * </pre>
103  *
104  * <p>
105  * Request: <br>http://www.some.host/documents/test
106  * </p>
107  * Result:
108  * <pre>
109  * &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;
110  * &lt;dir:directory name="subdirectory" lastModified="1010400942000" date="1/7/02 11:55 AM"/&gt;
111  * &lt;dir:file name="test.xml" lastModified="1011011579000" date="1/14/02 1:32 PM"&gt;
112  * &lt;dir:xpath query="/article/title"&gt;
113  * &lt;title&gt;This is a test document&lt;/title&gt;
114  * &lt;abstract&gt;
115  * &lt;para&gt;Abstract of my test article&lt;/para&gt;
116  * &lt;/abstract&gt;
117  * &lt;/dir:xpath&gt;
118  * &lt;/dir:file&gt;
119  * &lt;dir:file name="test.gif" lastModified="1011011579000" date="1/14/02 1:32 PM"/&gt;
120  * &lt;/dir:directory&gt;
121  * </pre>
122  *
123  * @author <a HREF="mailto:giacomo@apache.org">Giacomo Pati</a>
124  * @author <a HREF="mailto:gianugo@apache.org">Gianugo Rabellino</a>
125  * @author <a HREF="mailto:joerg@apache.org">Jörg Heinicke</a>
126  * @version CVS $Id: XPathDirectoryGenerator.java 123903 2005-01-02 21:26:59Z antonio $
127  */

128 public class XPathDirectoryGenerator
129 extends DirectoryGenerator {
130     /** Local name for the element that contains the included XML snippet. */
131     protected static final String JavaDoc XPATH_NODE_NAME = "xpath";
132
133     /** Attribute for the XPath query. */
134     protected static final String JavaDoc QUERY_ATTR_NAME = "query";
135
136     /** All the mapping files lastmodified dates */
137     protected static final Map JavaDoc mappingFiles = new HashMap JavaDoc();
138
139     /** The parser for the XML snippets to be included. */
140     protected DOMParser parser = null;
141
142     /** The document that should be parsed and (partly) included. */
143     protected Document JavaDoc doc = null;
144
145     /** The PrefixResolver responsable for processing current request (if any). */
146     protected PrefixResolver prefixResolver = null;
147
148     /** The regular expression for the XML files pattern. */
149     protected RE xmlRE = null;
150
151     /** The XPath. */
152     protected String JavaDoc xpath = null;
153
154     /** The XPath processor. */
155     protected XPathProcessor processor = null;
156
157     /**
158      * Disposable
159      */

160     public void dispose() {
161         if (this.manager != null) {
162             this.manager.release(this.processor);
163             this.manager.release(this.parser);
164             this.processor = null;
165             this.parser = null;
166         }
167
168         super.dispose();
169     }
170
171     /**
172      * Recycle resources
173      */

174     public void recycle() {
175         this.xpath = null;
176         this.doc = null;
177
178         //this.parser = null;
179
//this.processor = null;
180
super.recycle();
181     }
182
183     /**
184      * Serviceable
185      *
186      * @param manager the ComponentManager
187      *
188      * @throws ServiceException in case a component could not be found
189      */

190     public void service(ServiceManager manager)
191     throws ServiceException {
192         super.service(manager);
193         this.processor = (XPathProcessor)manager.lookup(XPathProcessor.ROLE);
194         this.parser = (DOMParser)manager.lookup(DOMParser.ROLE);
195     }
196
197     /**
198      * Setup this sitemap component
199      *
200      * @param resolver the SourceResolver
201      * @param objectModel The environmental object model
202      * @param src the source attribute
203      * @param par the parameters
204      *
205      * @throws ProcessingException if processing failes
206      * @throws SAXException in case of XML related errors
207      * @throws IOException in case of file related errors
208      */

209     public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc src, Parameters par)
210     throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
211         super.setup(resolver, objectModel, src, par);
212
213         // See if an XPath was specified
214
this.xpath = par.getParameter("xpath", null);
215         this.cacheKeyParList.add(this.xpath);
216
217         if (getLogger().isDebugEnabled()) {
218             getLogger().debug("Applying XPath: " + this.xpath + " to directory " + this.source);
219         }
220
221         final String JavaDoc mappings = par.getParameter("nsmapping", null);
222
223         if (null != mappings) {
224             final boolean mapping_reload = par.getParameterAsBoolean("nsmapping-reload", false);
225             final Source mappingSource = resolver.resolveURI(mappings);
226             final String JavaDoc mappingKey = mappingSource.getURI();
227             final MappingInfo mappingInfo = (MappingInfo)XPathDirectoryGenerator.mappingFiles.get(mappingKey);
228
229             if ((null == mappingInfo) || (mappingInfo.reload == false) ||
230                 (mappingInfo.mappingSource.getLastModified() < mappingSource.getLastModified())) {
231                 this.prefixResolver =
232                     new MappingInfo(getLogger().getChildLogger("prefix-resolver"), mappingSource, mapping_reload);
233                 XPathDirectoryGenerator.mappingFiles.put(mappingKey, this.prefixResolver);
234             } else {
235                 this.prefixResolver = mappingInfo;
236             }
237         }
238
239         String JavaDoc xmlFilesPattern = null;
240
241         try {
242             xmlFilesPattern = par.getParameter("xmlFiles", "\\.xml$");
243             this.cacheKeyParList.add(xmlFilesPattern);
244             this.xmlRE = new RE(xmlFilesPattern);
245
246             if (getLogger().isDebugEnabled()) {
247                 getLogger().debug("pattern for XML files: " + xmlFilesPattern);
248             }
249         } catch (RESyntaxException rese) {
250             throw new ProcessingException("Syntax error in regexp pattern '" + xmlFilesPattern + "'", rese);
251         }
252     }
253
254     /**
255      * Determines if a given File shall be handled as XML.
256      *
257      * @param path the File to check
258      *
259      * @return true if the given File shall handled as XML, false otherwise.
260      */

261     protected boolean isXML(File JavaDoc path) {
262         return this.xmlRE.match(path.getName());
263     }
264
265     /**
266      * Performs an XPath query on the file.
267      *
268      * @param xmlFile the File the XPath is performed on.
269      *
270      * @throws SAXException if something goes wrong while adding the XML snippet.
271      */

272     protected void performXPathQuery(File JavaDoc xmlFile)
273     throws SAXException JavaDoc {
274         this.doc = null;
275
276         Source source = null;
277
278         try {
279             source = resolver.resolveURI(xmlFile.toURL().toExternalForm());
280             this.doc = this.parser.parseDocument(SourceUtil.getInputSource(source));
281         } catch (SAXException JavaDoc e) {
282             getLogger().error("Warning:" + xmlFile.getName() + " is not a valid XML file. Ignoring.", e);
283         } catch (ProcessingException e) {
284             getLogger().error("Warning: Problem while reading the file " + xmlFile.getName() + ". Ignoring.", e);
285         } catch (IOException JavaDoc e) {
286             getLogger().error("Warning: Problem while reading the file " + xmlFile.getName() + ". Ignoring.", e);
287         } finally {
288             resolver.release(source);
289         }
290
291         if (doc != null) {
292             NodeList JavaDoc nl =
293                 (null == this.prefixResolver)
294                 ? this.processor.selectNodeList(this.doc.getDocumentElement(), this.xpath)
295                 : this.processor.selectNodeList(this.doc.getDocumentElement(), this.xpath, this.prefixResolver);
296             AttributesImpl JavaDoc attributes = new AttributesImpl JavaDoc();
297             attributes.addAttribute("", QUERY_ATTR_NAME, QUERY_ATTR_NAME, "CDATA", xpath);
298             super.contentHandler.startElement(URI, XPATH_NODE_NAME, PREFIX + ":" + XPATH_NODE_NAME, attributes);
299
300             DOMStreamer ds = new DOMStreamer(super.xmlConsumer);
301
302             for (int i = 0; i < nl.getLength(); i++) {
303                 ds.stream(nl.item(i));
304             }
305
306             super.contentHandler.endElement(URI, XPATH_NODE_NAME, PREFIX + ":" + XPATH_NODE_NAME);
307         }
308     }
309
310     /**
311      * Extends the startNode() method of the DirectoryGenerator by starting a possible XPath query on a file.
312      *
313      * @param nodeName the node currently processing
314      * @param path the file path
315      *
316      * @throws SAXException in case of errors
317      */

318     protected void startNode(String JavaDoc nodeName, File JavaDoc path)
319     throws SAXException JavaDoc {
320         super.startNode(nodeName, path);
321
322         if ((this.xpath != null) && path.isFile() && this.isXML(path)) {
323             performXPathQuery(path);
324         }
325     }
326
327     /**
328      * The MappingInfo class to reolve namespace prefixes to their namespace URI
329      *
330      * @author <a HREF="mailto:giacomo(at)apache.org">Giacomo Pati</a>
331      * @version CVS $Id: XPathDirectoryGenerator.java 123903 2005-01-02 21:26:59Z antonio $
332      */

333     private static class MappingInfo
334     implements PrefixResolver {
335         /** The Source of the mapping file */
336         public final Source mappingSource;
337
338         /** Whether to reload if mapping file has changed */
339         public final boolean reload;
340
341         /** Our Logger */
342         private final Logger logger;
343
344         /** Map of prefixes to namespaces */
345         private final Map JavaDoc prefixMap;
346
347         /**
348          * Creates a new MappingInfo object.
349          *
350          * @param logger DOCUMENT ME!
351          * @param mappingSource The Source of the mapping file
352          * @param reload Whether to reload if mapping file has changed
353          *
354          * @throws SourceNotFoundException In case the mentioned source is not there
355          * @throws IOException in case the source could not be read
356          */

357         public MappingInfo(final Logger logger, final Source mappingSource, final boolean reload)
358         throws SourceNotFoundException, IOException JavaDoc {
359             this.logger = logger;
360             this.mappingSource = mappingSource;
361             this.reload = reload;
362             prefixMap = new HashMap JavaDoc();
363
364             final BufferedReader JavaDoc br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(mappingSource.getInputStream()));
365
366             for (String JavaDoc line = br.readLine(); line != null; line = br.readLine()) {
367                 final int i = line.indexOf('=');
368
369                 if (i > 0) {
370                     final String JavaDoc prefix = line.substring(0, i);
371                     final String JavaDoc namespace = line.substring(i + 1);
372                     prefixMap.put(prefix, namespace);
373                     logger.debug("added mapping: '" + prefix + "'='" + namespace + "'");
374                 }
375             }
376         }
377
378         /* (non-Javadoc)
379          * @see org.apache.excalibur.xml.xpath.PrefixResolver#prefixToNamespace(java.lang.String)
380          */

381         public String JavaDoc prefixToNamespace(String JavaDoc prefix) {
382             final String JavaDoc namespace = (String JavaDoc)this.prefixMap.get(prefix);
383
384             if (logger.isDebugEnabled()) {
385                 logger.debug("have to resolve prefix='" + prefix + ", found namespace='" + namespace + "'");
386             }
387
388             return namespace;
389         }
390     }
391 }
392
Popular Tags