KickJava   Java API By Example, From Geeks To Geeks.

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


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 org.apache.avalon.excalibur.pool.Recyclable;
19 import org.apache.avalon.framework.activity.Disposable;
20 import org.apache.avalon.framework.configuration.Configurable;
21 import org.apache.avalon.framework.configuration.Configuration;
22 import org.apache.avalon.framework.configuration.ConfigurationException;
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.avalon.framework.service.Serviceable;
27
28 import org.apache.cocoon.ProcessingException;
29 import org.apache.cocoon.environment.Context;
30 import org.apache.cocoon.environment.ObjectModelHelper;
31 import org.apache.cocoon.environment.Request;
32 import org.apache.cocoon.environment.Response;
33 import org.apache.cocoon.environment.SourceResolver;
34 import org.apache.cocoon.transformation.helpers.ParametersRecorder;
35 import org.apache.cocoon.transformation.helpers.TextRecorder;
36 import org.apache.cocoon.util.ClassUtils;
37 import org.apache.cocoon.util.TraxErrorHandler;
38 import org.apache.cocoon.xml.AttributesImpl;
39 import org.apache.cocoon.xml.ImmutableAttributesImpl;
40 import org.apache.cocoon.xml.IncludeXMLConsumer;
41 import org.apache.cocoon.xml.SaxBuffer;
42 import org.apache.cocoon.xml.XMLConsumer;
43 import org.apache.cocoon.xml.XMLUtils;
44 import org.apache.cocoon.xml.dom.DOMBuilder;
45
46 import org.apache.excalibur.source.SourceParameters;
47 import org.apache.excalibur.xml.sax.XMLizable;
48 import org.w3c.dom.Document JavaDoc;
49 import org.w3c.dom.DocumentFragment JavaDoc;
50 import org.w3c.dom.Node JavaDoc;
51 import org.xml.sax.Attributes JavaDoc;
52 import org.xml.sax.ContentHandler JavaDoc;
53 import org.xml.sax.Locator JavaDoc;
54 import org.xml.sax.SAXException JavaDoc;
55 import org.xml.sax.ext.LexicalHandler JavaDoc;
56
57 import javax.xml.transform.TransformerFactory JavaDoc;
58 import javax.xml.transform.sax.SAXTransformerFactory JavaDoc;
59 import java.io.IOException JavaDoc;
60 import java.util.ArrayList JavaDoc;
61 import java.util.Iterator JavaDoc;
62 import java.util.List JavaDoc;
63 import java.util.Map JavaDoc;
64 import java.util.Properties JavaDoc;
65 import java.util.Stack JavaDoc;
66
67 /**
68  * This class is the basis for all transformers. It provides various useful
69  * methods and hooks for implementing own custom transformers.
70  *
71  * <p>The basic behaviour of each transformer consists of the following four
72  * parts:</p>
73  * <ul>
74  * <li>Listen for specific events with a given namespace</li>
75  * <li>Collect information via these events</li>
76  * <li>Process the information</li>
77  * <li>Create new events from the processed information</li>
78  * </ul>
79  *
80  * <p>For all these four purposes the AbstractSAXTransformer offers some
81  * powerful methods and hooks:</p>
82  *
83  * <h3>Namespace handling</h3>
84  * By setting the instance variable namespaceURI to the namespace the
85  * events are filtered and only events with this namespace are send to
86  * the two hooks: <code>startTransformingElement</code> and
87  * <code>endTransformingElement</code>. It is possible to override the default
88  * namespace for the transformer by specifying the parameter "namespaceURI"
89  * in the pipeline. This avoids possible namespace collisions.
90  *
91  * <h3>Recording of information</h3>
92  * There are several methods for recording information, e.g. startRecording(),
93  * startTextRecording() etc. These methods collect information from the xml
94  * stream for further processing.
95  *
96  * <h3>Creating new events</h3>
97  * New events can be easily created with the <code>sendEvents()</code>
98  * method, the <code>sendStartElementEvent()</code> methods, the
99  * <code>sendEndElementEvent()</code> method or the
100  * <code>sendTextEvent()</code> method.
101  *
102  * <h3>Initialization</h3>
103  * Before the document is processed the <code>setupTransforming</code> hook
104  * is invoked.
105  *
106  * @author <a HREF="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
107  * @version $Id: AbstractSAXTransformer.java 292148 2005-09-28 08:49:26Z cziegeler $
108 */

