cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jheym...@apache.org
Subject svn commit: r330548 [56/132] - in /cocoon/whiteboard/maven2/cocoon-flat-layout: ./ cocoon-ajax-block/ cocoon-ajax-block/api/ cocoon-ajax-block/api/src/ cocoon-ajax-block/api/src/main/ cocoon-ajax-block/api/src/main/java/ cocoon-ajax-block/api/src/main/...
Date Thu, 03 Nov 2005 14:00:48 GMT
Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AbstractSAXTransformer.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AbstractSAXTransformer.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AbstractSAXTransformer.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AbstractSAXTransformer.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,1088 @@
+/*
+ * Copyright 1999-2005 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.transformation;
+
+import org.apache.avalon.excalibur.pool.Recyclable;
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.Context;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.Response;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.transformation.helpers.ParametersRecorder;
+import org.apache.cocoon.transformation.helpers.TextRecorder;
+import org.apache.cocoon.util.ClassUtils;
+import org.apache.cocoon.util.TraxErrorHandler;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.ImmutableAttributesImpl;
+import org.apache.cocoon.xml.IncludeXMLConsumer;
+import org.apache.cocoon.xml.SaxBuffer;
+import org.apache.cocoon.xml.XMLConsumer;
+import org.apache.cocoon.xml.XMLUtils;
+import org.apache.cocoon.xml.dom.DOMBuilder;
+
+import org.apache.excalibur.source.SourceParameters;
+import org.apache.excalibur.xml.sax.XMLizable;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Stack;
+
+/**
+ * This class is the basis for all transformers. It provides various useful
+ * methods and hooks for implementing own custom transformers.
+ *
+ * <p>The basic behaviour of each transformer consists of the following four
+ * parts:</p>
+ * <ul>
+ * <li>Listen for specific events with a given namespace</li>
+ * <li>Collect information via these events</li>
+ * <li>Process the information</li>
+ * <li>Create new events from the processed information</li>
+ * </ul>
+ *
+ * <p>For all these four purposes the AbstractSAXTransformer offers some
+ * powerful methods and hooks:</p>
+ *
+ * <h3>Namespace handling</h3>
+ * By setting the instance variable namespaceURI to the namespace the
+ * events are filtered and only events with this namespace are send to
+ * the two hooks: <code>startTransformingElement</code> and
+ * <code>endTransformingElement</code>. It is possible to override the default
+ * namespace for the transformer by specifying the parameter "namespaceURI"
+ * in the pipeline. This avoids possible namespace collisions.
+ *
+ * <h3>Recording of information</h3>
+ * There are several methods for recording information, e.g. startRecording(),
+ * startTextRecording() etc. These methods collect information from the xml
+ * stream for further processing.
+ *
+ * <h3>Creating new events</h3>
+ * New events can be easily created with the <code>sendEvents()</code>
+ * method, the <code>sendStartElementEvent()</code> methods, the
+ * <code>sendEndElementEvent()</code> method or the
+ * <code>sendTextEvent()</code> method.
+ *
+ * <h3>Initialization</h3>
+ * Before the document is processed the <code>setupTransforming</code> hook
+ * is invoked.
+ *
+ * @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
+ * @version $Id: AbstractSAXTransformer.java 292151 2005-09-28 08:59:44Z cziegeler $
+*/
+public abstract class AbstractSAXTransformer extends AbstractTransformer
+                                             implements Serviceable, Configurable, Recyclable, Disposable {
+
+    /**
+     * Empty immutable attributes (for performance). Use them
+     * whenever creating an element with no attributes.
+     */
+    protected static final Attributes EMPTY_ATTRIBUTES = XMLUtils.EMPTY_ATTRIBUTES;
+
+    /**
+     * The trax <code>TransformerFactory</code> used by this transformer.
+     */
+    private SAXTransformerFactory tfactory;
+
+    /**
+     * Controlls SAX event handling.
+     * If set to true all whitespace events are ignored.
+     */
+    protected boolean ignoreWhitespaces;
+
+    /**
+     * Controlls SAX event handling.
+     * If set to true all characters events containing only whitespaces
+     * are ignored.
+     */
+    protected boolean ignoreEmptyCharacters;
+
+    /**
+     * Controlls SAX event handling.
+     * If this is incremented all events are not forwarded to the next
+     * pipeline component, but the hooks are still called.
+     */
+    protected int ignoreEventsCount;
+
+    /**
+     * Controlls SAX event handling.
+     * If this is greater than zero, the hooks are not called. Attention,
+     * make sure, that you decrement this counter properly as your hooks are
+     * not called anymore!
+     */
+    protected int ignoreHooksCount;
+
+    /**
+     * The namespace used by the transformer for the SAX events filtering.
+     * This either equals to the {@link #defaultNamespaceURI} or to the value
+     * set by the <code>namespaceURI</code> sitemap parameter for the pipeline.
+     * Must never be null.
+     */
+    protected String namespaceURI;
+
+    /**
+     * This is the default namespace used by the transformer.
+     * Implementations should set its value in the constructor.
+     * Must never be null.
+     */
+    protected String defaultNamespaceURI;
+
+    /**
+     * A stack for collecting information.
+     * The stack is important for collection information especially when
+     * the tags can be nested.
+     */
+    protected final Stack stack = new Stack();
+
+    /**
+     * The stack of current used recorders
+     */
+    protected final Stack recorderStack = new Stack();
+
+    /**
+     * The current Request object
+     */
+    protected Request request;
+
+    /**
+     * The current Response object
+     */
+    protected Response response;
+
+    /**
+     * The current Context object
+     */
+    protected Context context;
+
+    /**
+     * The current objectModel of the environment
+     */
+    protected Map objectModel;
+
+    /**
+     * The parameters specified in the sitemap
+     */
+    protected Parameters parameters;
+
+    /**
+     * The source attribute specified in the sitemap
+     */
+    protected String source;
+
+    /**
+     * The Avalon ServiceManager for getting Components
+     */
+    protected ServiceManager manager;
+
+    /**
+     * The SourceResolver for this request
+     */
+    protected SourceResolver resolver;
+
+    /**
+     * Are we already initialized for the current request?
+     */
+    private boolean isInitialized;
+
+    /**
+     * The namespaces and their prefixes
+     */
+    private final List namespaces = new ArrayList(5);
+
+    /**
+     * The current prefix for our namespace
+     */
+    private String ourPrefix;
+
+    //
+    // Lifecycle
+    //
+
+    /* (non-Javadoc)
+     * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
+     */
+    public void service(ServiceManager manager) throws ServiceException {
+        this.manager = manager;
+    }
+
+    /* (non-Javadoc)
+     * @see Configurable#configure(Configuration)
+     */
+    public void configure(Configuration configuration) throws ConfigurationException {
+        String tFactoryClass = configuration.getChild("transformer-factory").getValue(null);
+        if (tFactoryClass != null) {
+            try {
+                this.tfactory = (SAXTransformerFactory) ClassUtils.newInstance(tFactoryClass);
+                if (getLogger().isDebugEnabled()) {
+                    getLogger().debug("Using transformer factory " + tFactoryClass);
+                }
+            } catch (Exception e) {
+                throw new ConfigurationException("Cannot load transformer factory " + tFactoryClass, e);
+            }
+        } else {
+            // Standard TrAX behaviour
+            this.tfactory = (SAXTransformerFactory) TransformerFactory.newInstance();
+        }
+        tfactory.setErrorListener(new TraxErrorHandler(getLogger()));
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(SourceResolver, Map, String, Parameters)
+     */
+    public void setup(SourceResolver resolver,
+                      Map            objectModel,
+                      String         src,
+                      Parameters     params)
+    throws ProcessingException, SAXException, IOException {
+
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Setup resolver=" + resolver +
+                              ", objectModel=" + objectModel +
+                              ", src=" + src +
+                              ", parameters=" + params);
+        }
+
+        // defaultNamespaceURI should never be null
+        if (this.defaultNamespaceURI == null) {
+            this.defaultNamespaceURI = "";
+        }
+        this.objectModel = objectModel;
+
+        this.request = ObjectModelHelper.getRequest(objectModel);
+        this.response = ObjectModelHelper.getResponse(objectModel);
+        this.context = ObjectModelHelper.getContext(objectModel);
+        this.resolver = resolver;
+        this.parameters = params;
+        this.source = src;
+        this.isInitialized = false;
+
+        // get the current namespace
+        this.namespaceURI = params.getParameter("namespaceURI",
+                                                this.defaultNamespaceURI);
+
+        this.ignoreHooksCount = 0;
+        this.ignoreEventsCount = 0;
+        this.ignoreWhitespaces = true;
+        this.ignoreEmptyCharacters = false;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
+     */
+    public void recycle() {
+        this.namespaceURI = null;
+        this.objectModel = null;
+        this.request = null;
+        this.response = null;
+        this.context = null;
+        this.resolver = null;
+        this.stack.clear();
+        this.recorderStack.clear();
+        this.parameters = null;
+        this.source = null;
+        this.namespaces.clear();
+        this.ourPrefix = null;
+
+        super.recycle();
+    }
+
+    public void dispose() {
+        this.manager = null;
+    }
+
+    //
+    // SAX ContentHandler methods
+    //
+
+    /**
+     * Process the SAX event.
+     * @see ContentHandler#setDocumentLocator
+     */
+    public void setDocumentLocator(Locator locator) {
+        if (this.ignoreEventsCount == 0) {
+            super.setDocumentLocator(locator);
+        }
+    }
+
+    /**
+     * Process the SAX event. A new document is processed. The hook method
+     * {@link #setupTransforming} is invoked.
+     * @see ContentHandler#startDocument
+     */
+    public void startDocument()
+    throws SAXException {
+        if (!this.isInitialized) {
+            try {
+                setupTransforming();
+            } catch (ProcessingException e) {
+                throw new SAXException("ProcessingException: " + e, e);
+            } catch (IOException e) {
+                throw new SAXException("IOException: " + e, e);
+            }
+            this.isInitialized = true;
+        }
+
+        if (this.ignoreEventsCount == 0) {
+            super.startDocument();
+        }
+    }
+
+    /**
+     * Process the SAX event. The processing of the document is finished.
+     * @see org.xml.sax.ContentHandler#endDocument
+     */
+    public void endDocument()
+    throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.endDocument();
+        }
+    }
+
+    /**
+     * Process the SAX event.
+     * @see org.xml.sax.ContentHandler#startPrefixMapping
+     */
+    public void startPrefixMapping(String prefix, String uri)
+    throws SAXException {
+        if (prefix != null) {
+            this.namespaces.add(new String[] {prefix, uri});
+        }
+        if (namespaceURI.equals(uri)) {
+            this.ourPrefix = prefix;
+        }
+        if (this.ignoreEventsCount == 0) {
+            super.startPrefixMapping(prefix, uri);
+        }
+    }
+
+    /**
+     * Process the SAX event.
+     * @see org.xml.sax.ContentHandler#endPrefixMapping
+     */
+    public void endPrefixMapping(String prefix)
+    throws SAXException {
+
+        if (prefix != null) {
+            // Find and remove the namespace prefix
+            boolean found = false;
+            for (int i = this.namespaces.size() - 1; i >= 0; i--) {
+                final String[] prefixAndUri = (String[]) this.namespaces.get(i);
+                if (prefixAndUri[0].equals(prefix)) {
+                    this.namespaces.remove(i);
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                throw new SAXException("Namespace for prefix '" + prefix + "' not found.");
+            }
+
+            if (prefix.equals(this.ourPrefix)) {
+                // Reset our current prefix
+                this.ourPrefix = null;
+
+                // Now search if we have a different prefix for our namespace
+                for (int i = this.namespaces.size() - 1; i >= 0; i--) {
+                    final String[] prefixAndUri = (String[]) this.namespaces.get(i);
+                    if (namespaceURI.equals(prefixAndUri[1])) {
+                        this.ourPrefix = prefixAndUri[0];
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (this.ignoreEventsCount == 0) {
+            super.endPrefixMapping(prefix);
+        }
+    }
+
+    /**
+     * Process the SAX event. The namespace of the event is checked.
+     * If it is the defined namespace for this transformer,
+     * the {@link #startTransformingElement} hook is called.
+     * @see org.xml.sax.ContentHandler#startElement
+     */
+    public void startElement(String uri,
+                             String name,
+                             String raw,
+                             Attributes attr)
+    throws SAXException {
+        if (namespaceURI.equals(uri) && ignoreHooksCount == 0) {
+            // this is our namespace:
+            try {
+                startTransformingElement(uri, name, raw, attr);
+            } catch (ProcessingException e) {
+                throw new SAXException("ProcessingException: " + e, e);
+            } catch (IOException e) {
+                throw new SAXException("IOException occured during processing: " + e, e);
+            }
+        } else {
+            if (ignoreEventsCount == 0) {
+                super.startElement(uri, name, raw, attr);
+            }
+        }
+    }
+
+    /**
+     * Process the SAX event. The namespace of the event is checked.
+     * If it is the defined namespace for this transformer,
+     * the {@link #endTransformingElement} hook is called.
+     * @see org.xml.sax.ContentHandler#endElement
+     */
+    public void endElement(String uri, String name, String raw)
+    throws SAXException {
+        if (namespaceURI.equals(uri) && this.ignoreHooksCount == 0) {
+            // this is our namespace:
+            try {
+                endTransformingElement(uri, name, raw);
+            } catch (ProcessingException e) {
+                throw new SAXException("ProcessingException: " + e, e);
+            } catch (IOException e) {
+                throw new SAXException("IOException occured during processing: " + e, e);
+            }
+        } else {
+            if (ignoreEventsCount == 0) {
+                super.endElement(uri, name, raw);
+            }
+        }
+    }
+
+    /**
+     * Process the SAX event.
+     * @see org.xml.sax.ContentHandler#characters
+     */
+    public void characters(char[] p0, int p1, int p2)
+    throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            if (this.ignoreEmptyCharacters) {
+                String value = new String(p0, p1, p2);
+                if (value.trim().length() > 0) {
+                    super.characters(p0, p1, p2);
+                }
+            } else {
+                super.characters(p0, p1, p2);
+            }
+        }
+    }
+
+    /**
+     * Process the SAX event.
+     * @see org.xml.sax.ContentHandler#ignorableWhitespace
+     */
+    public void ignorableWhitespace(char[] p0, int p1, int p2)
+    throws SAXException {
+        if (ignoreWhitespaces == false && ignoreEventsCount == 0) {
+            super.ignorableWhitespace(p0, p1, p2);
+        }
+    }
+
+    /**
+     * Process the SAX event.
+     * @see ContentHandler#processingInstruction
+     */
+    public void processingInstruction(String target, String data)
+    throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.processingInstruction(target, data);
+        }
+    }
+
+    /**
+     * Process the SAX event.
+     * @see ContentHandler#skippedEntity
+     */
+    public void skippedEntity(String name)
+    throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.skippedEntity(name);
+        }
+    }
+
+    //
+    // SAX LexicalHandler methods
+    //
+
+    /**
+     * @see LexicalHandler#startDTD
+     */
+    public void startDTD(String name, String public_id, String system_id)
+    throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.startDTD(name, public_id, system_id);
+        }
+    }
+
+    /**
+     * @see LexicalHandler#endDTD
+     */
+    public void endDTD() throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.endDTD();
+        }
+    }
+
+    /**
+     * @see LexicalHandler#startEntity
+     */
+    public void startEntity (String name)
+    throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.startEntity(name);
+        }
+    }
+
+    /**
+     * @see LexicalHandler#endEntity
+     */
+    public void endEntity (String name)
+    throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.endEntity(name);
+        }
+    }
+
+    /**
+     * @see LexicalHandler#startCDATA
+     */
+    public void startCDATA() throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.startCDATA();
+        }
+    }
+
+    /**
+     * @see LexicalHandler#endCDATA
+     */
+    public void endCDATA() throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.endCDATA();
+        }
+    }
+
+    /**
+     * @see LexicalHandler#comment
+     */
+    public void comment(char ary[], int start, int length)
+    throws SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.comment(ary, start, length);
+        }
+    }
+
+
+    /*
+     * Recording of events.
+     * With this method all events are not forwarded to the next component in the pipeline.
+     * They are recorded to create a document fragment.
+     */
+
+    private LexicalHandler   originalLexicalHandler;
+    private ContentHandler   originalContentHandler;
+
+    /**
+     * Add a new recorder to the recording chain.
+     * Do not invoke this method directly.
+     */
+    protected void addRecorder(XMLConsumer recorder) {
+        if (this.recorderStack.empty()) {
+            // redirect if first (top) recorder
+            this.originalLexicalHandler = this.lexicalHandler;
+            this.originalContentHandler = this.contentHandler;
+        }
+        setContentHandler(recorder);
+        setLexicalHandler(recorder);
+        this.recorderStack.push(recorder);
+    }
+
+    /**
+     * Remove a recorder from the recording chain.
+     * Do not invoke this method directly.
+     */
+    protected Object removeRecorder() {
+        Object recorder = this.recorderStack.pop();
+        if (this.recorderStack.empty() == true) {
+            // undo redirect if no recorder any more
+            setContentHandler(originalContentHandler);
+            setLexicalHandler(originalLexicalHandler);
+            this.originalLexicalHandler = null;
+            this.originalContentHandler = null;
+        } else {
+            XMLConsumer next = (XMLConsumer) recorderStack.peek();
+            setContentHandler(next);
+            setLexicalHandler(next);
+        }
+
+        return recorder;
+    }
+
+    /**
+     * Start recording of SAX events.
+     * All incoming events are recorded and not forwarded. The resulting
+     * XMLizable can be obtained by the matching {@link #endSAXRecording} call.
+     * @since 2.1.5
+     */
+    public void startSAXRecording()
+    throws SAXException {
+        addRecorder(new SaxBuffer());
+        sendStartPrefixMapping();
+    }
+
+    /**
+     * Stop recording of SAX events.
+     * This method returns the resulting XMLizable.
+     * @since 2.1.5
+     */
+    public XMLizable endSAXRecording()
+    throws SAXException {
+        sendEndPrefixMapping();
+        return (XMLizable) removeRecorder();
+    }
+
+    /**
+     * Start recording of a text.
+     * No events forwarded, and all characters events
+     * are collected into a string.
+     */
+    public void startTextRecording()
+    throws SAXException {
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Start text recording");
+        }
+        addRecorder(new TextRecorder());
+        sendStartPrefixMapping();
+    }
+
+    /**
+     * Stop recording of text and return the recorded information.
+     * @return The String, trimmed.
+     */
+    public String endTextRecording()
+    throws SAXException {
+        sendEndPrefixMapping();
+
+        TextRecorder recorder = (TextRecorder) removeRecorder();
+        String text = recorder.getText();
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("End text recording. Text=" + text);
+        }
+        return text;
+    }
+
+    /**
+     * Start recording of serialized xml
+     * All events are converted to an xml string which can be retrieved by
+     * endSerializedXMLRecording.
+     * @param format The format for the serialized output. If <CODE>null</CODE>
+     *               is specified, the default format is used.
+     */
+    public void startSerializedXMLRecording(Properties format)
+    throws SAXException {
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Start serialized XML recording. Format=" + format);
+        }
+        this.stack.push(format == null? XMLUtils.createPropertiesForXML(false): format);
+        startSAXRecording();
+    }
+
+    /**
+     * Return the serialized xml string.
+     * @return A string containing the recorded xml information, formatted by
+     * the properties passed to the corresponding startSerializedXMLRecording().
+     */
+    public String endSerializedXMLRecording()
+    throws SAXException, ProcessingException {
+        XMLizable xml = endSAXRecording();
+        String text = XMLUtils.serialize(xml, (Properties) this.stack.pop());
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("End serialized XML recording. XML=" + text);
+        }
+        return text;
+    }
+
+    /**
+     * Start recording of parameters.
+     * All events are not forwarded and the incoming xml is converted to
+     * parameters. Each toplevel node is a parameter and its text subnodes
+     * form the value.
+     * The Parameters can eiter be retrieved by endParametersRecording().
+     */
+    public void startParametersRecording()
+    throws SAXException {
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Start parameters recording");
+        }
+        addRecorder(new ParametersRecorder());
+        sendStartPrefixMapping();
+    }
+
+    /**
+     * End recording of parameters
+     * If source is null a new parameters object is created, otherwise
+     * the parameters are added to this object.
+     * @param source An optional parameters object.
+     * @return The object containing all parameters.
+     */
+    public SourceParameters endParametersRecording(Parameters source)
+    throws SAXException {
+        sendEndPrefixMapping();
+
+        ParametersRecorder recorder = (ParametersRecorder) this.removeRecorder();
+        SourceParameters parameters = recorder.getParameters(source);
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("End parameters recording. Parameters=" + parameters);
+        }
+        return parameters;
+    }
+
+    /**
+     * End recording of parameters
+     * If source is null a new parameters object is created, otherwise
+     * the parameters are added to this object.
+     * @param source An optional parameters object.
+     * @return The object containing all parameters.
+     */
+    public SourceParameters endParametersRecording(SourceParameters source)
+    throws SAXException {
+        sendEndPrefixMapping();
+
+        ParametersRecorder recorder = (ParametersRecorder) removeRecorder();
+        SourceParameters parameters = recorder.getParameters(source);
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("End parameters recording. Parameters=" + parameters);
+        }
+        return parameters;
+    }
+
+    /**
+     * Start DOM DocumentFragment recording.
+     * All incoming events are recorded and not forwarded. The resulting
+     * DocumentFragment can be obtained by the matching {@link #endRecording} call.
+     */
+    public void startRecording()
+    throws SAXException {
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Start recording");
+        }
+        DOMBuilder builder = new DOMBuilder(this.tfactory);
+        addRecorder(builder);
+        builder.startDocument();
+        builder.startElement("", "cocoon", "cocoon", EMPTY_ATTRIBUTES);
+        sendStartPrefixMapping();
+    }
+
+    /**
+     * Stop DOM DocumentFragment recording.
+     * This method returns the resulting DocumentFragment, normalized.
+     */
+    public DocumentFragment endRecording()
+    throws SAXException {
+        sendEndPrefixMapping();
+
+        DOMBuilder builder = (DOMBuilder) removeRecorder();
+        builder.endElement("", "cocoon", "cocoon");
+        builder.endDocument();
+
+        // Create Document Fragment
+        final Document doc = builder.getDocument();
+        final DocumentFragment fragment = doc.createDocumentFragment();
+        final Node root = doc.getDocumentElement();
+
+        // Remove empty text nodes and collapse neighbouring text nodes
+        root.normalize();
+
+        // Move all nodes into the fragment
+        boolean space = true;
+        while (root.hasChildNodes()) {
+            Node child = root.getFirstChild();
+            root.removeChild(child);
+
+            // Leave out leading whitespace nodes
+            // FIXME: Why leading spaces are trimmed at all? Why not trailing spaces?
+            if (space && child.getNodeType() == Node.TEXT_NODE
+                    && child.getNodeValue().trim().length() == 0) {
+                continue;
+            }
+            space = false;
+
+            fragment.appendChild(child);
+        }
+
+        if (getLogger().isDebugEnabled()) {
+            Object serializedXML = null;
+            try {
+                serializedXML = fragment == null? "null": XMLUtils.serializeNode(fragment);
+            } catch (ProcessingException ignore) {
+                serializedXML = fragment;
+            }
+            getLogger().debug("End recording. Fragment=" + serializedXML);
+        }
+
+        return fragment;
+    }
+
+    //
+    // Hooks
+    //
+
+    /**
+     * Setup the transformation of an xml document.
+     * This method is called just before the transformation (sending of sax events)
+     * starts. It should be used to initialize setup parameter depending on the
+     * object modell.
+     */
+    public void setupTransforming()
+    throws IOException, ProcessingException, SAXException {
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("setupTransforming");
+        }
+        this.stack.clear();
+        this.recorderStack.clear();
+        this.ignoreWhitespaces = true;
+        this.ignoreEmptyCharacters = false;
+    }
+
+    /**
+     * Start processing elements of our namespace.
+     * This hook is invoked for each sax event with our namespace.
+     * @param uri The namespace of the element.
+     * @param name The local name of the element.
+     * @param raw The qualified name of the element.
+     * @param attr The attributes of the element.
+     */
+    public void startTransformingElement(String uri,
+                                         String name,
+                                         String raw,
+                                         Attributes attr)
+    throws ProcessingException, IOException, SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.startElement(uri, name, raw, attr);
+        }
+    }
+
+    /**
+     * Start processing elements of our namespace.
+     * This hook is invoked for each sax event with our namespace.
+     * @param uri The namespace of the element.
+     * @param name The local name of the element.
+     * @param raw The qualified name of the element.
+     */
+    public void endTransformingElement(String uri,
+                                       String name,
+                                       String raw)
+    throws ProcessingException, IOException, SAXException {
+        if (this.ignoreEventsCount == 0) {
+            super.endElement(uri, name, raw);
+        }
+    }
+
+    /**
+     * Send SAX events to the next pipeline component.
+     * The characters event for the given text is send to the next
+     * component in the current pipeline.
+     * @param text The string containing the information.
+     */
+    public void sendTextEvent(String text)
+    throws SAXException {
+        characters(text.toCharArray(), 0, text.length());
+    }
+
+    /**
+     * Send SAX events to the next pipeline component.
+     * The startElement event for the given element is send
+     * to the next component in the current pipeline.
+     * The element has no namespace and no attributes
+     * @param localname The name of the event.
+     */
+    public void sendStartElementEvent(String localname)
+    throws SAXException {
+        startElement("", localname, localname, EMPTY_ATTRIBUTES);
+    }
+
+    /**
+     * Send SAX events to the next pipeline component.
+     * The startElement event for the given element is send
+     * to the next component in the current pipeline.
+     * The element has the namespace of the transformer,
+     * but not attributes
+     * @param localname The name of the event.
+     */
+    public void sendStartElementEventNS(String localname)
+    throws SAXException {
+        startElement(this.namespaceURI,
+                     localname, this.ourPrefix + ':' + localname, EMPTY_ATTRIBUTES);
+    }
+
+    /**
+     * Send SAX events to the next pipeline component.
+     * The startElement event for the given element is send
+     * to the next component in the current pipeline.
+     * The element has no namespace.
+     * @param localname The name of the event.
+     * @param attr The Attributes of the element
+     */
+    public void sendStartElementEvent(String localname, Attributes attr)
+    throws SAXException {
+        startElement("", localname, localname, attr);
+    }
+
+    /**
+     * Send SAX events to the next pipeline component.
+     * The startElement event for the given element is send
+     * to the next component in the current pipeline.
+     * The element has the namespace of the transformer.
+     * @param localname The name of the event.
+     * @param attr The Attributes of the element
+     */
+    public void sendStartElementEventNS(String localname, Attributes attr)
+    throws SAXException {
+        startElement(this.namespaceURI,
+                     localname, this.ourPrefix + ':' + localname, attr);
+    }
+
+    /**
+     * Send SAX events to the next pipeline component.
+     * The endElement event for the given element is send
+     * to the next component in the current pipeline.
+     * The element has no namespace.
+     * @param localname The name of the event.
+     */
+    public void sendEndElementEvent(String localname)
+    throws SAXException {
+        endElement("", localname, localname);
+    }
+
+    /**
+     * Send SAX events to the next pipeline component.
+     * The endElement event for the given element is send
+     * to the next component in the current pipeline.
+     * The element has the namespace of the transformer.
+     * @param localname The name of the event.
+     */
+    public void sendEndElementEventNS(String localname)
+    throws SAXException {
+        endElement(this.namespaceURI,
+                   localname, this.ourPrefix + ':' + localname);
+    }
+
+    /**
+     * Send SAX events to the next pipeline component.
+     * The node is parsed and the events are send to
+     * the next component in the pipeline.
+     * @param node The tree to be included.
+     */
+    public void sendEvents(Node node)
+    throws SAXException {
+        IncludeXMLConsumer.includeNode(node, this, this);
+    }
+
+    /**
+     * Send SAX events for the <code>SourceParameters</code>.
+     * For each parametername/value pair an element is
+     * created with the name of the parameter and the content
+     * of this element is the value.
+     */
+    public void sendParametersEvents(SourceParameters pars)
+    throws SAXException {
+
+        if (pars != null) {
+            Iterator names = pars.getParameterNames();
+            while (names.hasNext()) {
+                final String currentName = (String)names.next();
+                Iterator values = pars.getParameterValues(currentName);
+                while (values.hasNext()) {
+                    final String currentValue = (String)values.next();
+                    sendStartElementEvent(currentName);
+                    sendTextEvent(currentValue);
+                    sendEndElementEvent(currentName);
+                }
+            }
+        }
+    }
+
+    /**
+     * Send all start prefix mapping events to the current content handler
+     */
+    protected void sendStartPrefixMapping()
+    throws SAXException {
+        final int l = this.namespaces.size();
+        for (int i = 0; i < l; i++) {
+            String[] prefixAndUri = (String[]) this.namespaces.get(i);
+            super.contentHandler.startPrefixMapping(prefixAndUri[0], prefixAndUri[1]);
+        }
+    }
+
+    /**
+     * Send all end prefix mapping events to the current content handler
+     */
+    protected void sendEndPrefixMapping()
+    throws SAXException {
+        final int l = this.namespaces.size();
+        for (int i = 0; i < l; i++) {
+            String[] prefixAndUri = (String[]) this.namespaces.get(i);
+            super.contentHandler.endPrefixMapping(prefixAndUri[0]);
+        }
+    }
+
+    /**
+     * Find prefix mapping for the given namespace URI.
+     * @return Prefix mapping or null if no prefix defined
+     */
+    protected String findPrefixMapping(String uri) {
+        final int l = this.namespaces.size();
+        for (int i = 0; i < l; i++) {
+            String[] prefixAndUri = (String[]) this.namespaces.get(i);
+            if (prefixAndUri[1].equals(uri)) {
+                return prefixAndUri[0];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Helper method to get a modifiable attribute set.
+     */
+    protected AttributesImpl getMutableAttributes(Attributes a) {
+        if ( a instanceof AttributesImpl && !(a instanceof ImmutableAttributesImpl)) {
+            return (AttributesImpl)a;
+        }
+        return new AttributesImpl(a);
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AbstractSAXTransformer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AbstractTransformer.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AbstractTransformer.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AbstractTransformer.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AbstractTransformer.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.transformation;
+
+import org.apache.cocoon.xml.AbstractXMLPipe;
+
+/**
+ *
+ * @author <a href="mailto:pier@apache.org">Pierpaolo Fumagalli</a>
+ *         (Apache Software Foundation)
+ * @version CVS $Id: AbstractTransformer.java 30941 2004-07-29 19:56:58Z vgritsenko $
+ */
+
+public abstract class AbstractTransformer extends AbstractXMLPipe implements Transformer {}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AbstractTransformer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AugmentTransformer.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AugmentTransformer.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AugmentTransformer.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AugmentTransformer.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,135 @@
+/*
+* 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.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.cocoon.transformation;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+* @cocoon.sitemap.component.documentation
+ * Augments all <code>href</code> attributes with the full path to
+ * the request. You can optionally specify the <code>mount</code>
+ * parameter.
+ * 
+ * @cocoon.sitemap.component.name   augment
+ * @cocoon.sitemap.component.logger sitemap.transformer.augment
+ * 
+ * @author <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
+ * @since October 10, 2001
+ * @version CVS $Id: AugmentTransformer.java 30941 2004-07-29 19:56:58Z vgritsenko $
+ */
+public class AugmentTransformer
+    extends AbstractTransformer {
+        
+    protected Map objectModel;
+    protected Request request;
+    protected String baseURI;
+  
+    public void setup(SourceResolver resolver,
+                      Map objectModel,
+                      String source,
+                      Parameters parameters)
+    throws ProcessingException, SAXException, IOException {
+        this.objectModel = objectModel;
+        this.request = ObjectModelHelper.getRequest( this.objectModel );
+    
+        String mountPoint = parameters.getParameter("mount", null);
+        
+        StringBuffer uribuf = new StringBuffer();
+        boolean isSecure = this.request.isSecure();
+        int port = this.request.getServerPort();
+    
+        if (isSecure) {
+            uribuf.append("https://");
+        } else {
+            uribuf.append("http://");
+        }
+        uribuf.append(request.getServerName());
+    
+        if (isSecure) {
+            if (port != 443) {
+                uribuf.append(":").append(port);
+            }
+        } else {
+            if (port != 80) {
+                uribuf.append(":").append(port);
+            }
+        }
+        if (mountPoint == null) {
+            String requestedURI = this.request.getRequestURI();
+            requestedURI = requestedURI.substring(0, requestedURI.lastIndexOf("/"));
+            uribuf.append(requestedURI);
+            uribuf.append("/");
+        } else {
+            uribuf.append(request.getContextPath());
+            uribuf.append("/");
+            uribuf.append(mountPoint);
+        }
+        this.baseURI = uribuf.toString();
+    }
+
+    public void startElement(String uri,
+                             String name,
+                             String qname,
+                             Attributes attrs)
+    throws SAXException {
+        AttributesImpl newAttrs = null;
+    
+        for (int i = 0, size = attrs.getLength(); i < size; i++) {
+            String attrName = attrs.getLocalName(i);
+            if (attrName.equals("href")) {
+                String value = attrs.getValue(i);
+
+                // Don't touch the attribute if it's an absolute URL
+                if (value.startsWith("http:") || value.startsWith("https:")) {
+                    continue;
+                }
+
+                if (newAttrs == null) {
+                    newAttrs = new AttributesImpl(attrs);
+                }
+
+                String newValue = baseURI + value;
+                newAttrs.setValue(i, newValue);
+            }
+        }
+
+        if (newAttrs == null) {
+            super.startElement(uri, name, qname, attrs);
+        } else {
+            super.startElement(uri, name, qname, newAttrs);
+        }
+    }
+
+    /**
+     * Recyclable
+     */
+    public void recycle() {
+        this.objectModel = null;
+        this.request = null;
+        this.baseURI = null;
+        super.recycle();
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/AugmentTransformer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/CIncludeTransformer.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/CIncludeTransformer.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/CIncludeTransformer.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/CIncludeTransformer.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,732 @@
+/*
+ * Copyright 1999-2005 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.transformation;
+
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.caching.CacheableProcessingComponent;
+import org.apache.cocoon.components.sax.XMLDeserializer;
+import org.apache.cocoon.components.sax.XMLSerializer;
+import org.apache.cocoon.components.source.SourceUtil;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.transformation.helpers.IncludeCacheManager;
+import org.apache.cocoon.transformation.helpers.IncludeCacheManagerSession;
+import org.apache.cocoon.xml.IncludeXMLConsumer;
+import org.apache.cocoon.xml.XMLConsumer;
+import org.apache.cocoon.xml.XMLUtils;
+
+import org.apache.commons.lang.BooleanUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceException;
+import org.apache.excalibur.source.SourceParameters;
+import org.apache.excalibur.source.SourceValidity;
+import org.apache.excalibur.xml.dom.DOMParser;
+import org.apache.excalibur.xml.xpath.XPathProcessor;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * @cocoon.sitemap.component.documentation
+ * This transformer triggers for the element <code>include</code> in the
+ * namespace "http://apache.org/cocoon/include/1.0".
+ * The <code>src</code> attribute contains the url which points to
+ * an xml resource which is include instead of the element.
+ * With the attributes <code>element</code>, <code>ns</code> and
+ * <code>prefix</code> it is possible to specify an element
+ * which surrounds the included content.
+ *
+ * @cocoon.sitemap.component.name   cinclude
+ * @cocoon.sitemap.component.logger sitemap.transformer.cinclude
+ * @cocoon.sitemap.component.documentation.caching
+ *               See documentation for further information.
+ *
+ * @cocoon.sitemap.component.pooling.max  16
+ *
+ * This transformer also supports a more verbose but flexible version:
+ * <cinclude:includexml xmlns:cinclude="http://apache.org/cocoon/include/1.0" ignoreErrors="false">
+ *     <cinclude:src>THE SRC URI</cinclude:src>
+ *     <!-- This is an optional configuration block -->
+ *     <cinclude:configuration>
+ *         <!-- For example if you want to make a HTTP POST -->
+ *         <cinclude:parameter>
+ *             <cinclude:name>method</cinclude:name>
+ *             <cinclude:value>POST</cinclude:value>
+ *         </cinclude:parameter>
+ *     </cinclude:configuration>
+ *     <!-- The following are optional parameters appended to the URI -->
+ *     <cinclude:parameters>
+ *         <cinclude:parameter>
+ *             <cinclude:name>a name</cinclude:name>
+ *             <cinclude:value>a value</cinclude:value>
+ *         </cinclude:parameter>
+ *         <!-- more can follow -->
+ *     </cinclude:parameters>
+ * </cinclude:includexml>
+ *
+ *
+ * This transformer also supports caching of the included content.
+ * Therefore it triggers for the element <code>cached-include</code> in the
+ * namespace "http://apache.org/cocoon/include/1.0".
+ * The <code>src</code> attribute contains the url which points to
+ * an xml resource which is include instead of the element.
+ * First, it works like the usual include command. But it can be
+ * configured with various parameters:
+ * The most important one is the <code>expires</code> parameter.
+ * If (and only if) this is set to a value greater than zero,
+ * all included content is cached for the given period of time.
+ * So if any other request includes the same URI, the content
+ * is fetched from the cache. The expires value is in seconds.
+ * Usually the content is cached in the usual store, but you
+ * can also define a writeable source with the <code>source</code> parameter,
+ * e.g. "file:/c:/temp". Then the cached content is written into this
+ * directory.
+ * With the optional <code>purge</code> set to <code>true</code>
+ * the cache is purged which means the cached content is regarded as
+ * invalid nevertheless if it has expired or not.
+ * With the optional parameter <code>parallel</code> the various
+ * included contents are processed (included) in parallel rather than
+ * in a series.
+ * With the optional parameter <code>preemptive</code> set to <code>true</code>
+ * a pre-emptive caching is activated. When a resource is requested with
+ * pre-emptive caching, this transformer always attempts to get the
+ * content from the cache. If the content is not in the cache, it is
+ * of course retrieved from the original source and cached.
+ * If the cached resource has expired, it is still provided. The cache
+ * is updated by a background task. This task has to be started
+ * beforehand.
+ *
+ * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
+ * @author <a href="mailto:acoliver@apache.org">Andrew C. Oliver</a>
+ * @version $Id: CIncludeTransformer.java 265722 2005-09-01 13:39:33Z cziegeler $
+ */
+public class CIncludeTransformer extends AbstractSAXTransformer
+                                 implements Disposable, CacheableProcessingComponent {
+
+    public static final String CINCLUDE_NAMESPACE_URI = "http://apache.org/cocoon/include/1.0";
+    public static final String CINCLUDE_INCLUDE_ELEMENT = "include";
+    public static final String CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE = "src";
+    public static final String CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE = "element";
+    public static final String CINCLUDE_INCLUDE_ELEMENT_SELECT_ATTRIBUTE = "select";
+    public static final String CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE = "ns";
+    public static final String CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE = "prefix";
+    public static final String CINCLUDE_INCLUDE_ELEMENT_STRIP_ROOT_ATTRIBUTE = "strip-root";
+
+    public static final String CINCLUDE_INCLUDEXML_ELEMENT    = "includexml";
+    public static final String CINCLUDE_INCLUDEXML_ELEMENT_IGNORE_ERRORS_ATTRIBUTE = "ignoreErrors";
+    public static final String CINCLUDE_SRC_ELEMENT           = "src";
+    public static final String CINCLUDE_CONFIGURATION_ELEMENT = "configuration";
+    public static final String CINCLUDE_PARAMETERS_ELEMENT    = "parameters";
+    public static final String CINCLUDE_PARAMETER_ELEMENT     = "parameter";
+    public static final String CINCLUDE_NAME_ELEMENT          = "name";
+    public static final String CINCLUDE_VALUE_ELEMENT         = "value";
+
+    public static final String CINCLUDE_CACHED_INCLUDE_ELEMENT = "cached-include";
+    protected static final String CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT = "cached-includep";
+
+    private static final int STATE_OUTSIDE   = 0;
+    private static final int STATE_INCLUDE   = 1;
+
+    /** The configuration of includexml */
+    protected Parameters configurationParameters;
+
+    /** The parameters for includexml */
+    protected SourceParameters resourceParameters;
+
+    /** The current state: STATE_ */
+    protected int state;
+
+    protected IncludeCacheManager cacheManager;
+
+    protected IncludeCacheManagerSession cachingSession;
+
+    protected boolean compiling;
+
+    protected IncludeXMLConsumer filter;
+
+    protected XMLSerializer recorder;
+
+    protected AttributesImpl srcAttributes = new AttributesImpl();
+
+    protected boolean supportCaching;
+
+    /** Remember the start time of the request for profiling */
+    protected long startTime;
+
+   /**
+     * Constructor
+     * Set the namespace
+     */
+    public CIncludeTransformer() {
+        this.defaultNamespaceURI = CINCLUDE_NAMESPACE_URI;
+    }
+
+    /**
+     * Setup the component.
+     */
+    public void setup(SourceResolver resolver, Map objectModel,
+                      String source, Parameters parameters)
+    throws ProcessingException, SAXException, IOException {
+        super.setup(resolver, objectModel, source, parameters);
+        this.state = STATE_OUTSIDE;
+        if ( null != this.cacheManager ) {
+            this.cachingSession = this.cacheManager.getSession( this.parameters );
+        }
+        this.compiling = false;
+        this.supportCaching = parameters.getParameterAsBoolean("support-caching", false);
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Starting, session " + this.cachingSession);
+            this.startTime = System.currentTimeMillis();
+        }
+    }
+
+    /**
+     * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
+     */
+    public void service(ServiceManager manager) throws ServiceException {
+        super.service(manager);
+        if (this.manager.hasService(IncludeCacheManager.ROLE)) {
+            this.cacheManager = (IncludeCacheManager) this.manager.lookup(IncludeCacheManager.ROLE);
+        } else {
+            getLogger().warn("The cinclude transformer cannot find the IncludeCacheManager. " +
+                             "Therefore caching is turned off for the include transformer.");
+        }
+    }
+
+    /**
+     * @see org.apache.avalon.framework.activity.Disposable#dispose()
+     */
+    public void dispose() {
+        if (null != this.manager) {
+            this.manager.release(this.cacheManager);
+            this.manager = null;
+        }
+        super.dispose();
+    }
+
+    /**
+     * Recycle the component
+     */
+    public void recycle() {
+        if ( null != this.cachingSession ) {
+            this.cacheManager.terminateSession( this.cachingSession );
+        }
+        this.cachingSession = null;
+        if ( null != this.recorder) {
+            this.manager.release( this.recorder );
+            this.recorder = null;
+        }
+
+        this.configurationParameters = null;
+        this.resourceParameters = null;
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Finishing, time: " +
+                              (System.currentTimeMillis() - this.startTime));
+            this.startTime = 0;
+        }
+        this.filter = null;
+
+        super.recycle();
+    }
+
+    public void startTransformingElement(String uri, String name, String raw, Attributes attr)
+    throws ProcessingException ,IOException, SAXException {
+        // Element: include
+        if (name.equals(CINCLUDE_INCLUDE_ELEMENT)) {
+            String stripRootValue = attr.getValue("", CINCLUDE_INCLUDE_ELEMENT_STRIP_ROOT_ATTRIBUTE);
+            boolean stripRoot = StringUtils.equals(stripRootValue, "true");
+
+            processCIncludeElement(attr.getValue("", CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE),
+                                   attr.getValue("", CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE),
+                                   attr.getValue("", CINCLUDE_INCLUDE_ELEMENT_SELECT_ATTRIBUTE),
+                                   attr.getValue("", CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE),
+                                   attr.getValue("", CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE),
+                                   stripRoot,
+                                   false);
+
+        // Element: includexml
+        } else if (name.equals(CINCLUDE_INCLUDEXML_ELEMENT)
+                   && this.state == STATE_OUTSIDE) {
+            this.state = STATE_INCLUDE;
+            String ignoreErrors = attr.getValue("", CINCLUDE_INCLUDEXML_ELEMENT_IGNORE_ERRORS_ATTRIBUTE);
+            if (ignoreErrors == null || ignoreErrors.length() == 0) {
+                ignoreErrors = "false";
+            }
+            this.stack.push(BooleanUtils.toBooleanObject(this.ignoreEmptyCharacters));
+            this.stack.push(BooleanUtils.toBooleanObject(this.ignoreWhitespaces));
+            this.stack.push(ignoreErrors);
+
+            this.ignoreEmptyCharacters = false;
+            this.ignoreWhitespaces = true;
+
+        // target
+        } else if (name.equals(CINCLUDE_SRC_ELEMENT)
+                   && this.state == STATE_INCLUDE) {
+            this.startTextRecording();
+
+        // configparameters
+        } else if (name.equals(CINCLUDE_CONFIGURATION_ELEMENT)
+                   && this.state == STATE_INCLUDE) {
+            stack.push("end");
+
+        // parameters
+        } else if (name.equals(CINCLUDE_PARAMETERS_ELEMENT)
+                   && this.state == STATE_INCLUDE) {
+            stack.push("end");
+
+        // parameter
+        } else if (name.equals(CINCLUDE_PARAMETER_ELEMENT)
+                   && this.state == STATE_INCLUDE) {
+
+        // parameter name
+        } else if (name.equals(CINCLUDE_NAME_ELEMENT)
+                   && this.state == STATE_INCLUDE) {
+            this.startTextRecording();
+
+        // parameter value
+        } else if (name.equals(CINCLUDE_VALUE_ELEMENT)
+                   && this.state == STATE_INCLUDE) {
+            this.startSerializedXMLRecording(XMLUtils.createPropertiesForXML(true));
+
+       } else if (name.equals(CINCLUDE_CACHED_INCLUDE_ELEMENT)) {
+
+           String src = processCIncludeElement(attr.getValue("", CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE),
+                                               null,
+                                               null,
+                                               null,
+                                               null,
+                                               false,
+                                               this.cacheManager != null);
+           if (this.compiling) {
+               this.srcAttributes.addAttribute("", CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE, CINCLUDE_SRC_ELEMENT, "CDATA", src);
+               super.startTransformingElement(uri,
+                                              CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT,
+                                              raw + "p",
+                                              this.srcAttributes);
+               this.srcAttributes.clear();
+           }
+        } else {
+            super.startTransformingElement(uri, name, raw, attr);
+        }
+    }
+
+    public void endTransformingElement(String uri, String name, String raw)
+    throws ProcessingException, IOException, SAXException {
+        if (name.equals(CINCLUDE_INCLUDE_ELEMENT)) {
+            // do nothing
+            return;
+
+        } else if (name.equals(CINCLUDE_INCLUDEXML_ELEMENT)
+                   && this.state == STATE_INCLUDE) {
+            // Element: includexml
+
+            this.state = STATE_OUTSIDE;
+
+            final String resource = (String)stack.pop();
+
+            final boolean ignoreErrors = stack.pop().equals("true");
+
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Processing includexml element: src=" + resource
+                              + ", ignoreErrors=" + ignoreErrors
+                              + ", configuration=" + this.configurationParameters
+                              + ", parameters=" + this.resourceParameters);
+            }
+            Source source = null;
+
+            try {
+                source = SourceUtil.getSource(resource,
+                                              this.configurationParameters,
+                                              this.resourceParameters,
+                                              this.resolver);
+
+                XMLSerializer serializer = null;
+                XMLDeserializer deserializer = null;
+                try {
+                    if ( ignoreErrors ) {
+                        serializer = (XMLSerializer) this.manager.lookup(XMLSerializer.ROLE);
+                        deserializer = (XMLDeserializer)this.manager.lookup(XMLDeserializer.ROLE);
+                        SourceUtil.toSAX(source, serializer, this.configurationParameters, true);
+                        deserializer.setConsumer( this.xmlConsumer );
+                        deserializer.deserialize( serializer.getSAXFragment() );
+                    } else {
+                        SourceUtil.toSAX(source, this.xmlConsumer, this.configurationParameters, true);
+                    }
+                } catch (ProcessingException pe) {
+                    if (!ignoreErrors) throw pe;
+                } catch (ServiceException ignore) {
+                } finally {
+                    this.manager.release( serializer );
+                    this.manager.release( deserializer );
+                }
+            } catch (SourceException se) {
+                if (!ignoreErrors) throw SourceUtil.handle(se);
+            } catch (SAXException se) {
+                if (!ignoreErrors) throw se;
+            } catch (IOException ioe) {
+                if (!ignoreErrors) throw ioe;
+            } finally {
+                this.resolver.release(source);
+            }
+
+            // restore values
+            this.ignoreWhitespaces = ((Boolean)stack.pop()).booleanValue();
+            this.ignoreEmptyCharacters = ((Boolean)stack.pop()).booleanValue();
+
+        // src element
+        } else if (name.equals(CINCLUDE_SRC_ELEMENT)
+                   && this.state == STATE_INCLUDE) {
+
+            this.stack.push(this.endTextRecording());
+
+        } else if (name.equals(CINCLUDE_PARAMETERS_ELEMENT)
+                   && this.state == STATE_INCLUDE) {
+            this.resourceParameters = new SourceParameters();
+            // Now get the parameters off the stack
+            String label = (String)stack.pop();
+            String key = null;
+            String value = null;
+            while (!label.equals("end")) {
+                if (label.equals("name")) key = (String)stack.pop();
+                if (label.equals("value")) value = (String)stack.pop();
+                if (key != null && value != null) {
+                    this.resourceParameters.setParameter(key, value);
+                    key = null;
+                    value = null;
+                }
+                label = (String)stack.pop();
+            }
+
+        } else if (name.equals(CINCLUDE_CONFIGURATION_ELEMENT) == true
+                 && this.state == STATE_INCLUDE) {
+            this.configurationParameters = new Parameters();
+            // Now get the parameters off the stack
+            String label = (String)stack.pop();
+            String key = null;
+            String value = null;
+            while (!label.equals("end")) {
+                if (label.equals("name")) key = (String)stack.pop();
+                if (label.equals("value")) value = (String)stack.pop();
+                if (key != null && value != null) {
+                    this.configurationParameters.setParameter(key, value);
+                    key = null;
+                    value = null;
+                }
+                label = (String)stack.pop();
+            }
+
+        } else if (name.equals(CINCLUDE_PARAMETER_ELEMENT) == true
+                   && this.state == STATE_INCLUDE) {
+
+        } else if (name.equals(CINCLUDE_NAME_ELEMENT) == true
+                   && this.state == STATE_INCLUDE) {
+            stack.push(this.endTextRecording());
+            stack.push("name");
+
+        // parameter value
+        } else if (name.equals(CINCLUDE_VALUE_ELEMENT) == true
+                   && this.state == STATE_INCLUDE) {
+            stack.push(this.endSerializedXMLRecording());
+            stack.push("value");
+
+        } else if (name.equals(CINCLUDE_CACHED_INCLUDE_ELEMENT)) {
+            if (this.compiling) {
+               super.endTransformingElement(uri,
+                                            CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT,
+                                            raw + "p");
+            }
+            // do nothing else
+        } else {
+            super.endTransformingElement(uri, name, raw);
+        }
+    }
+
+    protected String processCIncludeElement(String src, String element,
+                                            String select, String ns, String prefix,
+                                            boolean stripRoot,
+                                            boolean cache)
+    throws SAXException, IOException {
+
+        if (src == null) {
+            throw new SAXException("Missing 'src' attribute on cinclude:include element");
+        }
+
+        if (element == null) element="";
+        if (select == null) select="";
+        if (ns == null) ns="";
+        if (prefix == null) prefix="";
+
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Processing include element: src=" + src
+                          + ", element=" + element
+                          + ", select=" + select
+                          + ", ns=" + ns
+                          + ", prefix=" + prefix
+                          + ", stripRoot=" + stripRoot
+                          + ", caching=" + cache);
+        }
+
+        if (cache) {
+            src = this.cacheManager.load(src, this.cachingSession);
+
+            if (this.cachingSession.isParallel() && !this.cachingSession.isPreemptive()) {
+                if (!this.compiling) {
+                    this.compiling = true;
+                    this.startCompiledXMLRecording();
+                }
+            } else {
+                this.cacheManager.stream(src, this.cachingSession, this.filter);
+            }
+
+            return src;
+        }
+
+        // usual no caching stuff
+        if (!"".equals(element)) {
+            if (!ns.equals("")) {
+                super.startPrefixMapping(prefix, ns);
+            }
+            super.startElement(ns,
+                               element,
+                               (!ns.equals("") && !prefix.equals("") ? prefix+":"+element : element),
+                               XMLUtils.EMPTY_ATTRIBUTES);
+        }
+
+        Source source = null;
+        try {
+            source = this.resolver.resolveURI(src);
+
+            if (!"".equals(select)) {
+
+
+                DOMParser parser = null;
+                XPathProcessor processor = null;
+
+                try {
+                    parser = (DOMParser)this.manager.lookup(DOMParser.ROLE);
+                    processor = (XPathProcessor)this.manager.lookup(XPathProcessor.ROLE);
+
+                    InputSource input = SourceUtil.getInputSource(source);
+
+                    Document document = parser.parseDocument(input);
+                    NodeList list = processor.selectNodeList(document, select);
+                    int length = list.getLength();
+                    for (int i=0; i<length; i++) {
+                          IncludeXMLConsumer.includeNode(list.item(i),
+                                               this.filter,
+                                               this.filter);
+                    }
+                } finally {
+                    this.manager.release(parser);
+                    this.manager.release(processor);
+                }
+            } else {
+                String mimeType = null;
+                if (null != this.configurationParameters) {
+                    mimeType = this.configurationParameters.getParameter("mime-type", mimeType);
+                }
+                if (this.compiling) {
+                    SourceUtil.toSAX(source, mimeType, new IncludeXMLConsumer(this.contentHandler, this.lexicalHandler));
+                } else {
+                    this.filter.setIgnoreRootElement(stripRoot);
+                    SourceUtil.toSAX(source, mimeType, this.filter);
+                }
+            }
+
+        } catch (SourceException se) {
+            throw new SAXException("Exception in CIncludeTransformer",se);
+        } catch (IOException e) {
+            throw new SAXException("CIncludeTransformer could not read resource", e);
+        } catch (ProcessingException e){
+            throw new SAXException("Exception in CIncludeTransformer",e);
+        } catch(ServiceException e) {
+            throw new SAXException(e);
+        } finally {
+            this.resolver.release(source);
+        }
+
+        if (!"".equals(element)) {
+            super.endElement(ns, element, (!ns.equals("") && !prefix.equals("") ? prefix+":"+element : element));
+            if (!ns.equals("")) {
+                super.endPrefixMapping(prefix);
+            }
+        }
+        return src;
+    }
+
+    /**
+     * Start recording of compiled xml.
+     * The incomming SAX events are recorded and a compiled representation
+     * is created. These events are not forwarded to the next component in
+     * the pipeline.
+     */
+    protected void startCompiledXMLRecording()
+    throws SAXException {
+        if (this.getLogger().isDebugEnabled()) {
+            this.getLogger().debug("BEGIN startCompiledXMLRecording");
+        }
+
+        try {
+            this.recorder = (XMLSerializer)this.manager.lookup(XMLSerializer.ROLE);
+
+            this.addRecorder(recorder);
+
+        } catch (ServiceException ce) {
+            throw new SAXException("Unable to lookup xml serializer for compiling xml.", ce);
+        }
+        if (this.getLogger().isDebugEnabled()) {
+           this.getLogger().debug("END startCompiledXMLRecording");
+        }
+    }
+
+    /**
+     * Stop recording of compiled XML.
+     * @return The compiled XML.
+     */
+    protected Object endCompiledXMLRecording()
+    throws SAXException {
+        if (this.getLogger().isDebugEnabled()) {
+            this.getLogger().debug("BEGIN endCompiledXMLRecording");
+        }
+
+        XMLSerializer recorder = (XMLSerializer)this.removeRecorder();
+        Object text = recorder.getSAXFragment();
+
+        if (this.getLogger().isDebugEnabled()) {
+            this.getLogger().debug("END endCompiledXMLRecording text="+text);
+        }
+        return text;
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startDocument()
+     */
+    public void startDocument() throws SAXException {
+        this.filter = new MyFilter(this.xmlConsumer,
+                                   this);
+        super.startDocument();
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endDocument()
+     */
+    public void endDocument() throws SAXException {
+        if ( this.compiling ) {
+            Object compiledXML = this.endCompiledXMLRecording();
+            XMLDeserializer deserializer = null;
+            try {
+                deserializer = (XMLDeserializer)this.manager.lookup(XMLDeserializer.ROLE);
+                deserializer.setConsumer(this.filter);
+                deserializer.deserialize(compiledXML);
+            } catch (ServiceException ce) {
+                throw new SAXException("Unable to lookup xml deserializer.", ce);
+            } finally {
+                this.manager.release( deserializer );
+            }
+        }
+        super.endDocument();
+    }
+
+    /**
+     * @see org.apache.cocoon.caching.CacheableProcessingComponent#getKey()
+     */
+    public Serializable getKey() {
+        if (this.supportCaching
+            && null != this.cacheManager
+            && this.cachingSession.getExpires() > 0) {
+            return "1";
+        }
+        return null;
+    }
+
+    /**
+     * @see org.apache.cocoon.caching.CacheableProcessingComponent#getValidity()
+     */
+    public SourceValidity getValidity() {
+        if (this.supportCaching
+            && null != this.cacheManager
+            && this.cachingSession.getExpires() > 0
+            && !this.cachingSession.isPurging()) {
+            return this.cachingSession.getExpiresValidity();
+        }
+        return null;
+    }
+
+}
+
+final class MyFilter extends IncludeXMLConsumer {
+
+    private final CIncludeTransformer transformer;
+
+    /**
+     * This filter class post-processes the parallel fetching
+     * @param consumer
+     */
+    public MyFilter(XMLConsumer consumer,
+                    CIncludeTransformer transformer) {
+        super(consumer);
+        this.transformer = transformer;
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+     */
+    public void endElement(String uri, String local, String qName)
+    throws SAXException {
+        if (uri != null
+            && uri.equals(CIncludeTransformer.CINCLUDE_NAMESPACE_URI)
+            && local.equals(CIncludeTransformer.CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT)) {
+            // this is the placeholder element: do nothing
+        } else {
+            super.endElement(uri, local, qName);
+        }
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+     */
+    public void startElement(String uri,
+                                String local,
+                                String qName,
+                                Attributes attr)
+    throws SAXException {
+        if (uri != null
+            && uri.equals(CIncludeTransformer.CINCLUDE_NAMESPACE_URI)
+            && local.equals(CIncludeTransformer.CINCLUDE_CACHED_INCLUDE_PLACEHOLDER_ELEMENT)) {
+            // this is a placeholder
+            try {
+                final String src = attr.getValue("",CIncludeTransformer.CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE);
+                this.transformer.cacheManager.stream(src, this.transformer.cachingSession, this);
+            } catch (IOException ioe) {
+                throw new SAXException("IOException", ioe);
+            }
+        } else {
+            super.startElement(uri, local, qName, attr);
+        }
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/CIncludeTransformer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/EncodeURLTransformer.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/EncodeURLTransformer.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/EncodeURLTransformer.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/EncodeURLTransformer.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.transformation;
+
+import java.io.IOException;
+import java.util.Map;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.util.ElementAttributeMatching;
+import org.apache.cocoon.caching.CacheableProcessingComponent;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.Response;
+import org.apache.cocoon.environment.Session;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.excalibur.source.SourceValidity;
+import org.apache.excalibur.source.impl.validity.NOPValidity;
+import org.apache.regexp.RESyntaxException;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * @cocoon.sitemap.component.documentation
+ * The encodeURL transformer emits encoded URLs.
+ * 
+ * @cocoon.sitemap.component.name   encodeurl
+ * @cocoon.sitemap.component.logger sitemap.transformer.encodeURL
+ * @cocoon.sitemap.component.documentation.caching
+ *               TBD
+ * 
+ * @cocoon.sitemap.component.pooling.max  32
+ * 
+ * The encodeURL transformer emits encoded URLs.
+ * <p>
+ *   This transformer applies encodeURL method to URLs.
+ *   You may want to use this transform to avoid doing the manually
+ *   encodeURL() calls.
+ * </p>
+ * <p>
+ *   Usually this transformer is appended as last transformer before
+ *   the serialization process. In this case it is possible to encode
+ *   URLs introduced in the generator, and xslt transformer phase.
+ * </p>
+ * <p>
+ *   You can specify which attributes hold URL values in order to restrict
+ *   URL rewriting to specific attributes only.
+ * </p>
+ * <p>
+ * Usage in a sitemap:
+ * </p>
+ * <pre><tt>
+ *   &lt;map:composition&gt;
+ *   ...
+ *     &lt;map:transformers&gt;
+ *     ...
+ *       &lt;map:transformer type=&quot;encodeURL&quot;
+ *         src=&quot;org.apache.cocoon.optional.transformation.EncodeURLTransformer&quot;&gt;
+ *         &lt;exclude-name&gt;img/@src|a/@href=.&amp;asterik;adserver&lt;/exclude-name&gt;
+ *         &lt;include-name&gt;.&amp;asterik;/@href|.&amp;asterik;/@src|.&amp;asterik;/@action&lt;/include-name&gt;
+ *       &lt;/map:transformer&gt;
+ *   ...
+ *   &lt;map:pipelines&gt;
+ *     &lt;map:pipeline&gt;
+ *       ...
+ *       &lt;map:transform type=&quot;encodeURL&quot;/&gt;
+ *       ...
+ * </pre></tt>
+ *
+ * @author <a href="mailto:bh22351@i-one.at">Bernhard Huber</a>
+ * @version CVS $Id: EncodeURLTransformer.java 189598 2005-06-08 15:52:32Z bdelacretaz $
+ */
+public class EncodeURLTransformer
+  extends AbstractTransformer
+  implements Configurable, CacheableProcessingComponent {
+
+    /**
+     * Configuration name for specifying excluding patterns,
+     * ie exclude-name.
+     */
+    public final static String EXCLUDE_NAME = "exclude-name";
+
+    /**
+     * Configuration name for specifying including patterns,
+     * ie include-name.
+     */
+    public final static String INCLUDE_NAME = "include-name";
+
+    /**
+     * Configuration default exclude pattern,
+     * ie img/@src
+     */
+    public final static String EXCLUDE_NAME_DEFAULT = "img/@src";
+
+    /**
+     * Configuration default exclude pattern,
+     * ie .*\/@href|.*\/@action|frame/@src
+     */
+    public final static String INCLUDE_NAME_DEFAULT = ".*/@href|.*/@action|frame/@src";
+
+    private String includeNameConfigure = INCLUDE_NAME_DEFAULT;
+    private String excludeNameConfigure = EXCLUDE_NAME_DEFAULT;
+
+    private ElementAttributeMatching elementAttributeMatching;
+    private Response response;
+    private boolean isEncodeURLNeeded;
+    private Session session;
+
+    /**
+     * check if encoding of URLs is neccessary.
+     * 
+     * This is true if session object exists, and session-id   
+     * was provided from URL, or session is new.
+     * The result is stored in some instance variables
+     */
+    protected void checkForEncoding(Request request) {
+        this.session = request.getSession(false);
+        this.isEncodeURLNeeded = false;
+        
+        if ( null != this.session ) {
+            // do encoding if session id is from URL, or the session is new, 
+            // fixes BUG #13855, due to paint007@mc.duke.edu
+            if ( request.isRequestedSessionIdFromURL() || this.session.isNew()) {
+                this.isEncodeURLNeeded = true;
+            }
+        }
+    }
+
+    /**
+     * Setup the transformer.
+     * <p>
+     *   Setup include, and exclude patterns from the parameters
+     * </p>
+     *
+     * @param resolver source resolver
+     * @param objectModel sitemap objects
+     * @param parameters request parameters
+     *
+     */
+    public void setup(SourceResolver resolver, Map objectModel, String source, Parameters parameters)
+    throws ProcessingException, SAXException, IOException {
+
+        this.checkForEncoding(ObjectModelHelper.getRequest(objectModel));
+        
+        if (this.isEncodeURLNeeded) {
+            this.response = ObjectModelHelper.getResponse(objectModel);
+
+            // don't check if URL encoding is needed now, as
+            // a generator might create a new session 
+            final String includeName = parameters.getParameter(INCLUDE_NAME,
+                                                               this.includeNameConfigure);
+            final String excludeName = parameters.getParameter(EXCLUDE_NAME,
+                                                               this.excludeNameConfigure);
+            try {
+                this.elementAttributeMatching = new ElementAttributeMatching(includeName, excludeName);
+            } catch (RESyntaxException reex) {
+                final String message = "Cannot parse include-name: " + includeName + " " +
+                    "or exclude-name: " + excludeName + "!";
+                throw new ProcessingException(message, reex);
+            }
+        }
+    }
+
+
+    /**
+     * BEGIN SitemapComponent methods
+     *
+     * @param  configuration               Description of Parameter
+     * @exception  ConfigurationException  Description of Exception
+     */
+    public void configure(Configuration configuration) throws ConfigurationException {
+        Configuration child;
+
+        child = configuration.getChild(INCLUDE_NAME);
+        this.includeNameConfigure = child.getValue(INCLUDE_NAME_DEFAULT);
+
+        child = configuration.getChild(EXCLUDE_NAME);
+        this.excludeNameConfigure = child.getValue(EXCLUDE_NAME_DEFAULT);
+
+        if (this.includeNameConfigure == null) {
+            String message = "Configure " + INCLUDE_NAME + "!";
+            throw new ConfigurationException(message);
+        }
+        if (this.excludeNameConfigure == null) {
+            String message = "Configure " + EXCLUDE_NAME + "!";
+            throw new ConfigurationException(message);
+        }
+    }
+
+
+    /**
+     * Recycle resources of this transformer
+     */
+    public void recycle() {
+        super.recycle();
+        this.response = null;
+        this.session = null;
+        this.elementAttributeMatching = null;
+    }
+
+
+    /**
+     * Generate the unique key.
+     * This key must be unique inside the space of this component.
+     *
+     * @return The generated key hashes the src
+     */
+    public java.io.Serializable getKey() {
+        if (this.isEncodeURLNeeded) {
+            return null;
+        } else {
+            return "1";
+        }
+    }
+
+    /**
+     * Generate the validity object.
+     *
+     * @return The generated validity object or <code>null</code> if the
+     *         component is currently not cacheable.
+     */
+    public SourceValidity getValidity() {
+        if (this.isEncodeURLNeeded) {
+            return null;
+        } else {
+            return NOPValidity.SHARED_INSTANCE;
+        }
+    }
+
+    /**
+     * Start parsing an element
+     *
+     * @param  uri               of the element
+     * @param  name              of the element
+     * @param  raw               name of the element
+     * @param  attributes        list
+     * @exception  SAXException  Description of Exception
+     */
+    public void startElement(String uri, String name, String raw, Attributes attributes)
+    throws SAXException {
+        if (this.isEncodeURLNeeded && this.elementAttributeMatching != null) {
+            String lname = name;
+            if (attributes != null && attributes.getLength() > 0) {
+                AttributesImpl new_attributes = new AttributesImpl(attributes);
+                for (int i = 0; i < new_attributes.getLength(); i++) {
+                    String attr_lname = new_attributes.getLocalName(i);
+
+                    String value = new_attributes.getValue(i);
+
+                    if (elementAttributeMatching.matchesElementAttribute(lname, attr_lname, value)) {
+                        // don't use simply this.response.encodeURL(value)
+                        // but be more smart about the url encoding
+                        final String new_value = this.encodeURL(value);
+                        if (getLogger().isDebugEnabled()) {
+                            this.getLogger().debug("element/@attribute matches: " + name + "/@" + attr_lname);
+                            this.getLogger().debug("encodeURL: " + value + " -> " + new_value);
+                        }
+                        new_attributes.setValue(i, new_value);
+                    }
+                }
+                // parent handles element using encoded attribute values
+                super.contentHandler.startElement(uri, name, raw, new_attributes);
+                return;
+            }
+        }
+        // no match, parent handles element as-is
+        super.contentHandler.startElement(uri, name, raw, attributes);
+    }
+
+    /**
+     * Do the URL rewriting.
+     * <p>
+     *   Check if <code>url</code> contains already the sessionid, some servlet-engines
+     *   just appends the session-id without checking if the sessionid is already present.
+     * </p>
+     *
+     * @param  url       the URL probably without sessionid.
+     * @return           String the original url inclusive the sessionid
+     */
+    private String encodeURL(String url) {
+        String encoded_url;
+        if (this.response != null) {
+            // As some servlet-engine does not check if url has been already rewritten
+            if (this.session != null && url.indexOf(this.session.getId()) > -1) {
+                // url contains already the session id encoded
+                encoded_url = url;
+            } else {
+                // do encode the session id
+                encoded_url = this.response.encodeURL(url);
+            }
+        } else {
+            encoded_url = url;
+        }
+        return encoded_url;
+    }
+}
+

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/EncodeURLTransformer.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message