You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by sy...@apache.org on 2001/12/20 17:17:57 UTC

cvs commit: xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap ActSetNode.java ActTypeNode.java ActionSetNode.java ActionSetNodeBuilder.java AggregateNode.java AggregateNodeBuilder.java CallNode.java CallNodeBuilder.java SelectNode.java SelectNodeBuilder.java ActNodeBuilder.java ComponentsNodeBuilder.java GenerateNode.java GenerateNodeBuilder.java HandleErrorsNode.java HandleErrorsNodeBuilder.java MatchNode.java MatchNodeBuilder.java MountNode.java MountNodeBuilder.java PipelineNode.java PipelineNodeBuilder.java PipelinesNode.java PipelinesNodeBuilder.java PreparableMatchNode.java ReadNode.java ReadNodeBuilder.java RedirectToNodeBuilder.java RedirectToURINode.java SerializeNode.java SerializeNodeBuilder.java SitemapNode.java SitemapNodeBuilder.java TransformNode.java TransformNodeBuilder.java ActNode.java

sylvain     01/12/20 08:17:56

  Modified:    scratchpad/src/org/apache/cocoon/treeprocessor
                        AbstractParentProcessingNode.java
                        AbstractParentProcessingNodeBuilder.java
                        AbstractProcessingNode.java
                        AbstractProcessingNodeBuilder.java NullNode.java
                        NullNodeBuilder.java
                        ParameterizableProcessingNode.java
                        ProcessingNode.java ProcessingNodeBuilder.java
                        SimpleParentProcessingNode.java
                        SimpleSelectorProcessingNode.java TreeBuilder.java
                        TreeBuilderComponentManager.java TreeProcessor.java
                        treeprocessor.xconf
               scratchpad/src/org/apache/cocoon/treeprocessor/sitemap
                        ActNodeBuilder.java ComponentsNodeBuilder.java
                        GenerateNode.java GenerateNodeBuilder.java
                        HandleErrorsNode.java HandleErrorsNodeBuilder.java
                        MatchNode.java MatchNodeBuilder.java MountNode.java
                        MountNodeBuilder.java PipelineNode.java
                        PipelineNodeBuilder.java PipelinesNode.java
                        PipelinesNodeBuilder.java PreparableMatchNode.java
                        ReadNode.java ReadNodeBuilder.java
                        RedirectToNodeBuilder.java RedirectToURINode.java
                        SerializeNode.java SerializeNodeBuilder.java
                        SitemapNode.java SitemapNodeBuilder.java
                        TransformNode.java TransformNodeBuilder.java
  Added:       scratchpad/src/org/apache/cocoon/treeprocessor
                        CategoryNode.java CategoryNodeBuilder.java
                        ContainerNode.java ContainerNodeBuilder.java
                        InvokeContext.java LifecycleHelper.java
                        LinkedProcessingNodeBuilder.java
                        MapStackResolver.java NamedContainerNode.java
                        NamedContainerNodeBuilder.java
                        NamedProcessingNode.java
               scratchpad/src/org/apache/cocoon/treeprocessor/sitemap
                        ActSetNode.java ActTypeNode.java ActionSetNode.java
                        ActionSetNodeBuilder.java AggregateNode.java
                        AggregateNodeBuilder.java CallNode.java
                        CallNodeBuilder.java SelectNode.java
                        SelectNodeBuilder.java
  Removed:     scratchpad/src/org/apache/cocoon/treeprocessor
                        ComponentUtil.java ListOfMapsResolver.java
                        ProcessingLanguageException.java
               scratchpad/src/org/apache/cocoon/treeprocessor/sitemap
                        ActNode.java
  Log:
  Big update ! Still to do :
  - views (last missing sitemap feature),
  - javadocs (and sort imports :)
  
  You can try it with the following in cocoon.xconf :
  <sitemap class="org.apache.cocoon.treeprocessor.TreeProcessor"/>
  
  Revision  Changes    Path
  1.5       +9 -26     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractParentProcessingNode.java
  
  Index: AbstractParentProcessingNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractParentProcessingNode.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- AbstractParentProcessingNode.java	2001/11/25 21:37:55	1.4
  +++ AbstractParentProcessingNode.java	2001/12/20 16:17:54	1.5
  @@ -20,7 +20,7 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.4 $ $Date: 2001/11/25 21:37:55 $
  + * @version CVS $Revision: 1.5 $ $Date: 2001/12/20 16:17:54 $
    */
   
   public abstract class AbstractParentProcessingNode extends AbstractProcessingNode {
  @@ -37,19 +37,16 @@
        * @parameter currentMap the <code>Map<code> of parameters produced by this node,
        *            which is added to <code>listOfMap</code>.
        */
  -    protected static final boolean invokeNodes (
  +    protected static final boolean invokeNodes(
           ProcessingNode[] nodes,
           Environment env,
  -        StreamPipeline pipeline,
  -        EventPipeline eventPipeline,
  -        List listOfMaps, Map currentMap)
  +        InvokeContext context,
  +        Map currentMap)
         throws Exception {
  -        
  -        listOfMaps.add(currentMap);
  -        
  -        boolean success = invokeNodes(nodes, env, pipeline, eventPipeline, listOfMaps);
           
  -        listOfMaps.remove(listOfMaps.size() - 1);
  +        context.pushMap(currentMap);
  +        boolean success = invokeNodes(nodes, env, context);
  +        context.popMap();
           
           return success;
       }
  @@ -60,14 +57,12 @@
       protected static final boolean invokeNodes (
           ProcessingNode[] nodes,
           Environment env,
  -        StreamPipeline pipeline,
  -        EventPipeline eventPipeline,
  -        List listOfMaps)
  +        InvokeContext context)
         throws Exception {
           
           boolean debug = logger.isDebugEnabled();
           for (int i = 0; i < nodes.length; i++) {
  -            if (nodes[i].invoke(env, pipeline, eventPipeline, listOfMaps)) {
  +            if (nodes[i].invoke(env, context)) {
                   if (debug)
                       logger.debug("Node " + nodes[i] + " at " + nodes[i].getLocation() + " succeeded");
                   return true;
  @@ -75,17 +70,5 @@
           }
           
           return false;
  -    }
  -
  -    /**
  -     * Dispose all nodes in an array.
  -     */
  -    protected static final void disposeNodes(ProcessingNode[] nodes) {
  -        if (nodes == null)
  -            return;
  -
  -        for (int i = 0; i < nodes.length; i++) {
  -            nodes[i].dispose();
  -        }
       }
   }
  
  
  
  1.5       +61 -48    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractParentProcessingNodeBuilder.java
  
  Index: AbstractParentProcessingNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractParentProcessingNodeBuilder.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- AbstractParentProcessingNodeBuilder.java	2001/11/25 21:37:55	1.4
  +++ AbstractParentProcessingNodeBuilder.java	2001/12/20 16:17:54	1.5
  @@ -22,7 +22,7 @@
    * children nodes.
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.4 $ $Date: 2001/11/25 21:37:55 $
  + * @version CVS $Revision: 1.5 $ $Date: 2001/12/20 16:17:54 $
    */
   
   
  @@ -44,10 +44,60 @@
       }
   
       /**
  -     * Create the <code>ProcessingNodeBuilder</code>s for the children of a given node.
  +     * Checks if a child element and is allowed, and if not throws a <code>ConfigurationException</code>.
  +     *
  +     * @param child the child configuration to check.
  +     * @return <code>true</code> if this child should be considered or <code>false</code>
  +     *         if it should be ignored.
  +     * @throws ConfigurationException if this child isn't allowed.
  +     */
  +    protected boolean isChild(Configuration child) throws ConfigurationException {
  +        
  +        checkNamespace(child);
  +        
  +        String name = child.getName();
  +        
  +        // Is this a parameter of a parameterizable node builder ?
  +        if (isParameter(child)) {
  +            return false;
  +        }
  +        
  +        // Is this element to be ignored ?
  +        if (ignoredChildren != null && ignoredChildren.contains(name)) {
  +            getLogger().debug("Element '" + name + "' is ignored for building children of element '" +
  +                child.getName() + "'");
  +
  +            return false;
  +        }
  +        
  +        // Is it allowed ?
  +        if ( (allowedChildren != null && !allowedChildren.contains(name)) ||
  +             (forbiddenChildren != null && forbiddenChildren.contains(name)) ) {
  +            throw new ConfigurationException("Element '" + name + "' is not allowed at " +
  +                child.getLocation());
  +        }
  +        
  +        return true;
  +    }
  +    
  +    protected boolean isParameter(Configuration config) throws ConfigurationException {
  +        String name = config.getName();
  +        if (name.equals(this.treeBuilder.getParameterName())) {
  +            if (this.hasParameters()) {
  +                return true;
  +            } else {
  +                throw new ConfigurationException("Element '" + name + "' has no parameters at " +
  +                    config.getLocation());
  +            }
  +        }
  +        return false;
  +    }
  +
  +    /**
  +     * Create the <code>ProcessingNode</code>s for the children of a given node.
        * Child nodes are controlled to be actually allowed in this node.
        */
  -    protected List createChildBuilders(Configuration config, Map buildModel) throws Exception {
  +    protected List buildChildNodesList(Configuration config) throws Exception {
           
           Configuration[] children = config.getChildren();
           List result = new ArrayList();
  @@ -55,65 +105,28 @@
           for (int i = 0; i < children.length; i++) {
               
               Configuration child = children[i];
  -            String name = child.getName();
               try {
  -                checkNamespace(child);
  -                
  -                // Is this a parameter of a ParameterizableProcessingNode ?
  -                if (name.equals(this.treeBuilder.getParameterName()) && this.hasParameters()) {
  -                    continue;
  +                 if (isChild(child)) {
  +                    // OK : get a builder.
  +                    ProcessingNodeBuilder childBuilder = this.treeBuilder.createNodeBuilder(child);
  +                    result.add(childBuilder.buildNode(child));
                   }
                   
  -                // Is this element to be ignored ?
  -                if (ignoredChildren != null && ignoredChildren.contains(name)) {
  -                    getLogger().debug("Element '" + name + "' is ignored for building children of element '" +
  -                        config.getName() + "'");
  -    
  -                    continue;
  -                }
  -                
  -                // Is it allowed ?
  -                if ( (allowedChildren != null && !allowedChildren.contains(name)) ||
  -                     (forbiddenChildren != null && forbiddenChildren.contains(name)) ) {
  -                    throw new ConfigurationException("Element '" + name + "' is not allowed at " +
  -                        child.getLocation());
  -                }
  -                
  -                // OK : get a builder.
  -                ProcessingNodeBuilder childBuilder = this.treeBuilder.createNodeBuilder(child);
  -                childBuilder.buildNode(child, buildModel);
  -                result.add(childBuilder);
  -                
               } catch(ConfigurationException ce) {
                   throw ce;
               } catch(Exception e) {
  -                throw new ConfigurationException("Error while creating node '" + name +
  +                throw new ConfigurationException("Error while creating node '" + child.getName() +
                       "' at " + child.getLocation(), e);
               }
           }
           
           return result;
       }
  -    
  -    /**
  -     * Collect nodes from a list of builders, taking care of builders that may
  -     * return null nodes.
  -     */
  -    protected ProcessingNode[] getNodes(List builderList) throws Exception {
  -        
  -        List result = new ArrayList();
  -        
  -        Iterator iter = builderList.iterator();
  -        while(iter.hasNext()) {
  -            ProcessingNode node = ((ProcessingNodeBuilder)iter.next()).getNode();
  -            if (node != null) {
  -                result.add(node);
  -            }
  -        }
           
  -        return toNodeArray(result);
  +    protected ProcessingNode[] buildChildNodes(Configuration config) throws Exception {
  +        return toNodeArray(buildChildNodesList(config));
       }
  -    
  +
       /**
        * Convenience function that converts a <code>List</code> of <code>ProcessingNode</code>s
        * to an array.
  
  
  
  1.4       +1 -8      xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractProcessingNode.java
  
  Index: AbstractProcessingNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractProcessingNode.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- AbstractProcessingNode.java	2001/11/25 21:37:55	1.3
  +++ AbstractProcessingNode.java	2001/12/20 16:17:54	1.4
  @@ -17,7 +17,7 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.3 $ $Date: 2001/11/25 21:37:55 $
  + * @version CVS $Revision: 1.4 $ $Date: 2001/12/20 16:17:54 $
    */
   
   public abstract class AbstractProcessingNode extends AbstractLoggable implements ProcessingNode {
  @@ -43,12 +43,5 @@
        */
       public void setLocation(String location) {
           this.location = location;
  -    }
  -    
  -    /**
  -     * By default, do nothing. Subclasses can redefine it when needed.
  -     */
  -    public void dispose() {
  -        // Nothing
       }
   }
  
  
  
  1.4       +5 -5      xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractProcessingNodeBuilder.java
  
  Index: AbstractProcessingNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractProcessingNodeBuilder.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- AbstractProcessingNodeBuilder.java	2001/11/25 21:37:55	1.3
  +++ AbstractProcessingNodeBuilder.java	2001/12/20 16:17:54	1.4
  @@ -24,7 +24,7 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.3 $ $Date: 2001/11/25 21:37:55 $
  + * @version CVS $Revision: 1.4 $ $Date: 2001/12/20 16:17:54 $
    */
   
   
  @@ -65,7 +65,7 @@
               if (true) { // FIXME : check namespace
                   String value = child.getAttribute("value");
                   try {
  -                    params.put(child.getAttribute("name"), ListOfMapsResolver.getResolver(value));
  +                    params.put(child.getAttribute("name"), MapStackResolver.getResolver(value));
                   } catch(PatternException pe) {
                       throw new ConfigurationException("Invalid pattern '" + value +
                           " at " + child.getLocation());
  @@ -81,10 +81,10 @@
        * one given by the builder.
        */
       protected void checkNamespace(Configuration config) throws ConfigurationException {
  -        // FIXME : check namespace of config (need new Avalon)
  -        if (false) //!builder.getNamespace().equals(config.getNamespace().getURI())
  +        if (!this.treeBuilder.getNamespace().equals(config.getNamespace()))
           {
  -            throw new ConfigurationException("Invalid namespace, at " + config.getLocation());
  +            throw new ConfigurationException("Invalid namespace '" + config.getNamespace() +
  +                "' at " + config.getLocation());
           }
       }
   }
  
  
  
  1.2       +3 -7      xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/NullNode.java
  
  Index: NullNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/NullNode.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- NullNode.java	2001/11/18 20:58:44	1.1
  +++ NullNode.java	2001/12/20 16:17:54	1.2
  @@ -18,16 +18,12 @@
    * A no-op node to stub not yet implemented features.
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/18 20:58:44 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:54 $
    */
   
   public class NullNode extends AbstractProcessingNode {
  -    public boolean invoke(
  -        Environment env,
  -        StreamPipeline pipeline,
  -        EventPipeline eventPipeline,
  -        List listOfMaps
  -    ) throws Exception {
  +    
  +    public final boolean invoke(Environment env, InvokeContext context) throws Exception {
           
           getLogger().debug("Invoke on NullNode at " + getLocation());
           return false;
  
  
  
  1.2       +3 -11     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/NullNodeBuilder.java
  
  Index: NullNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/NullNodeBuilder.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- NullNodeBuilder.java	2001/11/18 20:58:44	1.1
  +++ NullNodeBuilder.java	2001/12/20 16:17:54	1.2
  @@ -15,20 +15,12 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/18 20:58:44 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:54 $
    */
   
   public class NullNodeBuilder extends AbstractProcessingNodeBuilder {
   
  -    private NullNode node;
  -
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  -        this.node = new NullNode();
  -        this.node.setLogger(getLogger());
  -        this.node.setLocation(config.getLocation());
  -    }
  -    
  -    public ProcessingNode getNode() throws Exception {
  -        return this.node;
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
  +        return this.treeBuilder.setupNode(new NullNode(), config);
       }
   }
  
  
  
  1.2       +2 -2      xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ParameterizableProcessingNode.java
  
  Index: ParameterizableProcessingNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ParameterizableProcessingNode.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ParameterizableProcessingNode.java	2001/11/25 21:37:55	1.1
  +++ ParameterizableProcessingNode.java	2001/12/20 16:17:54	1.2
  @@ -14,13 +14,13 @@
    * A <code>ProcessingNode</code> that has parameters.
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/25 21:37:55 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:54 $
    */
   
   public interface ParameterizableProcessingNode extends ProcessingNode {
   
       /**
  -     * Set the parameters of this node as a <code>Map</code> of <code>ListOfMapsResolver</code>s
  +     * Set the parameters of this node as a <code>Map</code> of <code>MapStackResolver</code>s
        * that will be resolved at process-time.
        */
       void setParameters(Map parameterMap);    
  
  
  
  1.4       +3 -18     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ProcessingNode.java
  
  Index: ProcessingNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ProcessingNode.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ProcessingNode.java	2001/11/13 22:06:39	1.3
  +++ ProcessingNode.java	2001/12/20 16:17:54	1.4
  @@ -14,19 +14,14 @@
   import org.apache.cocoon.components.pipeline.StreamPipeline;
   
   import java.util.List;
  -import org.apache.avalon.framework.activity.Disposable;
   
   /**
    *
  - * Note : this interface extends <code>Disposable</code> to ensure that resources
  - * held at any level in the hierarchy can be disposed by calling <code>dispose()</code>
  - * on the root node.
  - *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.3 $ $Date: 2001/11/13 22:06:39 $
  + * @version CVS $Revision: 1.4 $ $Date: 2001/12/20 16:17:54 $
    */
   
  -public interface ProcessingNode extends ThreadSafe, Disposable {
  +public interface ProcessingNode extends ThreadSafe {
       
       /**
        * The key of the <code>SourceResolver</code> in the object model.
  @@ -35,18 +30,8 @@
       
       /**
        * Process environment.
  -     */
  -    boolean invoke(
  -        Environment env,
  -        StreamPipeline pipeline,
  -        EventPipeline eventPipeline,
  -        List listOfMaps
  -    ) throws Exception;
  -    
  -    /**
  -     * Dispose recursively any resources held.
        */
  -    void dispose();
  +    boolean invoke(Environment env, InvokeContext context) throws Exception;
       
       /**
        * Get the location of this node.
  
  
  
  1.3       +5 -28     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ProcessingNodeBuilder.java
  
  Index: ProcessingNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ProcessingNodeBuilder.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ProcessingNodeBuilder.java	2001/11/12 18:07:43	1.2
  +++ ProcessingNodeBuilder.java	2001/12/20 16:17:54	1.3
  @@ -14,27 +14,9 @@
   
   /**
    * A <code>ProcessingNode</code> builder.
  - * <p>
  - * The lifecycle of <code>ProcessingNodeBuilder</code>s is as follows :
  - * <ul>
  - * <li>{@link #setBuilder(Map)} is called after all Avalon lifecycle methods.
  - * </li>
  - * <li>{@link #buildNode(Configuration, Map)} is called for every node builder in the hierarchy.<br>
  - *    This is where <code>ProcessingNodes</code> that should be made public to other
  - *    <code>ProcessingNodeBuilder</code>s (such as sitemap resources) should be
  - *    exported using <code>Builder.addNode()</code>..
  - * </li>
  - * <li>then {@link #getNode(Map)} is called for every node builder in the hierarchy.<br>
  - *    This is where node builders can resolve references to other public nodes
  - *    that have been registered during the <code>buildNode()</code> phase.
  - * </ul>
  - * The <code>buildModel</code> parameter passed to <code>buildNode()</code> contains
  - * additional information placed here by the ancestor <code>ProcessingNodeBuilder</code>s.
  - * The information added to or read from <code>buildModel</code> should be described in relevant
  - * implementations.
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/12 18:07:43 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:54 $
    */
   
   import java.util.Map;
  @@ -47,14 +29,9 @@
       void setBuilder(TreeBuilder builder);
       
       /**
  -     * Build the {@link ProcessingNode} from the given <code>Configuration</code>,
  -     * and register it in the {@link Builder} if needed.
  +     * Build the {@link ProcessingNode} and its children from the given
  +     * <code>Configuration</code>, and optionnaly register it in the {@link Builder}
  +     * for lookup by other <code>LinkedProcessingNodeBuilder</code>s.
        */
  -    void buildNode(Configuration config, Map buildModel) throws Exception;
  -    
  -    /**
  -     * Get the ProcessingNode, ready for use. Can be <code>null</code> for builders
  -     * that just set up the environment, such as <code>ComponentsNodeBuilder</code>.
  -     */
  -    ProcessingNode getNode() throws Exception;
  +    ProcessingNode buildNode(Configuration config) throws Exception;
   }
  
  
  
  1.2       +1 -12     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/SimpleParentProcessingNode.java
  
  Index: SimpleParentProcessingNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/SimpleParentProcessingNode.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- SimpleParentProcessingNode.java	2001/11/18 20:58:44	1.1
  +++ SimpleParentProcessingNode.java	2001/12/20 16:17:54	1.2
  @@ -14,7 +14,7 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/18 20:58:44 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:54 $
    */
   
   public abstract class SimpleParentProcessingNode extends AbstractParentProcessingNode {
  @@ -22,18 +22,7 @@
       /** The childrens of this matcher */
       protected ProcessingNode[] children;
   
  -    /** The node component name (e.g. action name, selector name, etc) */
  -    protected String componentName;
  -    
  -    public SimpleParentProcessingNode(String name) {
  -        this.componentName = name;
  -    }
  -    
       public void setChildren(ProcessingNode[] children) {
           this.children = children;
  -    }
  -    
  -    public void dispose() {
  -        this.disposeNodes(this.children);
       }
   }
  
  
  
  1.2       +13 -14    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/SimpleSelectorProcessingNode.java
  
  Index: SimpleSelectorProcessingNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/SimpleSelectorProcessingNode.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- SimpleSelectorProcessingNode.java	2001/11/18 20:58:44	1.1
  +++ SimpleSelectorProcessingNode.java	2001/12/20 16:17:54	1.2
  @@ -18,14 +18,11 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/18 20:58:44 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:54 $
    */
   
  -public abstract class SimpleSelectorProcessingNode extends AbstractParentProcessingNode {
  +public abstract class SimpleSelectorProcessingNode extends SimpleParentProcessingNode {
   
  -    /** The childrens of this matcher */
  -    protected ProcessingNode[] children;
  -
       /** The node component name (e.g. action name, selector name, etc) */
       protected String componentName;
       
  @@ -40,14 +37,6 @@
           this.selector = selector;
       }
           
  -    public void setChildren(ProcessingNode[] children) {
  -        this.children = children;
  -    }
  -    
  -    public void dispose() {
  -        this.disposeNodes(this.children);
  -    }
  -    
       /**
        * Tests is the component designated by this node using the selector and component name
        * is <code>ThreadSafe</code>, and return it if true.
  @@ -55,7 +44,17 @@
        * Note : this method must be called <i>after</i> <code>setSelector()</code>.
        */
       protected Component getThreadSafeComponent() throws ComponentException {
  -        Component component = this.selector.select(this.componentName);
  +        return getThreadSafeComponent(this.componentName);
  +    }
  +
  +    /**
  +     * Tests is the component designated by this node using the selector and component name
  +     * is <code>ThreadSafe</code>, and return it if true.
  +     * <p>
  +     * Note : this method must be called <i>after</i> <code>setSelector()</code>.
  +     */
  +    protected Component getThreadSafeComponent(String name) throws ComponentException {
  +        Component component = this.selector.select(name);
           if (component instanceof ThreadSafe) {
               return component;
           } else {
  
  
  
  1.5       +94 -39    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/TreeBuilder.java
  
  Index: TreeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/TreeBuilder.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- TreeBuilder.java	2001/11/25 21:37:55	1.4
  +++ TreeBuilder.java	2001/12/20 16:17:54	1.5
  @@ -8,14 +8,15 @@
   
   package org.apache.cocoon.treeprocessor;
   
  +import org.apache.avalon.framework.activity.Disposable;
   import org.apache.avalon.excalibur.component.DefaultRoleManager;
   import org.apache.avalon.excalibur.component.ExcaliburComponentSelector;
   import org.apache.avalon.excalibur.component.RoleManageable;
   import org.apache.avalon.excalibur.component.RoleManager;
  -
   import org.apache.avalon.excalibur.logger.LogKitManageable;
   import org.apache.avalon.excalibur.logger.LogKitManager;
   
  +import org.apache.avalon.framework.activity.Initializable;
   import org.apache.avalon.framework.component.Composable;
   import org.apache.avalon.framework.component.Component;
   import org.apache.avalon.framework.component.ComponentManager;
  @@ -40,17 +41,16 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.4 $ $Date: 2001/11/25 21:37:55 $
  + * @version CVS $Revision: 1.5 $ $Date: 2001/12/20 16:17:54 $
    */
   
   public class TreeBuilder extends AbstractLoggable implements
  -  Composable, Configurable, Contextualizable, LogKitManageable, RoleManageable {
  +  Composable, Configurable, Contextualizable, LogKitManageable, RoleManageable, Disposable {
       
  -    /**
  -     * The categories of node Maps.
  -     */
  -    private Map categories = new HashMap();
  +    private Map registeredNodes = new HashMap();
       
  +    private Map registeredBuilders = new HashMap();
  +    
       private Map attributes = new HashMap();
       
       /**
  @@ -70,7 +70,7 @@
       
       private ExcaliburComponentSelector builderSelector;
       
  -    private ComponentUtil compUtil;
  +    private LifecycleHelper lifecycle;
       
       private String namespace;
       
  @@ -78,6 +78,12 @@
       
       private String languageName;
       
  +    private List initializableNodes = new ArrayList();
  +    
  +    private List disposableNodes = new ArrayList();
  +    
  +    private List linkedBuilders = new ArrayList();
  +    
       private boolean canGetNode = false;
       
       public void contextualize(Context context) throws ContextException {
  @@ -101,7 +107,7 @@
           // Create the new component manager
           this.manager = new TreeBuilderComponentManager(this.parentManager);
           try {
  -            ComponentUtil.setupComponent(this.manager,
  +            LifecycleHelper.setupComponent(this.manager,
                   getLogger(),
                   this.context,
                   null, // component manager
  @@ -117,7 +123,7 @@
               
           try {
               // Create a helper object to setup components
  -            this.compUtil = new ComponentUtil(getLogger(),
  +            this.lifecycle = new LifecycleHelper(getLogger(),
                   this.context,
                   this.manager,
                   this.roleManager,
  @@ -134,7 +140,7 @@
           
               // Create the NodeBuilder selector.
               ExcaliburComponentSelector selector = new ExcaliburComponentSelector();
  -            this.compUtil.setupComponent(selector, false);
  +            this.lifecycle.setupComponent(selector, false);
               
               this.builderSelector = selector;
               
  @@ -199,30 +205,19 @@
       }
       
       /**
  -     * Register a named <code>ProcessingNode</code> in a given category.
  -     * For example, <code>ResourceNodeBuilder</code> stores here the <code>ProcessingNode</code>
  +     * Register a <code>ProcessingNode</code> under a given name.
  +     * For example, <code>ResourceNodeBuilder</code> stores here the <code>ProcessingNode</code>s
        * it produces for use by sitemap pipelines. This allows to turn the tree into a graph.
        */
  -    public void addNode(String category, String name,ProcessingNode node) {
  -        Map nodes = (Map)categories.get(category);
  -        if (nodes == null) {
  -            nodes = new HashMap();
  -            categories.put(category, nodes);
  -        }
  -        nodes.put(name, node);
  +    public void registerNode(String name, ProcessingNode node) {
  +        this.registeredNodes.put(name, node);
       }
       
  -    public ProcessingNode getNode(String category, String name) {
  -        if (this.canGetNode)
  -        {
  -            Map nodes = (Map)categories.get(category);
  -            if (nodes == null) {
  -                return null;
  -            } else {
  -                return (ProcessingNode)nodes.get(name);
  -            }
  +    public ProcessingNode getRegisteredNode(String name) {
  +        if (this.canGetNode) {
  +            return (ProcessingNode)this.registeredNodes.get(name);
           } else {
  -            throw new IllegalArgumentException("Cannot call getNode() now, but during buildNode()");
  +            throw new IllegalArgumentException("Categories are only available during buildNode()");
           }
       }
       
  @@ -252,8 +247,27 @@
           
           getLogger().debug("Creating node builder for " + nodeName);
   
  -        ProcessingNodeBuilder builder = (ProcessingNodeBuilder)this.builderSelector.select(nodeName);
  +        ProcessingNodeBuilder builder;
  +        try {
  +            builder = (ProcessingNodeBuilder)this.builderSelector.select(nodeName);
  +            
  +        } catch(ComponentException ce) {
  +            // Is it because this element is unknown ?
  +            if (this.builderSelector.hasComponent(nodeName)) {
  +                // No : rethrow
  +                throw ce;
  +            } else {
  +                // Throw a more meaningfull exception
  +                throw new ConfigurationException("Unkown element '" + nodeName + "' at " + config.getLocation());
  +            }
  +        }
  +        
           builder.setBuilder(this);
  +        
  +        if (builder instanceof LinkedProcessingNodeBuilder) {
  +            this.linkedBuilders.add(builder);
  +        }
  +
           return builder;
       }
       
  @@ -269,39 +283,76 @@
        */
       public ProcessingNode build(Configuration config) throws Exception {
           
  +        // Calls to getRegisteredNode() are forbidden
  +        this.canGetNode = false;
  +
           // Create a node builder from the top-level element
           ProcessingNodeBuilder rootBuilder = createNodeBuilder(config);
           
           // Build the whole tree (with an empty buildModel)
  -        rootBuilder.buildNode(config, new HashMap());
  +        ProcessingNode result = rootBuilder.buildNode(config);
           
           // Expose newly added components
           this.manager.makeVisible();
           
  -        // Calls to getNode() are now allowed
  +        // Calls to getRegisteredNode() are now allowed
           this.canGetNode = true;
  +
  +        // Resolve links
  +        Iterator iter = this.linkedBuilders.iterator();
  +        while(iter.hasNext()) {
  +            ((LinkedProcessingNodeBuilder)iter.next()).linkNode();
  +        }
  +        
  +        // Initialize all Initializable nodes
  +        iter = this.initializableNodes.iterator();
  +        while(iter.hasNext()) {
  +            ((Initializable)iter.next()).initialize();
  +        }
           
  -        // And get the tree
  -        return rootBuilder.getNode();
  +        // And that's all !
  +        return result;
  +    }
  +    
  +    /**
  +     * Return the list of <code>ProcessingNodes</code> part of this tree that are
  +     * <code>Disposable</code>. Care should be taken to properly dispose them before
  +     * trashing the processing tree.
  +     */
  +    public List getDisposableNodes() {
  +        return this.disposableNodes;
       }
       
       /**
        * Setup a <code>ProcessingNode</code> by setting its location, calling all
        * the lifecycle interfaces it implements and giving it the parameter map if
        * it's a <code>ParameterizableNode</code>.
  +     * <p>
  +     * As a convenience, the node is returned by this method to allow constructs
  +     * like <code>return treeBuilder.setupNode(new MyNode(), config)</code>.
        */
  -    public void setupNode(ProcessingNode node, Configuration config)
  +    public ProcessingNode setupNode(ProcessingNode node, Configuration config)
         throws Exception {
           if (node instanceof AbstractProcessingNode) {
               ((AbstractProcessingNode)node).setLocation(config.getLocation());
           }
           
  -        this.compUtil.setupComponent(node);
  +        this.lifecycle.setupComponent(node, false);
           
           if (node instanceof ParameterizableProcessingNode) {
               Map params = getParameters(config);
               ((ParameterizableProcessingNode)node).setParameters(params);
           }
  +        
  +        if (node instanceof Initializable) {
  +            this.initializableNodes.add(node);
  +        }
  +        
  +        if (node instanceof Disposable) {
  +            this.disposableNodes.add(node);
  +        }
  +        
  +        return node;
       }
   
       /**
  @@ -311,7 +362,7 @@
        * @return the Map of ListOfMapResolver, or <code>null</code> if there are no parameters.
        */
       protected Map getParameters(Configuration config) throws ConfigurationException {
  -        // FIXME : hardcoded prefix
  +
           Configuration[] children = config.getChildren(this.parameterElement);
           
           if (children.length == 0) {
  @@ -324,7 +375,7 @@
               if (true) { // FIXME : check namespace
                   String value = child.getAttribute("value");
                   try {
  -                    params.put(child.getAttribute("name"), ListOfMapsResolver.getResolver(value));
  +                    params.put(child.getAttribute("name"), MapStackResolver.getResolver(value));
                   } catch(PatternException pe) {
                       throw new ConfigurationException("Invalid pattern '" + value +
                           " at " + child.getLocation());
  @@ -333,5 +384,9 @@
           }
           
           return params;
  +    }
  +
  +    public void dispose() {
  +        this.builderSelector.dispose();
       }
   }
  
  
  
  1.5       +4 -9      xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/TreeBuilderComponentManager.java
  
  Index: TreeBuilderComponentManager.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/TreeBuilderComponentManager.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- TreeBuilderComponentManager.java	2001/12/13 00:56:32	1.4
  +++ TreeBuilderComponentManager.java	2001/12/20 16:17:54	1.5
  @@ -23,7 +23,7 @@
    * enriched through <code>Builder.addComponent()</code>.
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.4 $ $Date: 2001/12/13 00:56:32 $
  + * @version CVS $Revision: 1.5 $ $Date: 2001/12/20 16:17:54 $
    */
   
   
  @@ -85,13 +85,8 @@
        * Make the components managed by this manager visible (otherwhise, only parent
        * manager components are visible).
        */
  -    public void makeVisible() {
  -        try {
  -            super.initialize();
  -            visible = true;
  -        } catch (Exception e) {
  -            this.getLogger().error("makeVisible(): Exception",e);
  -        }
  +    public void makeVisible() throws Exception {
  +        super.initialize();
  +        visible = true;
       }
  -
   }
  
  
  
  1.5       +94 -41    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/TreeProcessor.java
  
  Index: TreeProcessor.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/TreeProcessor.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- TreeProcessor.java	2001/11/25 21:37:55	1.4
  +++ TreeProcessor.java	2001/12/20 16:17:54	1.5
  @@ -24,6 +24,7 @@
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
   import org.apache.avalon.framework.configuration.SAXConfigurationHandler;
  +import org.apache.avalon.framework.configuration.NamespacedSAXConfigurationHandler;
   import org.apache.avalon.framework.context.Context;
   import org.apache.avalon.framework.context.ContextException;
   import org.apache.avalon.framework.context.Contextualizable;
  @@ -33,22 +34,22 @@
   import org.apache.cocoon.Processor;
   import org.apache.cocoon.components.pipeline.EventPipeline;
   import org.apache.cocoon.components.pipeline.StreamPipeline;
  +import org.apache.cocoon.components.source.CocoonSourceFactory;
  +import org.apache.cocoon.components.source.DelayedLastModified;
  +import org.apache.cocoon.components.source.SourceHandler;
   import org.apache.cocoon.components.source.URLSource;
   import org.apache.cocoon.components.url.URLFactory;
  -import org.apache.cocoon.components.source.SourceHandler;
   import org.apache.cocoon.environment.Environment;
   import org.apache.cocoon.environment.Source;
   
   import java.io.InputStream;
  -import java.util.ArrayList;
  -import java.util.HashMap;
  -import java.util.Map;
  +import java.util.*;
   
   /**
  - * Interpreted tree-traversal implementation of the a Processor language.
  + * Interpreted tree-traversal implementation of a pipeline assembly language.
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.4 $ $Date: 2001/11/25 21:37:55 $
  + * @version CVS $Revision: 1.5 $ $Date: 2001/12/20 16:17:54 $
    */
   
   public class TreeProcessor extends AbstractLoggable implements ThreadSafe, Processor,
  @@ -78,15 +79,27 @@
       /** The root node of the processing tree */
       protected ProcessingNode rootNode;
       
  +    /** The list of processing nodes that should be disposed when disposing this processor */
  +    protected List disposableNodes;
  +    
       /** Last modification time */
       protected long lastModified = 0;
       
       /** The relative file name of the tree definition */
       protected String sourceName;
       
  +    /** The source of the tree definition */
  +    protected Source source;
  +    
  +    protected DelayedLastModified sourceLastModified;
  +    
  +    /** Delay for <code>sourceLastModified</code>. */
  +    protected long lastModifiedDelay;
  +    
       /** The current language configuration */
       protected Configuration currentLanguage;
       
  +    protected SourceHandler sourceHandler;
       /**
        * Create a TreeProcessor.
        */
  @@ -142,6 +155,7 @@
   
   /*
     <processor>
  +    <reload delay="10"/>
       <root-language name="sitemap"/>
       <language>...</language>
     </processor>
  @@ -155,6 +169,9 @@
               this.language = rootLangConfig.getAttribute("name");
           }
           
  +        // Reload check delay. Default is 1 second.
  +        this.lastModifiedDelay = config.getChild("reload").getAttributeAsLong("delay", 1000L);
  +        
           // Read the builtin languages definition file
           Configuration builtin;
           
  @@ -218,58 +235,79 @@
           
           this.sourceName = fileConfig.getAttribute("name");
   
  +        // Get a new Source handler
  +        this.sourceHandler = (SourceHandler)this.manager.lookup(SourceHandler.ROLE);
  +        
  +        // and add the special "cocoon:" source factory
  +        this.sourceHandler.addFactory("cocoon", new CocoonSourceFactory(this, this.manager));
       }
       
       public boolean process(Environment environment) throws Exception {
  -        return process(environment, null, null);
  +        InvokeContext context = new InvokeContext();
  +        try {
  +            return process(environment, context);
  +        } finally {
  +            context.dispose();
  +        }
       }
   
  -    public boolean process(Environment environment, StreamPipeline pipeline, EventPipeline eventPipeline) throws Exception {
  +    public boolean process(Environment environment, StreamPipeline pipeline, EventPipeline eventPipeline)
  +      throws Exception {
  +        InvokeContext context = new InvokeContext(pipeline, eventPipeline);
  +        try {
  +            return process(environment, context);
  +        } finally {
  +            context.dispose();
  +        }
  +    }
  +
  +    protected boolean process(Environment environment, InvokeContext context)
  +      throws Exception {
  +
           SourceHandler oldSourceHandler = environment.getSourceHandler();
  -        SourceHandler sourceHandler = (SourceHandler)this.manager.lookup(SourceHandler.ROLE);
  -        String uri = org.apache.cocoon.environment.ObjectModelHelper.getRequest(environment.getObjectModel()).getSitemapURI();
  -        System.out.println("Sitemap URI = '" + uri + "'");
   
           try {
  -            environment.setSourceHandler(sourceHandler);
  -            setupRootNode(environment);
  -            return this.rootNode.invoke(environment, pipeline, eventPipeline, new ArrayList());
  +            environment.setSourceHandler(this.sourceHandler);
  +            if (this.rootNode == null || this.sourceLastModified.get() > this.lastModified) {
  +                setupRootNode(environment);
  +            }
  +            return this.rootNode.invoke(environment, context);
           } finally {
               environment.setSourceHandler(oldSourceHandler);
           }
       }
       
  -    protected void setupRootNode(Environment env) throws Exception {
  -        Source source = env.resolve(this.sourceName);
  -        if (this.rootNode != null && source.getLastModified() <= this.lastModified) {
  +    protected synchronized void setupRootNode(Environment env) throws Exception {
  +        
  +        // Now that we entered the synchronized area, recheck what's already
  +        // been checked in process().
  +        if (this.rootNode != null && sourceLastModified.get() <= this.lastModified) {
               // Nothing changed
  -            source.recycle();
               return;
           }
           
           long startTime = System.currentTimeMillis();
           
  -        // Read the tree definition file as a Configuration
  -        Configuration treeConfig;
  -        try {
  -            getLogger().debug("Building " + this.language + " from " + source.getSystemId());
  +        if (this.rootNode == null) {
  +            // First call : create source
  +            this.source = env.resolve(this.sourceName);
  +            this.sourceLastModified = new DelayedLastModified(this.source, 10000L);
               
  -            SAXConfigurationHandler handler = new SAXConfigurationHandler();
  -            source.toSAX(handler);
  -            treeConfig = handler.getConfiguration();
  -        } finally {
  -            source.recycle();
  +        } else {
  +            // Dispose existing tree, we will build a new one.
  +            disposeTree();
           }
           
  -        // Dispose the current tree, if any
  -        if (this.rootNode != null) {
  -            this.rootNode.dispose();
  -        }
  +        // Read the tree definition file as a Configuration
  +        getLogger().debug("Building " + this.language + " from " + source.getSystemId());
  +        
  +        // Build a namespace-aware configuration object
  +        SAXConfigurationHandler handler = new NamespacedSAXConfigurationHandler();
  +        source.toSAX(handler);
  +        Configuration treeConfig = handler.getConfiguration();
           
  -        // Create a new builder to build the tree (it isn't stored since it is used
  -        // only at build-time, which shouldn't occur often in a production server).
  -        TreeBuilder builder = new TreeBuilder();
  -        ComponentUtil.setupComponent(builder,
  +        TreeBuilder treeBuilder = new TreeBuilder();
  +        LifecycleHelper.setupComponent(treeBuilder,
               getLogger(),
               this.context,
               this.manager,
  @@ -277,19 +315,23 @@
               this.logKit,
               this.currentLanguage);
   
  -        builder.setProcessor(this);
  +        treeBuilder.setProcessor(this);
           
           // Build the tree
  -        this.rootNode = builder.build(treeConfig);
  +        ProcessingNode root = treeBuilder.build(treeConfig);
           
  -        ComponentUtil.decommission(builder);
  -        
  +        LifecycleHelper.decommission(treeBuilder);
  +            
           this.lastModified = System.currentTimeMillis();
           
           if (getLogger().isDebugEnabled()) {
               double time = (this.lastModified - startTime) / 1000.0;
               getLogger().debug("TreeProcessor built in " + time + " secs from " + source.getSystemId());
  +            //System.out.println("TreeProcessor built in " + time + " secs from " + source.getSystemId());
           }
  +        
  +        // Finished
  +        this.rootNode = root;
       }
   
       private RoleManager getRoleManager() throws Exception {
  @@ -317,8 +359,19 @@
       }
   
       public void dispose() {
  -        if (this.rootNode != null) {
  -            this.rootNode.dispose();
  +        disposeTree();        
  +        this.manager.release(this.sourceHandler);
  +    }
  +    
  +    /**
  +     * Dispose all nodes in the tree that are disposable
  +     */
  +    protected void disposeTree() {
  +        if (this.disposableNodes != null) {
  +            Iterator iter = this.disposableNodes.iterator();
  +            while (iter.hasNext()) {
  +                ((Disposable)iter.next()).dispose();
  +            }
           }
       }
   }
  
  
  
  1.5       +45 -37    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/treeprocessor.xconf
  
  Index: treeprocessor.xconf
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/treeprocessor.xconf,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- treeprocessor.xconf	2001/11/25 21:37:55	1.4
  +++ treeprocessor.xconf	2001/12/20 16:17:54	1.5
  @@ -9,89 +9,97 @@
    *****************************************************************************
   
    @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - @version CVS $Revision: 1.4 $ $Date: 2001/11/25 21:37:55 $
  + @version CVS $Revision: 1.5 $ $Date: 2001/12/20 16:17:54 $
   -->
   
   <tree-processor>
     <!-- The sitemap language -->
     <language name="sitemap">
     
  -    <!-- The namespace for this language -->
  +    <!-- Namespace for this language -->
       <namespace uri="http://apache.org/cocoon/sitemap/1.0"/>
     
  -    <!-- The file name for this language -->
  +    <!-- File name for files in this language, relative to the environment prefix -->
       <file name="sitemap.xmap"/>
       
  -    <!-- Description the parameter element -->
  -    <parameter element="map:parameter"/>
  +    <!-- Description of the element for nodes parameters -->
  +    <parameter element="parameter"/>
       
       <nodes>
  -      <!-- Note : for now, names are prefixed, but they'll be removed with the namespace-aware Configuration-->
  +      <!-- All node names are given as local names in the above namespace (no prefix) -->
  +      
         <!-- Sitemap root node -->
  -      <node name="map:sitemap" builder="org.apache.cocoon.treeprocessor.sitemap.SitemapNodeBuilder">
  -        <allowed-children>map:components, map:views, map:action-sets, map:resources, map:pipelines</allowed-children>
  +      <node name="sitemap" builder="org.apache.cocoon.treeprocessor.sitemap.SitemapNodeBuilder">
  +        <allowed-children>components, views, action-sets, resources, pipelines</allowed-children>
         </node>
       
         <!-- Components definition : fills selectors on the TreeProcessor -->
  -      <node name="map:components" builder="org.apache.cocoon.treeprocessor.sitemap.ComponentsNodeBuilder">
  -        <selector section="map:matchers"     elements="map:matcher"
  +      <node name="components" builder="org.apache.cocoon.treeprocessor.sitemap.ComponentsNodeBuilder">
  +        <selector section="matchers"     elements="matcher"
                     role="org.apache.cocoon.matching.MatcherSelector"/>
  -        <selector section="map:selectors"    elements="map:selector"
  +        <selector section="selectors"    elements="selector"
                     role="org.apache.cocoon.selection.SelectorSelector"/>
  -        <selector section="map:actions"      elements="map:action"
  +        <selector section="actions"      elements="action"
                     role="org.apache.cocoon.acting.ActionSelector"/>
  -        <selector section="map:generators"   elements="map:generator"
  +        <selector section="generators"   elements="generator"
                     role="org.apache.cocoon.generation.GeneratorSelector"/>
  -        <selector section="map:transformers" elements="map:transformer"
  +        <selector section="transformers" elements="transformer"
                     role="org.apache.cocoon.transformation.TransformerSelector"/>
  -        <selector section="map:serializers"  elements="map:serializer"
  +        <selector section="serializers"  elements="serializer"
                     role="org.apache.cocoon.serialization.SerializerSelector"/>
  -        <selector section="map:readers"      elements="map:reader"
  +        <selector section="readers"      elements="reader"
                     role="org.apache.cocoon.reading.ReaderSelector"/>
         </node>
         
  -      <node name="map:pipelines" builder="org.apache.cocoon.treeprocessor.sitemap.PipelinesNodeBuilder">
  -        <allowed-children>map:pipeline</allowed-children>
  +      <node name="pipelines" builder="org.apache.cocoon.treeprocessor.sitemap.PipelinesNodeBuilder">
  +        <allowed-children>pipeline</allowed-children>
         </node>
  +      
  +      <node name="views" builder="org.apache.cocoon.treeprocessor.NullNodeBuilder"/>
         
  -      <node name="map:views" builder="org.apache.cocoon.treeprocessor.NullNodeBuilder"/>
  +      <node name="resources" builder="org.apache.cocoon.treeprocessor.CategoryNodeBuilder">
  +        <category-name>resources</category-name>
  +      </node>
         
  -      <node name="map:resources" builder="org.apache.cocoon.treeprocessor.NullNodeBuilder"/>
  +      <node name="resource" builder="org.apache.cocoon.treeprocessor.NamedContainerNodeBuilder"/>
  +      
  +      <node name="action-sets" builder="org.apache.cocoon.treeprocessor.CategoryNodeBuilder">
  +        <category-name>action-sets</category-name>
  +      </node>
         
  -      <node name="map:action-sets" builder="org.apache.cocoon.treeprocessor.NullNodeBuilder"/>
  +      <node name="action-set" builder="org.apache.cocoon.treeprocessor.sitemap.ActionSetNodeBuilder"/>
   
  -      <node name="map:pipeline" builder="org.apache.cocoon.treeprocessor.sitemap.PipelineNodeBuilder">
  -        <forbidden-children>map:sitemap, map:components, map:pipelines</forbidden-children>
  +      <node name="pipeline" builder="org.apache.cocoon.treeprocessor.sitemap.PipelineNodeBuilder">
  +        <forbidden-children>sitemap, components, pipelines</forbidden-children>
         </node>
         
  -      <node name="map:match" builder="org.apache.cocoon.treeprocessor.sitemap.MatchNodeBuilder">
  +      <node name="match" builder="org.apache.cocoon.treeprocessor.sitemap.MatchNodeBuilder">
           <forbidden-children>sitemap, components, pipeline, handle-errors</forbidden-children>
         </node>
   
  -      <node name="map:select" builder="org.apache.cocoon.treeprocessor.NullNodeBuilder"/>
  +      <node name="select" builder="org.apache.cocoon.treeprocessor.NullNodeBuilder"/>
   
  -      <node name="map:act" builder="org.apache.cocoon.treeprocessor.sitemap.ActNodeBuilder">
  +      <node name="act" builder="org.apache.cocoon.treeprocessor.sitemap.ActNodeBuilder">
           <forbidden-children>sitemap, components, pipeline, handle-errors</forbidden-children>
         </node>
   
  -      <node name="map:redirect-to" builder="org.apache.cocoon.treeprocessor.sitemap.RedirectToNodeBuilder"/>
  -
  -      <node name="map:mount" builder="org.apache.cocoon.treeprocessor.sitemap.MountNodeBuilder"/>
  +      <node name="redirect-to" builder="org.apache.cocoon.treeprocessor.sitemap.RedirectToNodeBuilder"/>
  +      
  +      <node name="call" builder="org.apache.cocoon.treeprocessor.sitemap.CallNodeBuilder"/>
   
  -      <node name="map:read" builder="org.apache.cocoon.treeprocessor.sitemap.ReadNodeBuilder">
  -      </node>
  +      <node name="mount" builder="org.apache.cocoon.treeprocessor.sitemap.MountNodeBuilder"/>
   
  -      <node name="map:aggregate" builder="org.apache.cocoon.treeprocessor.NullNodeBuilder"/>
  +      <node name="read" builder="org.apache.cocoon.treeprocessor.sitemap.ReadNodeBuilder"/>
   
  -      <node name="map:generate" builder="org.apache.cocoon.treeprocessor.sitemap.GenerateNodeBuilder">
  -      </node>
  +      <node name="aggregate" builder="org.apache.cocoon.treeprocessor.sitemap.AggregateNodeBuilder"/>
   
  -      <node name="map:transform" builder="org.apache.cocoon.treeprocessor.sitemap.TransformNodeBuilder"/>
  +      <node name="generate" builder="org.apache.cocoon.treeprocessor.sitemap.GenerateNodeBuilder"/>
   
  -      <node name="map:serialize" builder="org.apache.cocoon.treeprocessor.sitemap.SerializeNodeBuilder"/>
  +      <node name="transform" builder="org.apache.cocoon.treeprocessor.sitemap.TransformNodeBuilder"/>
   
  -      <node name="map:handle-errors" builder="org.apache.cocoon.treeprocessor.sitemap.HandleErrorsNodeBuilder"/>
  +      <node name="serialize" builder="org.apache.cocoon.treeprocessor.sitemap.SerializeNodeBuilder"/>
   
  +      <node name="handle-errors" builder="org.apache.cocoon.treeprocessor.sitemap.HandleErrorsNodeBuilder"/>
   
       </nodes>
       
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/CategoryNode.java
  
  Index: CategoryNode.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor;
  
  import org.apache.cocoon.ProcessingException;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.components.pipeline.EventPipeline;
  import org.apache.cocoon.components.pipeline.StreamPipeline;
  
  import java.util.*;
  
  /**
   * A generic container node that just invokes its children.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  
  public final class CategoryNode extends AbstractParentProcessingNode {
  
      /** The name of this category */
      private String categoryName;
  
      /** The Map of named nodes in this category */
      private Map nodes;
      
      public void setCategory(String categoryName, Map nodes) {
          this.categoryName = categoryName;
          this.nodes = (nodes != null) ? nodes : new HashMap(0);
      }
      
      public final boolean invoke(Environment env, InvokeContext context) throws Exception {
          throw new ProcessingException("Cannot invoke " + this.categoryName + " at " + getLocation());
      }
      
      public final ProcessingNode getNodeByName(String name) throws Exception {
          ProcessingNode node = (ProcessingNode)nodes.get(name);
          if (node == null) {
              String message = "Unknown " + this.categoryName + " named '" + name + "' at " + getLocation();
              getLogger().error(message);
              throw new ProcessingException(message);
          }
          
          return node;
      }
      
      public final boolean invokeByName(String name, Environment env, InvokeContext context)
        throws Exception {
          
          return getNodeByName(name).invoke(env, context);
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/CategoryNodeBuilder.java
  
  Index: CategoryNodeBuilder.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor;
  
  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.thread.ThreadSafe;
  
  import java.util.*;
  
  /**
   * Builds a generic container node.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  
  public class CategoryNodeBuilder extends AbstractParentProcessingNodeBuilder
    implements Configurable, ThreadSafe {
  
      // Prefix used for registering as a TreeBuilder attribute
      private static String PREFIX = CategoryNodeBuilder.class.getName() + "/";
      
      protected String name;
      
      public void configure(Configuration config) throws ConfigurationException {
          super.configure(config);
          this.name = config.getChild("category-name").getValue();
      }
  
      /** This builder has no parameters -- return <code>false</code> */
      protected boolean hasParameters() {
          return false;
      }
  
      public ProcessingNode buildNode(Configuration config) throws Exception {
          
          CategoryNode node = new CategoryNode();
          this.treeBuilder.setupNode(node, config);
          
          // Get all children and associate them to their name
          Map category = new HashMap();
          
          List children = buildChildNodesList(config);
          Iterator iter = children.iterator();
          while(iter.hasNext()) {
              NamedProcessingNode child = (NamedProcessingNode)iter.next();
              category.put(child.getName(), child);
          }
          
          node.setCategory(this.name, category);
          
          // Register node to allow lookup by other nodes
          this.treeBuilder.registerNode(PREFIX + this.name, node);
          
          return node;
      }
      
      public static CategoryNode getCategoryNode(TreeBuilder builder, String categoryName) {
          return (CategoryNode)builder.getRegisteredNode(PREFIX + categoryName);
      }
  
      public static ProcessingNode getNamedNode(TreeBuilder builder, String categoryName, String nodeName)
        throws Exception {
          CategoryNode category = getCategoryNode(builder, categoryName);
          
          return category.getNodeByName(nodeName);
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ContainerNode.java
  
  Index: ContainerNode.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor;
  
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.components.pipeline.EventPipeline;
  import org.apache.cocoon.components.pipeline.StreamPipeline;
  
  import java.util.*;
  
  /**
   * A generic container node that just invokes its children.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  
  public class ContainerNode extends AbstractParentProcessingNode {
  
      /** The childrens of this matcher */
      protected ProcessingNode[] children;
      
      public void setChildren(ProcessingNode[] children) {
          this.children = children;
      }
      
      public final boolean invoke(Environment env, InvokeContext context) throws Exception {
          
          return invokeNodes(this.children, env, context);
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ContainerNodeBuilder.java
  
  Index: ContainerNodeBuilder.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor;
  
  import org.apache.avalon.framework.thread.ThreadSafe;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  import java.util.*;
  
  /**
   * Builds a generic container node.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  
  public class ContainerNodeBuilder extends AbstractParentProcessingNodeBuilder implements ThreadSafe {
  
      /** This builder has no parameters -- return <code>false</code> */
      protected boolean hasParameters() {
          return false;
      }
  
      public ProcessingNode buildNode(Configuration config) throws Exception {
          
          ContainerNode node = new ContainerNode();
          setupNode(node, config);
          
          return node;
      }
      
      protected void setupNode(ContainerNode node, Configuration config)throws Exception {
          
          this.treeBuilder.setupNode(node, config);
          
          ProcessingNode[] children = buildChildNodes(config);
          if (children.length == 0) {
              throw new ConfigurationException("There must be at least one child at " +
                  config.getLocation());
          }
              
          node.setChildren(children);
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/InvokeContext.java
  
  Index: InvokeContext.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor;
  
  import org.apache.avalon.framework.component.Recomposable;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.thread.ThreadSafe;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.components.pipeline.EventPipeline;
  import org.apache.cocoon.components.pipeline.StreamPipeline;
  
  import java.util.*;
  import org.apache.avalon.framework.activity.Disposable;
  
  /**
   * The invocation context of <code>ProcessingNode</code>s.
   * <p>
   * This class serves two purposes :
   * <ul><li>Avoid explicit enumeration of all needed parameters in
   *         {@link ProcessingNode#invoke(Environment, InvokeContext)},
   *         thus allowing easier addition of new parameters,
   *     <li>Hold pipelines, and provide "just in time" lookup for them.
   * </ul>
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  
  public class InvokeContext implements Recomposable, Disposable {
      
      private List mapStack = new ArrayList();
      
      private StreamPipeline streamPipeline;
      
      private EventPipeline eventPipeline;
      
      private boolean isInternalRequest;
      
      /** The current component manager, as set by the last call to compose() or recompose() */
      private ComponentManager currentManager;
      
      /** The component manager that was used to get the pipelines */
      private ComponentManager pipelinesManager;
      
      /**
       * Create an <code>InvokeContext</code> without existing pipelines. This also means
       * the current request is external.
       */
      public InvokeContext() {
          this.isInternalRequest = false;
      }
      
      /**
       * Create an <code>InvokeContext</code> with existing pipelines. This also means
       * the current request is internal.
       */
      public InvokeContext(StreamPipeline pipeline, EventPipeline eventPipeline) {
          this.isInternalRequest = true;
          this.streamPipeline = pipeline;
          this.eventPipeline = eventPipeline;
      }
      
      public void compose(ComponentManager manager) throws ComponentException {
          this.currentManager = manager;
      }
  
      public void recompose(ComponentManager manager) throws ComponentException {
  
          this.currentManager = manager;
          
          // Recompose pipelines, if any.
          if (this.streamPipeline != null) {
              this.streamPipeline.recompose(manager);
              this.eventPipeline.recompose(manager);
          }
      }
  
      /**
       * Get the <code>EventPipeline</code>. If none already exists, a set of new
       * pipelines is looked up.
       */
      public final EventPipeline getEventPipeline() throws Exception {
          if (this.eventPipeline == null) {
              setupPipelines();
          }
          return this.eventPipeline;
      }
      
      /**
       * Get the <code>StreamPipeline</code>. If none already exists, a set of new
       * pipelines is looked up.
       */
      public final StreamPipeline getStreamPipeline() throws Exception {
          if (this.streamPipeline == null) {
              setupPipelines();
          }
          return this.streamPipeline;
      }
      
      private final void setupPipelines() throws Exception {
          // Keep current manager for proper release
          this.pipelinesManager = this.currentManager;
          
          // Lookup pipelines
          this.eventPipeline  = (EventPipeline)this.pipelinesManager.lookup(EventPipeline.ROLE);
          this.streamPipeline = (StreamPipeline)this.pipelinesManager.lookup(StreamPipeline.ROLE);
          this.streamPipeline.setEventPipeline(this.eventPipeline);
          
          // Immediately recompose them to the current manager : pipelines are created
          // by a parent of the current manager, but need to select component in this one,
          // and not its parent.
          this.eventPipeline.recompose(this.pipelinesManager);
          this.streamPipeline.recompose(this.pipelinesManager);
      }
  
      /**
       * Is this an internal request ?
       */
      public final boolean isInternalRequest() {
          return this.isInternalRequest;
      }
  
      /**
       * Get the current Map stack used to resolve expressions.
       */
      public final List getMapStack() {
          return this.mapStack;
      }
      
      /**
       * Push a Map on top of the current Map stack.
       */
      public final void pushMap(Map map) {
          mapStack.add(map);
          // FIXME : dump map contents to logger
      }
      
      /**
       * Pop the topmost element of the current Map stack.
       */
      public final void popMap() {
          mapStack.remove(mapStack.size() - 1);
      }
  
      /**
       * Release the pipelines, if any, if they were looked up by this context.
       */
      public void dispose() {
          // Release pipelines, if any
          if (!this.isInternalRequest && this.pipelinesManager != null) {
              
              this.pipelinesManager.release(this.eventPipeline);
              this.pipelinesManager.release(this.streamPipeline);
              
              this.pipelinesManager = null;
          }
      }
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/LifecycleHelper.java
  
  Index: LifecycleHelper.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.cocoon.treeprocessor;
  
  import org.apache.avalon.framework.activity.Startable;
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.parameters.Parameterizable;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.context.Contextualizable;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.avalon.framework.logger.Loggable;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  import org.apache.avalon.excalibur.component.RoleManageable;
  import org.apache.avalon.excalibur.component.RoleManager;
  
  import org.apache.avalon.excalibur.logger.LogKitManager;
  import org.apache.avalon.excalibur.logger.LogKitManageable;
  
  import org.apache.log.Logger;
  
  /**
   * Utility class for setting up Avalon components. Similar to Excalibur's
   * <code>DefaultComponentFactory</code>, but on existing objects.
   * <p>
   * Note : could be moved to Avalon.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  public class LifecycleHelper
  {
      /** The Logger for the component
       */
      private Logger                  m_logger;
  
      /** The Context for the component
       */
      private Context                 m_context;
  
      /** The component manager for this component.
       */
      private ComponentManager        m_componentManager;
  
      /** The configuration for this component.
       */
      private Configuration           m_configuration;
  
      /** The RoleManager for child ComponentSelectors
       */
      private RoleManager             m_roles;
  
      /** The LogKitManager for child ComponentSelectors
       */
      private LogKitManager           m_logkit;
  
      /**
       * Construct a new <code>LifecycleHelper</code> that can be used repeatedly to
       * setup several components. <b>Note</b> : if a parameter is <code>null</code>,
       * the corresponding method isn't called (e.g. if <code>configuration</code> is
       * <code>null</code>, <code>configure()</code> isn't called).
       *
       * @param logger the <code>Logger</code> to pass to <code>Loggable</code>s, unless there is
       *        a <code>LogKitManager</code> and the configuration specifies a logger name.
       * @param context the <code>Context</code> to pass to <code>Contexutalizable</code>s.
       * @param componentManager the component manager to pass to <code>Composable</code>s.
       * @param roles the <code>RoleManager</code> to pass to <code>DefaultComponentSelector</code>s.
       * @param configuration the <code>Configuration</code> object to pass to new instances.
       */
      public LifecycleHelper( final Logger logger,
              final Context context,
              final ComponentManager componentManager,
              final RoleManager roles,
              final LogKitManager logkit,
              final Configuration configuration )
      {
          m_logger = logger;
          m_context = context;
          m_componentManager = componentManager;
          m_roles = roles;
          m_logkit = logkit;
          m_configuration = configuration;
      }
  
      /**
       * Setup a component, including initialization and start.
       *
       * @param component the component to setup.
       * @return the component passed in, to allow function chaining.
       * @throws Exception if something went wrong.
       */
      public Object setupComponent(Object component)
          throws Exception
      {
          return setupComponent(component, true);
      }
  
      /**
       * Setup a component, and optionnaly initializes (if it's <code>Initializable</code>)
       * and starts it (if it's <code>Startable</code>).
       *
       * @param component the component to setup.
       * @param initializeAndStart if true, <code>intialize()</code> and <code>start()</code>
       *        will be called.
       * @return the component passed in, to allow function chaining.
       * @throws Exception if something went wrong.
       */
      public Object setupComponent( Object component, boolean initializeAndStart )
          throws Exception
      {
          return setupComponent( component,
                  m_logger,
                  m_context,
                  m_componentManager,
                  m_roles,
                  m_logkit,
                  m_configuration,
                  initializeAndStart );
      }
  
      /**
       * Static equivalent to {@link #setupComponent(Object)}, to be used when there's only one
       * component to setup.
       */
      public static Object setupComponent( final Object component,
              final Logger logger,
              final Context context,
              final ComponentManager componentManager,
              final RoleManager roles,
              final LogKitManager logkit,
              final Configuration configuration )
          throws Exception
      {
          return setupComponent( component,
                  logger,
                  context,
                  componentManager,
                  roles,
                  logkit,
                  configuration,
                  true );
      }
  
      /**
       * Static equivalent to {@link #setupComponent(Object, boolean)}, to be used when there's only one
       * component to setup.
       */
      public static Object setupComponent( final Object component,
              final Logger logger,
              final Context context,
              final ComponentManager componentManager,
              final RoleManager roles,
              final LogKitManager logkit,
              final Configuration configuration,
              final boolean initializeAndStart )
          throws Exception
      {
          if( component instanceof Loggable )
          {
              Logger usedLogger;
              if( null == logkit )
              {
                  usedLogger = logger;
              }
              else if( configuration == null )
              {
                  usedLogger = logger;
              }
              else
              {
                  final String loggerName = configuration.getAttribute( "logger", null );
                  if( null == loggerName )
                  {
                      // No logger attribute available, using standard logger
                      usedLogger = logger;
                  }
                  else
                  {
                      usedLogger = logkit.getLogger( loggerName );
                  }
              }
              
              ((Loggable)component).setLogger( usedLogger );
          }
  
          if( null != context && component instanceof Contextualizable )
          {
              ((Contextualizable)component).contextualize( context );
          }
  
          if( null != componentManager && component instanceof Composable )
          {
              ((Composable)component).compose( componentManager );
          }
  
          if( null != roles && component instanceof RoleManageable )
          {
              ((RoleManageable)component).setRoleManager( roles );
          }
  
          if( null != logkit && component instanceof LogKitManageable )
          {
              ((LogKitManageable)component).setLogKitManager( logkit );
          }
  
          if( null != configuration && component instanceof Configurable )
          {
              ((Configurable)component).configure( configuration );
          }
  
          if( null != configuration && component instanceof Parameterizable )
          {
              ((Parameterizable)component).
                  parameterize( Parameters.fromConfiguration( configuration ) );
          }
  
          if( initializeAndStart && component instanceof Initializable )
          {
              ((Initializable)component).initialize();
          }
  
          if( initializeAndStart && component instanceof Startable )
          {
              ((Startable)component).start();
          }
  
          return component;
      }
  
      /**
       * Decomission a component, by stopping (if it's <code>Startable</code>) and
       * disposing (if it's <code>Disposable</code>) a component.
       */
      public static final void decommission( final Object component )
          throws Exception
      {
          if( component instanceof Startable )
          {
              ((Startable)component).stop();
          }
  
          if( component instanceof Disposable )
          {
              ((Disposable)component).dispose();
          }
      }
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/LinkedProcessingNodeBuilder.java
  
  Index: LinkedProcessingNodeBuilder.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor;
  
  /**
   * A <code>ProcessingNode</code> builder that links its node to
   * other nodes in the hierarchy. This allows to turn the node tree
   * into a directed graph.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  
  public interface LinkedProcessingNodeBuilder extends ProcessingNodeBuilder {
  
      /**
       * Resolve the links needed by the node built by this builder.
       */
      void linkNode() throws Exception;
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/MapStackResolver.java
  
  Index: MapStackResolver.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor;
  
  import org.apache.avalon.framework.parameters.Parameters;
  
  import org.apache.cocoon.sitemap.PatternException;
  
  import java.util.*;
  
  /**
   * Utility class for handling {...} pattern substitutions from a List of Maps.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  
  public abstract class MapStackResolver {
      
      /**
       * An empty <code>Parameters</code> object, returned by <code>buildParameters()</code>
       * if it is given a <code>null</code> Map.
       */
      public static final Parameters EMPTY_PARAMETERS;
      
      static {
          EMPTY_PARAMETERS = new Parameters();
          EMPTY_PARAMETERS.makeReadOnly();
      }
      
      public static final Map EMPTY_MAP = Collections.unmodifiableMap(new java.util.HashMap(0));
      
      /**
       * Resolve all {...} patterns using the values given in the list of maps.
       */
      public abstract String resolve(List mapStack) throws PatternException;
      
      /**
       * Does an expression need resolving (i.e. contain {...} patterns) ?
       */
      public static boolean needsResolve(String expression) {
          if (expression == null || expression.length() == 0) {
              return false;
          }
          
          // Is the first char a '{' ?
          if (expression.charAt(0) == '{') {
              return true;
          }
          
          if (expression.length() < 2) {
              return false;
          }
          
          // Is there any unescaped '{' ?
          int pos = 1;
          while ( (pos = expression.indexOf('{', pos)) != -1) {
              // Found a '{' : is it escaped ?
              if (expression.charAt(pos - 1) != '\\') {
                  // No : need to resolve
                  return true;
              }            
              pos++;
          }
          // Nothing found...
          return false;
      }
      
      /**
       * Unescape an expression that doesn't need to be resolved, but may contain
       * escaped '{' characters.
       *
       * @param expression the expression to unescape.
       * @return the unescaped result, or <code>expression</code> if unescaping isn't necessary.
       */
      public static String unescape(String expression) {
          // Does it need escaping ?
          if (expression == null || expression.indexOf("\\{") == -1) {
              return expression;
          }
  
          StringBuffer buf = new StringBuffer();
          for (int i = 0; i < expression.length(); i++) {
              char ch = expression.charAt(i);
              if (ch != '\\' || i >= (expression.length() - 1) || expression.charAt(i+1) != '{') {
                  buf.append(ch);
              }
          }
          
          return buf.toString();
      }
      
      /**
       * Get a resolver for a given expression. Chooses the most efficient implementation
       * depending on <code>expression</code>.
       */
      public static MapStackResolver getResolver(String expression) throws PatternException {
          if (needsResolve(expression)) {
              return new RealResolver(expression);
          } else {
              return new NullResolver(expression);
          }
      }
      
      /**
       * Build a <code>Parameters</code> object from a Map of named <code>ListOfMapResolver</code>s and
       * a list of Maps used for resolution.
       *
       * @return a fully resolved <code>Parameters</code>.
       */
      public static Parameters buildParameters(Map expressions, List mapStack) throws PatternException {
          if (expressions == null || expressions.size() == 0) {
              return EMPTY_PARAMETERS;
          }
          
          Parameters result = new Parameters();
          
          Iterator iter = expressions.entrySet().iterator();
          while (iter.hasNext()) {
              Map.Entry entry = (Map.Entry)iter.next();
              String value = ((MapStackResolver)entry.getValue()).resolve(mapStack);
              result.setParameter((String)entry.getKey(), value);
          }
          
          return result;
      }
  
      /**
       * Resolve all values of a <code>Map</code> from a Map of named <code>ListOfMapResolver</code>s and
       * a list of Maps used for resolution.
       *
       * @return a fully resolved <code>Map</code>.
       */
      public static Map resolveMap(Map expressions, List mapStack) throws PatternException {
          int size;
          if (expressions == null || (size = expressions.size()) == 0) {
              return EMPTY_MAP;
          }
          
          Map result = new HashMap(size);
          
          Iterator iter = expressions.entrySet().iterator();
          while (iter.hasNext()) {
              Map.Entry entry = (Map.Entry)iter.next();
              String value = ((MapStackResolver)entry.getValue()).resolve(mapStack);
              result.put(entry.getKey(), value);
          }
          
          return result;
      }
  
      //-------------------------------------------------------------------------
      /**
       * No-op resolver for expressions that don't need to be resolved.
       */
      private static class NullResolver extends MapStackResolver {
          private String originalExpr = null;
          private String expression = null;
  
          public NullResolver(String expression) {
              if (expression != null) {
                  this.originalExpr = expression;
                  this.expression = this.unescape(expression);
              }
          }
          
          public String toString() {
              return this.originalExpr;
          }
          
          public String resolve(List mapStack) {
              return this.expression;
          }
      }
      
      //-------------------------------------------------------------------------
      /**
       * Real resolver for expressions containing {..} patterns
       */
      
  // TODO : for now, just borrowed from AbstractSitemap, but the
  // pattern should be precompiled for faster substitution.
      private static class RealResolver extends MapStackResolver {
          private String originalExpr;
          private String expression;
          
          public RealResolver(String expression) throws PatternException {
              this.originalExpr = expression;
              this.expression = expression;
          }
          
          public String toString() {
              return this.originalExpr;
          }
  
          public String resolve(List mapStack) throws PatternException {
              if (expression == null) {
                  return null;
              }
              StringBuffer result = new StringBuffer();
              String s = null;
              int j = 0;
              int k = 0;
              int l = 0;
              int m = 0;
              int ii = 0;
              int i = -1;
              try {
                  while (ii <= expression.length() && (i = expression.indexOf('{', ii)) != -1) {
                      result.append(expression.substring(ii, i));
                      j = expression.indexOf('}', i);
                      if (j < i) {
                          throw new PatternException("invalid expression in \"" + expression + "\"");
                      }
                      ii = j + 1;
                      k = mapStack.size() - 1;
                      s = expression.substring(i + 1, j);
                      l = -3;
                      m = -1;
                      while ((l = s.indexOf("../", l + 3)) != -1) {
                          k--;
                          m = l;
                      }
                      if (m != -1) {
                          s = s.substring(m + 3);
                      }
                      Object value = ((Map)mapStack.get(k)).get(s);
                      if (value != null){
                          result.append(value.toString());
                          //getLogger().debug("Substitute evaluated value for " + s + " as " + value);
                      }else{
                          //getLogger().warn("Substitute: value not found for " + s + " while evaluating " + expression);
                      }
                  }
                  if (ii < expression.length()) {
                      result.append(expression.substring(ii));
                  }
                  return (result.toString());
              } catch (Exception e) {
                  //getLogger().error("AbstractSitemap:substitute()", e);
                  throw new PatternException("error occurred during evaluation of expression \"" + expression + "\" at position " +
                      (i + 1) + " : " + e.getMessage(), e);
              }
          }
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/NamedContainerNode.java
  
  Index: NamedContainerNode.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor;
  
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.components.pipeline.EventPipeline;
  import org.apache.cocoon.components.pipeline.StreamPipeline;
  
  import java.util.*;
  
  /**
   * A named container node that just invokes its children.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  
  public class NamedContainerNode extends ContainerNode implements NamedProcessingNode {
  
      private String name;
  
      public NamedContainerNode(String name) {
          this.name = name;
      }
      
      public String getName() {
          return this.name;
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/NamedContainerNodeBuilder.java
  
  Index: NamedContainerNodeBuilder.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor;
  
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  /**
   * Builds a generic named container node.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  
  public class NamedContainerNodeBuilder extends ContainerNodeBuilder implements Configurable {
  
      private String nameAttr;
  
      public void configure(Configuration config) throws ConfigurationException {
          super.configure(config);
          this.nameAttr = config.getChild("name-attribute").getValue("name");
      }
  
      public ProcessingNode buildNode(Configuration config) throws Exception {
          
          NamedContainerNode node = new NamedContainerNode(config.getAttribute(this.nameAttr));
          this.setupNode(node, config);
          return node;
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/NamedProcessingNode.java
  
  Index: NamedProcessingNode.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor;
  
  import java.util.Map;
  
  /**
   * A <code>ProcessingNode</code> that has a name. This is primarily used by
   * <code>CategoryNode</code> to access its children.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:54 $
   */
  
  public interface NamedProcessingNode extends ProcessingNode {
  
      String getName();    
  }
  
  
  
  1.3       +42 -29    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ActNodeBuilder.java
  
  Index: ActNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ActNodeBuilder.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ActNodeBuilder.java	2001/11/25 21:37:56	1.2
  +++ ActNodeBuilder.java	2001/12/20 16:17:55	1.3
  @@ -16,65 +16,78 @@
   
   import org.apache.cocoon.acting.Action;
   import org.apache.cocoon.treeprocessor.AbstractParentProcessingNodeBuilder;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
   import org.apache.cocoon.treeprocessor.NullNode;
  +import org.apache.cocoon.treeprocessor.LinkedProcessingNodeBuilder;
  +import org.apache.cocoon.treeprocessor.CategoryNodeBuilder;
   
   import java.util.*;
   
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
  -public class ActNodeBuilder extends AbstractParentProcessingNodeBuilder implements Composable {
  +public class ActNodeBuilder extends AbstractParentProcessingNodeBuilder
  +  implements Composable, LinkedProcessingNodeBuilder {
   
       private ComponentManager manager;
  -    private ActNode actNode;
  -    private NullNode setNode;
  -    private List builders;
  +    
  +    private ActTypeNode actTypeNode;
  +    
  +    private ActSetNode  actSetNode;
  +    private String      actSetName;
   
       public void compose(ComponentManager manager) {
           this.manager = manager;
       }
       
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
           
           String source = config.getAttribute("src", null);
           
           // Is it an action-set call ?
  -        String set = config.getAttribute("set", null);
  -        if (set == null) {
  -            String type = ComponentsNodeBuilder.getComponentType("action", config, this.treeBuilder);
  -            this.actNode = new ActNode(type, source);
  -            this.treeBuilder.setupNode(this.actNode, config);
  -            // Selector is set in getNode()
  +        this.actSetName = config.getAttribute("set", null);
  +        if (this.actSetName == null) {
  +            
  +            String type = ComponentsNodeBuilder.getComponentType(Action.ROLE + "Selector", config, this.treeBuilder);
  +            this.actTypeNode = new ActTypeNode(type, source);
  +            this.treeBuilder.setupNode(this.actTypeNode, config);
  +            // Selector is set in linkNode() since it isn't visible now
  +            
  +            this.actTypeNode.setChildren(buildChildNodes(config));
               
  -            // Get all children
  -            this.builders = createChildBuilders(config, buildModel);
  +            return this.actTypeNode;
               
           } else {
  -            getLogger().warn("action sets not yet implemented");
  -            this.setNode = new NullNode();
  -            this.setNode.setLogger(getLogger());
  -            this.setNode.setLocation(config.getLocation());
  +            
  +            // Action set call
  +            this.actSetNode = new ActSetNode(source);
  +            this.treeBuilder.setupNode(this.actSetNode, config);
  +            
  +            this.actSetNode.setChildren(buildChildNodes(config));
  +            
  +            return this.actSetNode;
           }
       }
  -    
  -    public ProcessingNode getNode() throws Exception {
  -        if (this.actNode != null) {
  +
  +    public void linkNode() throws Exception {
  +
  +        if (this.actTypeNode != null) {
  +
               // Get the selector (it's now visible in the manager)
               ComponentSelector selector = (ComponentSelector)manager.lookup(Action.ROLE + "Selector");
  -            this.actNode.setSelector(selector);
  -            
  -            this.actNode.setChildren(getNodes(this.builders));
  -            
  -            return this.actNode;
  -            
  +            this.actTypeNode.setSelector(selector);
  +
           } else {
  -            return this.setNode;
  +            
  +            this.actSetNode.setActionSet(
  +                (ActionSetNode)CategoryNodeBuilder.getNamedNode(
  +                    this.treeBuilder, "action-sets", this.actSetName)
  +            );
           }
       }
   }
  
  
  
  1.5       +106 -45   xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ComponentsNodeBuilder.java
  
  Index: ComponentsNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ComponentsNodeBuilder.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- ComponentsNodeBuilder.java	2001/11/25 21:37:56	1.4
  +++ ComponentsNodeBuilder.java	2001/12/20 16:17:55	1.5
  @@ -35,7 +35,7 @@
   import org.apache.cocoon.sitemap.SitemapComponentSelector;
   
   import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
  -import org.apache.cocoon.treeprocessor.ComponentUtil;
  +import org.apache.cocoon.treeprocessor.LifecycleHelper;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
   import org.apache.cocoon.treeprocessor.TreeBuilder;
   
  @@ -48,18 +48,13 @@
    * but creates <code>ComponentSelectors</code> that are made available to other nodes.
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.4 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.5 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class ComponentsNodeBuilder extends AbstractProcessingNodeBuilder implements
     Composable, Configurable, Contextualizable, LogKitManageable {
   
  -    /**
  -     * The prefix used to store default components as <code>TreeBuilder</code> attributes.
  -     * For example, the default generator (if any) can be obtained using 
  -     * <code>builder.getAttribute(ComponentsNodeBuilder.DEFAULT_PREFIX + "generator")</code>.
  -     */
  -    public static final String DEFAULT_PREFIX = "components:default-";
  +    private static final String COMPONENTS_ATTRIBUTE = ComponentsNodeBuilder.class.getName();
       
       private Context context;
       
  @@ -69,14 +64,17 @@
       
       protected ComponentManager manager;
       
  -    /** Association of roles to selectors */
  +    /** The selector for each role */
       private HashMap selectors = new HashMap();
       
  -    /** Association of element names to section names */
  +    /** The element name for each section name */
       protected Map sectionElements = new HashMap();
       
  -    /** Association of role names to section names */
  +    /** The role name for each section name */
       protected Map sectionRoles = new HashMap();
  +    
  +    /** The default type for each role name (used for static lookup methods) */
  +    protected Map defaultTypes = new HashMap();
           
       public void contextualize(Context context) {
           this.context = context;
  @@ -124,8 +122,10 @@
        * <p>
        * For each section for which a default is provided (e.g. <code>&lt;generators default="file"&gt;</code>),
        * this default is registred as an attribute on the builder
  +     *
  +     * @return null (nothing to do at runtime).
        */
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
           
           // Iterate on all sections
           Configuration[] sections = config.getChildren();
  @@ -143,23 +143,20 @@
                       "', at " + section.getLocation());
               }
               
  +            String role = (String)this.sectionRoles.get(sectionName);
  +            
               // default component
               String defaultElement = section.getAttribute("default", null);
               if (defaultElement == null) {
                   getLogger().debug("Component section '" + sectionName + "' has no default");
               } else {
  -                // FIXME : for now configs have a qualified name. Remove it
  -                int pos = elementName.indexOf(':');
  -                String localName = (pos == -1) ? elementName : elementName.substring(pos + 1);
  -                getLogger().debug("Setting default " + localName + " type to " + defaultElement);
  -                this.treeBuilder.setAttribute(DEFAULT_PREFIX + localName, defaultElement );
  +                getLogger().debug("Setting default " + elementName + " type to " + defaultElement);
  +                this.defaultTypes.put(role, defaultElement);
               }
   
               // Create the selector for this section
               getLogger().debug("Creating component selector for " + sectionName);
               
  -            String role = (String)this.sectionRoles.get(sectionName);
  -            
               SitemapComponentSelector selector = getSelector(role);
               SitemapComponentSelector parentSelector = getParentSelector(role);
   
  @@ -240,34 +237,15 @@
               selector.initialize();
               
               this.treeBuilder.addComponentInstance(role, selector);
  -        }
  -    }
  -    
  -    /**
  -     * Always return <code>null</code>.
  -     */
  -    public ProcessingNode getNode() {
  -        // Nothing to do at runtime.
  -        return null;
  -    }
  -    
  -    public static final String getComponentType(String component, Configuration config, TreeBuilder treeBuilder)
  -      throws ConfigurationException {
  -        
  -        String type = config.getAttribute("type", null);
  -        if (type == null) {
  -            type = (String)treeBuilder.getAttribute(DEFAULT_PREFIX + component);
  +            
           }
           
  -        if (type == null) {
  -            throw new ConfigurationException(
  -                "There is no default " + component + " defined. Cannot create " +
  -                component + " at " + config.getLocation());
  -        }
  +        // Register ourself in the treebuilder for use in static lookup methods below.
  +        this.treeBuilder.setAttribute(COMPONENTS_ATTRIBUTE, this);
           
  -        return type;
  +        return null;
       }
  -
  +    
       protected SitemapComponentSelector getParentSelector(String role) {
           try {
               return (SitemapComponentSelector)this.manager.lookup(role);
  @@ -285,6 +263,8 @@
           
           if (result == null) {
   
  +            getLogger().debug("Creating selector for role " + role);
  +
               // Prepare an empty role manager
               DefaultRoleManager emptyRoleManager = new DefaultRoleManager();
               emptyRoleManager.setLogger(getLogger());
  @@ -294,7 +274,7 @@
               selectorConfig.setAttribute("role", role);
               
               result = new SitemapComponentSelector();
  -            ComponentUtil.setupComponent(
  +            LifecycleHelper.setupComponent(
                   result,
                   getLogger(),
                   this.context,
  @@ -327,10 +307,91 @@
           
           SitemapComponentSelector generators = getSelector(Generator.ROLE + "Selector");
           
  -        generators.addComponent("!error-notifier'", org.apache.cocoon.sitemap.ErrorNotifier.class, emptyConfig);
  +        generators.addComponent("!error-notifier!", org.apache.cocoon.sitemap.ErrorNotifier.class, emptyConfig);
           generators.addComponent("!content-aggregator!", org.apache.cocoon.sitemap.ContentAggregator.class, emptyConfig);
           
           SitemapComponentSelector transformers = getSelector(Transformer.ROLE + "Selector");
           transformers.addComponent("!link-translator!", org.apache.cocoon.sitemap.LinkTranslator.class, emptyConfig);
  +    }
  +
  +    //----- lookup methods for other builders
  +    
  +    /**
  +     * Get the actual type for an element defined by <code>config</code>. If there is
  +     * no 'type' attribute, the default type for this role is returned.
  +     *
  +     * @throws ConfigurationException if no type is given and there is no default type
  +     *         defined for this role, or if there is no definition for the given type.
  +     */
  +    public static final String getComponentType(String role, Configuration config, TreeBuilder treeBuilder)
  +      throws ConfigurationException {
  +        
  +        ComponentsNodeBuilder components =
  +            (ComponentsNodeBuilder)treeBuilder.getAttribute(COMPONENTS_ATTRIBUTE);
  +
  +        if (components == null) {
  +            throw new ConfigurationException("Cannot find components definition to handle " +
  +                config.getName() + " at " + config.getLocation());
  +        }
  +        
  +        return components.doGetComponentType(role, config);
  +    }
  +    
  +    /**
  +     * Get the class for a type and role.
  +     */
  +    public static final Class getComponentClass(String role, String type, TreeBuilder treeBuilder)
  +      throws Exception {
  +        
  +        ComponentsNodeBuilder components =
  +            (ComponentsNodeBuilder)treeBuilder.getAttribute(COMPONENTS_ATTRIBUTE);
  +
  +        if (components == null) {
  +            // Cannot give location information here, but this is unlikely
  +            // to happen since users of this method generally call getComponentType()
  +            // before this one.
  +            throw new ConfigurationException("Cannot find components definition");
  +        }
  +        
  +        return components.doGetComponentClass(role, type);
  +    }
  +    
  +    private String doGetComponentType(String role, Configuration config) throws ConfigurationException {
  +        
  +        String type = config.getAttribute("type", null);
  +        if (type == null) {
  +            type = (String)this.defaultTypes.get(role);
  +        }
  +        
  +        if (type == null) {
  +            throw new ConfigurationException(
  +                "There is no default type defined for " +
  +                config.getName() + " at " + config.getLocation());
  +        }
  +        
  +        return type;
  +    }
  +    
  +    private Class doGetComponentClass(String role, String type) throws Exception {
  +
  +        Class result = null;
  +
  +        // Is there a selector explicitly defined for this role ?
  +        // (cannot directly lookup manager since locally-defined selectors aren't yet visible)
  +        ComponentSelector selector = getSelector(role);
  +        if (selector == null) {
  +            // Try parent
  +            selector = getParentSelector(role);
  +        }
  +        
  +        if (selector == null) {
  +            throw new IllegalArgumentException("Cannot find a selector for role " + role);
  +        }
  +        
  +        Component object = selector.select(type);
  +        result = object.getClass();
  +        selector.release(object);
  +        
  +        return result;
       }
   }
  
  
  
  1.3       +11 -8     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/GenerateNode.java
  
  Index: GenerateNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/GenerateNode.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- GenerateNode.java	2001/11/25 21:37:56	1.2
  +++ GenerateNode.java	2001/12/20 16:17:55	1.3
  @@ -13,41 +13,44 @@
   import org.apache.cocoon.components.pipeline.StreamPipeline;
   import org.apache.cocoon.sitemap.PatternException;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
   
   import java.util.List;
   import org.apache.cocoon.treeprocessor.ParameterizableProcessingNode;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   import java.util.Map;
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class GenerateNode extends AbstractProcessingNode implements ParameterizableProcessingNode {
   
       private String generatorName;
       
  -    private ListOfMapsResolver source;
  +    private MapStackResolver source;
       
       private Map parameters;
   
       public GenerateNode(String name, String source) throws PatternException {
           this.generatorName = name;
  -        this.source = ListOfMapsResolver.getResolver(source);
  +        this.source = MapStackResolver.getResolver(source);
       }
       
       public void setParameters(Map parameterMap) {
           this.parameters = parameterMap;
       }
       
  -    public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public final boolean invoke(Environment env, InvokeContext context)
         throws Exception {
           
  -        eventPipeline.setGenerator(
  +        List mapStack = context.getMapStack();
  +        
  +        context.getEventPipeline().setGenerator(
               this.generatorName,
  -            source.resolve(listOfMaps),
  -            ListOfMapsResolver.buildParameters(this.parameters, listOfMaps)
  +            source.resolve(mapStack),
  +            MapStackResolver.buildParameters(this.parameters, mapStack)
           );
           
           // Return false to contine sitemap invocation
  
  
  
  1.3       +11 -15    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/GenerateNodeBuilder.java
  
  Index: GenerateNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/GenerateNodeBuilder.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- GenerateNodeBuilder.java	2001/11/25 21:37:56	1.2
  +++ GenerateNodeBuilder.java	2001/12/20 16:17:55	1.3
  @@ -10,31 +10,27 @@
   
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
  +import org.apache.avalon.framework.thread.ThreadSafe;
  +
  +import org.apache.cocoon.generation.Generator;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
   
  -import java.util.Map;
  -
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
  -
  -public class GenerateNodeBuilder extends AbstractProcessingNodeBuilder {
   
  -    private GenerateNode node;
  +public class GenerateNodeBuilder extends AbstractProcessingNodeBuilder implements ThreadSafe {
   
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
           
  -        String type = ComponentsNodeBuilder.getComponentType("generator", config, this.treeBuilder);
  -        
  -        this.node = new GenerateNode(type, config.getAttribute("src", null));
  -        this.treeBuilder.setupNode(this.node, config);
  +        String type = ComponentsNodeBuilder.getComponentType(
  +            Generator.ROLE + "Selector", config, this.treeBuilder);
  +
  +        String src  = config.getAttribute("src", null);
           
  -    }
  -    
  -    public ProcessingNode getNode() {
  -        return this.node;
  +        return this.treeBuilder.setupNode(new GenerateNode(type, src), config);
       }
   }
  
  
  
  1.2       +13 -9     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/HandleErrorsNode.java
  
  Index: HandleErrorsNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/HandleErrorsNode.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- HandleErrorsNode.java	2001/11/25 21:37:56	1.1
  +++ HandleErrorsNode.java	2001/12/20 16:17:55	1.2
  @@ -19,6 +19,7 @@
   import org.apache.cocoon.components.pipeline.StreamPipeline;
   import org.apache.cocoon.treeprocessor.AbstractParentProcessingNode;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.*;
   
  @@ -26,30 +27,33 @@
    * Handles &lt;map:handle-errors&gt;
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public final class HandleErrorsNode extends AbstractParentProcessingNode {
   
       private ProcessingNode[] children;
  +    private int statusCode;
       
  +    public HandleErrorsNode(int statusCode) {
  +        this.statusCode = statusCode;
  +    }
  +    
  +    public int getStatusCode() {
  +        return this.statusCode;
  +    }
  +    
       public void setChildren(ProcessingNode[] nodes)
       {
           this.children = nodes;
       }
       
  -    public final boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public final boolean invoke(Environment env, InvokeContext context)
         throws Exception {
           
           if (getLogger().isDebugEnabled()) {
               getLogger().debug("Processing handle-errors at " + getLocation());
           }
  -        return invokeNodes(this.children, env, pipeline, eventPipeline, listOfMaps);
  +        return invokeNodes(this.children, env, context);
       }
  -    
  -    public final void dispose() {
  -        
  -        disposeNodes(this.children);
  -    }
  -    
   }
  
  
  
  1.2       +9 -29     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/HandleErrorsNodeBuilder.java
  
  Index: HandleErrorsNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/HandleErrorsNodeBuilder.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- HandleErrorsNodeBuilder.java	2001/11/25 21:37:56	1.1
  +++ HandleErrorsNodeBuilder.java	2001/12/20 16:17:55	1.2
  @@ -9,53 +9,33 @@
   package org.apache.cocoon.treeprocessor.sitemap;
   
   import org.apache.avalon.framework.configuration.Configuration;
  +import org.apache.avalon.framework.thread.ThreadSafe;
   
   import org.apache.cocoon.treeprocessor.AbstractParentProcessingNodeBuilder;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
   
  -import java.util.*;
  -
   /**
    * Builds a &lt;map:handle-errors&gt;
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:55 $
    */
  -
  -public class HandleErrorsNodeBuilder extends AbstractParentProcessingNodeBuilder {
   
  -    private HandleErrorsNode node;
  -    private List builders;
  -    private int statusCode;
  -    private String location;
  +public class HandleErrorsNodeBuilder extends AbstractParentProcessingNodeBuilder implements ThreadSafe {
   
       /** This builder has no parameters -- return <code>false</code> */
       protected boolean hasParameters() {
           return false;
       }
       
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  -        this.location = config.getLocation();
  -        this.statusCode = config.getAttributeAsInteger("type", 500);
  -        this.node = new HandleErrorsNode();
  -        this.treeBuilder.setupNode(this.node, config);
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
  +        
  +        HandleErrorsNode node = new HandleErrorsNode(config.getAttributeAsInteger("type", 500));
  +        this.treeBuilder.setupNode(node, config);
           
           // Get all children
  -        this.builders = createChildBuilders(config, buildModel);        
  -    }
  -    
  -    public int getStatusCode() {
  -        return this.statusCode;
  -    }
  -    
  -    public String getLocation() {
  -        return this.location;
  -    }
  -
  -    public ProcessingNode getNode() throws Exception {
  +        node.setChildren(buildChildNodes(config));
           
  -        this.node.setChildren(getNodes(this.builders));
  -                
  -        return this.node;
  +        return node;  
       }
   }
  
  
  
  1.3       +11 -8     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/MatchNode.java
  
  Index: MatchNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/MatchNode.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- MatchNode.java	2001/11/25 21:37:56	1.2
  +++ MatchNode.java	2001/12/20 16:17:55	1.3
  @@ -13,11 +13,12 @@
   import org.apache.cocoon.components.pipeline.StreamPipeline;
   import org.apache.cocoon.sitemap.PatternException;
   import org.apache.cocoon.treeprocessor.SimpleSelectorProcessingNode;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
   import org.apache.cocoon.matching.Matcher;
   import org.apache.avalon.framework.component.ComponentSelector;
   import org.apache.avalon.framework.component.ComponentException;
   import org.apache.avalon.framework.parameters.Parameters;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.*;
   import org.apache.cocoon.treeprocessor.ParameterizableProcessingNode;
  @@ -26,13 +27,13 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class MatchNode extends SimpleSelectorProcessingNode implements ParameterizableProcessingNode {
       
       /** The 'pattern' attribute */
  -    private ListOfMapsResolver pattern;
  +    private MapStackResolver pattern;
       
       /** The matcher, if it's ThreadSafe */
       private Matcher threadSafeMatcher;
  @@ -41,7 +42,7 @@
       
       public MatchNode(String name, String pattern) throws PatternException {
           super(name);
  -        this.pattern = ListOfMapsResolver.getResolver(pattern);
  +        this.pattern = MapStackResolver.getResolver(pattern);
       }
        
       public void setParameters(Map parameterMap) {
  @@ -53,12 +54,14 @@
           this.threadSafeMatcher = (Matcher)this.getThreadSafeComponent();
       }
       
  -    public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public final boolean invoke(Environment env, InvokeContext context)
         throws Exception {
           
  -        String resolvedPattern = pattern.resolve(listOfMaps);
  +        List mapStack = context.getMapStack();
  +        
  +        String resolvedPattern = pattern.resolve(mapStack);
           Map objectModel = env.getObjectModel();
  -        Parameters resolvedParams = ListOfMapsResolver.buildParameters(this.parameters, listOfMaps);
  +        Parameters resolvedParams = MapStackResolver.buildParameters(this.parameters, mapStack);
           
           Map result = null;
           
  @@ -83,7 +86,7 @@
               }
               
               // Invoke children with the matcher results
  -            return this.invokeNodes(children, env, pipeline, eventPipeline, listOfMaps, result);
  +            return this.invokeNodes(children, env, context, result);
               
           } else {
               // Matcher failed
  
  
  
  1.3       +37 -47    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/MatchNodeBuilder.java
  
  Index: MatchNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/MatchNodeBuilder.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- MatchNodeBuilder.java	2001/11/25 21:37:56	1.2
  +++ MatchNodeBuilder.java	2001/12/20 16:17:55	1.3
  @@ -17,8 +17,9 @@
   import org.apache.cocoon.matching.Matcher;
   import org.apache.cocoon.matching.PreparableMatcher;
   import org.apache.cocoon.treeprocessor.AbstractParentProcessingNodeBuilder;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
  +import org.apache.cocoon.treeprocessor.LinkedProcessingNodeBuilder;
   import org.apache.cocoon.treeprocessor.SimpleSelectorProcessingNode;
   
   import java.util.*;
  @@ -26,71 +27,60 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
  -public class MatchNodeBuilder extends AbstractParentProcessingNodeBuilder implements Composable {
  +public class MatchNodeBuilder extends AbstractParentProcessingNodeBuilder
  +  implements Composable, LinkedProcessingNodeBuilder {
   
  +    private static final String SELECTOR_ROLE = Matcher.ROLE + "Selector";
  +
       private ComponentManager manager;
       private SimpleSelectorProcessingNode node;
  -    private List builders;
  -    private Configuration config;
   
       public void compose(ComponentManager manager) {
           this.manager = manager;
       }
   
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
           
  -        this.config = config;
  +        String pattern = config.getAttribute("pattern", null);
           
  -        // Actual creation of node is deferred to getNode() since we need the Selector
  +        // Get the type and class for this matcher
  +        String type = ComponentsNodeBuilder.getComponentType(SELECTOR_ROLE, config, this.treeBuilder);
  +        Class clazz = ComponentsNodeBuilder.getComponentClass(SELECTOR_ROLE, type, this.treeBuilder);
  +    
  +        // PreparableMatcher are only prepared if pattern doesn't need request-time resolution.
  +        boolean preparable =
  +            PreparableMatcher.class.isAssignableFrom(clazz) &&
  +            !MapStackResolver.needsResolve(pattern);
  +        
  +        // Instanciate appropriate node
  +        if (preparable) {
  +            this.node = new PreparableMatchNode(type, pattern);
  +        } else {
  +            this.node = new MatchNode(type, pattern);
  +        }
           
  +        this.treeBuilder.setupNode(this.node, config);
  +        
           // Get all children
  -        this.builders = createChildBuilders(config, buildModel);
  +        ProcessingNode[] children = buildChildNodes(config);
           
  -        if (this.builders.size() == 0) {
  +        if (children.length == 0) {
               throw new ConfigurationException("There must be at least one child in match at " +
                   config.getLocation());
           }
  +       
  +        this.node.setChildren(children);
           
  -    }
  -    
  -    public ProcessingNode getNode() throws Exception {
  -        // Protect against multiple calls (shouldn't normally occur)
  -        if (this.node == null) {
  -            
  -            String type = ComponentsNodeBuilder.getComponentType("matcher", config, this.treeBuilder);
  -        
  -            String pattern = this.config.getAttribute("pattern", null);
  -            
  -            // Get the selector
  -            ComponentSelector selector = (ComponentSelector)manager.lookup(Matcher.ROLE + "Selector");
  -
  -            // Get a matcher instance and check if its a PreparableMatcher
  -            Matcher matcher = (Matcher)selector.select(type);
  -            boolean preparable;
  -            try {
  -                // Prepared matchers are only prepared if pattern doesn't need
  -                // request-time resolution.
  -                preparable =
  -                   (matcher instanceof PreparableMatcher) && !ListOfMapsResolver.needsResolve(pattern);
  -            } finally {
  -                selector.release(matcher);
  -            }
  -            
  -            // Instanciate corresponding node
  -            if (preparable) {
  -                this.node = new PreparableMatchNode(type, pattern);
  -            } else {
  -                this.node = new MatchNode(type, pattern);
  -            }
  -            
  -            this.treeBuilder.setupNode(this.node, this.config);
  -            this.node.setSelector(selector);
  -            this.node.setChildren(getNodes(this.builders));
  -        }
  -                
           return this.node;
  +    }
  +
  +    public void linkNode() throws Exception {
  +        
  +        // Get the selector (it's now visible in the manager)
  +        ComponentSelector selector = (ComponentSelector)manager.lookup(SELECTOR_ROLE);
  +        this.node.setSelector(selector);
       }
   }
  
  
  
  1.2       +20 -14    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/MountNode.java
  
  Index: MountNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/MountNode.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- MountNode.java	2001/11/25 21:37:56	1.1
  +++ MountNode.java	2001/12/20 16:17:55	1.2
  @@ -16,9 +16,10 @@
   import org.apache.cocoon.environment.Environment;
   import org.apache.cocoon.sitemap.PatternException;
   
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
   import org.apache.cocoon.treeprocessor.TreeProcessor;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.*;
   import org.apache.avalon.framework.component.Composable;
  @@ -27,16 +28,16 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class MountNode extends AbstractProcessingNode implements Composable {
       
       /** The 'uri-prefix' attribute */
  -    private ListOfMapsResolver prefix;
  +    private MapStackResolver prefix;
       
       /** The 'src' attribute */
  -    private ListOfMapsResolver source;
  +    private MapStackResolver source;
       
       /** Processors for sources */
       private Map processors = new HashMap();
  @@ -52,8 +53,8 @@
       
       public MountNode(String prefix, String source, String language, TreeProcessor parentProcessor)
         throws PatternException {
  -        this.prefix = ListOfMapsResolver.getResolver(prefix);
  -        this.source = ListOfMapsResolver.getResolver(source);
  +        this.prefix = MapStackResolver.getResolver(prefix);
  +        this.source = MapStackResolver.getResolver(source);
           this.language = language;
           this.parentProcessor = parentProcessor;
       }
  @@ -62,32 +63,38 @@
           this.manager = manager;
       }
       
  -    public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public final boolean invoke(Environment env, InvokeContext context)
         throws Exception {
           
  -        String resolvedSource = this.source.resolve(listOfMaps);
  +        List mapStack = context.getMapStack();
  +        
  +        String resolvedSource = this.source.resolve(mapStack);
           TreeProcessor processor = (TreeProcessor)processors.get(resolvedSource);
           if (processor == null) {
               processor = getProcessor(resolvedSource);
           }
           
  -        String resolvedPrefix = this.prefix.resolve(listOfMaps);
  +        String resolvedPrefix = this.prefix.resolve(mapStack);
   
           String oldPrefix = env.getURIPrefix();
           String oldURI    = env.getURI();
           try {
               env.changeContext(resolvedPrefix, resolvedSource);
   
  -            if (SitemapNode.isInternalRequest(env)) {
  +            if (context.isInternalRequest()) {
                   // Propagate pipelines
  -                return processor.process(env, pipeline, eventPipeline);
  +                return processor.process(env, context.getStreamPipeline(), context.getEventPipeline());
               } else {
                   // Processor will create its own pipelines
                   return processor.process(env);
               }
  -
  -        } finally{
  +            
  +        } finally {
  +            // Restore context
               env.setContext(oldPrefix, oldURI);
  +            
  +            // Recompose pipelines which may have been recomposed by subsitemap
  +            context.recompose(this.manager);
           }
       }
       
  @@ -105,7 +112,6 @@
       }
       
       public void dispose() {
  -        super.dispose();
           Iterator iter = this.processors.values().iterator();
           while(iter.hasNext()) {
               ((TreeProcessor)iter.next()).dispose();
  
  
  
  1.2       +7 -14     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/MountNodeBuilder.java
  
  Index: MountNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/MountNodeBuilder.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- MountNodeBuilder.java	2001/11/25 21:37:56	1.1
  +++ MountNodeBuilder.java	2001/12/20 16:17:55	1.2
  @@ -9,39 +9,32 @@
   package org.apache.cocoon.treeprocessor.sitemap;
   
   import org.apache.avalon.framework.configuration.Configuration;
  +import org.apache.avalon.framework.thread.ThreadSafe;
   
   import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
   
  -import java.util.Map;
  -
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:55 $
    */
   
  -public class MountNodeBuilder extends AbstractProcessingNodeBuilder {
  +public class MountNodeBuilder extends AbstractProcessingNodeBuilder implements ThreadSafe {
   
  -    private MountNode node;
  -    
       /** This builder has no parameters -- return <code>false</code> */
       protected boolean hasParameters() {
  -        return true;
  +        return false;
       }
   
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
           
  -        this.node = new MountNode(
  +        MountNode node = new MountNode(
               config.getAttribute("uri-prefix"),
               config.getAttribute("src"),
               config.getAttribute("language", null),
               this.treeBuilder.getProcessor()
           );
  -        this.treeBuilder.setupNode(this.node, config);
  -    }
  -    
  -    public ProcessingNode getNode() {
  -        return this.node;
  +        return (this.treeBuilder.setupNode(node, config));
       }
   }
  
  
  
  1.5       +47 -38    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PipelineNode.java
  
  Index: PipelineNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PipelineNode.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- PipelineNode.java	2001/11/25 21:37:56	1.4
  +++ PipelineNode.java	2001/12/20 16:17:55	1.5
  @@ -12,6 +12,7 @@
   import org.apache.avalon.framework.component.Composable;
   import org.apache.avalon.framework.activity.Disposable;
   
  +import org.apache.cocoon.ConnectionResetException;
   import org.apache.cocoon.ResourceNotFoundException;
   
   import org.apache.cocoon.environment.Environment;
  @@ -20,8 +21,9 @@
   import org.apache.cocoon.components.pipeline.EventPipeline;
   import org.apache.cocoon.components.pipeline.StreamPipeline;
   import org.apache.cocoon.treeprocessor.AbstractParentProcessingNode;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.*;
   
  @@ -29,7 +31,7 @@
    * 
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.4 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.5 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class PipelineNode extends AbstractParentProcessingNode implements Composable {
  @@ -47,6 +49,9 @@
       
       private boolean internalOnly = false;
       
  +    /** Is it the last <pipeline> in the enclosing <pipelines> ? */
  +    private boolean isLast = false;
  +    
       /**
        * The component manager is used to create error pipelines
        */
  @@ -59,6 +64,10 @@
           this.children = nodes;
       }
       
  +    public void setLast(boolean isLast) {
  +        this.isLast = isLast;
  +    }
  +    
       public void set404Handler(ProcessingNode node)
       {
           this.error404 = node;
  @@ -73,25 +82,35 @@
           this.internalOnly = internalOnly;
       }
   
  -    public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public final boolean invoke(Environment env, InvokeContext context)
         throws Exception {
           
           // Always fail on external resquests if internal only.
  -        if (this.internalOnly && !SitemapNode.isInternalRequest(env)) {
  +        if (this.internalOnly && !context.isInternalRequest()) {
               return false;
           }
           
           try {
               
  -            return invokeNodes(children, env, pipeline, eventPipeline, listOfMaps);
  +            if (invokeNodes(children, env, context)) {
  +                return true;
  +            } else if (this.isLast) {
  +                throw new ResourceNotFoundException("No pipeline matched request: " +
  +                    env.getURIPrefix() + '/' + env.getURI());
  +            } else {
  +                return false;
  +            }
               
  +        } catch (ConnectionResetException cre) {
  +            // Will be reported by CocoonServlet, rethrowing
  +            throw cre;
  +
           } catch(ResourceNotFoundException rnfe) {
  -            getLogger().debug("Resource not found in pipeline at " + getLocation(), rnfe);
  +            getLogger().warn("Resource not found in pipeline at " + getLocation(), rnfe);
               
               if (error404 != null) {
                   // There's a handler
  -                invokeErrorHandler(error404, rnfe, env);
  -                return true;
  +                return invokeErrorHandler(error404, rnfe, env);
                   
               } else {
                   // No handler : propagate
  @@ -99,16 +118,18 @@
               }
   
           } catch(Exception e) {
  -            getLogger().debug("Error while processing pipeline at " + getLocation(), e);
               
               // Rethrow exception for internal requests
  -            if (error500 != null || !SitemapNode.isInternalRequest(env)) {
  +            if (error500 != null && !context.isInternalRequest()) {
                   
  -                invokeErrorHandler(error500, e, env);
  -                return true;
  +                getLogger().warn("Error while processing pipeline at " + getLocation(), e);
                   
  +                return invokeErrorHandler(error500, e, env);
  +                
               } else {
                   // No handler : propagate
  +                
  +                getLogger().error("Error while processing pipeline at " + getLocation(), e);
                   throw e;
               }
           }
  @@ -116,27 +137,29 @@
       
       private boolean invokeErrorHandler(ProcessingNode node, Exception ex, Environment env)
         throws Exception {
  -        EventPipeline eventPipeline = null;
  -        StreamPipeline pipeline = null;
  +
  +        InvokeContext errorContext = null;
  +        
           try {
               tryResetResponse(env);
  -            eventPipeline = (EventPipeline) this.manager.lookup(EventPipeline.ROLE);
  -            pipeline = (StreamPipeline) this.manager.lookup(StreamPipeline.ROLE);
  -            pipeline.setEventPipeline(eventPipeline);
  -            List listOfMaps = new ArrayList();
  +            
  +            // Build a new context
  +            errorContext = new InvokeContext();
  +            errorContext.compose(this.manager);
               
  -            eventPipeline.setGenerator ("!error-notifier!", ex.getMessage(),
  -                ListOfMapsResolver.EMPTY_PARAMETERS, ex);
  +            errorContext.getEventPipeline().setGenerator("!error-notifier!", ex.getMessage(),
  +                MapStackResolver.EMPTY_PARAMETERS, ex);
               
  -            return node.invoke(env, pipeline, eventPipeline, listOfMaps);
  +            return node.invoke(env, errorContext);
               
           } catch (Exception subEx) {
               getLogger().error("error notifier barfs", subEx);
  -            throw ex;
  +            return false;
               
           } finally {
  -            this.manager.release(eventPipeline);
  -            this.manager.release(pipeline);
  +            if (errorContext != null) {
  +                errorContext.dispose();
  +            }
           }
       }
   
  @@ -165,19 +188,5 @@
           }
           
           getLogger().debug("Response wasn't reset");
  -    }
  -    
  -    
  -    public void dispose() {
  -        
  -        disposeNodes(children);
  -        
  -        if (this.error404 != null) {
  -            this.error404.dispose();
  -        }
  -        
  -        if (this.error500 != null) {
  -            this.error500.dispose();
  -        }
       }
   }
  
  
  
  1.4       +34 -46    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PipelineNodeBuilder.java
  
  Index: PipelineNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PipelineNodeBuilder.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- PipelineNodeBuilder.java	2001/11/25 21:37:56	1.3
  +++ PipelineNodeBuilder.java	2001/12/20 16:17:55	1.4
  @@ -10,6 +10,7 @@
   
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
  +import org.apache.avalon.framework.thread.ThreadSafe;
   
   import org.apache.cocoon.treeprocessor.AbstractParentProcessingNodeBuilder;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
  @@ -21,80 +22,67 @@
    * Builds a &lt;map:pipeline&gt;
    
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.3 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.4 $ $Date: 2001/12/20 16:17:55 $
    */
   
  -public class PipelineNodeBuilder extends AbstractParentProcessingNodeBuilder {
  +public class PipelineNodeBuilder extends AbstractParentProcessingNodeBuilder implements ThreadSafe {
   
  -    private PipelineNode node;
  -    private List builders;
  -    private ProcessingNodeBuilder error404Builder;
  -    private ProcessingNodeBuilder error500Builder;
  -    
       /** This builder has no parameters -- return <code>false</code> */
       protected boolean hasParameters() {
           return true;
       }
   
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
           
  -        this.node = new PipelineNode();
  -        this.treeBuilder.setupNode(this.node, config);
  +        PipelineNode node = new PipelineNode();
  +        this.treeBuilder.setupNode(node, config);
   
  -        this.node.setInternalOnly(config.getAttributeAsBoolean("internal-only", false));
  -        
  -        // Get all children but the error handlers
  -        if (this.ignoredChildren == null)
  -            this.ignoredChildren = new ArrayList();
  -            
  -        List allBuilders = createChildBuilders(config, buildModel);
  +        node.setInternalOnly(config.getAttributeAsBoolean("internal-only", false));
  +
  +        ProcessingNode error404Handler = null;
  +        ProcessingNode error500Handler = null;
           
  -        // Separate regular nodes and error handlers
  -        this.builders = new ArrayList();
  +        Configuration[] childConfigs = config.getChildren();
  +        List children = new ArrayList();
           
  -        Iterator iter = allBuilders.iterator();
  -        while(iter.hasNext()) {
  -            Object next = iter.next();
  +        childLoop : for (int i = 0; i < childConfigs.length; i++) {
  +            Configuration childConfig = childConfigs[i];
  +            if (!isChild(childConfig)) {
  +                continue childLoop;
  +            }
  +                
  +            ProcessingNodeBuilder builder = this.treeBuilder.createNodeBuilder(childConfig);
               
  -            if (next instanceof HandleErrorsNodeBuilder) {
  +            if (builder instanceof HandleErrorsNodeBuilder) {
                   // Error handler : check type
  -                HandleErrorsNodeBuilder builder = (HandleErrorsNodeBuilder)next;
  -                int type = builder.getStatusCode();
  +                HandleErrorsNode handler = (HandleErrorsNode)builder.buildNode(childConfig);
  +                int type = handler.getStatusCode();
                   
  -                if ( (type == 404 && this.error404Builder != null) ||
  -                     (type == 500 && this.error500Builder != null) ) {
  -                    throw new ConfigurationException("Duplicate handle-errors at " + builder.getLocation());
  +                if ( (type == 404 && error404Handler != null) ||
  +                     (type == 500 && error500Handler != null) ) {
  +                    throw new ConfigurationException("Duplicate handle-errors at " + handler.getLocation());
                   }
                   
                   if (type == 404) {
  -                    this.error404Builder = builder;
  +                    error404Handler = handler;
                   } else if (type == 500) {
  -                    this.error500Builder = builder;
  +                    error500Handler = handler;
                   } else {
                       throw new ConfigurationException("Unkown handle-errors type (" + type + ") at " +
  -                        builder.getLocation());
  +                        handler.getLocation());
                   }
   
               } else {
                   // Regular builder
  -                this.builders.add(next);
  +                children.add(builder.buildNode(childConfig));
               }
  -        }
  -    }
  -    
  -    public ProcessingNode getNode() throws Exception {
  -        
  -        this.node.setChildren(this.getNodes(builders));
  -        
  -        if (this.error404Builder != null) {
  -            this.node.set404Handler(this.error404Builder.getNode());
  -        }
  -        
  -        if (this.error500Builder != null) {
  -            this.node.set500Handler(this.error500Builder.getNode());
           }
  +         
  +        node.setChildren(toNodeArray(children));
  +        node.set404Handler(error404Handler);
  +        node.set500Handler(error500Handler);
           
  -        return this.node;
  +        return node;
       }
   
   }
  
  
  
  1.3       +7 -17     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PipelinesNode.java
  
  Index: PipelinesNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PipelinesNode.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- PipelinesNode.java	2001/11/18 20:58:45	1.2
  +++ PipelinesNode.java	2001/12/20 16:17:55	1.3
  @@ -17,8 +17,9 @@
   import org.apache.cocoon.environment.Environment;
   import org.apache.cocoon.components.pipeline.EventPipeline;
   import org.apache.cocoon.components.pipeline.StreamPipeline;
  -import org.apache.cocoon.treeprocessor.AbstractParentProcessingNode;
  +import org.apache.cocoon.treeprocessor.ContainerNode;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.*;
   
  @@ -26,27 +27,16 @@
    * Handles &lt;map:pipelines&gt;
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/18 20:58:45 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
  -public final class PipelinesNode extends AbstractParentProcessingNode {
  +public final class PipelinesNode extends ContainerNode {
   
  -    private ProcessingNode[] children;
  -    
       public void setChildren(ProcessingNode[] nodes)
       {
  -        this.children = nodes;
  -    }
  -    
  -    public final boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  -      throws Exception {
  -        
  -        return invokeNodes(this.children, env, pipeline, eventPipeline, listOfMaps);
  -    }
  -    
  -    public final void dispose() {
  +        // Mark the last pipeline so that it can throw a ResourceNotFoundException
  +        ((PipelineNode)nodes[nodes.length - 1]).setLast(true);
           
  -        disposeNodes(this.children);
  +        super.setChildren(nodes);
       }
  -    
   }
  
  
  
  1.4       +9 -35     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PipelinesNodeBuilder.java
  
  Index: PipelinesNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PipelinesNodeBuilder.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- PipelinesNodeBuilder.java	2001/11/25 21:37:56	1.3
  +++ PipelinesNodeBuilder.java	2001/12/20 16:17:55	1.4
  @@ -9,49 +9,23 @@
   package org.apache.cocoon.treeprocessor.sitemap;
   
   import org.apache.avalon.framework.configuration.Configuration;
  -import org.apache.avalon.framework.configuration.ConfigurationException;
  +import org.apache.avalon.framework.thread.ThreadSafe;
   
  -import org.apache.cocoon.treeprocessor.AbstractParentProcessingNodeBuilder;
  +import org.apache.cocoon.treeprocessor.ContainerNodeBuilder;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
  -import org.apache.cocoon.treeprocessor.ProcessingNodeBuilder;
   
  -import java.util.*;
  -
   /**
  - * Buildes a &lt;map:pipelines&gt;
  + * Builds a &lt;map:pipelines&gt;
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.3 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.4 $ $Date: 2001/12/20 16:17:55 $
    */
  -
  -public class PipelinesNodeBuilder extends AbstractParentProcessingNodeBuilder {
  -
  -    private PipelinesNode node;
  -    private List builders;
   
  -    /** This builder has no parameters -- return <code>false</code> */
  -    protected boolean hasParameters() {
  -        return false;
  -    }
  +public class PipelinesNodeBuilder extends ContainerNodeBuilder implements ThreadSafe {
   
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  -        
  -        this.node = new PipelinesNode();
  -        this.treeBuilder.setupNode(this.node, config);
  -        
  -        // Get all children
  -        this.builders = createChildBuilders(config, buildModel);
  -        
  -        if (this.builders.size() == 0) {
  -            throw new ConfigurationException("There must be at least one pipeline at " +
  -                config.getLocation());
  -        }
  -    }
  -    
  -    public ProcessingNode getNode() throws Exception {
  -        
  -        this.node.setChildren(getNodes(this.builders));
  -                
  -        return this.node;
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
  +        PipelinesNode node = new PipelinesNode();
  +        this.setupNode(node, config);
  +        return node;
       }
   }
  
  
  
  1.3       +7 -6      xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PreparableMatchNode.java
  
  Index: PreparableMatchNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PreparableMatchNode.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- PreparableMatchNode.java	2001/11/25 21:37:56	1.2
  +++ PreparableMatchNode.java	2001/12/20 16:17:55	1.3
  @@ -13,13 +13,14 @@
   import org.apache.cocoon.components.pipeline.StreamPipeline;
   import org.apache.cocoon.sitemap.PatternException;
   import org.apache.cocoon.treeprocessor.SimpleSelectorProcessingNode;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
   import org.apache.cocoon.matching.PreparableMatcher;
   import org.apache.avalon.framework.component.ComponentSelector;
   import org.apache.avalon.framework.component.ComponentException;
   import org.apache.avalon.framework.parameters.Parameters;
   import org.apache.avalon.framework.thread.ThreadSafe;
   import org.apache.cocoon.treeprocessor.ParameterizableProcessingNode;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.*;
   import java.util.Map;
  @@ -27,7 +28,7 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class PreparableMatchNode extends SimpleSelectorProcessingNode implements ParameterizableProcessingNode {
  @@ -62,7 +63,7 @@
           }
           
           try {
  -            this.preparedPattern = matcher.preparePattern(ListOfMapsResolver.unescape(this.pattern));
  +            this.preparedPattern = matcher.preparePattern(MapStackResolver.unescape(this.pattern));
   
           } finally {
               if (this.threadSafeMatcher == null) {
  @@ -71,11 +72,11 @@
           }
       }
       
  -    public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public final boolean invoke(Environment env, InvokeContext context)
         throws Exception {
           
           Map objectModel = env.getObjectModel();
  -        Parameters resolvedParams = ListOfMapsResolver.buildParameters(this.parameters, listOfMaps);
  +        Parameters resolvedParams = MapStackResolver.buildParameters(this.parameters, context.getMapStack());
           
           Map result = null;
           
  @@ -101,7 +102,7 @@
               }
               
               // Invoke children with the matcher results
  -            return this.invokeNodes(children, env, pipeline, eventPipeline, listOfMaps, result);
  +            return this.invokeNodes(children, env, context, result);
               
           } else {
               // Matcher failed
  
  
  
  1.3       +15 -10    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ReadNode.java
  
  Index: ReadNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ReadNode.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ReadNode.java	2001/11/25 21:37:56	1.2
  +++ ReadNode.java	2001/12/20 16:17:55	1.3
  @@ -13,7 +13,8 @@
   import org.apache.cocoon.components.pipeline.StreamPipeline;
   import org.apache.cocoon.sitemap.PatternException;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.List;
   import org.apache.cocoon.treeprocessor.ParameterizableProcessingNode;
  @@ -21,14 +22,14 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class ReadNode extends AbstractProcessingNode implements ParameterizableProcessingNode {
   
       private String readerName;
       
  -    private ListOfMapsResolver source;
  +    private MapStackResolver source;
       
       private String mimeType;
       
  @@ -45,7 +46,7 @@
        */
       public ReadNode(String name, String source, String mimeType, int statusCode) throws PatternException {
           this.readerName = name;
  -        this.source = ListOfMapsResolver.getResolver(source);
  +        this.source = MapStackResolver.getResolver(source);
           this.mimeType = mimeType;
           this.statusCode = statusCode;
       }
  @@ -54,23 +55,27 @@
           this.parameters = parameterMap;
       }
   
  -    public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public final boolean invoke(Environment env,  InvokeContext context)
         throws Exception {
           
  +        List mapStack = context.getMapStack();
  +        
  +        StreamPipeline pipeline = context.getStreamPipeline();
  +        
           if (this.mimeType == null) {
               // No mime-type set on node
               pipeline.setReader(
                   this.readerName,
  -                source.resolve(listOfMaps),
  -                ListOfMapsResolver.buildParameters(this.parameters, listOfMaps)
  +                source.resolve(mapStack),
  +                MapStackResolver.buildParameters(this.parameters, mapStack)
               );
               
           } else {
               // mime-type set on node
               pipeline.setReader(
                   this.readerName,
  -                source.resolve(listOfMaps),
  -                ListOfMapsResolver.buildParameters(this.parameters, listOfMaps),
  +                source.resolve(mapStack),
  +                MapStackResolver.buildParameters(this.parameters, mapStack),
                   this.mimeType
               );
           }
  @@ -80,7 +85,7 @@
               env.setStatus(this.statusCode);
           }
           
  -        if (! SitemapNode.isInternalRequest(env)) {
  +        if (! context.isInternalRequest()) {
               // Process pipeline
               return pipeline.process(env);
               
  
  
  
  1.3       +10 -14    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ReadNodeBuilder.java
  
  Index: ReadNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ReadNodeBuilder.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ReadNodeBuilder.java	2001/11/25 21:37:56	1.2
  +++ ReadNodeBuilder.java	2001/12/20 16:17:55	1.3
  @@ -10,36 +10,32 @@
   
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
  +import org.apache.avalon.framework.thread.ThreadSafe;
  +
  +import org.apache.cocoon.reading.Reader;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
   
  -import java.util.Map;
  -
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
  -
  -public class ReadNodeBuilder extends AbstractProcessingNodeBuilder {
   
  -    private ReadNode node;
  +public class ReadNodeBuilder extends AbstractProcessingNodeBuilder implements ThreadSafe {
   
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
           
  -        String type = ComponentsNodeBuilder.getComponentType("reader", config, this.treeBuilder);
  +        String type = ComponentsNodeBuilder.getComponentType(
  +            Reader.ROLE + "Selector", config, this.treeBuilder);
           
  -        this.node = new ReadNode(
  +        ReadNode node = new ReadNode(
               type,
               config.getAttribute("src", null),
               config.getAttribute("mime-type", null),
               config.getAttributeAsInteger("status-code", -1)
           );
   
  -        this.treeBuilder.setupNode(this.node, config);
  -    }
  -    
  -    public ProcessingNode getNode() {
  -        return this.node;
  +        return this.treeBuilder.setupNode(node, config);
       }
   }
  
  
  
  1.3       +30 -16    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/RedirectToNodeBuilder.java
  
  Index: RedirectToNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/RedirectToNodeBuilder.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- RedirectToNodeBuilder.java	2001/11/25 21:37:56	1.2
  +++ RedirectToNodeBuilder.java	2001/12/20 16:17:55	1.3
  @@ -9,47 +9,61 @@
   package org.apache.cocoon.treeprocessor.sitemap;
   
   import org.apache.avalon.framework.configuration.Configuration;
  -import org.apache.avalon.framework.configuration.ConfigurationException;
   
  -import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.CategoryNodeBuilder;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
  +import org.apache.cocoon.treeprocessor.LinkedProcessingNodeBuilder;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
   
  -import org.apache.cocoon.treeprocessor.NullNode;
  -
   import java.util.*;
   
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
  -public class RedirectToNodeBuilder extends AbstractProcessingNodeBuilder {
  +public class RedirectToNodeBuilder extends AbstractProcessingNodeBuilder
  +  implements LinkedProcessingNodeBuilder {
   
  -    private AbstractProcessingNode node;
  +    private CallNode callNode;
  +    private String resourceName;
       
       /** This builder has no parameters -- return <code>false</code> */
       protected boolean hasParameters() {
  -        return true;
  +        return false;
       }
   
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
           
           // Is it a redirect to resource ?
           String uri = config.getAttribute("uri", null);
           if (uri != null) {
  -            this.node = new RedirectToURINode(uri, config.getAttributeAsBoolean("session", false));
  +            ProcessingNode URINode = new RedirectToURINode(uri, config.getAttributeAsBoolean("session", false));   
  +            return this.treeBuilder.setupNode(URINode, config);
               
           } else {
  -            getLogger().warn("redirect to resource not yet implemented");
  -            this.node = new NullNode();
  +            this.resourceName = config.getAttribute("resource");
  +            this.callNode = new CallNode();
  +            this.treeBuilder.setupNode(this.callNode, config);
  +            
  +            String target = config.getAttribute("target", null);
  +            if (target != null) {
  +                Map params = new HashMap(1);
  +                params.put("target", MapStackResolver.getResolver(target));
  +                this.callNode.setParameters(params);
  +            }
  +            return this.callNode;
           }
  -        this.treeBuilder.setupNode(this.node, config);
       }
       
  -    public ProcessingNode getNode() throws Exception {
  -        return this.node;
  +    public void linkNode() throws Exception {
  +        if (this.callNode != null) {
  +            this.callNode.setResource(
  +                CategoryNodeBuilder.getCategoryNode(this.treeBuilder, "resources"),
  +                this.resourceName
  +            );
  +        }
       }
   }
  
  
  
  1.2       +7 -6      xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/RedirectToURINode.java
  
  Index: RedirectToURINode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/RedirectToURINode.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- RedirectToURINode.java	2001/11/18 20:58:45	1.1
  +++ RedirectToURINode.java	2001/12/20 16:17:55	1.2
  @@ -26,30 +26,31 @@
   
   import org.apache.cocoon.treeprocessor.ProcessingNode;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.List;
   import java.util.Map;
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/18 20:58:45 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class RedirectToURINode extends AbstractProcessingNode {
           
       /** The 'uri' attribute */
  -    private ListOfMapsResolver uri;
  +    private MapStackResolver uri;
       
       private boolean keepSession;
   
       public RedirectToURINode(String uri, boolean keepSession) throws PatternException {
  -        this.uri = ListOfMapsResolver.getResolver(uri);
  +        this.uri = MapStackResolver.getResolver(uri);
       }
       
  -    public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public final boolean invoke(Environment env, InvokeContext context)
         throws Exception {
  -        String resolvedURI = uri.resolve(listOfMaps);
  +        String resolvedURI = uri.resolve(context.getMapStack());
           
           if (getLogger().isDebugEnabled()) {
               getLogger().debug("Sitemap: session='" + this.keepSession + "', redirecting to " + resolvedURI);
  
  
  
  1.2       +9 -6      xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SerializeNode.java
  
  Index: SerializeNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SerializeNode.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- SerializeNode.java	2001/11/18 20:58:45	1.1
  +++ SerializeNode.java	2001/12/20 16:17:55	1.2
  @@ -13,13 +13,14 @@
   import org.apache.cocoon.components.pipeline.StreamPipeline;
   import org.apache.cocoon.sitemap.PatternException;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.List;
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/18 20:58:45 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class SerializeNode extends AbstractProcessingNode {
  @@ -43,15 +44,17 @@
           this.statusCode = statusCode;
       }
       
  -    public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public boolean invoke(Environment env, InvokeContext context)
         throws Exception {
           
  +        StreamPipeline pipeline = context.getStreamPipeline();
  +        
           if (this.mimeType == null) {
               // No mime-type set on node
               pipeline.setSerializer(
                   this.serializerName,
                   null,
  -                ListOfMapsResolver.EMPTY_PARAMETERS // No parameters on serializers
  +                MapStackResolver.EMPTY_PARAMETERS // No parameters on serializers
               );
               
           } else {
  @@ -59,7 +62,7 @@
               pipeline.setSerializer(
                   this.serializerName,
                   null,
  -                ListOfMapsResolver.EMPTY_PARAMETERS,
  +                MapStackResolver.EMPTY_PARAMETERS,
                   this.mimeType
               );
           }
  @@ -69,7 +72,7 @@
               env.setStatus(this.statusCode);
           }
           
  -        if (! SitemapNode.isInternalRequest(env)) {
  +        if (! context.isInternalRequest()) {
               // Process pipeline
               return pipeline.process(env);
               
  
  
  
  1.3       +11 -13    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SerializeNodeBuilder.java
  
  Index: SerializeNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SerializeNodeBuilder.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- SerializeNodeBuilder.java	2001/11/25 21:37:56	1.2
  +++ SerializeNodeBuilder.java	2001/12/20 16:17:55	1.3
  @@ -10,6 +10,9 @@
   
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
  +import org.apache.avalon.framework.thread.ThreadSafe;
  +
  +import org.apache.cocoon.serialization.Serializer;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
   
  @@ -18,31 +21,26 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
  -public class SerializeNodeBuilder extends AbstractProcessingNodeBuilder {
  +public class SerializeNodeBuilder extends AbstractProcessingNodeBuilder implements ThreadSafe {
   
  -    private SerializeNode node;
  -    
       /** This builder has no parameters -- return <code>false</code> */
       protected boolean hasParameters() {
  -        return true;
  +        return false;
       }
   
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
           
  -        String type = ComponentsNodeBuilder.getComponentType("serializer", config, this.treeBuilder);
  +        String type = ComponentsNodeBuilder.getComponentType(
  +            Serializer.ROLE + "Selector", config, this.treeBuilder);
           
  -        this.node = new SerializeNode(
  +        SerializeNode node = new SerializeNode(
               type,
               config.getAttribute("mime-type", null),
               config.getAttributeAsInteger("status-code", -1)
           );
  -        this.treeBuilder.setupNode(this.node, config);
  -    }
  -    
  -    public ProcessingNode getNode() {
  -        return this.node;
  +        return this.treeBuilder.setupNode(node, config);
       }
   }
  
  
  
  1.3       +10 -51    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SitemapNode.java
  
  Index: SitemapNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SitemapNode.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- SitemapNode.java	2001/11/12 18:07:43	1.2
  +++ SitemapNode.java	2001/12/20 16:17:55	1.3
  @@ -23,6 +23,7 @@
   import org.apache.cocoon.treeprocessor.AbstractParentProcessingNode;
   import org.apache.cocoon.treeprocessor.EnvironmentSourceResolver;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.*;
   
  @@ -30,25 +31,19 @@
    * The root node of a sitemap.
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/12 18:07:43 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class SitemapNode extends AbstractParentProcessingNode implements Composable {
   
  -    private static final String INTERNAL_ATTR = "sitemap:internal-request";
  -    
       private static final String REDIRECTOR_ATTR = "sitemap:redirector";
       
       protected ComponentManager manager;
       
  -    protected PipelinesNode pipelines;
  +    protected ProcessingNode pipelines;
       
       protected ProcessingNode[] otherNodes;
       
  -    public static boolean isInternalRequest(Environment env) {
  -        return env.getAttribute(INTERNAL_ATTR) != null;
  -    }
  -    
       public static SitemapRedirector getRedirector(Environment env) {
           return (SitemapRedirector)env.getAttribute(REDIRECTOR_ATTR);
       }
  @@ -64,7 +59,7 @@
       /**
        * Set the pipeline nodes that will process the environment.
        */
  -    public void setPipelines(PipelinesNode pipelines) {
  +    public void setPipelines(ProcessingNode pipelines) {
           this.pipelines = pipelines;
       }
       
  @@ -81,49 +76,17 @@
        * and a <code>Redirector</code> in the object model. The previous resolver and
        * redirector, if any, are restored before return.
        */
  -    public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public boolean invoke(Environment env, InvokeContext context)
         throws Exception {
           
  -        boolean internal = false;
  -        
  -        if (pipeline == null && eventPipeline == null) {
  -            // External request : create pipelines
  -            try {
  -                eventPipeline = (EventPipeline) this.manager.lookup(EventPipeline.ROLE);
  -                pipeline = (StreamPipeline) this.manager.lookup(StreamPipeline.ROLE);
  -                pipeline.setEventPipeline(eventPipeline);
  -        
  -                return doInvoke(env, pipeline, eventPipeline, listOfMaps);
  -    
  -            } finally {
  -                this.manager.release(eventPipeline);
  -                this.manager.release(pipeline);
  -            }
  -        } else {
  -            // Internal request
  -            Object oldInternal = env.getAttribute(INTERNAL_ATTR);
  -
  -            try {
  -                // Mark environment as internal request
  -                env.setAttribute(INTERNAL_ATTR, Boolean.TRUE);
  -                
  -                return doInvoke(env, pipeline, eventPipeline, listOfMaps);
  -                
  -            } finally {
  -                // Restore old internal-request value
  -                env.setAttribute(INTERNAL_ATTR, oldInternal);
  -            }
  -        }
  -    }
  +        // Recompose context (and pipelines) to the local component manager
  +        context.recompose(this.manager);
   
  -    protected final boolean doInvoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  -      throws Exception {
           // Create the source resolver relative to the environment.
           
           // Design note : creating the SourceResolver could also have been done
           // in TreeProcessor.invoke(), but doing it here ensures we use the local
  -        // component manager used by all other nodes, which may redefine the
  -        // SourceHandler to use.
  +        // component manager used by all other nodes.
           EnvironmentSourceResolver resolver = new EnvironmentSourceResolver(this.manager, env);
           SitemapRedirector redirector = new SitemapRedirector(env);
           
  @@ -139,7 +102,7 @@
               // FIXME : is there any useful information that can be passed as top-level parameters,
               //         such as the URI of the mount point ?
               
  -            return this.pipelines.invoke(env, pipeline, eventPipeline, listOfMaps);
  +            return this.pipelines.invoke(env, context);
               
           } finally {
   
  @@ -153,13 +116,9 @@
       }
       
       /**
  -     * Dispose all children and the component manager.
  +     * Dispose the component manager.
        */
       public void dispose() {
  -        
  -        this.pipelines.dispose();
  -        disposeNodes(this.otherNodes);
  -        
           if (this.manager instanceof Disposable) {
               ((Disposable)this.manager).dispose();
           }
  
  
  
  1.5       +60 -39    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SitemapNodeBuilder.java
  
  Index: SitemapNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SitemapNodeBuilder.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- SitemapNodeBuilder.java	2001/11/25 21:37:56	1.4
  +++ SitemapNodeBuilder.java	2001/12/20 16:17:55	1.5
  @@ -10,65 +10,86 @@
   
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
  +import org.apache.avalon.framework.thread.ThreadSafe;
   
  -import org.apache.cocoon.treeprocessor.ProcessingNode;
   import org.apache.cocoon.treeprocessor.AbstractParentProcessingNodeBuilder;
  +import org.apache.cocoon.treeprocessor.ProcessingNode;
  +import org.apache.cocoon.treeprocessor.ProcessingNodeBuilder;
   
   import java.util.*;
   
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.4 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.5 $ $Date: 2001/12/20 16:17:55 $
    */
   
  -public class SitemapNodeBuilder extends AbstractParentProcessingNodeBuilder {
  +public class SitemapNodeBuilder extends AbstractParentProcessingNodeBuilder implements ThreadSafe {
   
  -    private SitemapNode node;
  -    
  -    private List childBuilders;
  -    
  -    private String location;
  -    
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  -        this.location = config.getLocation();
  -        this.node = new SitemapNode();
  -        this.treeBuilder.setupNode(this.node, config);
  -        
  -        // FIXME : should we make the assumption that map:components comes first in the sitemap ?
  -        // if not, default component types will not be set up properly for use by other NodeBuilder.
  -        this.childBuilders = createChildBuilders(config, buildModel);
  -    }
  -    
  -    public ProcessingNode getNode() throws Exception {
  -        
  -        ProcessingNode[] childNodes = getNodes(childBuilders);
  -        
  -        // Children contain resources, view and pipelines : separate pipelines
  -        PipelinesNode pipelines = null;
  -        List otherNodes = new ArrayList();
  -        
  -        for (int i = 0; i < childNodes.length; i++) {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
  +        SitemapNode node = new SitemapNode();
  +        this.treeBuilder.setupNode(node, config);
  +        
  +        // We need to separate the various sorts of builders before building
  +        // the nodes because Components must be created first, otherwhise other
  +        // builders won't know the default type (generators, matchers, etc.)
  +        ProcessingNodeBuilder componentsBuilder = null;
  +        Configuration componentsConfig = null;
  +        
  +        List otherBuildersAndConfig = new ArrayList();
  +        
  +        ProcessingNodeBuilder pipelinesBuilder = null;
  +        Configuration pipelinesConfig = null;
  +        
  +        Configuration[] childConfigs = config.getChildren();
  +        buildChildren : for (int i = 0; i < childConfigs.length; i++) {
  +            Configuration childConfig = childConfigs[i];
               
  -            if (childNodes[i] instanceof PipelinesNode) {
  -                if (pipelines != null) {
  -                    throw new ConfigurationException("There can only be one 'map:pipelines' at " +
  -                        this.location);
  -                } else {
  -                    pipelines = (PipelinesNode) childNodes[i];
  +            if (!isChild(childConfig)) {
  +                continue buildChildren;
  +            }
  +
  +            ProcessingNodeBuilder builder = this.treeBuilder.createNodeBuilder(childConfig);
  +            
  +            if (builder instanceof ComponentsNodeBuilder) {
  +                if (componentsBuilder != null) {
  +                    throw new ConfigurationException("Only one map:components is allowed, at " +
  +                        config.getLocation());
  +                }
  +                componentsBuilder = builder;
  +                componentsConfig = childConfig;
  +                
  +            } else if (builder instanceof PipelinesNodeBuilder) {
  +                if (pipelinesBuilder != null) {
  +                    throw new ConfigurationException("Only one map:pipelines is allowed, at " +
  +                        config.getLocation());
                   }
  +                pipelinesBuilder = builder;
  +                pipelinesConfig = childConfig;
  +                
               } else {
  -                otherNodes.add(childNodes[i]);
  +                otherBuildersAndConfig.add(builder);
  +                otherBuildersAndConfig.add(childConfig);
               }
           }
  +        
  +        // Now build nodes
  +        if (componentsBuilder != null) {
  +            // Doesn't actually build a node
  +            componentsBuilder.buildNode(componentsConfig);
  +        }
           
  -        if (pipelines == null) {
  -            throw new ConfigurationException("There is no 'map:pipelines' in sitemap at " + this.location);
  +        List otherNodes = new ArrayList(otherBuildersAndConfig.size() / 2);
  +        Iterator iter = otherBuildersAndConfig.iterator();
  +        while(iter.hasNext()) {
  +            ProcessingNodeBuilder builder = (ProcessingNodeBuilder)iter.next();
  +            Configuration builderConfig = (Configuration)iter.next();
  +            otherNodes.add(builder.buildNode(builderConfig));
           }
           
  -        node.setPipelines(pipelines);
           node.setOtherNodes(toNodeArray(otherNodes));
  +        node.setPipelines(pipelinesBuilder.buildNode(pipelinesConfig));
   
  -        return this.node;
  +        return node;
       }
   }
  
  
  
  1.3       +11 -8     xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/TransformNode.java
  
  Index: TransformNode.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/TransformNode.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- TransformNode.java	2001/11/25 21:37:56	1.2
  +++ TransformNode.java	2001/12/20 16:17:55	1.3
  @@ -13,7 +13,8 @@
   import org.apache.cocoon.components.pipeline.StreamPipeline;
   import org.apache.cocoon.sitemap.PatternException;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
  -import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  +import org.apache.cocoon.treeprocessor.MapStackResolver;
  +import org.apache.cocoon.treeprocessor.InvokeContext;
   
   import java.util.List;
   import org.apache.cocoon.treeprocessor.ParameterizableProcessingNode;
  @@ -21,33 +22,35 @@
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
   
   public class TransformNode extends AbstractProcessingNode implements ParameterizableProcessingNode {
   
       private String transformerName;
       
  -    private ListOfMapsResolver source;
  +    private MapStackResolver source;
       
       private Map parameters;
       
       public TransformNode(String name, String source) throws PatternException {
           this.transformerName = name;
  -        this.source = ListOfMapsResolver.getResolver(source);
  +        this.source = MapStackResolver.getResolver(source);
       }
          
       public void setParameters(Map parameterMap) {
           this.parameters = parameterMap;
       }
   
  -    public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
  +    public boolean invoke(Environment env, InvokeContext context)
         throws Exception {
           
  -        eventPipeline.addTransformer(
  +        List mapStack = context.getMapStack();
  +        
  +        context.getEventPipeline().addTransformer(
               this.transformerName,
  -            source.resolve(listOfMaps),
  -            ListOfMapsResolver.buildParameters(this.parameters, listOfMaps)
  +            source.resolve(mapStack),
  +            MapStackResolver.buildParameters(this.parameters, mapStack)
           );
           
           // Return false to contine sitemap invocation
  
  
  
  1.3       +10 -15    xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/TransformNodeBuilder.java
  
  Index: TransformNodeBuilder.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/TransformNodeBuilder.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- TransformNodeBuilder.java	2001/11/25 21:37:56	1.2
  +++ TransformNodeBuilder.java	2001/12/20 16:17:55	1.3
  @@ -9,31 +9,26 @@
   package org.apache.cocoon.treeprocessor.sitemap;
   
   import org.apache.avalon.framework.configuration.Configuration;
  -import org.apache.avalon.framework.configuration.ConfigurationException;
  +import org.apache.avalon.framework.thread.ThreadSafe;
  +
  +import org.apache.cocoon.transformation.Transformer;
   import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
   import org.apache.cocoon.treeprocessor.ProcessingNode;
   
  -import java.util.Map;
  -
   /**
    *
    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  - * @version CVS $Revision: 1.2 $ $Date: 2001/11/25 21:37:56 $
  + * @version CVS $Revision: 1.3 $ $Date: 2001/12/20 16:17:55 $
    */
  -
  -public class TransformNodeBuilder extends AbstractProcessingNodeBuilder {
   
  -    private TransformNode node;
  +public class TransformNodeBuilder extends AbstractProcessingNodeBuilder implements ThreadSafe {
   
  -    public void buildNode(Configuration config, Map buildModel) throws Exception {
  +    public ProcessingNode buildNode(Configuration config) throws Exception {
           
  -        String type = ComponentsNodeBuilder.getComponentType("transformer", config, this.treeBuilder);
  +        String type = ComponentsNodeBuilder.getComponentType(
  +            Transformer.ROLE + "Selector", config, this.treeBuilder);
           
  -        this.node = new TransformNode(type, config.getAttribute("src", null));
  -        this.treeBuilder.setupNode(this.node, config);   
  -    }
  -    
  -    public ProcessingNode getNode() {
  -        return this.node;
  +        TransformNode node = new TransformNode(type, config.getAttribute("src", null));
  +        return this.treeBuilder.setupNode(node, config);   
       }
   }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ActSetNode.java
  
  Index: ActSetNode.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor.sitemap;
  
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentSelector;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  import org.apache.cocoon.acting.Action;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.Redirector;
  import org.apache.cocoon.environment.SourceResolver;
  import org.apache.cocoon.components.pipeline.EventPipeline;
  import org.apache.cocoon.components.pipeline.StreamPipeline;
  
  import org.apache.cocoon.sitemap.PatternException;
  import org.apache.cocoon.sitemap.SitemapRedirector;
  
  import org.apache.cocoon.treeprocessor.ProcessingNode;
  import org.apache.cocoon.treeprocessor.SimpleParentProcessingNode;
  import org.apache.cocoon.treeprocessor.InvokeContext;
  import org.apache.cocoon.treeprocessor.MapStackResolver;
  import org.apache.cocoon.treeprocessor.ParameterizableProcessingNode;
  
  import java.util.List;
  import java.util.Map;
  
  /**
   * Handles &lt;map:act type="..."&gt; (action-sets calls are handled by {@link ActSetNode}).
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:55 $
   */
  
  public class ActSetNode extends SimpleParentProcessingNode
    implements ParameterizableProcessingNode {
      
      /** The parameters of this node */
      private Map parameters;
  
      /** The 'src' attribute */
      protected MapStackResolver source;
      
      /** The action set to call */
      private ActionSetNode actionSet;
  
      public ActSetNode(String source) throws PatternException {
          this.source = MapStackResolver.getResolver(source);
      }
      
      public void setParameters(Map parameterMap) {
          this.parameters = parameterMap;
      }
      
      public void setActionSet(ActionSetNode actionSet) {
          this.actionSet = actionSet;
      }
  
      public final boolean invoke(Environment env, InvokeContext context)
        throws Exception {
          
          List       mapStack = context.getMapStack();
          String     resolvedSource = source.resolve(mapStack);
          Parameters resolvedParams = MapStackResolver.buildParameters(this.parameters, mapStack);
  
          Map result = this.actionSet.call(env, context, resolvedSource, resolvedParams);
          
          if (SitemapNode.getRedirector(env).hasRedirected()) {
              return true;
              
          } else if (result == null) {
              return false;
              
          } else if (this.children == null) {
              return true;
              
          } else {
              return this.invokeNodes(this.children, env, context, result);
          }
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ActTypeNode.java
  
  Index: ActTypeNode.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor.sitemap;
  
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.component.ComponentSelector;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.parameters.Parameters;
  
  import org.apache.cocoon.acting.Action;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.SourceResolver;
  
  import org.apache.cocoon.sitemap.PatternException;
  import org.apache.cocoon.sitemap.SitemapRedirector;
  
  import org.apache.cocoon.treeprocessor.InvokeContext;
  import org.apache.cocoon.treeprocessor.MapStackResolver;
  import org.apache.cocoon.treeprocessor.ParameterizableProcessingNode;
  import org.apache.cocoon.treeprocessor.SimpleSelectorProcessingNode;
  
  import java.util.*;
  
  /**
   * Handles &lt;map:act type="..."&gt; (action-sets calls are handled by {@link ActSetNode}).
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:55 $
   */
  
  public class ActTypeNode extends SimpleSelectorProcessingNode
    implements ParameterizableProcessingNode, Disposable {
      
      /** The parameters of this node */
      private Map parameters;
  
      /** The 'src' attribute */
      protected MapStackResolver source;
  
      /** Pre-selected action, if it's ThreadSafe */
      protected Action threadSafeAction;
  
      public ActTypeNode(String name, String source) throws PatternException {
          super(name);
          this.source = MapStackResolver.getResolver(source);
      }
      
      public void setParameters(Map parameterMap) {
          this.parameters = parameterMap;
      }
  
      public void setSelector(ComponentSelector selector) throws Exception {
          super.setSelector(selector);
  
          // Get the action, if it's thread safe
          this.threadSafeAction = (Action)this.getThreadSafeComponent();
      }
      
      public final boolean invoke(Environment env, InvokeContext context)
        throws Exception {
          
          // Prepare data needed by the action
          Map               objectModel    = env.getObjectModel();
          SitemapRedirector redirector     = SitemapNode.getRedirector(env);
          SourceResolver    resolver       = getSourceResolver(objectModel);
          List              mapStack       = context.getMapStack();
          String            resolvedSource = source.resolve(mapStack);
          Parameters        resolvedParams = MapStackResolver.buildParameters(this.parameters, mapStack);
          
          Map actionResult;
          
          // If action is ThreadSafe, avoid select() and try/catch block (faster !)
          if (this.threadSafeAction != null) {
              actionResult = this.threadSafeAction.act(
                  redirector, resolver, objectModel, resolvedSource, resolvedParams );
              
          } else {
              Action action = (Action)this.selector.select(this.componentName);
              try {
                  actionResult = action.act(
                  redirector, resolver, objectModel, resolvedSource, resolvedParams );
                  
              } finally {
                  this.selector.release(action);
              }
          }
          
          if (redirector.hasRedirected()) {
              return true;
          }
          
          if (actionResult == null) {
              // Action failed
              return false;
              
          } else {
              // Action succeeded : process children if there are some, with the action result
              if (this.children != null) {
                  return this.invokeNodes(this.children, env, context, actionResult);
              } else {
                  return true;
              }
          }
      }
      
      public void dispose() {
          if (this.threadSafeAction != null) {
              this.selector.release(this.threadSafeAction);
          }
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ActionSetNode.java
  
  Index: ActionSetNode.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor.sitemap;
  
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.component.ComponentSelector;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.parameters.Parameters;
  
  import org.apache.cocoon.acting.Action;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.SourceResolver;
  
  import org.apache.cocoon.sitemap.PatternException;
  import org.apache.cocoon.sitemap.SitemapRedirector;
  
  import org.apache.cocoon.treeprocessor.InvokeContext;
  import org.apache.cocoon.treeprocessor.MapStackResolver;
  import org.apache.cocoon.treeprocessor.NamedProcessingNode;
  import org.apache.cocoon.treeprocessor.SimpleSelectorProcessingNode;
  
  import java.util.*;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:55 $
   */
  
  public class ActionSetNode extends SimpleSelectorProcessingNode
    implements Disposable, NamedProcessingNode {
      
      /** The action types */
      private String[] types;
      
      /** The 'action' attribute for each action */
      private String[] actionNames;
      
      /** The actions that are ThreadSafe, to avoid lookups */
      private Action[] threadSafeActions;
      
      public ActionSetNode(String name, String[] types, String[] actionNames) {
          super(name);
          this.types = types;
          this.actionNames = actionNames;
      }
      
      public void setSelector(ComponentSelector selector) throws Exception {
          super.setSelector(selector);
  
          // Get all actions that are thread safe
          this.threadSafeActions = new Action[types.length];
          
          for (int i = 0; i < this.types.length; i++) {
              this.threadSafeActions[i] = (Action)this.getThreadSafeComponent(this.types[i]);
          }
      }
      
      public final boolean invoke(Environment env, InvokeContext context)
        throws Exception {
          throw new UnsupportedOperationException("An action-set cannot be invoked");
      }
      
      /**
       * Call the actions composing the action-set and return the combined result of
       * these actions.
       */
      public final Map call(Environment env, InvokeContext context, String source, Parameters params) throws Exception {
          
          // Prepare data needed by the actions
          Map               objectModel    = env.getObjectModel();
          SitemapRedirector redirector     = SitemapNode.getRedirector(env);
          SourceResolver    resolver       = getSourceResolver(objectModel);
          
          String cocoonAction = env.getAction();
          
          Map result = null;
          
          // Call each action that either has no cocoonAction, or whose cocoonAction equals
          // the one from the environment.
          
          for (int i = 0; i < types.length; i++) {
              
              Map actionResult;
              Action action;
              
              String actionName = actionNames[i];
              if (actionName == null || actionName.equals(cocoonAction)) {
                  
                  // If action is ThreadSafe, avoid select() and try/catch block (faster !)
                  if ((action = this.threadSafeActions[i]) != null) {
                      
                      actionResult = action.act(
                          redirector, resolver, objectModel, source, params);
  
                  } else {
                      
                      action = (Action)this.selector.select(this.componentName);
                      try {
                          actionResult = action.act(
                              redirector, resolver, objectModel, source, params);
                      } finally {
                          this.selector.release(action);
                      }
                  }
                  
                  if (actionResult != null) {
                      // Merge the result in the global result, creating it if necessary.
                      if (result == null) {
                          result = new HashMap(actionResult);
                      } else {
                          result.putAll(actionResult);
                      }
                  }
              } // if (actionName...
          } // for (int i...
          
          return result;
      }
      
      public void dispose() {
          // Dispose all ThreadSafe actions
          for (int i = 0; i < this.threadSafeActions.length; i++) {
              this.selector.release(this.threadSafeActions[i]);
          }
      }
  
      /**
       * Implementation of <code>NamedProcessingNode</code>.
       */
  
      public String getName() {
          return this.componentName;
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ActionSetNodeBuilder.java
  
  Index: ActionSetNodeBuilder.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor.sitemap;
  
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentSelector;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  import org.apache.cocoon.acting.Action;
  import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
  import org.apache.cocoon.treeprocessor.MapStackResolver;
  import org.apache.cocoon.treeprocessor.ProcessingNode;
  import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
  import org.apache.cocoon.treeprocessor.LinkedProcessingNodeBuilder;
  
  import java.util.*;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:55 $
   */
  
  public class ActionSetNodeBuilder extends AbstractProcessingNodeBuilder
    implements Composable, LinkedProcessingNodeBuilder {
  
      private ComponentManager manager;
      private ActionSetNode node;
  
      public void compose(ComponentManager manager) {
          this.manager = manager;
      }
      
      public ProcessingNode buildNode(Configuration config) throws Exception {
          
          String actionSetName = config.getAttribute("name");
          
          // Lists of action types and names for each map:act
          List actionTypes = new ArrayList();
          List actionNames = new ArrayList();
                  
          Configuration[] childrenConfig = config.getChildren();
          for (int i = 0; i < childrenConfig.length; i++) {
              
              Configuration childConfig = childrenConfig[i];
              String name = childConfig.getName();
              
              if ("act".equals(name)) {
  
                  checkNamespace(childConfig);
                  String type = ComponentsNodeBuilder.getComponentType(
                      Action.ROLE + "Selector", childConfig, this.treeBuilder);
                  
                  actionTypes.add(type);
                  actionNames.add(childConfig.getAttribute("action", null));
                  
              } else {
                  // Unknown element
                  throw new ConfigurationException("Unknown element " + name + " at " + childConfig.getLocation());
              }
          }
          
          String[] types   = (String[])actionTypes.toArray(new String[actionTypes.size()]);
          String[] actions = (String[])actionNames.toArray(new String[actionNames.size()]);
          
          this.node = new ActionSetNode(actionSetName, types, actions);
          this.treeBuilder.setupNode(this.node, config);
          // Selector is set in linkNode() since it isn't visible now
          
          return this.node;
      }
      
      public void linkNode() throws Exception {
          
          // Get the selector (it's now visible in the manager)
          ComponentSelector selector = (ComponentSelector)manager.lookup(Action.ROLE + "Selector");
          this.node.setSelector(selector);
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/AggregateNode.java
  
  Index: AggregateNode.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor.sitemap;
  
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.components.pipeline.EventPipeline;
  import org.apache.cocoon.components.pipeline.StreamPipeline;
  
  import org.apache.cocoon.sitemap.PatternException;
  import org.apache.cocoon.sitemap.ContentAggregator;
  
  import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
  import org.apache.cocoon.treeprocessor.InvokeContext;
  import org.apache.cocoon.treeprocessor.MapStackResolver;
  import org.apache.cocoon.treeprocessor.ProcessingNode;
  
  import java.util.*;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:55 $
   */
  
  public class AggregateNode extends AbstractProcessingNode {
      
      Part[] parts;
      String element;
      String nsURI;
      String nsPrefix;
      
      public AggregateNode(String element, String nsURI, String nsPrefix) {
          this.element = element;
          this.nsURI = nsURI;
          this.nsPrefix = nsPrefix;
      }
      
      public void setParts(Part[] parts) {
          this.parts = parts;
      }
      
      public boolean invoke(Environment env, InvokeContext context)
        throws Exception {
          
          List mapStack = context.getMapStack();
  
          // Setup aggregator
          EventPipeline eventPipeline = context.getEventPipeline();
          
          eventPipeline.setGenerator("!content-aggregator!", null, MapStackResolver.EMPTY_PARAMETERS);
  
          ContentAggregator aggregator = (ContentAggregator)eventPipeline.getGenerator();
          aggregator.setRootElement(this.element, this.nsURI, this.nsPrefix);
  
          // Add parts
          for (int i = 0; i < parts.length; i++) {
              Part part = parts[i];
              // FIXME : update ContentAggregator to accept boolean for stripRoot
              aggregator.addPart(part.source.resolve(mapStack),
                  part.element, part.nsURI, String.valueOf(part.stripRoot), part.nsPrefix
              );
          }
          
          // Return false to continue sitemap invocation
          return false;
      }
      
      public static class Part {
          public Part(String source, String element, String nsURI, String nsPrefix, boolean stripRoot)
            throws PatternException {
              this.source = MapStackResolver.getResolver(source);
              this.element = element;
              this.nsURI = nsURI;
              this.nsPrefix = nsPrefix;
              this.stripRoot = stripRoot;
          }
  
          private MapStackResolver source;
          private String element;
          private String nsURI;
          private String nsPrefix;
          private boolean stripRoot;
      }
      
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/AggregateNodeBuilder.java
  
  Index: AggregateNodeBuilder.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor.sitemap;
  
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
  import org.apache.cocoon.treeprocessor.MapStackResolver;
  import org.apache.cocoon.treeprocessor.ProcessingNode;
  
  import java.util.*;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:55 $
   */
  
  public class AggregateNodeBuilder extends AbstractProcessingNodeBuilder {
  
      public ProcessingNode buildNode(Configuration config) throws Exception {
          
          // Get root node data
          AggregateNode node = new AggregateNode(
              config.getAttribute("element"),
              config.getAttribute("ns", ""),
              config.getAttribute("prefix", "")
          );
          this.treeBuilder.setupNode(node, config);
          
          // Build parts
          List parts = new ArrayList();
          
          Configuration[] childConfigs = config.getChildren();
          for (int i = 0; i < childConfigs.length; i++) {
              Configuration childConfig = childConfigs[i];
              
              if (!"part".equals(childConfig.getName())) {
                  throw new ConfigurationException("Unknown element '" + childConfig.getName() +
                      "' at " + childConfig.getLocation());
              }
              
              checkNamespace(childConfig);
              
              parts.add(new AggregateNode.Part(
                  childConfig.getAttribute("src"),
                  childConfig.getAttribute("element", ""),
                  childConfig.getAttribute("ns", ""),
                  childConfig.getAttribute("prefix", ""),
                  childConfig.getAttributeAsBoolean("strip-root", false)
              ));
          }
          
          if (parts.size() == 0) {
              throw new ConfigurationException("There must be at least one part in map:aggregate at " +
                  config.getLocation());
          }
          
          AggregateNode.Part[] partArray = (AggregateNode.Part[])parts.toArray(
              new AggregateNode.Part[parts.size()]);
          
          node.setParts(partArray);
          
          return node;
          
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/CallNode.java
  
  Index: CallNode.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor.sitemap;
  
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentSelector;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  import org.apache.cocoon.acting.Action;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.Redirector;
  import org.apache.cocoon.environment.SourceResolver;
  import org.apache.cocoon.components.pipeline.EventPipeline;
  import org.apache.cocoon.components.pipeline.StreamPipeline;
  
  import org.apache.cocoon.sitemap.PatternException;
  import org.apache.cocoon.sitemap.SitemapRedirector;
  
  import org.apache.cocoon.treeprocessor.CategoryNode;
  import org.apache.cocoon.treeprocessor.ProcessingNode;
  import org.apache.cocoon.treeprocessor.InvokeContext;
  import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
  import org.apache.cocoon.treeprocessor.MapStackResolver;
  import org.apache.cocoon.treeprocessor.ParameterizableProcessingNode;
  
  import java.util.List;
  import java.util.Map;
  import org.apache.avalon.framework.activity.Initializable;
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:55 $
   */
  
  public class CallNode extends AbstractProcessingNode
      implements Initializable, ParameterizableProcessingNode {
      
      /** The parameters of this node */
      private Map parameters;
  
      /** The 'resource' attribute */
      private String resourceName;
      
      private MapStackResolver resourceResolver;
      
      /** The category node */
      private CategoryNode resources;
      
      private ProcessingNode resourceNode;
      
      public void setParameters(Map parameterMap) {
          this.parameters = parameterMap;
      }
  
      public void setResource(CategoryNode resources, String resourceName) throws Exception {
          this.resourceName = resourceName;
          this.resources = resources;
      }
  
      public void initialize() throws Exception {
          if (MapStackResolver.needsResolve(this.resourceName)) {
              // Will always be resolved at invoke time
              this.resourceResolver = MapStackResolver.getResolver(this.resourceName);
          } else {
              // Static name : get it now
              this.resourceNode = this.resources.getNodeByName(MapStackResolver.unescape(this.resourceName));
          }
      }
  
      public final boolean invoke(Environment env, InvokeContext context)
        throws Exception {
          
          // Resolve parameters
          if (this.parameters != null) {
              // In redirect-to a level is added to the map stack *only* if the
              // 'target' attribute is present.
              Map params = MapStackResolver.resolveMap(this.parameters, context.getMapStack());
              context.pushMap(params);
          }
          
          boolean result;
  
          if (this.resourceNode != null) {
              // Static resource name
                  result = this.resourceNode.invoke(env, context);
              
          } else {
              // Resolved resource name
              String name = this.resourceResolver.resolve(context.getMapStack());
              
              if (getLogger().isDebugEnabled()) {
                  getLogger().debug("Calling resource " + name);
              }
              
              result = this.resources.invokeByName(name, env, context);
          }
          
          if (this.parameters != null) {
              context.popMap();
          }
          return result;
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/CallNodeBuilder.java
  
  Index: CallNodeBuilder.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor.sitemap;
  
  import org.apache.avalon.framework.configuration.Configuration;
  
  import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
  import org.apache.cocoon.treeprocessor.CategoryNodeBuilder;
  import org.apache.cocoon.treeprocessor.MapStackResolver;
  import org.apache.cocoon.treeprocessor.LinkedProcessingNodeBuilder;
  import org.apache.cocoon.treeprocessor.ProcessingNode;
  
  import java.util.*;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:55 $
   */
  
  public class CallNodeBuilder extends AbstractProcessingNodeBuilder
    implements LinkedProcessingNodeBuilder {
  
      private CallNode node;
      private String resourceName;
      
      public ProcessingNode buildNode(Configuration config) throws Exception {
          
          this.resourceName = config.getAttribute("resource");
          this.node = new CallNode();
          this.treeBuilder.setupNode(this.node, config);
          
          return this.node;
      }
      
      public void linkNode() throws Exception {
          this.node.setResource(
              CategoryNodeBuilder.getCategoryNode(this.treeBuilder, "resources"),
              this.resourceName
          );
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SelectNode.java
  
  Index: SelectNode.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor.sitemap;
  
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentSelector;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.components.pipeline.EventPipeline;
  import org.apache.cocoon.components.pipeline.StreamPipeline;
  import org.apache.cocoon.selection.Selector;
  
  import org.apache.cocoon.sitemap.PatternException;
  
  import org.apache.cocoon.treeprocessor.ProcessingNode;
  import org.apache.cocoon.treeprocessor.AbstractParentProcessingNode;
  import org.apache.cocoon.treeprocessor.MapStackResolver;
  import org.apache.cocoon.treeprocessor.ParameterizableProcessingNode;
  import org.apache.cocoon.treeprocessor.InvokeContext;
  
  import java.util.List;
  import java.util.Map;
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:55 $
   */
  
  public class SelectNode extends AbstractParentProcessingNode
    implements ParameterizableProcessingNode, Disposable {
      
      /** The parameters of this node */
      private Map parameters;
  
      /** The action name */
      protected String name;
      
      /** Selector where to get Actions from */
      protected ComponentSelector componentSelector;
      
      /** Pre-selected selector, if it's ThreadSafe */
      protected Selector threadSafeSelector;
      
      private ProcessingNode[][] whenNodes;
      
      private MapStackResolver[] whenTests;
      
      private ProcessingNode[] otherwhiseNodes;
  
      public SelectNode(String name) throws PatternException {
          this.name = name;
      }
      
      public void setParameters(Map parameterMap) {
          this.parameters = parameterMap;
      }
  
      public void setCases(ProcessingNode[][] whenNodes, MapStackResolver[] whenTests, ProcessingNode[] otherwhiseNodes) {
          this.whenNodes = whenNodes;
          this.whenTests = whenTests;
          this.otherwhiseNodes = otherwhiseNodes;
      }
      
      public void setSelector(ComponentSelector componentSelector) throws ComponentException {
          
          this.componentSelector = componentSelector;
          
          // Is it a ThreadSafe selector ?
          Selector selector = (Selector)componentSelector.select(name);
          if (selector instanceof ThreadSafe) {
              // Yes : keep it.
              this.threadSafeSelector = selector;
          } else {
              this.threadSafeSelector = null;
              this.componentSelector.release(selector);
          }
      }
      
      public final boolean invoke(Environment env, InvokeContext context)
        throws Exception {
          
          // Prepare data needed by the action
          Map objectModel = env.getObjectModel();
          List mapStack   = context.getMapStack();
          Parameters resolvedParams = MapStackResolver.buildParameters(this.parameters, mapStack);
          
          // If selector is ThreadSafe, avoid select() and try/catch block (faster !)
          if (this.threadSafeSelector != null) {
              
              for (int i = 0; i < this.whenTests.length; i++) {
                  if (this.threadSafeSelector.select(
                          whenTests[i].resolve(mapStack),
                          objectModel,
                          resolvedParams)) {
                      return invokeNodes(this.whenNodes[i], env, context);
                  }
              }
              
              if (this.otherwhiseNodes != null) {
                  return invokeNodes(this.otherwhiseNodes, env, context);
              }
              
              return false;
              
          } else {
              Selector selector = (Selector)this.componentSelector.select(this.name);
              try {
  
                  for (int i = 0; i < this.whenTests.length; i++) {
                      if (selector.select(
                              whenTests[i].resolve(mapStack),
                              objectModel,
                              resolvedParams)) {
                          return invokeNodes(this.whenNodes[i], env, context);
                      }
                  }
                  
                  if (this.otherwhiseNodes != null) {
                      return invokeNodes(this.otherwhiseNodes, env, context);
                  }
                  
                  return false;
              } finally {
                  this.componentSelector.release(selector);
              }
          }
      }
      
      public void dispose() {
          if (this.threadSafeSelector != null) {
              this.componentSelector.release(this.threadSafeSelector);
          }
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SelectNodeBuilder.java
  
  Index: SelectNodeBuilder.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.cocoon.treeprocessor.sitemap;
  
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentSelector;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  import org.apache.cocoon.selection.Selector;
  import org.apache.cocoon.treeprocessor.AbstractParentProcessingNodeBuilder;
  import org.apache.cocoon.treeprocessor.MapStackResolver;
  import org.apache.cocoon.treeprocessor.ProcessingNode;
  import org.apache.cocoon.treeprocessor.AbstractProcessingNode;
  import org.apache.cocoon.treeprocessor.LinkedProcessingNodeBuilder;
  
  import java.util.*;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/12/20 16:17:55 $
   */
  
  public class SelectNodeBuilder extends AbstractParentProcessingNodeBuilder
    implements Composable, LinkedProcessingNodeBuilder {
  
      private ComponentManager manager;
      private SelectNode node;
  
      public void compose(ComponentManager manager) {
          this.manager = manager;
      }
      
      public ProcessingNode buildNode(Configuration config) throws Exception {
          
          String type = ComponentsNodeBuilder.getComponentType(Selector.ROLE + "Selector", config, this.treeBuilder);
          this.node = new SelectNode(type);
          this.treeBuilder.setupNode(this.node, config);
          // Selector is set in linkNode() since it isn't visible now
          
          // Lists of ProcessingNode[] and test resolvers for each "when"
          List whenChildren = new ArrayList();
          List whenTests = new ArrayList();
          
          // Nodes for otherwhise (if any)
          ProcessingNode[] otherwhiseNodes = null;
          
          Configuration[] childrenConfig = config.getChildren();
          for (int i = 0; i < childrenConfig.length; i++) {
              
              Configuration childConfig = childrenConfig[i];
              String name = childConfig.getName();
              
              if ("when".equals(name)) {
                  
                  checkNamespace(childConfig);
                  whenTests.add(MapStackResolver.getResolver(childConfig.getAttribute("test")));
                  whenChildren.add(buildChildNodes(childConfig));
                  
              } else if ("otherwhise".equals(name)) {
                  
                  checkNamespace(childConfig);
                  if (otherwhiseNodes != null) {
                      throw new ConfigurationException("Duplicate " + name + " (only one is allowed) at " +
                          childConfig.getLocation());
                  }
                  
                  otherwhiseNodes = buildChildNodes(childConfig);
                  
              } else if (isParameter(childConfig)) {
                  // ignore it. It is handled automatically in setupNode()
                  
              } else {
                  // Unknown element
                  throw new ConfigurationException("Unknown element " + name + " at " + childConfig.getLocation());
              }
          }
          
          this.node.setCases(
              (ProcessingNode[][])whenChildren.toArray(new ProcessingNode[0][0]),
              (MapStackResolver[])whenTests.toArray(new MapStackResolver[whenTests.size()]),
              otherwhiseNodes
          );
          
          return this.node;
      }
      
      public void linkNode() throws Exception {
          
          // Get the selector (it's now visible in the manager)
          ComponentSelector selector = (ComponentSelector)manager.lookup(Selector.ROLE + "Selector");
          this.node.setSelector(selector);
      }
  }
  
  

----------------------------------------------------------------------
In case of troubles, e-mail:     webmaster@xml.apache.org
To unsubscribe, e-mail:          cocoon-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: cocoon-cvs-help@xml.apache.org