109 public abstract class AbstractSAXTransformer extends AbstractTransformer
110                                              implements Serviceable, Configurable, Recyclable, Disposable {
111
112     /**
113      * Empty immutable attributes (for performance). Use them
114      * whenever creating an element with no attributes.
115      */

116     protected static final Attributes JavaDoc EMPTY_ATTRIBUTES = XMLUtils.EMPTY_ATTRIBUTES;
117
118     /**
119      * The trax <code>TransformerFactory</code> used by this transformer.
120      */

121     private SAXTransformerFactory JavaDoc tfactory;
122
123     /**
124      * Controlls SAX event handling.
125      * If set to true all whitespace events are ignored.
126      */

127     protected boolean ignoreWhitespaces;
128
129     /**
130      * Controlls SAX event handling.
131      * If set to true all characters events containing only whitespaces
132      * are ignored.
133      */

134     protected boolean ignoreEmptyCharacters;
135
136     /**
137      * Controlls SAX event handling.
138      * If this is incremented all events are not forwarded to the next
139      * pipeline component, but the hooks are still called.
140      */

141     protected int ignoreEventsCount;
142
143     /**
144      * Controlls SAX event handling.
145      * If this is greater than zero, the hooks are not called. Attention,
146      * make sure, that you decrement this counter properly as your hooks are
147      * not called anymore!
148      */

149     protected int ignoreHooksCount;
150
151     /**
152      * The namespace used by the transformer for the SAX events filtering.
153      * This either equals to the {@link #defaultNamespaceURI} or to the value
154      * set by the <code>namespaceURI</code> sitemap parameter for the pipeline.
155      * Must never be null.
156      */

157     protected String JavaDoc namespaceURI;
158
159     /**
160      * This is the default namespace used by the transformer.
161      * Implementations should set its value in the constructor.
162      * Must never be null.
163      */

164     protected String JavaDoc defaultNamespaceURI;
165
166     /**
167      * A stack for collecting information.
168      * The stack is important for collection information especially when
169      * the tags can be nested.
170      */

171     protected final Stack JavaDoc stack = new Stack JavaDoc();
172
173     /**
174      * The stack of current used recorders
175      */

176     protected final Stack JavaDoc recorderStack = new Stack JavaDoc();
177
178     /**
179      * The current Request object
180      */

181     protected Request request;
182
183     /**
184      * The current Response object
185      */

186     protected Response response;
187
188     /**
189      * The current Context object
190      */

191     protected Context context;
192
193     /**
194      * The current objectModel of the environment
195      */

196     protected Map JavaDoc objectModel;
197
198     /**
199      * The parameters specified in the sitemap
200      */

201     protected Parameters parameters;
202
203     /**
204      * The source attribute specified in the sitemap
205      */

206     protected String JavaDoc source;
207
208     /**
209      * The Avalon ServiceManager for getting Components
210      */

211     protected ServiceManager manager;
212
213     /**
214      * The SourceResolver for this request
215      */

216     protected SourceResolver resolver;
217
218     /**
219      * Are we already initialized for the current request?
220      */

221     private boolean isInitialized;
222
223     /**
224      * Empty attributes (for performance). This can be used
225      * do create own attributes, but make sure to clean them
226      * afterwords.
227      * @deprecated Use {@link AbstractSAXTransformer#EMPTY_ATTRIBUTES}.
228      */

229     protected Attributes JavaDoc emptyAttributes = EMPTY_ATTRIBUTES;
230
231     /**
232      * The namespaces and their prefixes
233      */

234     private final List JavaDoc namespaces = new ArrayList JavaDoc(5);
235
236     /**
237      * The current prefix for our namespace
238      */

239     private String JavaDoc ourPrefix;
240
241     //
242
// Lifecycle
243
//
244

245     /* (non-Javadoc)
246      * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
247      */

248     public void service(ServiceManager manager) throws ServiceException {
249         this.manager = manager;
250     }
251
252     /* (non-Javadoc)
253      * @see Configurable#configure(Configuration)
254      */

255     public void configure(Configuration configuration) throws ConfigurationException {
256         String JavaDoc tFactoryClass = configuration.getChild("transformer-factory").getValue(null);
257         if (tFactoryClass != null) {
258             try {
259                 this.tfactory = (SAXTransformerFactory JavaDoc) ClassUtils.newInstance(tFactoryClass);
260                 if (getLogger().isDebugEnabled()) {
261                     getLogger().debug("Using transformer factory " + tFactoryClass);
262                 }
263             } catch (Exception JavaDoc e) {
264                 throw new ConfigurationException("Cannot load transformer factory " + tFactoryClass, e);
265             }
266         } else {
267             // Standard TrAX behaviour
268
this.tfactory = (SAXTransformerFactory JavaDoc) TransformerFactory.newInstance();
269         }
270         tfactory.setErrorListener(new TraxErrorHandler(getLogger()));
271     }
272
273     /* (non-Javadoc)
274      * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(SourceResolver, Map, String, Parameters)
275      */

276     public void setup(SourceResolver resolver,
277                       Map JavaDoc objectModel,
278                       String JavaDoc src,
279                       Parameters params)
280     throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
281
282         if (getLogger().isDebugEnabled()) {
283             getLogger().debug("Setup resolver=" + resolver +
284                               ", objectModel=" + objectModel +
285                               ", SRC=" + src +
286                               ", parameters=" + params);
287         }
288
289         // defaultNamespaceURI should never be null
290
if (this.defaultNamespaceURI == null) {
291             this.defaultNamespaceURI = "";
292         }
293         this.objectModel = objectModel;
294
295         this.request = ObjectModelHelper.getRequest(objectModel);
296         this.response = ObjectModelHelper.getResponse(objectModel);
297         this.context = ObjectModelHelper.getContext(objectModel);
298         this.resolver = resolver;
299         this.parameters = params;
300         this.source = src;
301         this.isInitialized = false;
302
303         // get the current namespace
304
this.namespaceURI = params.getParameter("namespaceURI",
305                                                 this.defaultNamespaceURI);
306
307         this.ignoreHooksCount = 0;
308         this.ignoreEventsCount = 0;
309         this.ignoreWhitespaces = true;
310         this.ignoreEmptyCharacters = false;
311     }
312
313     /* (non-Javadoc)
314      * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
315      */

