KickJava   Java API By Example, From Geeks To Geeks.

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


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.transformation;
17
18 import org.apache.avalon.excalibur.pool.Recyclable;
19 import org.apache.avalon.framework.activity.Disposable;
20 import org.apache.avalon.framework.component.ComponentSelector;
21 import org.apache.avalon.framework.configuration.Configurable;
22 import org.apache.avalon.framework.configuration.Configuration;
23 import org.apache.avalon.framework.configuration.ConfigurationException;
24 import org.apache.avalon.framework.parameters.Parameters;
25 import org.apache.avalon.framework.service.ServiceException;
26 import org.apache.avalon.framework.service.ServiceManager;
27 import org.apache.avalon.framework.service.ServiceSelector;
28 import org.apache.avalon.framework.service.Serviceable;
29
30 import org.apache.cocoon.components.sax.XMLDeserializer;
31 import org.apache.cocoon.components.sax.XMLSerializer;
32 import org.apache.cocoon.environment.SourceResolver;
33 import org.apache.cocoon.taglib.IterationTag;
34 import org.apache.cocoon.taglib.Tag;
35 import org.apache.cocoon.taglib.BodyTag;
36 import org.apache.cocoon.taglib.BodyContent;
37 import org.apache.cocoon.xml.AbstractXMLProducer;
38 import org.apache.cocoon.xml.XMLConsumer;
39 import org.apache.cocoon.xml.XMLProducer;
40 import org.apache.cocoon.xml.SaxBuffer;
41
42 import org.apache.commons.collections.ArrayStack;
43 import org.apache.commons.collections.map.StaticBucketMap;
44 import org.xml.sax.Attributes JavaDoc;
45 import org.xml.sax.SAXException JavaDoc;
46
47 import java.beans.BeanInfo JavaDoc;
48 import java.beans.IntrospectionException JavaDoc;
49 import java.beans.Introspector JavaDoc;
50 import java.beans.PropertyDescriptor JavaDoc;
51 import java.io.IOException JavaDoc;
52 import java.lang.reflect.Method JavaDoc;
53 import java.util.HashMap JavaDoc;
54 import java.util.Map JavaDoc;
55
56 /**
57  * Transformer which implements the taglib functionalty.
58  *
59  * <p>Transformer processes incoming SAX events and for each element it tries to
60  * find {@link Tag} component with matching namespace and tag name.
61  *
62  * @author <a HREF="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a>
63  * @version CVS $Id: TagTransformer.java 46206 2004-09-16 20:06:28Z vgritsenko $
64  */

