You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by vg...@apache.org on 2004/09/02 22:36:22 UTC

svn commit: rev 37421 - in cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon: taglib transformation

Author: vgritsenko
Date: Thu Sep  2 13:36:21 2004
New Revision: 37421

Modified:
   cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/taglib/TagSupport.java
   cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/taglib/XMLProducerTagSupport.java
   cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/transformation/TagTransformer.java
Log:
Allow tags extending from TagSupport to throw IOException in setup() method.
Set consumer for XML producing tags before setup method.
Refactor some code into methods.
Exception handling: do not ignore tag setup() exceptions.


Modified: cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/taglib/TagSupport.java
==============================================================================
--- cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/taglib/TagSupport.java	(original)
+++ cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/taglib/TagSupport.java	Thu Sep  2 13:36:21 2004
@@ -1,12 +1,12 @@
 /*
  * Copyright 1999-2004 The Apache Software Foundation.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,6 +16,7 @@
 package org.apache.cocoon.taglib;
 
 import java.util.Map;
+import java.io.IOException;
 
 import org.apache.avalon.excalibur.pool.Recyclable;
 import org.apache.avalon.framework.logger.AbstractLogEnabled;
@@ -31,12 +32,11 @@
 
 /**
  * Abstract implementation for all Tags
- * 
+ *
  * @author <a href="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a>
- * @version CVS $Id: TagSupport.java,v 1.3 2004/03/05 13:02:24 bdelacretaz Exp $
+ * @version CVS $Id$
  */
 public abstract class TagSupport extends AbstractLogEnabled implements Tag, Recyclable {
-    protected String var;
     protected Tag parent;
     protected SourceResolver resolver;
     protected Map objectModel;
@@ -82,7 +82,8 @@
      * @return EVAL_PAGE.
      * @throws SAXException
      */
-    public int doEndTag(String namespaceURI, String localName, String qName) throws SAXException {
+    public int doEndTag(String namespaceURI, String localName, String qName)
+    throws SAXException {
         return EVAL_PAGE;
     }
 
@@ -96,7 +97,8 @@
      *
      * @return EVAL_BODY or SKIP_BODY.
      */
-    public int doStartTag(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+    public int doStartTag(String namespaceURI, String localName, String qName, Attributes atts)
+    throws SAXException {
         return EVAL_BODY;
     }
 
@@ -135,7 +137,6 @@
 
     public void recycle() {
         getLogger().debug("recycle");
-        this.var = null;
         this.parent = null;
         this.resolver = null;
         this.objectModel = null;
@@ -160,7 +161,8 @@
      * Set the <code>SourceResolver</code>, objectModel <code>Map</code>
      * and sitemap <code>Parameters</code> used to process the request.
      */
-    public void setup(SourceResolver resolver, Map objectModel, Parameters parameters) {
+    public void setup(SourceResolver resolver, Map objectModel, Parameters parameters)
+    throws SAXException, IOException {
         this.resolver = resolver;
         this.objectModel = objectModel;
         this.parameters = parameters;

Modified: cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/taglib/XMLProducerTagSupport.java
==============================================================================
--- cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/taglib/XMLProducerTagSupport.java	(original)
+++ cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/taglib/XMLProducerTagSupport.java	Thu Sep  2 13:36:21 2004
@@ -1,12 +1,12 @@
 /*
  * Copyright 1999-2004 The Apache Software Foundation.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -19,10 +19,10 @@
 
 /**
  * @author <a href="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a>
- * @version CVS $Id: XMLProducerTagSupport.java,v 1.3 2004/03/05 13:02:24 bdelacretaz Exp $
+ * @version CVS $Id$
  */
 public abstract class XMLProducerTagSupport extends TagSupport implements XMLProducerTag {
-    
+
     protected XMLConsumer xmlConsumer;
 
     /*
@@ -31,12 +31,12 @@
     public void setConsumer(XMLConsumer xmlConsumer) {
         this.xmlConsumer = xmlConsumer;
     }
-    
+
     /*
      * @see Recyclable#recycle()
      */
     public void recycle() {
-        xmlConsumer = null;
+        this.xmlConsumer = null;
         super.recycle();
     }
 }

Modified: cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/transformation/TagTransformer.java
==============================================================================
--- cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/transformation/TagTransformer.java	(original)
+++ cocoon/trunk/src/blocks/taglib/java/org/apache/cocoon/transformation/TagTransformer.java	Thu Sep  2 13:36:21 2004
@@ -1,12 +1,12 @@
 /*
  * Copyright 1999-2004 The Apache Software Foundation.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,17 +15,6 @@
  */
 package org.apache.cocoon.transformation;
 
-import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.collections.ArrayStack;
-import org.apache.commons.collections.map.StaticBucketMap;
 import org.apache.avalon.excalibur.pool.Recyclable;
 import org.apache.avalon.framework.activity.Disposable;
 import org.apache.avalon.framework.component.ComponentSelector;
@@ -37,6 +26,7 @@
 import org.apache.avalon.framework.service.ServiceManager;
 import org.apache.avalon.framework.service.ServiceSelector;
 import org.apache.avalon.framework.service.Serviceable;
+
 import org.apache.cocoon.components.sax.XMLDeserializer;
 import org.apache.cocoon.components.sax.XMLSerializer;
 import org.apache.cocoon.environment.SourceResolver;
@@ -46,31 +36,49 @@
 import org.apache.cocoon.xml.XMLConsumer;
 import org.apache.cocoon.xml.XMLProducer;
 
+import org.apache.commons.collections.ArrayStack;
+import org.apache.commons.collections.map.StaticBucketMap;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
 /**
- * Transformer which implements the dynamic Tag functionalty.
+ * Transformer which implements the taglib functionalty.
+ *
+ * <p>Transformer processes incoming SAX events and for each element it tries to
+ * find {@link Tag} component with matching namespace and tag name.
  *
  * @author <a href="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a>
- * @version CVS $Id: TagTransformer.java,v 1.7 2004/06/24 13:45:29 cziegeler Exp $
+ * @version CVS $Id$
  */
 public class TagTransformer
-    extends AbstractXMLProducer
-    implements Transformer, Serviceable, Configurable, Disposable, Recyclable {
+        extends AbstractXMLProducer
+        implements Transformer, Serviceable, Configurable, Disposable, Recyclable {
+
+    private int recordingLevel;
+    private int skipLevel;
 
-    private int recordingLevel = 0;
-    private int skipLevel = 0;
     private String transformerHint;
+    private ServiceSelector transformerSelector;
+
+    private final ArrayStack tagStack = new ArrayStack();
+    private final ArrayStack tagSelectorStack = new ArrayStack();
+    private final ArrayStack tagTransformerStack = new ArrayStack();
 
-    private ArrayStack tagStack = new ArrayStack();
-    private ArrayStack tagSelectorStack = new ArrayStack();
-    private ArrayStack tagTransformerStack = new ArrayStack();
     private ServiceSelector tagNamespaceSelector;
-    private ServiceSelector transformerSelector;
     private Tag currentTag;
+
     /** current SAX Event Consumer  */
     private XMLConsumer currentConsumer;
+
     /** backup of currentConsumer while recording */
     private XMLConsumer currentConsumerBackup;
 
@@ -78,39 +86,26 @@
 
     /** The SourceResolver for this request */
     private SourceResolver resolver;
+
     /** The current objectModel of the environment */
     private Map objectModel;
+
     /** The parameters specified in the sitemap */
     private Parameters parameters;
+
     /** The Avalon ServiceManager */
     private ServiceManager manager;
 
-    /** Array for dynamic calling of Tag set property methods */
-    private String[] paramArray = new String[1];
-    /** Map for caching Tag Introspection */
-    private static Map writeMethodMap = new StaticBucketMap();
-
-    /**
-     * SAX Event handling
-     */
-    public void characters(char[] ch, int start, int length) throws SAXException {
-        // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
-            return;
 
-        currentConsumer.characters(ch, start, length);
-    }
+    /** Array for dynamic calling of Tag set property methods */
+    private final String[] paramArray = new String[1];
 
-    /**
-     * SAX Event handling
-     */
-    public void comment(char[] ch, int start, int length) throws SAXException {
-        // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
-            return;
+    /** Map for caching Tag Introspection */
+    private static Map TAG_PROPERTIES_MAP = new StaticBucketMap();
 
-        currentConsumer.comment(ch, start, length);
-    }
+    //
+    // Component Lifecycle Methods
+    //
 
     /**
      * Avalon Serviceable Interface
@@ -118,77 +113,267 @@
      */
     public void service(ServiceManager manager) throws ServiceException {
         this.manager = manager;
-        tagNamespaceSelector = (ServiceSelector) manager.lookup(Tag.ROLE + "Selector");
+        this.tagNamespaceSelector = (ServiceSelector) manager.lookup(Tag.ROLE + "Selector");
     }
 
     /**
      * Avalon Configurable Interface
      */
     public void configure(Configuration conf) throws ConfigurationException {
-        transformerHint = conf.getChild("transformer-hint").getValue(null);
-        if (transformerHint != null) {
+        this.transformerHint = conf.getChild("transformer-hint").getValue(null);
+        if (this.transformerHint != null) {
             try {
-                transformerSelector = (ServiceSelector) manager.lookup(Transformer.ROLE + "Selector");
+                this.transformerSelector = (ServiceSelector) manager.lookup(Transformer.ROLE + "Selector");
             } catch (ServiceException e) {
-                String message = "can't lookup transformer";
-                getLogger().error(message, e);
+                String message = "Can't lookup transformer selector";
+                if (getLogger().isDebugEnabled()) {
+                    getLogger().debug(message, e);
+                }
                 throw new ConfigurationException(message, e);
             }
         }
     }
 
     /**
-     * SAX Event handling
+     * Set the <code>EntityResolver</code>, objectModel <code>Map</code>,
+     * the source and sitemap <code>Parameters</code> used to process the request.
      */
-    public void endCDATA() throws SAXException {
-        // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
-            return;
-
-        currentConsumer.endCDATA();
+    public void setup(SourceResolver resolver, Map objectModel, String source, Parameters parameters)
+    throws IOException, SAXException {
+        this.resolver = resolver;
+        this.objectModel = objectModel;
+        this.parameters = parameters;
     }
 
     /**
-     * SAX Event handling
+     *  Recycle this component.
      */
-    public void endDocument() throws SAXException {
-        currentConsumer.endDocument();
-        getLogger().debug("endDocument");
+    public void recycle() {
+        this.recordingLevel = 0;
+        this.skipLevel = 0;
+        this.resolver = null;
+        this.objectModel = null;
+        this.parameters = null;
+        this.currentTag = null;
+        this.currentConsumer = null;
+        this.currentConsumerBackup = null;
+
+        // can happen if there was a error in the pipeline
+        if (xmlSerializer != null) {
+            manager.release(xmlSerializer);
+            xmlSerializer = null;
+        }
+
+        while (!tagStack.isEmpty()) {
+            Tag tag = (Tag) tagStack.pop();
+            if (tag == null)
+                continue;
+            ComponentSelector tagSelector = (ComponentSelector)tagSelectorStack.pop();
+            tagSelector.release(tag);
+
+            tagNamespaceSelector.release(tagSelector);
+        }
+
+        while (!tagTransformerStack.isEmpty()) {
+            Transformer transformer = (Transformer) tagTransformerStack.pop();
+            transformerSelector.release(transformer);
+        }
+
+        if (!tagSelectorStack.isEmpty()) {
+            getLogger().fatalError("recycle: internal Error, tagSelectorStack not empty");
+            tagSelectorStack.clear();
+        }
+
+        super.recycle();
     }
 
     /**
-     * SAX Event handling
+     *  Dispose this component.
+     */
+    public void dispose() {
+        this.manager.release(tagNamespaceSelector);
+        tagNamespaceSelector = null;
+        if (transformerSelector != null) {
+            this.manager.release(transformerSelector);
+            transformerSelector = null;
+        }
+    }
+
+    /*
+     * @see XMLProducer#setConsumer(XMLConsumer)
      */
+    public void setConsumer(XMLConsumer consumer) {
+        this.currentConsumer = consumer;
+        super.setConsumer(consumer);
+    }
+
+
+    //
+    // SAX Events Methods
+    //
+
+    public void setDocumentLocator(org.xml.sax.Locator locator) {
+        // If we are skipping the body of a tag, ignore this...
+        if (this.skipLevel > 0) {
+            return;
+        }
+
+        this.currentConsumer.setDocumentLocator(locator);
+    }
+
+    public void startDocument() throws SAXException {
+        this.currentConsumer.startDocument();
+    }
+
+    public void endDocument() throws SAXException {
+        this.currentConsumer.endDocument();
+    }
+
+    public void processingInstruction(String target, String data) throws SAXException {
+        // If we are skipping the body of a tag, ignore this...
+        if (this.skipLevel > 0) {
+            return;
+        }
+
+        this.currentConsumer.processingInstruction(target, data);
+    }
+
+    public void startDTD(String name, String publicId, String systemId) throws SAXException {
+        // If we are skipping the body of a tag, ignore this...
+        if (this.skipLevel > 0) {
+            return;
+        }
+
+        this.currentConsumer.startDTD(name, publicId, systemId);
+    }
+
     public void endDTD() throws SAXException {
         // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
+        if (this.skipLevel > 0) {
             return;
+        }
 
-        currentConsumer.endDTD();
+        this.currentConsumer.endDTD();
+    }
+
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        // If we are skipping the body of a tag, ignore this...
+        if (this.skipLevel > 0) {
+            return;
+        }
+
+        this.currentConsumer.startPrefixMapping(prefix, uri);
+    }
+
+    public void endPrefixMapping(String prefix) throws SAXException {
+        // If we are skipping the body of a tag, ignore this...
+        if (this.skipLevel > 0) {
+            return;
+        }
+
+        this.currentConsumer.endPrefixMapping(prefix);
+    }
+
+    public void startCDATA() throws SAXException {
+        // If we are skipping the body of a tag, ignore this...
+        if (this.skipLevel > 0) {
+            return;
+        }
+
+        this.currentConsumer.startCDATA();
+    }
+
+    public void endCDATA() throws SAXException {
+        // If we are skipping the body of a tag, ignore this...
+        if (this.skipLevel > 0) {
+            return;
+        }
+
+        this.currentConsumer.endCDATA();
+    }
+
+    public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
+    throws SAXException {
+        // Are we recording for iteration ?
+        if (this.recordingLevel > 0) {
+            this.recordingLevel ++;
+            this.currentConsumer.startElement(namespaceURI, localName, qName, atts);
+            return;
+        }
+
+        // If we are skipping the body of a Tag
+        if (this.skipLevel > 0) {
+            // Remember to skip one more end element
+            this.skipLevel ++;
+            // and ignore this start element
+            return;
+        }
+
+        Tag tag = null;
+        if (namespaceURI != null && namespaceURI.length() > 0) {
+            // Try to find Tag corresponding to this element
+            ComponentSelector tagSelector = null;
+            try {
+                tagSelector = (ComponentSelector) tagNamespaceSelector.select(namespaceURI);
+                tagSelectorStack.push(tagSelector);
+
+                // namespace matches tag library, lookup tag now.
+                tag = (Tag) tagSelector.select(localName);
+                if (getLogger().isDebugEnabled()) {
+                    getLogger().debug("startElement: Got tag " + qName);
+                }
+
+                setupTag(tag, qName, atts);
+            } catch (SAXException e) {
+                throw e;
+            } catch (Exception ignore) {
+                // No namespace or tag found, process it as normal element (tag == null)
+            }
+        }
+
+        tagStack.push(tag);
+        if (tag == null) {
+            currentConsumer.startElement(namespaceURI, localName, qName, atts);
+            return;
+        }
+
+        // Execute Tag
+        int eval = tag.doStartTag(namespaceURI, localName, qName, atts);
+        switch (eval) {
+            case Tag.EVAL_BODY :
+                skipLevel = 0;
+                if (tag instanceof IterationTag) {
+                    // start recording for IterationTag
+                    startRecording();
+                }
+                break;
+
+            case Tag.SKIP_BODY :
+                skipLevel = 1;
+                break;
+
+            default :
+                String tagName = tag.getClass().getName();
+                getLogger().warn("Bad return value from doStartTag(" + tagName + "): " + eval);
+                break;
+        }
     }
 
-    /**
-     * SAX Event handling
-     */
     public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
         Object saxFragment = null;
-        // recording for iteration ?
+
+        // Are we recording?
         if (recordingLevel > 0) {
             if (--recordingLevel > 0) {
                 currentConsumer.endElement(namespaceURI, localName, qName);
                 return;
             }
-            //recording finished
-            currentConsumer = currentConsumerBackup;
-            saxFragment = xmlSerializer.getSAXFragment();
-            manager.release(xmlSerializer);
-            xmlSerializer = null;
+            // Recording finished
+            saxFragment = endRecording();
         }
 
         if (skipLevel > 0) {
-            --skipLevel;
-
-            if (skipLevel > 0) {
+            if (--skipLevel > 0) {
                 return;
             }
         }
@@ -198,7 +383,7 @@
             ComponentSelector tagSelector = (ComponentSelector)tagSelectorStack.pop();
             try {
                 if (saxFragment != null) {
-                    //start Iteration
+                    // Start Iteration
                     IterationTag iterTag = (IterationTag) tag;
                     XMLDeserializer xmlDeserializer = null;
                     try {
@@ -207,350 +392,217 @@
                         do {
                             xmlDeserializer.deserialize(saxFragment);
                         } while (iterTag.doAfterBody() != Tag.SKIP_BODY);
-
                     } catch (ServiceException e) {
-                        throw new SAXException("lookup XMLDeserializer failed", e);
-                    }
-                    finally {
-                        if (xmlDeserializer != null)
+                        throw new SAXException("Can't obtain XMLDeserializer", e);
+                    } finally {
+                        if (xmlDeserializer != null) {
                             manager.release(xmlDeserializer);
+                        }
                     }
                 }
                 tag.doEndTag(namespaceURI, localName, qName);
                 currentTag = tag.getParent();
 
-                if (tag == currentConsumer) {
-                    // search next XMLConsumer
-                    Tag loop = currentTag;
-                    for (; loop != null; loop = loop.getParent()) {
-                        if (loop instanceof XMLConsumer)
-                            break;
-                    }
-                    if (loop != null) {
-                        currentConsumer = (XMLConsumer) loop;
-                    } else {
-                        currentConsumer = this.xmlConsumer;
-                    }
+                if (tag == this.currentConsumer) {
+                    popConsumer();
                 }
             } finally {
-                getLogger().debug("endElement: release Tag");
-                tagSelector.release(tag);
+                if (getLogger().isDebugEnabled()) {
+                    getLogger().debug("endElement: Release tag " + qName);
+                }
 
+                tagSelector.release(tag);
                 tagNamespaceSelector.release(tagSelector);
 
                 if (transformerSelector != null && tag instanceof XMLProducer) {
-                    getLogger().debug("endElement: release transformer");
+                    getLogger().debug("endElement: Release transformer");
                     Transformer transformer = (Transformer) tagTransformerStack.pop();
                     transformerSelector.release(transformer);
                 }
             }
         } else {
-            currentConsumer.endElement(namespaceURI, localName, qName);
+            this.currentConsumer.endElement(namespaceURI, localName, qName);
         }
     }
 
-    /**
-     * SAX Event handling
-     */
-    public void endEntity(String name) throws SAXException {
+    public void startEntity(String name) throws SAXException {
         // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
+        if (this.skipLevel > 0) {
             return;
+        }
 
-        currentConsumer.endEntity(name);
+        this.currentConsumer.startEntity(name);
     }
 
-    /**
-     * SAX Event handling
-     */
-    public void endPrefixMapping(String prefix) throws SAXException {
+    public void endEntity(String name) throws SAXException {
         // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
+        if (this.skipLevel > 0) {
             return;
+        }
 
-        currentConsumer.endPrefixMapping(prefix);
+        this.currentConsumer.endEntity(name);
     }
 
-    /**
-     * SAX Event handling
-     */
-    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+    public void skippedEntity(String name) throws SAXException {
         // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
+        if (this.skipLevel > 0) {
             return;
+        }
 
-        currentConsumer.ignorableWhitespace(ch, start, length);
+        this.currentConsumer.skippedEntity(name);
     }
 
-    /**
-     * SAX Event handling
-     */
-    public void processingInstruction(String target, String data) throws SAXException {
+    public void characters(char[] ch, int start, int length) throws SAXException {
         // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
+        if (this.skipLevel > 0) {
             return;
-
-        currentConsumer.processingInstruction(target, data);
-    }
-
-    /**
-     *  Recycle this component.
-     */
-    public void recycle() {
-        recordingLevel = 0;
-        skipLevel = 0;
-        resolver = null;
-        objectModel = null;
-        parameters = null;
-        currentTag = null;
-        currentConsumer = null;
-        currentConsumerBackup = null;
-
-        // can happen if there was a error in the pipeline
-        if (xmlSerializer != null) {
-            manager.release(xmlSerializer);
-            xmlSerializer = null;
-        }
-
-        while (!tagStack.isEmpty()) {
-            Tag tag = (Tag) tagStack.pop();
-            if (tag == null)
-                continue;
-            ComponentSelector tagSelector = (ComponentSelector)tagSelectorStack.pop();
-            tagSelector.release(tag);
-
-            tagNamespaceSelector.release(tagSelector);
         }
 
-        while (!tagTransformerStack.isEmpty()) {
-            Transformer transformer = (Transformer) tagTransformerStack.pop();
-            transformerSelector.release(transformer);
-        }
-
-        if (!tagSelectorStack.isEmpty()) {
-            getLogger().fatalError("recycle: internal Error, tagSelectorStack not empty");
-            tagSelectorStack.clear();
-        }
-
-        super.recycle();
+        this.currentConsumer.characters(ch, start, length);
     }
 
-    /*
-     * @see XMLProducer#setConsumer(XMLConsumer)
-     */
-    public void setConsumer(XMLConsumer consumer) {
-        this.currentConsumer = consumer;
-        super.setConsumer(consumer);
-    }
-
-    /**
-     * SAX Event handling
-     */
-    public void setDocumentLocator(org.xml.sax.Locator locator) {
+    public void comment(char[] ch, int start, int length) throws SAXException {
         // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
+        if (this.skipLevel > 0) {
             return;
+        }
 
-        currentConsumer.setDocumentLocator(locator);
-    }
-
-    /**
-     * Set the <code>EntityResolver</code>, objectModel <code>Map</code>,
-     * the source and sitemap <code>Parameters</code> used to process the request.
-     */
-    public void setup(SourceResolver resolver, Map objectModel, String source, Parameters parameters)
-        throws IOException, SAXException {
-        this.resolver = resolver;
-        this.objectModel = objectModel;
-        this.parameters = parameters;
+        this.currentConsumer.comment(ch, start, length);
     }
 
-    /**
-     * SAX Event handling
-     */
-    public void skippedEntity(String name) throws SAXException {
+    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
         // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
+        if (this.skipLevel > 0) {
             return;
+        }
 
-        currentConsumer.skippedEntity(name);
+        this.currentConsumer.ignorableWhitespace(ch, start, length);
     }
 
-    /**
-     * SAX Event handling
-     */
-    public void startCDATA() throws SAXException {
-        // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
-            return;
 
-        currentConsumer.startCDATA();
-    }
+    //
+    // Internal Implementation Methods
+    //
 
-    /**
-     * SAX Event handling
-     */
-    public void startDocument() throws SAXException {
-        currentConsumer.startDocument();
-    }
+    private void setupTag(Tag tag, String name, Attributes atts) throws SAXException {
+        // Set Tag Parent
+        tag.setParent(this.currentTag);
 
-    /**
-     * SAX Event handling
-     */
-    public void startDTD(String name, String publicId, String systemId) throws SAXException {
-        // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
-            return;
+        // Set Tag XML Consumer
+        if (tag instanceof XMLProducer) {
+            XMLConsumer tagConsumer;
+            if (transformerSelector != null) {
+                Transformer tagTransformer = null;
+                try {
+                    // Add additional (Tag)Transformer to the output of the Tag
+                    tagTransformer = (Transformer) transformerSelector.select(transformerHint);
+                    tagTransformerStack.push(tagTransformer);
+                    tagTransformer.setConsumer(currentConsumer);
+                    tagTransformer.setup(this.resolver, this.objectModel, null, this.parameters);
+                } catch (SAXException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new SAXException("Failed to setup tag transformer " + transformerHint, e);
+                }
+                tagConsumer = tagTransformer;
+            } else {
+                tagConsumer = this.currentConsumer;
+            }
 
-        currentConsumer.startDTD(name, publicId, systemId);
-    }
+            ((XMLProducer) tag).setConsumer(tagConsumer);
+        }
 
-    /**
-     * SAX Event handling
-     */
-    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
-        // recording for iteration ?
-        if (recordingLevel > 0) {
-            ++recordingLevel;
-            currentConsumer.startElement(namespaceURI, localName, qName, atts);
-            return;
+        // Setup Tag
+        try {
+            tag.setup(this.resolver, this.objectModel, this.parameters);
+        } catch (IOException e) {
+            throw new SAXException("Could not set up tag " + name, e);
         }
-        // If we are skipping the body of a Tag
-        if (skipLevel > 0) {
-            // Remember to skip one more end element
-            skipLevel++;
-            // and ignore this start element
-            return;
+
+        if (tag instanceof XMLConsumer) {
+            this.currentConsumer = (XMLConsumer) tag;
         }
+        this.currentTag = tag;
 
-        Tag tag = null;
-        if (namespaceURI != null && namespaceURI.length() > 0) {
-            ComponentSelector tagSelector = null;
-            Transformer tagTransformer = null;
+        // Set Tag-Attributes, Attributes are mapped to the coresponding Tag method
+        for (int i = 0; i < atts.getLength(); i++) {
+            String attributeName = atts.getLocalName(i);
+            String attributeValue = atts.getValue(i);
+            this.paramArray[0] = attributeValue;
             try {
-                tagSelector = (ComponentSelector) tagNamespaceSelector.select(namespaceURI);
-                tagSelectorStack.push(tagSelector);
-
-                // namespace matches tag library, lookup tag now.
-                tag = (Tag) tagSelector.select(localName);
-
-                // tag found, setup Tag and connect it to pipeline
-                tag.setParent(currentTag);
-                tag.setup(this.resolver, this.objectModel, this.parameters);
-
-                if (tag instanceof XMLProducer) {
-                    if (transformerSelector != null) {
-                        // add additional (Tag)Transformer to the output of the Tag
-                        tagTransformer = (Transformer) transformerSelector.select(transformerHint);
-                        tagTransformerStack.push(tagTransformer);
-                        tagTransformer.setup(this.resolver, this.objectModel, null, this.parameters);
-                        ((XMLProducer) tag).setConsumer(tagTransformer);
-                        tagTransformer.setConsumer(currentConsumer);
-                    }
-                }
-                if (tag instanceof XMLConsumer) {
-                    currentConsumer = (XMLConsumer) tag;
-                }
-
-                currentTag = tag;
-
-                // Set Tag-Attributes, Attributes are mapped to the coresponding Tag method
-                for (int i = 0; i < atts.getLength(); i++) {
-                    String attributeName = atts.getLocalName(i);
-                    String attributeValue = atts.getValue(i);
-                    paramArray[0] = attributeValue;
-                    try {
-                        Method method = getWriteMethod(tag.getClass(), attributeName);
-                        method.invoke(tag, paramArray);
-                    } catch (Throwable e) {
-                        if (getLogger().isInfoEnabled())
-                            getLogger().info("startElement(" + localName + "): Attribute " + attributeName + " not set", e);
-                    }
+                Method method = getWriteMethod(tag.getClass(), attributeName);
+                method.invoke(tag, this.paramArray);
+            } catch (Throwable e) {
+                if (getLogger().isInfoEnabled()) {
+                    getLogger().info("Tag " + name + " attribute " + attributeName + " not set", e);
                 }
-            } catch (Exception ignore) {
-                // No namespace or tag found, process it as normal element (tag == null)
-            }
-        }
-
-        tagStack.push(tag);
-        if (tag == null) {
-            currentConsumer.startElement(namespaceURI, localName, qName, atts);
-        } else {
-            int eval = tag.doStartTag(namespaceURI, localName, qName, atts);
-            switch (eval) {
-                case Tag.EVAL_BODY :
-                    skipLevel = 0;
-                    if (tag instanceof IterationTag) {
-                        // start recording for IterationTag
-                        try {
-                            xmlSerializer = (XMLSerializer) manager.lookup(XMLSerializer.ROLE);
-                            currentConsumerBackup = currentConsumer;
-                            currentConsumer = xmlSerializer;
-                            recordingLevel = 1;
-                        } catch (ServiceException e) {
-                            throw new SAXException("lookup XMLSerializer failed", e);
-                        }
-                    }
-                    break;
-
-                case Tag.SKIP_BODY :
-                    skipLevel = 1;
-                    break;
-
-                default :
-                    String tagName = tag.getClass().getName();
-                    getLogger().warn("Bad return value from doStartTag(" + tagName + "): " + eval);
-                    break;
             }
         }
     }
 
     /**
-     * SAX Event handling
+     * Start recording for the iterator tag.
      */
-    public void startEntity(String name) throws SAXException {
-        // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
-            return;
+    private void startRecording() throws SAXException {
+        try {
+            this.xmlSerializer = (XMLSerializer) manager.lookup(XMLSerializer.ROLE);
+        } catch (ServiceException e) {
+            throw new SAXException("Can't lookup XMLSerializer", e);
+        }
 
-        currentConsumer.startEntity(name);
+        this.currentConsumerBackup = this.currentConsumer;
+        this.currentConsumer = this.xmlSerializer;
+        this.recordingLevel = 1;
     }
 
     /**
-     * SAX Event handling
+     * End recording for the iterator tag and returns recorded XML fragment.
      */
-    public void startPrefixMapping(String prefix, String uri) throws SAXException {
-        // If we are skipping the body of a tag, ignore this...
-        if (skipLevel > 0)
-            return;
+    private Object endRecording() {
+        // Restore XML Consumer
+        this.currentConsumer = this.currentConsumerBackup;
+        this.currentConsumerBackup = null;
 
-        currentConsumer.startPrefixMapping(prefix, uri);
+        // Get XML Fragment
+        Object saxFragment = this.xmlSerializer.getSAXFragment();
+
+        // Release Serializer
+        this.manager.release(this.xmlSerializer);
+        this.xmlSerializer = null;
+
+        return saxFragment;
     }
 
     /**
-     *  Dispose this component.
+     * Find previous XML consumer when processing of current consumer
+     * is complete.
      */
-    public void dispose() {
-        this.manager.release(tagNamespaceSelector);
-        tagNamespaceSelector = null;
-        if (transformerSelector != null) {
-            this.manager.release(transformerSelector);
-            transformerSelector = null;
+    private void popConsumer() {
+        Tag loop = this.currentTag;
+        for (; loop != null; loop = loop.getParent()) {
+            if (loop instanceof XMLConsumer) {
+                this.currentConsumer = (XMLConsumer) loop;
+                return;
+            }
         }
+
+        this.currentConsumer = this.xmlConsumer;
     }
 
     private static Method getWriteMethod(Class type, String propertyName) throws IntrospectionException {
         Map map = getWriteMethodMap(type);
         Method method = (Method) map.get(propertyName);
-        if (method == null)
+        if (method == null) {
             throw new IntrospectionException("No such property: " + propertyName);
+        }
         return method;
     }
 
     private static Map getWriteMethodMap(Class beanClass) throws IntrospectionException {
-        Map map = (Map) writeMethodMap.get(beanClass);
-        if (map != null)
+        Map map = (Map) TAG_PROPERTIES_MAP.get(beanClass);
+        if (map != null) {
             return map;
+        }
 
         BeanInfo info = Introspector.getBeanInfo(beanClass);
         if (info != null) {
@@ -566,7 +618,7 @@
                 map.put(name, method);
             }
         }
-        writeMethodMap.put(beanClass, map);
+        TAG_PROPERTIES_MAP.put(beanClass, map);
         return map;
     }
 }