316     public void recycle() {
317         this.namespaceURI = null;
318         this.objectModel = null;
319         this.request = null;
320         this.response = null;
321         this.context = null;
322         this.resolver = null;
323         this.stack.clear();
324         this.recorderStack.clear();
325         this.parameters = null;
326         this.source = null;
327         this.namespaces.clear();
328         this.ourPrefix = null;
329
330         super.recycle();
331     }
332
333     public void dispose() {
334         this.manager = null;
335     }
336
337     //
338
// SAX ContentHandler methods
339
//
340

341     /**
342      * Process the SAX event.
343      * @see ContentHandler#setDocumentLocator
344      */

345     public void setDocumentLocator(Locator JavaDoc locator) {
346         if (this.ignoreEventsCount == 0) {
347             super.setDocumentLocator(locator);
348         }
349     }
350
351     /**
352      * Process the SAX event. A new document is processed. The hook method
353      * {@link #setupTransforming} is invoked.
354      * @see ContentHandler#startDocument
355      */

356     public void startDocument()
357     throws SAXException JavaDoc {
358         if (!this.isInitialized) {
359             try {
360                 setupTransforming();
361             } catch (ProcessingException e) {
362                 throw new SAXException JavaDoc("ProcessingException: " + e, e);
363             } catch (IOException JavaDoc e) {
364                 throw new SAXException JavaDoc("IOException: " + e, e);
365             }
366             this.isInitialized = true;
367         }
368
369         if (this.ignoreEventsCount == 0) {
370             super.startDocument();
371         }
372     }
373
374     /**
375      * Process the SAX event. The processing of the document is finished.
376      * @see org.xml.sax.ContentHandler#endDocument
377      */

378     public void endDocument()
379     throws SAXException JavaDoc {
380         if (this.ignoreEventsCount == 0) {
381             super.endDocument();
382         }
383     }
384
385     /**
386      * Process the SAX event.
387      * @see org.xml.sax.ContentHandler#startPrefixMapping
388      */

389     public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri)
390     throws SAXException JavaDoc {
391         if (prefix != null) {
392             this.namespaces.add(new String JavaDoc[] {prefix, uri});
393         }
394         if (namespaceURI.equals(uri)) {
395             this.ourPrefix = prefix;
396         }
397         if (this.ignoreEventsCount == 0) {
398             super.startPrefixMapping(prefix, uri);
399         }
400     }
401
402     /**
403      * Process the SAX event.
404      * @see org.xml.sax.ContentHandler#endPrefixMapping
405      */