65 public class TagTransformer
66         extends AbstractXMLProducer
67         implements Transformer, Serviceable, Configurable, Disposable, Recyclable {
68
69     private int recordingLevel;
70     private int skipLevel;
71
72     private String JavaDoc transformerHint;
73     private ServiceSelector transformerSelector;
74
75     private final ArrayStack tagStack = new ArrayStack();
76     private final ArrayStack tagSelectorStack = new ArrayStack();
77     private final ArrayStack tagTransformerStack = new ArrayStack();
78
79     private ServiceSelector tagNamespaceSelector;
80     private Tag currentTag;
81
82     /** current SAX Event Consumer */
83     private XMLConsumer currentConsumer;
84
85     /** backup of currentConsumer while recording */
86     private XMLConsumer currentConsumerBackup;
87
88     private XMLSerializer xmlSerializer;
89
90     /** The SourceResolver for this request */
91     private SourceResolver resolver;
92
93     /** The current objectModel of the environment */
94     private Map objectModel;
95
96     /** The parameters specified in the sitemap */
97     private Parameters parameters;
98
99     /** The Avalon ServiceManager */
100     private ServiceManager manager;
101
102
103     /** Array for dynamic calling of Tag set property methods */
104     private final String JavaDoc[] paramArray = new String JavaDoc[1];
105
106     /** Map for caching Tag Introspection */
107     private static Map TAG_PROPERTIES_MAP = new StaticBucketMap();
108
109     //
110
// Component Lifecycle Methods
111
//
112

113     /**
114      * Avalon Serviceable Interface
115      * @param manager The Avalon Service Manager
116      */

117     public void service(ServiceManager manager) throws ServiceException {
118         this.manager = manager;
119         this.tagNamespaceSelector = (ServiceSelector) manager.lookup(Tag.ROLE + "Selector");
120     }
121
122     /**
123      * Avalon Configurable Interface
124      */

125     public void configure(Configuration conf) throws ConfigurationException {
126         this.transformerHint = conf.getChild("transformer-hint").getValue(null);
127         if (this.transformerHint != null) {
128             try {
129                 this.transformerSelector = (ServiceSelector) manager.lookup(Transformer.ROLE + "Selector");
130             } catch (ServiceException e) {
131                 String JavaDoc message = "Can't lookup transformer selector";
132                 if (getLogger().isDebugEnabled()) {
133                     getLogger().debug(message, e);
134                 }
135                 throw new ConfigurationException(message, e);
136             }
137         }
138     }
139
140     /**
141      * Set the <code>EntityResolver</code>, objectModel <code>Map</code>,
142      * the source and sitemap <code>Parameters</code> used to process the request.
143      */

144     public void setup(SourceResolver resolver, Map objectModel, String JavaDoc source, Parameters parameters)
145     throws IOException JavaDoc, SAXException JavaDoc {
146         this.resolver = resolver;
147         this.objectModel = objectModel;
148         this.parameters = parameters;
149     }
150
151     /**
152      * Recycle this component.
153      */

154     public void recycle() {
155         this.recordingLevel = 0;
156         this.skipLevel = 0;
157         this.resolver = null;
158         this.objectModel = null;
159         this.parameters = null;
160         this.currentTag = null;
161         this.currentConsumer = null;
162         this.currentConsumerBackup = null;
163
164         // can happen if there was a error in the pipeline
165
if (xmlSerializer != null) {
166             manager.release(xmlSerializer);
167             xmlSerializer = null;
168         }
169
170         while (!tagStack.isEmpty()) {
171             Tag tag = (Tag) tagStack.pop();
172             if (tag == null)
173                 continue;
174             ComponentSelector tagSelector = (ComponentSelector)tagSelectorStack.pop();
175             tagSelector.release(tag);
176
177             tagNamespaceSelector.release(tagSelector);
178         }
179
180         while (!tagTransformerStack.isEmpty()) {
181             Transformer transformer = (Transformer) tagTransformerStack.pop();
182             transformerSelector.release(transformer);
183         }
184
185         if (!tagSelectorStack.isEmpty()) {
186             getLogger().fatalError("recycle: internal Error, tagSelectorStack not empty");
187             tagSelectorStack.clear();
188         }
189
190         super.recycle();
191     }
192
193     /**
194      * Dispose this component.
195      */

196     public void dispose() {
197         this.manager.release(tagNamespaceSelector);
198         tagNamespaceSelector = null;
199         if (transformerSelector != null) {
200             this.manager.release(transformerSelector);
201             transformerSelector = null;
202         }
203     }
204
205     /*
206      * @see XMLProducer#setConsumer(XMLConsumer)
207      */

208     public void setConsumer(XMLConsumer consumer) {
209         this.currentConsumer = consumer;
210         super.setConsumer(consumer);
211     }
212
213
214     //
215
// SAX Events Methods
216
//
217

218     public void setDocumentLocator(org.xml.sax.Locator JavaDoc locator) {
219         // If we are skipping the body of a tag, ignore this...
220
if (this.skipLevel > 0) {
221             return;
222         }
223
224         this.currentConsumer.setDocumentLocator(locator);
225     }
226
227     public void startDocument() throws SAXException JavaDoc {
228         this.currentConsumer.startDocument();
229     }
230
231     public void endDocument() throws SAXException JavaDoc {
232         this.currentConsumer.endDocument();
233     }
234
235     public void processingInstruction(String JavaDoc target, String JavaDoc data) throws SAXException JavaDoc {
236         // If we are skipping the body of a tag, ignore this...
237
if (this.skipLevel > 0) {
238             return;
239         }
240
241         this.currentConsumer.processingInstruction(target, data);
242     }
243
244     public void startDTD(String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId) throws SAXException JavaDoc {
245         // If we are skipping the body of a tag, ignore this...
246
if (this.skipLevel > 0) {
247             return;
248         }
249
250         this.currentConsumer.startDTD(name, publicId, systemId);
251     }
252
253     public void endDTD() throws SAXException JavaDoc {
254         // If we are skipping the body of a tag, ignore this...
255
if (this.skipLevel > 0) {
256             return;
257         }
258
259         this.currentConsumer.endDTD();
260     }
261
262     public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri) throws SAXException JavaDoc {
263         // If we are skipping the body of a tag, ignore this...
264
if (this.skipLevel > 0) {
265             return;
266         }
267
268         this.currentConsumer.startPrefixMapping(prefix, uri);
269     }
270
271     public void endPrefixMapping(String JavaDoc prefix) throws SAXException JavaDoc {
272         // If we are skipping the body of a tag, ignore this...
273
if (this.skipLevel > 0) {
274             return;
275         }
276
277         this.currentConsumer.endPrefixMapping(prefix);
278     }
279
280     public void startCDATA() throws SAXException JavaDoc {
281         // If we are skipping the body of a tag, ignore this...
282
if (this.skipLevel > 0) {
283             return;
284         }
285
286         this.currentConsumer.startCDATA();
287     }
288
289     public void endCDATA() throws SAXException JavaDoc {
290         // If we are skipping the body of a tag, ignore this...
291
if (this.skipLevel > 0) {
292             return;
293         }
294
295         this.currentConsumer.endCDATA();
296     }
297
298     public void startElement(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc qName, Attributes JavaDoc atts)
299     throws SAXException JavaDoc {
300         // Are we recording for iteration ?
301
if (this.recordingLevel > 0) {
302             this.recordingLevel ++;
303             this.currentConsumer.startElement(namespaceURI, localName, qName, atts);
304             return;
305         }
306
307         // If we are skipping the body of a Tag
308
if (this.skipLevel > 0) {
309             // Remember to skip one more end element
310
this.skipLevel ++;
311             // and ignore this start element
312
return;
313         }
314
315         Tag tag = null;
316         if (namespaceURI != null && namespaceURI.length() > 0) {
317             // Try to find Tag corresponding to this element
318
ComponentSelector tagSelector = null;
319             try {
320                 tagSelector = (ComponentSelector) tagNamespaceSelector.select(namespaceURI);
321                 tagSelectorStack.push(tagSelector);
322
323                 // namespace matches tag library, lookup tag now.
324
tag = (Tag) tagSelector.select(localName);
325                 if (getLogger().isDebugEnabled()) {
326                     getLogger().debug("startElement: Got tag " + qName);
327                 }
328
329                 setupTag(tag, qName, atts);
330             } catch (SAXException JavaDoc e) {
331                 throw e;
332             } catch (Exception JavaDoc ignore) {
333                 // No namespace or tag found, process it as normal element (tag == null)
334
}
335         }
336
337         tagStack.push(tag);
338         if (tag == null) {
339             currentConsumer.startElement(namespaceURI, localName, qName, atts);
340             return;
341         }
342
343         // Execute Tag
344
int eval = tag.doStartTag(namespaceURI, localName, qName, atts);
345         switch (eval) {
346             case Tag.EVAL_BODY :
347                 skipLevel = 0;
348                 if (tag instanceof IterationTag) {
349                     // start recording for IterationTag
350
startRecording();
351                 }
352                 break;
353
354             case Tag.SKIP_BODY :
355                 skipLevel = 1;
356                 break;
357
358             default :
359                 String JavaDoc tagName = tag.getClass().getName();
360                 getLogger().warn("Bad return value from doStartTag(" + tagName + "): " + eval);
361                 break;
362         }
363     }
364
365     public void endElement(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc qName) throws SAXException JavaDoc {
366         Object JavaDoc saxFragment = null;
367
368         // Are we recording?
369
if (recordingLevel > 0) {
370             if (--recordingLevel > 0) {
371                 currentConsumer.endElement(namespaceURI, localName, qName);
372                 return;
373             }
374             // Recording finished
375
saxFragment = endRecording();
376         }
377
378         if (skipLevel > 0) {
379             if (--skipLevel > 0) {
380                 return;
381             }
382         }
383
384         Tag tag = (Tag) tagStack.pop();
385         if (tag != null) {
386             ComponentSelector tagSelector = (ComponentSelector)tagSelectorStack.pop();
387             try {
388                 if (saxFragment != null) {
389                     // Start Iteration
390
IterationTag iterTag = (IterationTag) tag;
391                     XMLDeserializer xmlDeserializer = null;
392                     try {
393                         xmlDeserializer = (XMLDeserializer) manager.lookup(XMLDeserializer.ROLE);
394                         xmlDeserializer.setConsumer(this);
395
396                         // BodyTag Support
397
XMLConsumer backup = this.currentConsumer;
398                         if (tag instanceof BodyTag) {
399                             SaxBuffer content = new SaxBuffer();
400                             this.currentConsumer = content;
401                             ((BodyTag)tag).setBodyContent(new BodyContent(content, backup));
402                             ((BodyTag)tag).doInitBody();
403                         }
404
405                         do {
406                             xmlDeserializer.deserialize(saxFragment);
407                         } while (iterTag.doAfterBody() != Tag.SKIP_BODY);
408
409                         // BodyTag Support
410
if (tag instanceof BodyTag) {
411                             this.currentConsumer = backup;
412                         }
413
414                     } catch (ServiceException e) {
415                         throw new SAXException JavaDoc("Can't obtain XMLDeserializer", e);
416                     } finally {
417                         if (xmlDeserializer != null) {
418                             manager.release(xmlDeserializer);
419                         }
420                     }
421                 }
422                 tag.doEndTag(namespaceURI, localName, qName);
423                 currentTag = tag.getParent();
424
425                 if (tag == this.currentConsumer) {
426                     popConsumer();
427                 }
428             } finally {
429                 if (getLogger().isDebugEnabled()) {
430                     getLogger().debug("endElement: Release tag " + qName);
431                 }
432
433                 tagSelector.release(tag);
434                 tagNamespaceSelector.release(tagSelector);
435
436                 if (transformerSelector != null && tag instanceof XMLProducer) {
437                     getLogger().debug("endElement: Release transformer");
438                     Transformer transformer = (Transformer) tagTransformerStack.pop();
439                     transformerSelector.release(transformer);
440                 }
441             }
442         } else {
443             this.currentConsumer.endElement(namespaceURI, localName, qName);
444         }
445     }
446
447     public void startEntity(String JavaDoc name) throws SAXException JavaDoc {
448         // If we are skipping the body of a tag, ignore this...
449
if (this.skipLevel > 0) {
450             return;
451         }
452
453         this.currentConsumer.startEntity(name);
454     }
455
456     public void endEntity(String JavaDoc name) throws SAXException JavaDoc {
457         // If we are skipping the body of a tag, ignore this...
458
if (this.skipLevel > 0) {
459             return;
460         }
461
462         this.currentConsumer.endEntity(name);
463     }
464
465     public void skippedEntity(String JavaDoc name) throws SAXException JavaDoc {
466         // If we are skipping the body of a tag, ignore this...
467
if (this.skipLevel > 0) {
468             return;
469         }
470
471         this.currentConsumer.skippedEntity(name);
472     }
473
474     public void characters(char[] ch, int start, int length) throws SAXException JavaDoc {
475         // If we are skipping the body of a tag, ignore this...
476
if (this.skipLevel > 0) {
477             return;
478         }
479
480         this.currentConsumer.characters(ch, start, length);
481     }
482
483     public void comment(char[] ch, int start, int length) throws SAXException JavaDoc {
484         // If we are skipping the body of a tag, ignore this...
485
if (this.skipLevel > 0) {
486             return;
487         }
488
489         this.currentConsumer.comment(ch, start, length);
490     }
491
492     public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException JavaDoc {
493         // If we are skipping the body of a tag, ignore this...
494
if (this.skipLevel > 0) {
495             return;
496         }
497
498         this.currentConsumer.ignorableWhitespace(ch, start, length);
499     }
500
501
502     //
503
// Internal Implementation Methods
504
//
505

