KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Copyright 1999-2005 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.IOException JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.Map JavaDoc;
21
22 import org.apache.avalon.framework.logger.Logger;
23 import org.apache.avalon.framework.parameters.Parameters;
24 import org.apache.avalon.framework.service.ServiceException;
25 import org.apache.avalon.framework.service.ServiceManager;
26 import org.apache.cocoon.ProcessingException;
27 import org.apache.cocoon.components.source.SourceUtil;
28 import org.apache.cocoon.environment.Context;
29 import org.apache.cocoon.environment.ObjectModelHelper;
30 import org.apache.cocoon.environment.SourceResolver;
31 import org.apache.cocoon.xml.dom.DOMStreamer;
32 import org.apache.excalibur.source.TraversableSource;
33 import org.apache.excalibur.xml.xpath.PrefixResolver;
34 import org.apache.excalibur.xml.xpath.XPathProcessor;
35 import org.apache.regexp.RE;
36 import org.apache.regexp.RESyntaxException;
37 import org.w3c.dom.Document JavaDoc;
38 import org.w3c.dom.NodeList JavaDoc;
39 import org.xml.sax.SAXException JavaDoc;
40 import org.xml.sax.helpers.AttributesImpl JavaDoc;
41
42 /**
43  * Generates an XML collection listing performing XPath queries on XML sources.
44  * It can be used both as a plain TraversableGenerator or, if an XPath is
45  * specified, it will perform an XPath query on every XML resource, where "xml
46  * resource" is, by default, any resource ending with ".xml", which can be
47  * overriden by setting the (regexp) pattern "xmlFiles as a sitemap parameter,
48  * or where the name of the resource has a container-wide mime-type mapping to
49  * 'text/xml' such as specified by mime-mapping elements in a web.xml
50  * descriptor file.
51  *
52  * The XPath can be specified in two ways:
53  * <ol>
54  * <li>By using an XPointerish syntax in the URL: everything following the
55  * pound sign (possiby preceding query
56  * string arguments) will be treated as the XPath;
57  * </li>
58  * <li>Specifying it as a sitemap parameter named "xpath"
59  * </ol>
60  *
61  * Sample usage:
62  *
63  * Sitemap:
64  * &lt;map:match pattern="documents/**"&gt;
65  * &lt;map:generate type="xpathdirectory"
66  * SRC=" docs/{1}#/article/title|/article/abstract" &gt;
67  * &lt; map:parameter name="xmlFiles" value="\.xml$"/&gt;
68  * &lt;/map:generate&gt;
69  * &lt;map: serialize type="xml" /&gt; &lt;/map:match&gt;
70  *
71  * Request:
72  * http://www.some.host/documents/test
73  * Result:
74  * &lt;collection:collection
75  * name="test" lastModified="1010400942000"
76  * date="1/7/02 11:55 AM" requested="true"
77  * xmlns:collection="http://apache.org/cocoon/collection/1.0"&gt;
78  * &lt;collection:collection name="subdirectory" lastModified="1010400942000" date="1/7/02 11:55 AM" /&gt;
79  * &lt;collection:resource name="test.xml" lastModified="1011011579000" date="1/14/02 1:32 PM"&gt;
80  * &lt;collection:xpath docid="test.xml" query="/article/title"&gt;
81  * &lt;title&gt;This is a test document&lt;/title&gt;
82  * &lt;abstract&gt;
83  * &lt;para&gt;Abstract of my test article&lt;/para&gt;
84  * &lt;/abstract&gt;
85  * &lt;/collection:xpath&gt;
86  * &lt;/collection:resource&gt;
87  * &lt;collection:resource name="test.gif" lastModified="1011011579000" date="1/14/02 1:32 PM"&gt;
88  * &lt;/collection:collection&gt;
89  *
90  * If you need to use namespaces, you can set them as sitemap parameters in
91  * the form:
92  * lt;map:parameter name="xmlns:<i>your prefix</i>" value="nsURI"/**"&gt;
93  *
94  * @author <a HREF="mailto:gianugo@apache.org">Gianugo Rabellino</a>
95  * @author <a HREF="mailto:d.madama@pro-netics.com">Daniele Madama</a>
96  * @version CVS $Id: XPathTraversableGenerator.java 240317 2005-08-26 19:25:55Z vgritsenko $
97  */