406     public void endPrefixMapping(String JavaDoc prefix)
407     throws SAXException JavaDoc {
408
409         if (prefix != null) {
410             // Find and remove the namespace prefix
411
boolean found = false;
412             for (int i = this.namespaces.size() - 1; i >= 0; i--) {
413                 final String JavaDoc[] prefixAndUri = (String JavaDoc[]) this.namespaces.get(i);
414                 if (prefixAndUri[0].equals(prefix)) {
415                     this.namespaces.remove(i);
416                     found = true;
417                     break;
418                 }
419             }
420             if (!found) {
421                 throw new SAXException JavaDoc("Namespace for prefix '" + prefix + "' not found.");
422             }
423
424             if (prefix.equals(this.ourPrefix)) {
425                 // Reset our current prefix
426
this.ourPrefix = null;
427
428                 // Now search if we have a different prefix for our namespace
429
for (int i = this.namespaces.size() - 1; i >= 0; i--) {
430                     final String JavaDoc[] prefixAndUri = (String JavaDoc[]) this.namespaces.get(i);
431                     if (namespaceURI.equals(prefixAndUri[1])) {
432                         this.ourPrefix = prefixAndUri[0];
433                         break;
434                     }
435                 }
436             }
437         }
438
439         if (this.ignoreEventsCount == 0) {
440             super.endPrefixMapping(prefix);
441         }
442     }
443
444     /**
445      * Process the SAX event. The namespace of the event is checked.
446      * If it is the defined namespace for this transformer,
447      * the {@link #startTransformingElement} hook is called.
448      * @see org.xml.sax.ContentHandler#startElement
449      */

450     public void startElement(String JavaDoc uri,
451                              String JavaDoc name,
452                              String JavaDoc raw,
453                              Attributes JavaDoc attr)
454     throws SAXException JavaDoc {
455         if (namespaceURI.equals(uri) && ignoreHooksCount == 0) {
456             // this is our namespace:
457
try {
458                 startTransformingElement(uri, name, raw, attr);
459             } catch (ProcessingException e) {
460                 throw new SAXException JavaDoc("ProcessingException: " + e, e);
461             } catch (IOException JavaDoc e) {
462                 throw new SAXException JavaDoc("IOException occured during processing: " + e, e);
463             }
464         } else {
465             if (ignoreEventsCount == 0) {
466                 super.startElement(uri, name, raw, attr);
467             }
468         }
469     }
470
471     /**
472      * Process the SAX event. The namespace of the event is checked.
473      * If it is the defined namespace for this transformer,
474      * the {@link #endTransformingElement} hook is called.
475      * @see org.xml.sax.ContentHandler#endElement
476      */

477     public void endElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw)
478     throws SAXException JavaDoc {
479         if (namespaceURI.equals(uri) && this.ignoreHooksCount == 0) {
480             // this is our namespace:
481
try {
482                 endTransformingElement(uri, name, raw);
483             } catch (ProcessingException e) {
484                 throw new SAXException JavaDoc("ProcessingException: " + e, e);
485             } catch (IOException JavaDoc e) {
486                 throw new SAXException JavaDoc("IOException occured during processing: " + e, e);
487             }
488         } else {
489             if (ignoreEventsCount == 0) {
490                 super.endElement(uri, name, raw);
491             }
492         }
493     }
494
495     /**
496      * Process the SAX event.
497      * @see org.xml.sax.ContentHandler#characters
498      */

499     public void characters(char[] p0, int p1, int p2)
500     throws SAXException JavaDoc {
501         if (this.ignoreEventsCount == 0) {
502             if (this.ignoreEmptyCharacters) {
503                 String JavaDoc value = new String JavaDoc(p0, p1, p2);
504                 if (value.trim().length() > 0) {
505                     super.characters(p0, p1, p2);
506                 }
507             } else {
508                 super.characters(p0, p1, p2);
509             }
510         }
511     }
512
513     /**
514      * Process the SAX event.
515      * @see org.xml.sax.ContentHandler#ignorableWhitespace
516      */

517     public void ignorableWhitespace(char[] p0, int p1, int p2)
518     throws SAXException JavaDoc {
519         if (ignoreWhitespaces == false && ignoreEventsCount == 0) {
520             super.ignorableWhitespace(p0, p1, p2);
521         }
522     }
523
524     /**
525      * Process the SAX event.
526      * @see ContentHandler#processingInstruction
527      */

528     public void processingInstruction(String JavaDoc target, String JavaDoc data)
529     throws SAXException JavaDoc {
530         if (this.ignoreEventsCount == 0) {
531             super.processingInstruction(target, data);
532         }
533     }
534
535     /**
536      * Process the SAX event.
537      * @see ContentHandler#skippedEntity
538      */

539     public void skippedEntity(String JavaDoc name)
540     throws SAXException JavaDoc {
541         if (this.ignoreEventsCount == 0) {
542             super.skippedEntity(name);
543         }
544     }
545
546     //
547
// SAX LexicalHandler methods
548
//
549

550     /**
551      * @see LexicalHandler#startDTD
552      */

553     public void startDTD(String JavaDoc name, String JavaDoc public_id, String JavaDoc system_id)
554     throws SAXException JavaDoc {
555         if (this.ignoreEventsCount == 0) {
556             super.startDTD(name, public_id, system_id);
557         }
558     }
559
560     /**
561      * @see LexicalHandler#endDTD
562      */