506     private void setupTag(Tag tag, String JavaDoc name, Attributes JavaDoc atts) throws SAXException JavaDoc {
507         // Set Tag Parent
508
tag.setParent(this.currentTag);
509
510         // Set Tag XML Consumer
511
if (tag instanceof XMLProducer) {
512             XMLConsumer tagConsumer;
513             if (transformerSelector != null) {
514                 Transformer tagTransformer = null;
515                 try {
516                     // Add additional (Tag)Transformer to the output of the Tag
517
tagTransformer = (Transformer) transformerSelector.select(transformerHint);
518                     tagTransformerStack.push(tagTransformer);
519                     tagTransformer.setConsumer(currentConsumer);
520                     tagTransformer.setup(this.resolver, this.objectModel, null, this.parameters);
521                 } catch (SAXException JavaDoc e) {
522                     throw e;
523                 } catch (Exception JavaDoc e) {
524                     throw new SAXException JavaDoc("Failed to setup tag transformer " + transformerHint, e);
525                 }
526                 tagConsumer = tagTransformer;
527             } else {
528                 tagConsumer = this.currentConsumer;
529             }
530
531             ((XMLProducer) tag).setConsumer(tagConsumer);
532         }
533
534         // Setup Tag
535
try {
536             tag.setup(this.resolver, this.objectModel, this.parameters);
537         } catch (IOException JavaDoc e) {
538             throw new SAXException JavaDoc("Could not set up tag " + name, e);
539         }
540
541         if (tag instanceof XMLConsumer) {
542             this.currentConsumer = (XMLConsumer) tag;
543         }
544         this.currentTag = tag;
545
546         // Set Tag-Attributes, Attributes are mapped to the coresponding Tag method
547
for (int i = 0; i < atts.getLength(); i++) {
548             String JavaDoc attributeName = atts.getLocalName(i);
549             String JavaDoc attributeValue = atts.getValue(i);
550             this.paramArray[0] = attributeValue;
551             try {
552                 Method JavaDoc method = getWriteMethod(tag.getClass(), attributeName);
553                 method.invoke(tag, this.paramArray);
554             } catch (Throwable JavaDoc e) {
555                 if (getLogger().isInfoEnabled()) {
556                     getLogger().info("Tag " + name + " attribute " + attributeName + " not set", e);
557                 }
558             }
559         }
560     }
561
562     /**
563      * Start recording for the iterator tag.
564      */

