KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > transformation > XIncludeTransformer


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.transformation;
17
18 import java.io.BufferedReader JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21 import java.io.InputStreamReader JavaDoc;
22 import java.io.Reader JavaDoc;
23 import java.io.Serializable JavaDoc;
24 import java.net.MalformedURLException JavaDoc;
25 import java.util.Map JavaDoc;
26
27 import org.apache.avalon.framework.CascadingException;
28 import org.apache.avalon.framework.CascadingRuntimeException;
29 import org.apache.avalon.framework.parameters.Parameters;
30 import org.apache.avalon.framework.service.ServiceManager;
31 import org.apache.avalon.framework.service.Serviceable;
32 import org.apache.cocoon.ProcessingException;
33 import org.apache.cocoon.ResourceNotFoundException;
34 import org.apache.cocoon.caching.CacheableProcessingComponent;
35 import org.apache.cocoon.components.source.SourceUtil;
36 import org.apache.cocoon.components.source.impl.MultiSourceValidity;
37 import org.apache.cocoon.components.xpointer.XPointer;
38 import org.apache.cocoon.components.xpointer.XPointerContext;
39 import org.apache.cocoon.components.xpointer.parser.ParseException;
40 import org.apache.cocoon.components.xpointer.parser.XPointerFrameworkParser;
41 import org.apache.cocoon.environment.SourceResolver;
42 import org.apache.cocoon.util.NetUtils;
43 import org.apache.cocoon.xml.AbstractXMLPipe;
44 import org.apache.cocoon.xml.IncludeXMLConsumer;
45 import org.apache.cocoon.xml.XMLBaseSupport;
46 import org.apache.cocoon.xml.XMLConsumer;
47 import org.apache.excalibur.source.Source;
48 import org.apache.excalibur.source.SourceException;
49 import org.apache.excalibur.source.SourceValidity;
50 import org.xml.sax.Attributes JavaDoc;
51 import org.xml.sax.ContentHandler JavaDoc;
52 import org.xml.sax.Locator JavaDoc;
53 import org.xml.sax.SAXException JavaDoc;
54 import org.xml.sax.ext.LexicalHandler JavaDoc;
55
56 /**
57  * @cocoon.sitemap.component.documentation
58  * Implementation of an XInclude transformer.
59  *
60  * @cocoon.sitemap.component.name xinclude
61  * @cocoon.sitemap.component.logger sitemap.transformer.xinclude
62  *
63  * @cocoon.sitemap.component.pooling.max 16
64  *
65  * Implementation of an XInclude transformer. It supports xml:base attributes,
66  * XPointer fragment identifiers (see the xpointer package to see what exactly is
67  * supported), fallback elements, and does xinclude processing on the included content
68  * and on the content of fallback elements (with loop inclusion detection).
69  *
70  * @author <a HREF="mailto:balld@webslingerZ.com">Donald Ball</a> (wrote the original version)
71  * @version SVN $Id: XIncludeTransformer.java 289887 2005-09-18 04:53:03Z antonio $
72  */