563     public void endDTD() throws SAXException JavaDoc {
564         if (this.ignoreEventsCount == 0) {
565             super.endDTD();
566         }
567     }
568
569     /**
570      * @see LexicalHandler#startEntity
571      */

572     public void startEntity (String JavaDoc name)
573     throws SAXException JavaDoc {
574         if (this.ignoreEventsCount == 0) {
575             super.startEntity(name);
576         }
577     }
578
579     /**
580      * @see LexicalHandler#endEntity
581      */

582     public void endEntity (String JavaDoc name)
583     throws SAXException JavaDoc {
584         if (this.ignoreEventsCount == 0) {
585             super.endEntity(name);
586         }
587     }
588
589     /**
590      * @see LexicalHandler#startCDATA
591      */

592     public void startCDATA() throws SAXException JavaDoc {
593         if (this.ignoreEventsCount == 0) {
594             super.startCDATA();
595         }
596     }
597
598     /**
599      * @see LexicalHandler#endCDATA
600      */

601     public void endCDATA() throws SAXException JavaDoc {
602         if (this.ignoreEventsCount == 0) {
603             super.endCDATA();
604         }
605     }
606
607     /**
608      * @see LexicalHandler#comment
609      */

610     public void comment(char ary[], int start, int length)
611     throws SAXException JavaDoc {
612         if (this.ignoreEventsCount == 0) {
613             super.comment(ary, start, length);
614         }
615     }
616
617
618     /*
619      * Recording of events.
620      * With this method all events are not forwarded to the next component in the pipeline.
621      * They are recorded to create a document fragment.
622      */

623
624     private LexicalHandler JavaDoc originalLexicalHandler;
625     private ContentHandler JavaDoc originalContentHandler;
626
627     /**
628      * Add a new recorder to the recording chain.
629      * Do not invoke this method directly.
630      */

631     protected void addRecorder(XMLConsumer recorder) {
632         if (this.recorderStack.empty()) {
633             // redirect if first (top) recorder
634
this.originalLexicalHandler = this.lexicalHandler;
635             this.originalContentHandler = this.contentHandler;
636         }
637         setContentHandler(recorder);
638         setLexicalHandler(recorder);
639         this.recorderStack.push(recorder);
640     }
641
642     /**
643      * Remove a recorder from the recording chain.
644      * Do not invoke this method directly.
645      */

646     protected Object JavaDoc removeRecorder() {
647         Object JavaDoc recorder = this.recorderStack.pop();
648         if (this.recorderStack.empty() == true) {
649             // undo redirect if no recorder any more
650
setContentHandler(originalContentHandler);
651             setLexicalHandler(originalLexicalHandler);
652             this.originalLexicalHandler = null;
653             this.originalContentHandler = null;
654         } else {
655             XMLConsumer next = (XMLConsumer) recorderStack.peek();
656             setContentHandler(next);
657             setLexicalHandler(next);
658         }
659
660         return recorder;
661     }
662
663     /**
664      * Start recording of SAX events.
665      * All incoming events are recorded and not forwarded. The resulting
666      * XMLizable can be obtained by the matching {@link #endSAXRecording} call.
667      * @since 2.1.5
668      */

669     public void startSAXRecording()
670     throws SAXException JavaDoc {
671         addRecorder(new SaxBuffer());
672         sendStartPrefixMapping();
673     }
674
675     /**
676      * Stop recording of SAX events.
677      * This method returns the resulting XMLizable.
678      * @since 2.1.5
679      */

680     public XMLizable endSAXRecording()
681     throws SAXException JavaDoc {
682         sendEndPrefixMapping();
683         return (XMLizable) removeRecorder();
684     }
685
686     /**
687      * Start recording of a text.
688      * No events forwarded, and all characters events
689      * are collected into a string.
690      */

691     public void startTextRecording()
692     throws SAXException JavaDoc {
693         if (getLogger().isDebugEnabled()) {
694             getLogger().debug("Start text recording");
695         }
696         addRecorder(new TextRecorder());
697         sendStartPrefixMapping();
698     }
699
700     /**
701      * Stop recording of text and return the recorded information.
702      * @return The String, trimmed.
703      */

704     public String JavaDoc endTextRecording()
705     throws SAXException JavaDoc {
706         sendEndPrefixMapping();
707
708         TextRecorder recorder = (TextRecorder) removeRecorder();
709         String JavaDoc text = recorder.getText();
710         if (getLogger().isDebugEnabled()) {
711             getLogger().debug("End text recording. Text=" + text);
712         }
713         return text;
714     }
715
716     /**
717      * Start recording of serialized xml
718      * All events are converted to an xml string which can be retrieved by
719      * endSerializedXMLRecording.
720      * @param format The format for the serialized output. If <CODE>null</CODE>
721      * is specified, the default format is used.
722      */