565     private void startRecording() throws SAXException JavaDoc {
566         try {
567             this.xmlSerializer = (XMLSerializer) manager.lookup(XMLSerializer.ROLE);
568         } catch (ServiceException e) {
569             throw new SAXException JavaDoc("Can't lookup XMLSerializer", e);
570         }
571
572         this.currentConsumerBackup = this.currentConsumer;
573         this.currentConsumer = this.xmlSerializer;
574         this.recordingLevel = 1;
575     }
576
577     /**
578      * End recording for the iterator tag and returns recorded XML fragment.
579      */

580     private Object JavaDoc endRecording() {
581         // Restore XML Consumer
582
this.currentConsumer = this.currentConsumerBackup;
583         this.currentConsumerBackup = null;
584
585         // Get XML Fragment
586
Object JavaDoc saxFragment = this.xmlSerializer.getSAXFragment();
587
588         // Release Serializer
589
this.manager.release(this.xmlSerializer);
590         this.xmlSerializer = null;
591
592         return saxFragment;
593     }
594
595     /**
596      * Find previous XML consumer when processing of current consumer
597      * is complete.
598      */

599     private void popConsumer() {
600         Tag loop = this.currentTag;
601         for (; loop != null; loop = loop.getParent()) {
602             if (loop instanceof XMLConsumer) {
603                 this.currentConsumer = (XMLConsumer) loop;
604                 return;
605             }
606         }
607
608         this.currentConsumer = this.xmlConsumer;
609     }
610
611     private static Method JavaDoc getWriteMethod(Class JavaDoc type, String JavaDoc propertyName) throws IntrospectionException JavaDoc {
612         Map map = getWriteMethodMap(type);
613         Method JavaDoc method = (Method JavaDoc) map.get(propertyName);
614         if (method == null) {
615             throw new IntrospectionException JavaDoc("No such property: " + propertyName);
616         }
617         return method;
618     }
619
620     private static Map getWriteMethodMap(Class JavaDoc beanClass) throws IntrospectionException JavaDoc {
621         Map map = (Map) TAG_PROPERTIES_MAP.get(beanClass);
622         if (map != null) {
623             return map;
624         }
625
626         BeanInfo JavaDoc info = Introspector.getBeanInfo(beanClass);
627         if (info != null) {
628             PropertyDescriptor JavaDoc pds[] = info.getPropertyDescriptors();
629             map = new HashMap JavaDoc(pds.length * 4 / 3, 1);
630             for (int i = 0; i < pds.length; i++) {
631                 PropertyDescriptor JavaDoc pd = pds[i];
632                 String JavaDoc name = pd.getName();
633                 Method JavaDoc method = pd.getWriteMethod();
634                 Class JavaDoc type = pd.getPropertyType();
635                 if (type != String JavaDoc.class) // only String properties
636
continue;
637                 map.put(name, method);
638             }
639         }
640         TAG_PROPERTIES_MAP.put(beanClass, map);
641         return map;
642     }
643 }
644
Popular Tags