98 public class XPathTraversableGenerator extends TraversableGenerator {
99
100     /** Local name for the element that contains the included XML snippet. */
101     protected static final String JavaDoc XPATH_NODE_NAME = "xpath";
102     /** Attribute for the XPath query. */
103     protected static final String JavaDoc QUERY_ATTR_NAME = "query";
104     /** The document containing a successful XPath query */
105     protected static final String JavaDoc RESULT_DOCID_ATTR = "docid";
106
107     /** The regular expression for the XML files pattern. */
108     protected RE xmlRE;
109
110     /** The document that should be parsed and (partly) included. */
111     protected Document JavaDoc doc;
112
113     /** The XPath. */
114     protected String JavaDoc xpath;
115
116     /** The XPath processor. */
117     protected XPathProcessor processor;
118
119     /** The prefix resolver for namespaced queries */
120     protected XPathPrefixResolver prefixResolver;
121
122     /** The cocoon context used for mime-type mappings */
123     protected Context context;
124
125
126     public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc src, Parameters par)
127     throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
128         super.setup(resolver, objectModel, src, par);
129
130         // See if an XPath was specified
131
int pointer;
132         if ((pointer = this.source.indexOf("#")) != -1) {
133             int endpointer = this.source.indexOf('?');
134             if (endpointer != -1) {
135                 this.xpath = source.substring(pointer + 1, endpointer);
136             } else {
137                 this.xpath = source.substring(pointer + 1);
138             }
139             this.source = src.substring(0, pointer);
140             if (endpointer != -1) {
141                 this.source += src.substring(endpointer);
142             }
143         } else {
144             this.xpath = par.getParameter("xpath", null);
145         }
146         this.cacheKeyParList.add(this.xpath);
147         if (getLogger().isDebugEnabled()) {
148             getLogger().debug("Applying XPath: " + xpath + " to collection " + source);
149         }
150
151         String JavaDoc xmlFilesPattern = null;
152         try {
153             xmlFilesPattern = par.getParameter("xmlFiles", "\\.xml$");
154             this.cacheKeyParList.add(xmlFilesPattern);
155             this.xmlRE = new RE(xmlFilesPattern);
156             if (this.getLogger().isDebugEnabled()) {
157                 this.getLogger().debug("pattern for XML files: " + xmlFilesPattern);
158             }
159         } catch (RESyntaxException rese) {
160             throw new ProcessingException("Syntax error in regexp pattern '"
161                     + xmlFilesPattern + "'", rese);
162         }
163
164         String JavaDoc[] params = par.getNames();
165         this.prefixResolver = new XPathPrefixResolver(this.getLogger());
166         for (int i = 0; i < params.length; i++) {
167             if (params[i].startsWith("xmlns:")) {
168                 String JavaDoc paramValue = par.getParameter(params[i], "");
169                 String JavaDoc paramName = params[i].substring(6);
170                 if (getLogger().isDebugEnabled()) {
171                     getLogger().debug("add param to prefixResolver: " + paramName);
172                 }
173                 this.prefixResolver.addPrefix(paramName, paramValue);
174             }
175         }
176
177         this.context = ObjectModelHelper.getContext(objectModel);
178     }
179
180     public void service(ServiceManager manager) throws ServiceException {
181         super.service(manager);
182         processor = (XPathProcessor)manager.lookup(XPathProcessor.ROLE);
183     }
184
185     public void dispose() {
186         if ( this.manager != null ) {
187             this.manager.release( processor );
188             this.processor = null;
189         }
190         super.dispose();
191     }
192
193     protected void addContent(TraversableSource source)
194     throws SAXException JavaDoc, ProcessingException {
195         super.addContent(source);
196         if (!source.isCollection() && isXML(source) && xpath != null) {
197             performXPathQuery(source);
198         }
199     }
200
201     /**
202      * Determines if a given TraversableSource shall be handled as XML.
203      *
204      * @param path the TraversableSource to check
205      * @return true if the given TraversableSource shall handled as XML, false
206      * otherwise.
207      */