723     public void startSerializedXMLRecording(Properties JavaDoc format)
724     throws SAXException JavaDoc {
725         if (getLogger().isDebugEnabled()) {
726             getLogger().debug("Start serialized XML recording. Format=" + format);
727         }
728         this.stack.push(format == null? XMLUtils.createPropertiesForXML(false): format);
729         startSAXRecording();
730     }
731
732     /**
733      * Return the serialized xml string.
734      * @return A string containing the recorded xml information, formatted by
735      * the properties passed to the corresponding startSerializedXMLRecording().
736      */

737     public String JavaDoc endSerializedXMLRecording()
738     throws SAXException JavaDoc, ProcessingException {
739         XMLizable xml = endSAXRecording();
740         String JavaDoc text = XMLUtils.serialize(xml, (Properties JavaDoc) this.stack.pop());
741         if (getLogger().isDebugEnabled()) {
742             getLogger().debug("End serialized XML recording. XML=" + text);
743         }
744         return text;
745     }
746
747     /**
748      * Start recording of parameters.
749      * All events are not forwarded and the incoming xml is converted to
750      * parameters. Each toplevel node is a parameter and its text subnodes
751      * form the value.
752      * The Parameters can eiter be retrieved by endParametersRecording().
753      */

754     public void startParametersRecording()
755     throws SAXException JavaDoc {
756         if (getLogger().isDebugEnabled()) {
757             getLogger().debug("Start parameters recording");
758         }
759         addRecorder(new ParametersRecorder());
760         sendStartPrefixMapping();
761     }
762
763     /**
764      * End recording of parameters
765      * If source is null a new parameters object is created, otherwise
766      * the parameters are added to this object.
767      * @param source An optional parameters object.
768      * @return The object containing all parameters.
769      */

770     public SourceParameters endParametersRecording(Parameters source)
771     throws SAXException JavaDoc {
772         sendEndPrefixMapping();
773
774         ParametersRecorder recorder = (ParametersRecorder) this.removeRecorder();
775         SourceParameters parameters = recorder.getParameters(source);
776         if (getLogger().isDebugEnabled()) {
777             getLogger().debug("End parameters recording. Parameters=" + parameters);
778         }
779         return parameters;
780     }
781
782     /**
783      * End recording of parameters
784      * If source is null a new parameters object is created, otherwise
785      * the parameters are added to this object.
786      * @param source An optional parameters object.
787      * @return The object containing all parameters.
788      */

789     public SourceParameters endParametersRecording(SourceParameters source)
790     throws SAXException JavaDoc {
791         sendEndPrefixMapping();
792
793         ParametersRecorder recorder = (ParametersRecorder) removeRecorder();
794         SourceParameters parameters = recorder.getParameters(source);
795         if (getLogger().isDebugEnabled()) {
796             getLogger().debug("End parameters recording. Parameters=" + parameters);
797         }
798         return parameters;
799     }
800
801     /**
802      * Start DOM DocumentFragment recording.
803      * All incoming events are recorded and not forwarded. The resulting
804      * DocumentFragment can be obtained by the matching {@link #endRecording} call.
805      */

806     public void startRecording()
807     throws SAXException JavaDoc {
808         if (getLogger().isDebugEnabled()) {
809             getLogger().debug("Start recording");
810         }
811         DOMBuilder builder = new DOMBuilder(this.tfactory);
812         addRecorder(builder);
813         builder.startDocument();
814         builder.startElement("", "cocoon", "cocoon", EMPTY_ATTRIBUTES);
815         sendStartPrefixMapping();
816     }
817
818     /**
819      * Stop DOM DocumentFragment recording.
820      * This method returns the resulting DocumentFragment, normalized.
821      */