73 public class XIncludeTransformer extends AbstractTransformer implements Serviceable, CacheableProcessingComponent {
74     protected SourceResolver resolver;
75     protected ServiceManager manager;
76     private XIncludePipe xIncludePipe;
77
78     /**
79      * @deprecated Should be removed in cocoon 2.2. Use javax.xml.XMLConstants.XML_NS_URI instead.
80      */

81     public static final String JavaDoc XMLBASE_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace";
82     public static final String JavaDoc XMLBASE_ATTRIBUTE = "base";
83
84     public static final String JavaDoc XINCLUDE_NAMESPACE_URI = "http://www.w3.org/2001/XInclude";
85     public static final String JavaDoc XINCLUDE_INCLUDE_ELEMENT = "include";
86     public static final String JavaDoc XINCLUDE_FALLBACK_ELEMENT = "fallback";
87     public static final String JavaDoc XINCLUDE_INCLUDE_ELEMENT_HREF_ATTRIBUTE = "href";
88     public static final String JavaDoc XINCLUDE_INCLUDE_ELEMENT_PARSE_ATTRIBUTE = "parse";
89
90     private static final String JavaDoc XINCLUDE_CACHE_KEY = "XInclude";
91
92     /** The {@link SourceValidity} instance associated with this request. */
93     private MultiSourceValidity validity;
94
95     public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc source, Parameters parameters)
96             throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
97         this.resolver = resolver;
98         this.validity = new MultiSourceValidity(resolver, MultiSourceValidity.CHECK_ALWAYS);
99         this.xIncludePipe = new XIncludePipe();
100         this.xIncludePipe.enableLogging(getLogger());
101         this.xIncludePipe.init(null);
102         super.setContentHandler(xIncludePipe);
103         super.setLexicalHandler(xIncludePipe);
104     }
105
106     public void setConsumer(XMLConsumer consumer) {
107         xIncludePipe.setConsumer(consumer);
108     }
109
110     public void setContentHandler(ContentHandler JavaDoc handler) {
111         xIncludePipe.setContentHandler(handler);
112     }
113
114     public void setLexicalHandler(LexicalHandler JavaDoc handler) {
115         xIncludePipe.setLexicalHandler(handler);
116     }
117
118     public void service(ServiceManager manager) {
119         this.manager = manager;
120     }
121
122     /** Key to be used for caching */
123     public Serializable JavaDoc getKey() {
124         return XINCLUDE_CACHE_KEY;
125     }
126
127     /** Get the validity for this transform */
128     public SourceValidity getValidity() {
129         return this.validity;
130     }
131
132     public void recycle()
133     {
134         // Reset all variables to initial state.
135
this.resolver = null;
136         this.validity = null;
137         this.xIncludePipe = null;
138         super.recycle();
139     }
140
141     /**
142      * XMLPipe that processes XInclude elements. To perform XInclude processing on included content,
143      * this class is instantiated recursively.
144      */