208     protected boolean isXML(TraversableSource path) {
209         String JavaDoc mimeType = this.context.getMimeType(path.getName());
210         return this.xmlRE.match(path.getName()) || "text/xml".equalsIgnoreCase(mimeType);
211     }
212
213     /**
214      * Performs an XPath query on the source.
215      * @param in the Source the XPath is performed on.
216      * @throws SAXException if something goes wrong while adding the XML snippet.
217      */

218     protected void performXPathQuery(TraversableSource in) throws SAXException JavaDoc {
219         doc = null;
220         try {
221             doc = SourceUtil.toDOM(this.manager, "text/xml", in);
222         } catch (SAXException JavaDoc se) {
223             getLogger().error("Warning:" + in.getName() + " is not a valid XML document. Ignoring");
224         } catch (Exception JavaDoc e) {
225             this.getLogger().error("Unable to resolve and parse document" + e);
226         }
227         if (doc != null) {
228             NodeList JavaDoc nl = processor.selectNodeList(doc.getDocumentElement(), xpath, this.prefixResolver);
229             final String JavaDoc id = in.getName();
230             AttributesImpl JavaDoc attributes = new AttributesImpl JavaDoc();
231             attributes.addAttribute("", RESULT_DOCID_ATTR, RESULT_DOCID_ATTR," CDATA", id);
232             attributes.addAttribute("", QUERY_ATTR_NAME, QUERY_ATTR_NAME, "CDATA",xpath);
233             super.contentHandler.startElement(URI, XPATH_NODE_NAME, PREFIX + ":" + XPATH_NODE_NAME, attributes);
234             DOMStreamer ds = new DOMStreamer(super.xmlConsumer);
235             for (int i = 0; i < nl.getLength(); i++) {
236                 ds.stream(nl.item(i));
237             }
238             super.contentHandler.endElement(URI, XPATH_NODE_NAME, PREFIX + ":" + XPATH_NODE_NAME);
239         }
240     }
241
242     /**
243      * Recycle resources
244      *
245      */

246     public void recycle() {
247         this.xpath = null;
248         this.doc = null;
249         this.xmlRE = null;
250         this.prefixResolver = null;
251         this.context = null;
252         super.recycle();
253     }
254
255     /**
256      * A brain-dead PrefixResolver implementation
257      */

258     static class XPathPrefixResolver implements PrefixResolver {
259
260         private Map JavaDoc params;
261
262         private Logger logger;
263
264         public XPathPrefixResolver(Logger logger) {
265             this.params = new HashMap JavaDoc();
266             this.logger = logger;
267         }
268
269         /**
270          * Get a namespace URI given a prefix.
271          *
272          * @see org.apache.excalibur.xml.xpath.PrefixResolver#prefixToNamespace(java.lang.String)
273          */

274         public String JavaDoc prefixToNamespace(String JavaDoc prefix) {
275             if (this.logger.isDebugEnabled()) {
276                 this.logger.debug("prefix: " + prefix);
277             }
278             if (this.params.containsKey(prefix)) {
279                 if(this.logger.isDebugEnabled()) {
280                     this.logger.debug("prefix; " + prefix + " - namespace: " + this.params.get(prefix));
281                 }
282                 return (String JavaDoc) this.params.get(prefix);
283             }
284             return null;
285         }
286
287         public void addPrefix(String JavaDoc prefix, String JavaDoc uri) {
288             this.params.put(prefix, uri);
289         }
290     }
291 }
292
Popular Tags