822     public DocumentFragment JavaDoc endRecording()
823     throws SAXException JavaDoc {
824         sendEndPrefixMapping();
825
826         DOMBuilder builder = (DOMBuilder) removeRecorder();
827         builder.endElement("", "cocoon", "cocoon");
828         builder.endDocument();
829
830         // Create Document Fragment
831
final Document JavaDoc doc = builder.getDocument();
832         final DocumentFragment JavaDoc fragment = doc.createDocumentFragment();
833         final Node JavaDoc root = doc.getDocumentElement();
834
835         // Remove empty text nodes and collapse neighbouring text nodes
836
root.normalize();
837
838         // Move all nodes into the fragment
839
boolean space = true;
840         while (root.hasChildNodes()) {
841             Node JavaDoc child = root.getFirstChild();
842             root.removeChild(child);
843
844             // Leave out leading whitespace nodes
845
// FIXME: Why leading spaces are trimmed at all? Why not trailing spaces?
846
if (space && child.getNodeType() == Node.TEXT_NODE
847                     && child.getNodeValue().trim().length() == 0) {
848                 continue;
849             }
850             space = false;
851
852             fragment.appendChild(child);
853         }
854
855         if (getLogger().isDebugEnabled()) {
856             Object JavaDoc serializedXML = null;
857             try {
858                 serializedXML = fragment == null? "null": XMLUtils.serializeNode(fragment, XMLUtils.createPropertiesForXML(false));
859             } catch (ProcessingException ignore) {
860                 serializedXML = fragment;
861             }
862             getLogger().debug("End recording. Fragment=" + serializedXML);
863         }
864
865         return fragment;
866     }
867
868     //
869
// Hooks
870
//
871

872     /**
873      * Setup the transformation of an xml document.
874      * This method is called just before the transformation (sending of sax events)
875      * starts. It should be used to initialize setup parameter depending on the
876      * object modell.
877      */

878     public void setupTransforming()
879     throws IOException JavaDoc, ProcessingException, SAXException JavaDoc {
880         if (getLogger().isDebugEnabled()) {
881             getLogger().debug("setupTransforming");
882         }
883         this.stack.clear();
884         this.recorderStack.clear();
885         this.ignoreWhitespaces = true;
886         this.ignoreEmptyCharacters = false;
887     }
888
889     /**
890      * Start processing elements of our namespace.
891      * This hook is invoked for each sax event with our namespace.
892      * @param uri The namespace of the element.
893      * @param name The local name of the element.
894      * @param raw The qualified name of the element.
895      * @param attr The attributes of the element.
896      */

897     public void startTransformingElement(String JavaDoc uri,
898                                          String JavaDoc name,
899                                          String JavaDoc raw,
900                                          Attributes JavaDoc attr)
901     throws ProcessingException, IOException JavaDoc, SAXException JavaDoc {
902         if (this.ignoreEventsCount == 0) {
903             super.startElement(uri, name, raw, attr);
904         }
905     }
906
907     /**
908      * Start processing elements of our namespace.
909      * This hook is invoked for each sax event with our namespace.
910      * @param uri The namespace of the element.
911      * @param name The local name of the element.
912      * @param raw The qualified name of the element.
913      */

914     public void endTransformingElement(String JavaDoc uri,
915                                        String JavaDoc name,
916                                        String JavaDoc raw)
917     throws ProcessingException, IOException JavaDoc, SAXException JavaDoc {
918         if (this.ignoreEventsCount == 0) {
919             super.endElement(uri, name, raw);
920         }
921     }
922
923     /**
924      * Send SAX events to the next pipeline component.
925      * The characters event for the given text is send to the next
926      * component in the current pipeline.
927      * @param text The string containing the information.
928      */

929     public void sendTextEvent(String JavaDoc text)
930     throws SAXException JavaDoc {
931         characters(text.toCharArray(), 0, text.length());
932     }
933
934     /**
935      * Send SAX events to the next pipeline component.
936      * The startElement event for the given element is send
937      * to the next component in the current pipeline.
938      * The element has no namespace and no attributes
939      * @param localname The name of the event.
940      */

941     public void sendStartElementEvent(String JavaDoc localname)
942     throws SAXException JavaDoc {
943         startElement("", localname, localname, EMPTY_ATTRIBUTES);
944     }
945
946     /**
947      * Send SAX events to the next pipeline component.
948      * The startElement event for the given element is send
949      * to the next component in the current pipeline.
950      * The element has the namespace of the transformer,
951      * but not attributes
952      * @param localname The name of the event.
953      */

954     public void sendStartElementEventNS(String JavaDoc localname)
955     throws SAXException JavaDoc {
956         startElement(this.namespaceURI,
957                      localname, this.ourPrefix + ':' + localname, EMPTY_ATTRIBUTES);
958     }
959
960     /**
961      * Send SAX events to the next pipeline component.
962      * The startElement event for the given element is send
963      * to the next component in the current pipeline.
964      * The element has no namespace.
965      * @param localname The name of the event.
966      * @param attr The Attributes of the element
967      */

968     public void sendStartElementEvent(String JavaDoc localname, Attributes JavaDoc attr)
969     throws SAXException JavaDoc {
970         startElement("", localname, localname, attr);
971     }
972
973     /**
974      * Send SAX events to the next pipeline component.
975      * The startElement event for the given element is send
976      * to the next component in the current pipeline.
977      * The element has the namespace of the transformer.
978      * @param localname The name of the event.
979      * @param attr The Attributes of the element
980      */