145     private class XIncludePipe extends AbstractXMLPipe {
146         /** Helper class to keep track of xml:base attributes */
147         private XMLBaseSupport xmlBaseSupport;
148         /** Element nesting level when inside an xi:include element. */
149         private int xIncludeLevel = 0;
150         /** Should the content of the fallback element be inserted when it is encountered? */
151         private boolean useFallback = false;
152         /** Element nesting level when inside the fallback element. */
153         private int fallbackLevel;
154         /** In case {@link #useFallback} = true, then this should contain the exception that caused fallback to be needed. */
155         private Exception JavaDoc fallBackException;
156         /**
157          * Locator of the current stream, stored here so that it can be restored after
158          * another document send its content to the consumer.
159          */

160         private Locator JavaDoc locator;
161         /**
162          * Value of the href attribute of the xi:include element that caused the creation of the this
163          * XIncludePipe. Used to detect loop inclusions.
164          * */

165         private String JavaDoc href;
166         private XIncludePipe parent;
167
168         public void init(String JavaDoc uri) {
169             this.href = uri;
170             this.xmlBaseSupport = new XMLBaseSupport(resolver, getLogger());
171         }
172
173         public void setParent(XIncludePipe parent) {
174             this.parent = parent;
175         }
176
177         public XIncludePipe getParent() {
178             return parent;
179         }
180
181         public String JavaDoc getHref() {
182             return href;
183         }
184
185         public void endDocument() throws SAXException JavaDoc {
186             // We won't be getting any more sources so mark the MultiSourceValidity as finished.
187
validity.close();
188             super.endDocument();
189         }
190
191         public void startElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attr) throws SAXException JavaDoc {
192             if (xIncludeLevel == 1 && useFallback && XINCLUDE_NAMESPACE_URI.equals(uri) && XINCLUDE_FALLBACK_ELEMENT.equals(name)) {
193                 fallbackLevel++;
194
195                 // don't need these anymore
196
useFallback = false;
197                 fallBackException = null;
198
199                 return;
200             } else if (xIncludeLevel > 0 && fallbackLevel < 1) {
201                 xIncludeLevel++;
202                 return;
203             } else if (xIncludeLevel > 0 && fallbackLevel > 0) {
204                 fallbackLevel++;
205             }
206
207             xmlBaseSupport.startElement(uri, name, raw, attr);
208             if (XINCLUDE_NAMESPACE_URI.equals(uri)) {
209                 if (XINCLUDE_INCLUDE_ELEMENT.equals(name)) {
210                     String JavaDoc href = attr.getValue("",XINCLUDE_INCLUDE_ELEMENT_HREF_ATTRIBUTE);
211                     if (href == null) {
212                         throw new SAXException JavaDoc(raw + " must have a 'href' attribute at " + getLocation());
213                     }
214
215                     String JavaDoc parse = attr.getValue("",XINCLUDE_INCLUDE_ELEMENT_PARSE_ATTRIBUTE);
216
217                     if (null == parse) parse="xml";
218                     xIncludeLevel++;
219
220                     try {
221                         processXIncludeElement(href, parse);
222                     } catch (ProcessingException e) {
223                         getLogger().debug("Rethrowing exception", e);
224                         throw new SAXException JavaDoc(e);
225                     } catch (IOException JavaDoc e) {
226                         getLogger().debug("Rethrowing exception", e);
227                         throw new SAXException JavaDoc(e);
228                     }
229                     return;
230                 }
231
232                 throw new SAXException JavaDoc("Unknown XInclude element " + raw + " at " + getLocation());
233
234             } else {
235                 super.startElement(uri,name,raw,attr);
236             }
237         }
238
239         public void endElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw) throws SAXException JavaDoc {
240             if (xIncludeLevel > 0 && fallbackLevel < 1) {
241                 xIncludeLevel--;
242                 if (xIncludeLevel == 0)
243                     xmlBaseSupport.endElement(uri, name, raw);
244                 if (xIncludeLevel == 0 && useFallback) {
245                     // an error was encountered but a fallback element was not found: throw the error now
246
useFallback = false;
247                     Exception JavaDoc localFallBackException = fallBackException;
248                     fallBackException = null;
249                     fallbackLevel = 0;
250                     getLogger().error("Exception occured during xinclude processing, and did not find a fallback element.", localFallBackException);
251                     throw new SAXException JavaDoc("Exception occured during xinclude processing, and did not find a fallback element.", localFallBackException);
252                 }
253                 return;
254             }
255
256             if (fallbackLevel > 0) {
257                 fallbackLevel--;
258                 if (fallbackLevel == 0)
259                     return;
260             }
261
262             xmlBaseSupport.endElement(uri, name, raw);
263             super.endElement(uri,name,raw);
264         }
265
266         public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri)
267                 throws SAXException JavaDoc {
268             if (xIncludeLevel > 0 && fallbackLevel < 1)
269                 return;
270             super.startPrefixMapping(prefix, uri);
271         }
272
273         public void endPrefixMapping(String JavaDoc prefix)
274                 throws SAXException JavaDoc {
275             if (xIncludeLevel > 0 && fallbackLevel < 1)
276                 return;
277             super.endPrefixMapping(prefix);
278         }
279
280         public void characters(char c[], int start, int len)
281                 throws SAXException JavaDoc {
282             if (xIncludeLevel > 0 && fallbackLevel < 1)
283                 return;
284             super.characters(c, start, len);
285         }
286
287         public void ignorableWhitespace(char c[], int start, int len)
288                 throws SAXException JavaDoc {
289             if (xIncludeLevel > 0 && fallbackLevel < 1)
290                 return;
291             super.ignorableWhitespace(c, start, len);
292         }
293
294         public void processingInstruction(String JavaDoc target, String JavaDoc data)
295                 throws SAXException JavaDoc {
296             if (xIncludeLevel > 0 && fallbackLevel < 1)
297                 return;
298             super.processingInstruction(target, data);
299         }
300
301         public void skippedEntity(String JavaDoc name)
302                 throws SAXException JavaDoc {
303             if (xIncludeLevel > 0 && fallbackLevel < 1)
304                 return;
305             super.skippedEntity(name);
306         }
307
308         public void startEntity(String JavaDoc name)
309                 throws SAXException JavaDoc {
310             if (xIncludeLevel > 0 && fallbackLevel < 1)
311                 return;
312             super.startEntity(name);
313         }
314
315         public void endEntity(String JavaDoc name)
316                 throws SAXException JavaDoc {
317             if (xIncludeLevel > 0 && fallbackLevel < 1)
318                 return;
319             super.endEntity(name);
320         }
321
322         public void startCDATA()
323                 throws SAXException JavaDoc {
324             if (xIncludeLevel > 0 && fallbackLevel < 1)
325                 return;
326             super.startCDATA();
327         }
328
329         public void endCDATA()
330                 throws SAXException JavaDoc {
331             if (xIncludeLevel > 0 && fallbackLevel < 1)
332                 return;
333             super.endCDATA();
334         }
335
336         public void comment(char ch[], int start, int len)
337                 throws SAXException JavaDoc {
338             if (xIncludeLevel > 0 && fallbackLevel < 1)
339                 return;
340             super.comment(ch, start, len);
341         }
342
343         public void setDocumentLocator(Locator JavaDoc locator) {
344             try {
345                 if (getLogger().isDebugEnabled()) {
346                     getLogger().debug("setDocumentLocator called " + locator.getSystemId());
347                 }
348
349                 // When using SAXON to serialize a DOM tree to SAX, a locator is passed with a "null" system id
350
if (locator.getSystemId() != null) {
351                     Source source = resolver.resolveURI(locator.getSystemId());
352                     try {
353                         xmlBaseSupport.setDocumentLocation(source.getURI());
354                         // only for the "root" XIncludePipe, we'll have to set the href here, in the other cases
355
// the href is taken from the xi:include href attribute
356
if (href == null)
357                             href = source.getURI();
358                     } finally {
359                         resolver.release(source);
360                     }
361                 }
362             } catch (Exception JavaDoc e) {
363                 throw new CascadingRuntimeException("Error in XIncludeTransformer while trying to resolve base URL for document", e);
364             }
365             this.locator = locator;
366             super.setDocumentLocator(locator);
367         }
368
369         protected void processXIncludeElement(String JavaDoc href, String JavaDoc parse)
370         throws SAXException JavaDoc,ProcessingException,IOException JavaDoc {
371             if (getLogger().isDebugEnabled()) {
372                 getLogger().debug("Processing XInclude element: HREF="+href+", parse="+parse);
373             }
374
375             Source url = null;
376             String JavaDoc suffix = "";
377             try {
378                 int fragmentIdentifierPos = href.indexOf('#');
379                 if (fragmentIdentifierPos != -1) {
380                     suffix = href.substring(fragmentIdentifierPos + 1);
381                     href = href.substring(0, fragmentIdentifierPos);
382                 }
383
384                 // an empty href is a reference to the current document -- this can be different than the current base
385
if (href.equals("")) {
386                     if (this.href == null)
387                         throw new SAXException JavaDoc("XIncludeTransformer: encountered empty href (= href pointing to the current document) but the location of the current document is unknown.");
388                     int fragmentIdentifierPos2 = this.href.indexOf('#');
389                     if (fragmentIdentifierPos2 != -1)
390                         href = this.href.substring(0, fragmentIdentifierPos2);
391                     else
392                         href = this.href;
393                 }
394
395                 url = xmlBaseSupport.makeAbsolute(href);
396                 if (getLogger().isDebugEnabled()) {
397                     getLogger().debug("URL: " + url.getURI() + "\nSuffix: " + suffix);
398                 }
399
400                 // add the source to the SourceValidity
401
validity.addSource(url);
402  
403                 // check loop inclusion
404
String JavaDoc canonicURI = url.getURI() + (suffix.length() > 0 ? "#" + suffix: "");
405                 if (isLoopInclusion(canonicURI))
406                     throw new ProcessingException("Detected loop inclusion of " + canonicURI);
407
408                 if (parse.equals("text")) {
409                     getLogger().debug("Parse type is text");
410                     InputStream JavaDoc input = url.getInputStream();
411                     Reader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(input));
412                     int read;
413                     char ary[] = new char[1024];
414                     if (reader != null) {
415                         while ((read = reader.read(ary)) != -1) {
416                             super.characters(ary,0,read);
417                         }
418                         reader.close();
419                     }
420                 } else if (parse.equals("xml")) {
421                     XIncludePipe subPipe = new XIncludePipe();
422                     subPipe.enableLogging(getLogger());
423                     subPipe.init(canonicURI);
424                     subPipe.setConsumer(xmlConsumer);
425                     subPipe.setParent(this);
426
427                     getLogger().debug("Parse type is XML");
428                     try {
429                         if (suffix.length() > 0) {
430                             XPointer xpointer;
431                             xpointer = XPointerFrameworkParser.parse(NetUtils.decodePath(suffix));
432                             XPointerContext context = new XPointerContext(suffix, url, subPipe, getLogger(), manager);
433                             xpointer.process(context);
434                         } else {
435                             SourceUtil.toSAX(url, new IncludeXMLConsumer(subPipe));
436                         }
437                         // restore locator on the consumer
438
if (locator != null)
439                             xmlConsumer.setDocumentLocator(locator);
440                     } catch (ResourceNotFoundException e) {
441                         useFallback = true;
442                         fallBackException = new CascadingException("Resource not found: " + url.getURI());
443                         getLogger().error("xIncluded resource not found: " + url.getURI(), e);
444                     } catch (ParseException e) {
445                         // this exception is thrown in case of an invalid xpointer expression
446
useFallback = true;
447                         fallBackException = new CascadingException("Error parsing xPointer expression", e);
448                         fallBackException.fillInStackTrace();
449                         getLogger().error("Error parsing XPointer expression, will try to use fallback.", e);
450                     } catch(SAXException JavaDoc e) {
451                         getLogger().error("Error in processXIncludeElement", e);
452                         throw e;
453                     } catch(ProcessingException e) {
454                         getLogger().error("Error in processXIncludeElement", e);
455                         throw e;
456                     } catch(MalformedURLException JavaDoc e) {
457                         useFallback = true;
458                         fallBackException = e;
459                         getLogger().error("Error processing an xInclude, will try to use fallback.", e);
460                     } catch(IOException JavaDoc e) {
461                         useFallback = true;
462                         fallBackException = e;
463                         getLogger().error("Error processing an xInclude, will try to use fallback.", e);
464                     }
465                 }
466             } catch (SourceException se) {
467                 throw SourceUtil.handle(se);
468             } finally {
469                 if (url != null) {
470                     resolver.release(url);
471                 }
472             }
473         }
474
475         public boolean isLoopInclusion(String JavaDoc uri) {
476             if (uri.equals(this.href)) {
477                 return true;
478             }
479
480             XIncludePipe parent = getParent();
481             while (parent != null) {
482                 if (uri.equals(parent.getHref())) {
483                     return true;
484                 }
485                 parent = parent.getParent();
486             }
487             return false;
488         }
489
490         private String JavaDoc getLocation() {
491             if (this.locator == null) {
492                 return "unknown location";
493             } else {
494                 return this.locator.getSystemId() + ":" + this.locator.getColumnNumber() + ":" + this.locator.getLineNumber();
495             }
496         }
497     }
498 }
499
Popular Tags