981     public void sendStartElementEventNS(String JavaDoc localname, Attributes JavaDoc attr)
982     throws SAXException JavaDoc {
983         startElement(this.namespaceURI,
984                      localname, this.ourPrefix + ':' + localname, attr);
985     }
986
987     /**
988      * Send SAX events to the next pipeline component.
989      * The endElement event for the given element is send
990      * to the next component in the current pipeline.
991      * The element has no namespace.
992      * @param localname The name of the event.
993      */

994     public void sendEndElementEvent(String JavaDoc localname)
995     throws SAXException JavaDoc {
996         endElement("", localname, localname);
997     }
998
999     /**
1000     * Send SAX events to the next pipeline component.
1001     * The endElement event for the given element is send
1002     * to the next component in the current pipeline.
1003     * The element has the namespace of the transformer.
1004     * @param localname The name of the event.
1005     */

1006    public void sendEndElementEventNS(String JavaDoc localname)
1007    throws SAXException JavaDoc {
1008        endElement(this.namespaceURI,
1009                   localname, this.ourPrefix + ':' + localname);
1010    }
1011
1012    /**
1013     * Send SAX events to the next pipeline component.
1014     * The node is parsed and the events are send to
1015     * the next component in the pipeline.
1016     * @param node The tree to be included.
1017     */

1018    public void sendEvents(Node JavaDoc node)
1019    throws SAXException JavaDoc {
1020        IncludeXMLConsumer.includeNode(node, this, this);
1021    }
1022
1023    /**
1024     * Send SAX events for the <code>SourceParameters</code>.
1025     * For each parametername/value pair an element is
1026     * created with the name of the parameter and the content
1027     * of this element is the value.
1028     */

1029    public void sendParametersEvents(SourceParameters pars)
1030    throws SAXException JavaDoc {
1031
1032        if (pars != null) {
1033            Iterator JavaDoc names = pars.getParameterNames();
1034            while (names.hasNext()) {
1035                final String JavaDoc currentName = (String JavaDoc)names.next();
1036                Iterator JavaDoc values = pars.getParameterValues(currentName);
1037                while (values.hasNext()) {
1038                    final String JavaDoc currentValue = (String JavaDoc)values.next();
1039                    sendStartElementEvent(currentName);
1040                    sendTextEvent(currentValue);
1041                    sendEndElementEvent(currentName);
1042                }
1043            }
1044        }
1045    }
1046
1047    /**
1048     * Send all start prefix mapping events to the current content handler
1049     */

1050    protected void sendStartPrefixMapping()
1051    throws SAXException JavaDoc {
1052        final int l = this.namespaces.size();
1053        for (int i = 0; i < l; i++) {
1054            String JavaDoc[] prefixAndUri = (String JavaDoc[]) this.namespaces.get(i);
1055            super.contentHandler.startPrefixMapping(prefixAndUri[0], prefixAndUri[1]);
1056        }
1057    }
1058
1059    /**
1060     * Send all end prefix mapping events to the current content handler
1061     */

1062    protected void sendEndPrefixMapping()
1063    throws SAXException JavaDoc {
1064        final int l = this.namespaces.size();
1065        for (int i = 0; i < l; i++) {
1066            String JavaDoc[] prefixAndUri = (String JavaDoc[]) this.namespaces.get(i);
1067            super.contentHandler.endPrefixMapping(prefixAndUri[0]);
1068        }
1069    }
1070
1071    /**
1072     * Find prefix mapping for the given namespace URI.
1073     * @return Prefix mapping or null if no prefix defined
1074     */

1075    protected String JavaDoc findPrefixMapping(String JavaDoc uri) {
1076        final int l = this.namespaces.size();
1077        for (int i = 0; i < l; i++) {
1078            String JavaDoc[] prefixAndUri = (String JavaDoc[]) this.namespaces.get(i);
1079            if (prefixAndUri[1].equals(uri)) {
1080                return prefixAndUri[0];
1081            }
1082        }
1083
1084        return null;
1085    }
1086
1087    /**
1088     * Helper method to get a modifiable attribute set.
1089     */

1090    protected AttributesImpl getMutableAttributes(Attributes JavaDoc a) {
1091        if ( a instanceof AttributesImpl && !(a instanceof ImmutableAttributesImpl)) {
1092            return (AttributesImpl)a;
1093        }
1094        return new AttributesImpl(a);
1095    }
1096}
1097
Popular Tags