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/11/08 15:39:24 UTC

cvs commit: xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap ActionNode.java ComponentsNodeBuilder.java GeneratorNode.java GeneratorNodeBuilder.java PipelineNode.java SitemapNode.java SitemapNodeBuilder.java

sylvain     01/11/08 06:39:24

  Added:       scratchpad/src/org/apache/cocoon treeprocessor.txt
               scratchpad/src/org/apache/cocoon/treeprocessor
                        AbstractParentProcessingNode.java
                        AbstractParentProcessingNodeBuilder.java
                        AbstractProcessingNode.java
                        AbstractProcessingNodeBuilder.java
                        EnvironmentSourceResolver.java
                        ListOfMapsResolver.java ProcessingNode.java
                        ProcessingNodeBuilder.java TreeBuilder.java
                        TreeBuilderComponentManager.java TreeProcessor.java
                        treeprocessor.xconf
               scratchpad/src/org/apache/cocoon/treeprocessor/sitemap
                        ActionNode.java ComponentsNodeBuilder.java
                        GeneratorNode.java GeneratorNodeBuilder.java
                        PipelineNode.java SitemapNode.java
                        SitemapNodeBuilder.java
  Log:
  Scratchpad for the new TreeProcessor, for Ovidiu to look at !
  
  Revision  Changes    Path
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor.txt
  
  Index: treeprocessor.txt
  ===================================================================
  The treeprocessor package is the reimplementation of the sitemap as a tree traversal engine.
  
  This is the current snapshot from my hard disk, as asked by Ovidiu who wanted to look at it. It is compilable, but not finished, so don't ask why it doesn't work :)
  
  Any suggestions are welcome.
  
  Sylvain Wallez
  sylvain@apache.org
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractParentProcessingNode.java
  
  Index: AbstractParentProcessingNode.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.logger.AbstractLoggable;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.components.pipeline.EventPipeline;
  import org.apache.cocoon.components.pipeline.StreamPipeline;
  
  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/08 14:39:23 $
   */
  
  public abstract class AbstractParentProcessingNode extends AbstractProcessingNode {
  
      protected ProcessingNode[] childNodes;
      
      /**
       * Invoke in order all children, until one fails.
       *
       * @parameter currentMap the <code>Map<code> of parameters produced by this node,
       *            which is added to <code>listOfMap</code> if not null.
       */
      protected boolean invokeChildren (
          Environment env,
          StreamPipeline pipeline,
          EventPipeline eventPipeline,
          List listOfMaps, Map currentMap)
        throws Exception {
          
          if (currentMap != null) {
              listOfMaps.add(currentMap);
          }
          
          boolean success = true;
          for (int i = 0; i < childNodes.length; i++) {
              if (! childNodes[i].invoke(env, pipeline, eventPipeline, listOfMaps)) {
                  success = false;
                  break;
              }
          }
          
          if (currentMap != null) {
              listOfMaps.remove(listOfMaps.size() - 1);
          }
          
          return success;
      }
      
      /**
       * Disposes all children of this node.
       */
      protected void disposeChildren() {
          for (int i = 0; i < childNodes.length; i++) {
              childNodes[i].dispose();
          }
      }
      
      /**
       * Dispose this node. At this level, simply call <code>disposeChildren()</code>.
       */
      public void dispose() {
          disposeChildren();
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractParentProcessingNodeBuilder.java
  
  Index: AbstractParentProcessingNodeBuilder.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.logger.AbstractLoggable;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:23 $
   */
  
  
  public abstract class AbstractParentProcessingNodeBuilder extends AbstractProcessingNodeBuilder {
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractProcessingNode.java
  
  Index: AbstractProcessingNode.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.logger.AbstractLoggable;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  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 java.util.Iterator;
  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/08 14:39:23 $
   */
  
  public abstract class AbstractProcessingNode extends AbstractLoggable implements ProcessingNode {
  
      /** The node parameters, as a <code>Map</code> of <code>ListOfMapsResolver</code>. */
      protected Map parameters;
      
      protected SourceResolver getSourceResolver(Map objectModel) {
          return (SourceResolver)objectModel.get(OBJECT_SOURCE_RESOLVER);
      }
      
      protected Redirector getRedirector(Map objectModel) {
          return (Redirector)objectModel.get(OBJECT_REDIRECTOR);
      }
      
      /**
       * By default, do nothing. Subclasses can redefine it when needed.
       */
      public void dispose() {
          // Nothing
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/AbstractProcessingNodeBuilder.java
  
  Index: AbstractProcessingNodeBuilder.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.logger.AbstractLoggable;
  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.component.Composable;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentException;
  
  import org.apache.cocoon.sitemap.PatternException;
  
  import java.util.Map;
  import java.util.HashMap;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:23 $
   */
  
  
  public abstract class AbstractProcessingNodeBuilder extends AbstractLoggable
    implements ProcessingNodeBuilder {
  
      protected TreeBuilder builder;
      
      public void setBuilder(TreeBuilder builder) {
          this.builder = builder;
      }
  
      /**
       * Get &lt;xxx:parameter&gt; elements as a <ocd>Map</code> of </code>ListOfMapResolver</code>s,
       * that can be turned into parameters using <code>ListOfMapResolver.buildParameters()</code>.
       *
       * @return the Map of ListOfMapResolver, or <code>null</code> if there are no parameters.
       */
      protected Map getParameters(Configuration config) throws ConfigurationException {
          Configuration[] children = config.getChildren("parameter");
          
          if (children.length == 0) {
              return null;
          }
          
          Map params = new HashMap();
          for (int i = 0; i < children.length; i++) {
              Configuration child = children[i];
              if (true) { // FIXME : check namespace
                  String value = child.getAttribute("value");
                  try {
                      params.put(child.getAttribute("name"), ListOfMapsResolver.getResolver(value));
                  } catch(PatternException pe) {
                      throw new ConfigurationException("Invalid pattern '" + value +
                          ", at " + child.getLocation());
                  }
              }
          }
          
          return params;
      }
  
      /**
       * Check if the namespace URI of the given configuraition is the same as the
       * 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())
          {
              throw new ConfigurationException("Invalid namespace, at " + config.getLocation());
          }
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/EnvironmentSourceResolver.java
  
  Index: EnvironmentSourceResolver.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.Disposable;
  import org.apache.avalon.framework.component.ComponentManager;
  
  import org.apache.cocoon.components.source.SourceHandler;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.Source;
  import org.apache.cocoon.environment.SourceResolver;
  import org.apache.cocoon.ProcessingException;
  
  import org.xml.sax.SAXException;
  
  import java.io.IOException;
  import java.net.URL;
  
  /**
   * A <code>SourceResolver</code> that resolves URIs relative to an <code>Environment</code>.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:23 $
   */
  
  public class EnvironmentSourceResolver implements SourceResolver, Disposable {
      
      private ComponentManager manager;
      
      private Source contextSource;
      
      private SourceHandler sourceHandler;
  
      public EnvironmentSourceResolver(ComponentManager manager, Environment env) throws Exception {
          this.manager = manager;
          this.contextSource = env.resolve("");
          this.sourceHandler = (SourceHandler)manager.lookup(SourceHandler.ROLE);
      }
      
      /**
       * Resolve an entity.
       */
      public Source resolve(String systemId) throws ProcessingException, SAXException, IOException {
          if (systemId == null) throw new SAXException("Invalid System ID");
  
          URL context = new URL(this.contextSource.getSystemId());
  
          if (systemId.length() == 0)
              return this.sourceHandler.getSource(null, context, systemId);
          if (systemId.indexOf(":") > 1)
              return this.sourceHandler.getSource(null, systemId);
          if (systemId.charAt(0) == '/')
              return this.sourceHandler.getSource(null, context.getProtocol() + ":" + systemId);
          return this.sourceHandler.getSource(null, context, systemId);
      }
  
      public void dispose() {
          if (contextSource != null)
          {
              contextSource.recycle();
          }
          manager.release(sourceHandler);
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ListOfMapsResolver.java
  
  Index: ListOfMapsResolver.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.logger.AbstractLoggable;
  import org.apache.avalon.framework.parameters.Parameters;
  
  import org.apache.cocoon.sitemap.PatternException;
  
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  
  /**
   * 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/11/08 14:39:23 $
   */
  
  public abstract class ListOfMapsResolver extends AbstractLoggable {
      
      /**
       * 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();
      }
      
      /**
       * Resolve all {...} patterns using the values given in the list of maps.
       */
      public abstract String resolve(List listOfMaps) 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 ListOfMapsResolver 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 listOfMaps) 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 = ((ListOfMapsResolver)entry.getValue()).resolve(listOfMaps);
              result.setParameter((String)entry.getKey(), value);
          }
          
          return result;
      }
  
      //-------------------------------------------------------------------------
      /**
       * No-op resolver for expressions that don't need to be resolved.
       */
      private static class NullResolver extends ListOfMapsResolver {
          private String expression;
  
          public NullResolver(String expression) {
              this.expression = this.unescape(expression);
          }
          
          public String resolve(List listOfMaps) {
              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 ListOfMapsResolver {
          private String expression;
          
          public RealResolver(String expression) throws PatternException {
              this.expression = expression;
          }
          
          public String resolve(List listOfMaps) 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 = listOfMaps.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)listOfMaps.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) + "\n" + e.getMessage());
              }
          }
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ProcessingNode.java
  
  Index: ProcessingNode.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.cocoon.environment.Environment;
  import org.apache.cocoon.components.pipeline.EventPipeline;
  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.1 $ $Date: 2001/11/08 14:39:23 $
   */
  
  public interface ProcessingNode extends ThreadSafe, Disposable {
      
      /**
       * The key of the <code>SourceResolver</code> in the object model.
       */
      public static String OBJECT_SOURCE_RESOLVER = "source-resolver";
      
      /**
       * Process environment.
       */
      boolean invoke(
          Environment env,
          StreamPipeline pipeline,
          EventPipeline eventPipeline,
          List listOfMaps
      ) throws Exception;
      
      /**
       * Dispose recursively any resources held.
       */
      void dispose();
      
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/ProcessingNodeBuilder.java
  
  Index: ProcessingNodeBuilder.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.Component;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  /**
   * 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.1 $ $Date: 2001/11/08 14:39:23 $
   */
  
  import java.util.Map;
  
  public interface ProcessingNodeBuilder extends Component {
  
      /**
       * Set the builder for which we are building.
       */
      void setBuilder(TreeBuilder builder);
      
      /**
       * Build the {@link ProcessingNode} from the given <code>Configuration</code>,
       * and register it in the {@link Builder} if needed.
       */
      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;
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/TreeBuilder.java
  
  Index: TreeBuilder.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.excalibur.component.DefaultRoleManager;
  import org.apache.avalon.excalibur.component.ExcaliburComponentSelector;
  
  import org.apache.avalon.excalibur.logger.LogKitManageable;
  import org.apache.avalon.excalibur.logger.LogKitManager;
  
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentException;
  
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.configuration.DefaultConfiguration;
  
  import org.apache.avalon.framework.context.Contextualizable;
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.context.ContextException;
  
  import org.apache.avalon.framework.logger.AbstractLoggable;
  
  import org.apache.cocoon.util.ClassUtils;
  
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:23 $
   */
  
  public class TreeBuilder extends AbstractLoggable implements
    Composable, Configurable, Contextualizable, LogKitManageable {
      
      /**
       * The role name to be used by composable <code>ProcessingNodeBuilder</code>s to get
       * the <code>Builder</code> for which they operate.
       */
      public static final String ROLE = "org.apache.cocoon.components.treeprocessor.Builder";
      
      /**
       * The categories of node Maps.
       */
      private Map categories = new HashMap();
      
      /**
       * The tree processor that we're building.
       */
      private TreeProcessor processor;
      
      private Context context;
      
      private LogKitManager logKit;
  
      private ComponentManager parentManager;
      
      private TreeBuilderComponentManager manager;
      
      private ExcaliburComponentSelector builderSelector;
      
      private DefaultRoleManager emptyRoleManager;
      
      private String namespace;
      
      private String languageName;
      
      private boolean canGetNode = false;
      
      public void contextualize(Context context) throws ContextException {
          this.context = context;
      }
      
      public void setLogKitManager(LogKitManager logKit) {
          this.logKit = logKit;
      }
  
      public void compose(ComponentManager manager) throws ComponentException {
          this.parentManager = manager;
          
          // Setup role manager for the component manager
          // FIXME : what roles should we put here ? Pipelines and Connector roles from sitemap.roles ?
          this.emptyRoleManager = new DefaultRoleManager();
          this.emptyRoleManager.setLogger(getLogger());
          try {
              this.emptyRoleManager.configure(new DefaultConfiguration("role-list", "-"));
          } catch(ConfigurationException ce) {
              throw new ComponentException("Cannot configure Builder role manager", ce);
          }
          
          // Create the new component manager
          this.manager = new TreeBuilderComponentManager(this.parentManager);
          this.manager.setLogger(getLogger());
          this.manager.contextualize(this.context);
          this.manager.setRoleManager(this.emptyRoleManager);
          this.manager.setLogKitManager(this.logKit);
          // this.manager.configure( - no configuration - );
          // initialize() is called later. In the meanwhile, only parent components will be visible.
      }
  
      public void configure(Configuration config) throws ConfigurationException {
          
          this.languageName = config.getAttribute("name");
          getLogger().debug("Configuring Builder for language : " + this.languageName);
          
          this.namespace = config.getAttribute("namespace");
          
          try {
              // Create the NodeBuilder selector.
              ExcaliburComponentSelector selector = new ExcaliburComponentSelector();
              selector.setLogger(getLogger());
              selector.contextualize(this.context);
              selector.setRoleManager(this.emptyRoleManager);
              selector.setLogKitManager(logKit);
              selector.compose(this.manager);
              //selector.configure(new DefaultConfiguration("node-builders","-"));
              
              this.builderSelector = selector;
              
              // Add components in the selector for all nodes in the configuration
              Configuration nodesConfig = config.getChild("nodes", false);
              if (nodesConfig == null) {
                  throw new ConfigurationException("Missing 'nodes' element, at " + config.getLocation());
              }
              
              Configuration[] nodes = nodesConfig.getChildren("node");
              for (int i = 0; i < nodes.length; i++) {
                  Configuration node = nodes[i];
                  String name = node.getAttribute("name");
                  String className = node.getAttribute("builder");
                  Class clazz;
                  try {
                      clazz = ClassUtils.loadClass(className);
                  } catch(Exception e) {
                      throw new ConfigurationException("Cannot load class '" + className +
                          "', at " + node.getLocation());
                  }
                  
                  if (! ProcessingNodeBuilder.class.isAssignableFrom(clazz)) {
                      throw new ConfigurationException("Class '" + className +
                          "' does not implement ProcessingNodeBuilder, at " + node.getLocation());
                  }
                  
                  // Add it to the builder selector
                  getLogger().debug("Adding ProcessingNodeBuilder '" + name + "' : class = " + className);
                  selector.addComponent(name, clazz, node);
              }
              
              selector.initialize();
              
          } catch(ComponentException ce) {
              throw new ConfigurationException("Cannot setup selector for node builders", ce);
          }
      }
  
      public void setProcessor(TreeProcessor processor) {
          this.processor = processor;
      }
      
      public TreeProcessor getProcessor() {
          return this.processor;
      }
      
      /**
       * Returns the language that is being built (e.g. "sitemap").
       */
      public String getLanguage() {
          return this.languageName;
      }
      
      /**
       * Register a named <code>ProcessingNode</code> in a given category.
       * For example, <code>ResourceNodeBuilder</code> stores here the <code>ProcessingNode</code>
       * it produces for use by sitemap pipelines. This allows to turn the tree into a graph.
       */
      public void addNode(ProcessingNode node, String category, String name) {
          Map nodes = (Map)categories.get(category);
          if (nodes == null) {
              nodes = new HashMap();
              categories.put(category, nodes);
          }
          nodes.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);
              }
          } else {
              throw new IllegalArgumentException("Cannot call getNode() now, but during buildNode()");
          }
      }
      
      /**
       * Some NodeBuilders create components to be used by others
       */
      public void addComponent(String role, Class component, Configuration configuration)
        throws ComponentException {
          this.manager.addComponent(role, component, configuration);
      }
      
      public void addComponentInstance(String role, Component instance) {
          this.manager.addComponentInstance(role, instance);
      }
      
      public ProcessingNodeBuilder createNodeBuilder(Configuration config) throws Exception {
          //FIXME : check namespace
          String nodeName = config.getName();
          int pos;
          if ((pos = nodeName.indexOf(':')) != -1)
              nodeName = nodeName.substring(pos+1);
  
          ProcessingNodeBuilder builder = (ProcessingNodeBuilder)this.builderSelector.select(nodeName);
          builder.setBuilder(this);
          return builder;
      }
      
      /**
       * Get the namespace URI that builders should use to find their nodes.
       */
      public String getNamespace() {
          return this.namespace;
      }
      
      /**
       * Build a processing tree from a <code>Configuration</code>.
       */
      public ProcessingNode build(Configuration config) throws Exception {
          
          // 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());
          
          // Expose newly added components
          this.manager.initialize();
          
          // Calls to getNode() are now allowed
          this.canGetNode = true;
          
          // And get the tree
          return rootBuilder.getNode();
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/TreeBuilderComponentManager.java
  
  Index: TreeBuilderComponentManager.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.Component;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.excalibur.component.ExcaliburComponentManager;
  
  /**
   * An <code>ExcaliburComponentManager</code> which directs all calls to
   * <code>ComponentManager</code> methods to its parent while it isn't
   * initialized.
   * <p>
   * This allows to give <code>ProcessingNodesBuilder</code>s a component manager
   * they can use to lookup parent-defined components while also allowing it to be
   * enriched through <code>Builder.addComponent()</code>.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:23 $
   */
  
  
  public class TreeBuilderComponentManager extends ExcaliburComponentManager {
      
      private ComponentManager parent;
      
      private boolean initialized = false;
      
      public TreeBuilderComponentManager(ComponentManager parent) {
          super(parent);
          this.parent = parent;
      }
      
      public Component lookup(String role) throws ComponentException {
          if (initialized) {
              return super.lookup(role);
          } else {
              return parent.lookup(role);
          }
      }
  
      /**
       * Check to see if a <code>Component</code> exists for a role.
       *
       * @param role  a string identifying the role to check.
       * @return True if the component exists, False if it does not.
       */
      public boolean hasComponent(String role) {
          if (initialized) {
              return super.hasComponent(role);
          } else {
              return parent.hasComponent(role);
          }
      }
  
      /**
       * Return the <code>Component</code> when you are finished with it.  This
       * allows the <code>ComponentManager</code> to handle the End-Of-Life Lifecycle
       * events associated with the Component.  Please note, that no Exceptions
       * should be thrown at this point.  This is to allow easy use of the
       * ComponentManager system without having to trap Exceptions on a release.
       *
       * @param component The Component we are releasing.
       */
      public void release(Component component) {
          if (initialized) {
              super.release(component);
          } else {
              parent.release(component);
          }
      }
      
      public void initialize() {
          super.initialize();
          initialized = true;
      }
  
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/TreeProcessor.java
  
  Index: TreeProcessor.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.excalibur.logger.LogKitManageable;
  import org.apache.avalon.excalibur.logger.LogKitManager;
  
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentSelector;
  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.configuration.SAXConfigurationHandler;
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.context.ContextException;
  import org.apache.avalon.framework.context.Contextualizable;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  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.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;
  
  /**
   * Interpreted tree-traversal implementation of the a Processor language.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:23 $
   */
  
  public class TreeProcessor extends AbstractLoggable implements ThreadSafe, Processor,
    Composable, Configurable, LogKitManageable, Initializable, Contextualizable {
      
      private static final String XCONF_URL = "resource://org/apache/cocoon/treeprocessor/treeprocessor.xconf";
      
      /** The parent TreeProcessor, if any */
      protected TreeProcessor parent;
      
      /** The context */
      protected Context context;
      
      /** The component manager */
      protected ComponentManager manager;
      
      /** The logkit manager to get Loggers */
      protected LogKitManager logKit;
      
      /** The language used by this processor */
      protected String language;
      
      /** The configuration for each language */
      protected Map languageConfigs;
      
      /** The root node of the processing tree */
      protected ProcessingNode rootNode;
      
      /** Last modification time */
      protected long lastModified = 0;
      
      /** The relative file name of the tree definition */
      protected String sourceName;
      
      /** The current language configuration */
      protected Configuration currentLanguage;
      
      /** Build the root TreeProcessor */
      public TreeProcessor() {
          // Language can be overriden in the configuration.
          this(null, "sitemap");
      }
      
      /** Build a child processor for the same language as its parent */
      public TreeProcessor(TreeProcessor parent) {
          this(parent, parent.language);
      }
      
      /** Build a child processor for a given language */
      public TreeProcessor(TreeProcessor parent, String language) {
          this.parent = parent;
          this.language = language;
      }
      
      public void contextualize(Context context) throws ContextException {
          this.context = context;
      }
      
      public void compose(ComponentManager manager) throws ComponentException {
          this.manager = manager;
      }
  
      public void setLogKitManager(LogKitManager logKit) {
          this.logKit = logKit;
      }
  
  
  /*
    <processor>
      <root-language name="sitemap"/>
      <language>...</language>
    </processor>
  */
      public void configure(Configuration config) throws ConfigurationException {
          
          if (this.parent != null) {
              // Configuration is inherited from parent
              getLogger().info("Child TreeProcessor ignores call to configure()");
              return;
          }
          
          Configuration rootLangConfig = config.getChild("root-language", false);
          if (rootLangConfig != null) {
              this.language = rootLangConfig.getAttribute("name");
          }
          
          // Read the builtin languages definition file
          Configuration builtin;
          
          try {
              URLFactory factory = (URLFactory)this.manager.lookup(URLFactory.ROLE);
              URLSource source = new URLSource(factory.getURL(XCONF_URL), this.manager);
              try {
                  SAXConfigurationHandler handler = new SAXConfigurationHandler();
                  source.toSAX(handler);
                  builtin = handler.getConfiguration();
              } finally {
                  this.manager.release((Component)factory);
                  if (source != null) {
                      source.recycle();
                  }
              }
          } catch(Exception e) {
              String msg = "Error while reading treeprocessor.xconf : " + e.getMessage();
              getLogger().error(msg, e);
              throw new ConfigurationException(msg, e);
          }
          
          this.languageConfigs = new HashMap();
  
          // Add builtin languages
          addLanguages("builtin", builtin.getChildren("language"));
          
          // Add additional languages from the configuration
          addLanguages("additional", config.getChildren("language"));
      }
      
      private void addLanguages(String type, Configuration[] languages)
        throws ConfigurationException {
          
          for (int i = 0; i < languages.length; i++) {
              
              String name = languages[i].getAttribute("name");
              
              if (this.languageConfigs.containsKey(name)) {
                  getLogger().info("Redefining language '" + name + "' in " + type + " configuration.");
              } else {
                  getLogger().debug("Adding " + type + " language '" + name + "'");
              }
  
              this.languageConfigs.put(name, languages[i]);
          }
      }
  
      public void initialize() throws Exception {
          if (this.parent != null) {
              // Copy parent configuration
              this.languageConfigs = this.parent.languageConfigs;
          }
          
          this.currentLanguage = (Configuration)this.languageConfigs.get(this.language);
          if (this.currentLanguage == null) {
              throw new ConfigurationException("No configuration defined for language '" + this.language + "'");
          }
          
          Configuration fileConfig = this.currentLanguage.getChild("file", false);
          if (fileConfig == null) {
              throw new ConfigurationException("Missing 'file' configuration for language '" +
                  this.language + "', at " + this.currentLanguage.getLocation());
          }
          
          this.sourceName = fileConfig.getAttribute("name");
      }
      
      public boolean process(Environment environment) throws Exception {
          return process(environment, null, null);        
      }
  
      public boolean process(Environment environment, StreamPipeline pipeline, EventPipeline eventPipeline) throws Exception {
          SourceHandler oldSourceHandler = environment.getSourceHandler();
          SourceHandler sourceHandler = (SourceHandler)this.manager.lookup(SourceHandler.ROLE);
          try {
              environment.setSourceHandler(sourceHandler);
              setupRootNode(environment);
              return this.rootNode.invoke(environment, pipeline, eventPipeline, new ArrayList());
          } 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) {
              // Nothing changed
              source.recycle();
              return;
          }
          
          // Read the tree definition file as a Configuration
          Configuration treeConfig;
          try {
              getLogger().debug("Building " + this.language + " from " + source.getSystemId());
              
              SAXConfigurationHandler handler = new SAXConfigurationHandler();
              source.toSAX(handler);
              treeConfig = handler.getConfiguration();
          } finally {
              source.recycle();
          }
          
          // Dispose the current tree, if any
          if (this.rootNode != null) {
              this.rootNode.dispose();
          }
          
          TreeBuilder builder = new TreeBuilder();
          builder.setLogger(getLogger());
          builder.contextualize(this.context);
          // builder.setRoleManager( - none - );
          builder.setLogKitManager(this.logKit);
          builder.compose(this.manager);
          builder.configure(this.currentLanguage);
          //builder.initialize();
          
          this.rootNode = builder.build(treeConfig);
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/treeprocessor.xconf
  
  Index: treeprocessor.xconf
  ===================================================================
  <?xml version="1.0" encoding="UTF-8"?>
  <!--
   *****************************************************************************
   * 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.                                                         *
   *****************************************************************************
  
   @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:23 $
  -->
  
  <tree-processor>
    <!-- The sitemap language -->
    <language name="sitemap" namespace="http://apache.org/cocoon/sitemap/1.0">
    
      <!-- Should we allow to specify the builder class ? -->
      <!-- builder class="org.apache.cocoon.treeprocessor.Builder"/ -->
      
      <!-- The file name for this language -->
      <file name="sitemap.xmap"/>
      
      <nodes>
        <!-- Sitemap root node -->
        <node name="sitemap" builder="org.apache.cocoon.treeprocessor.sitemap.SitemapNodeBuilder">
          <allowed-children>components, pipeline, views, resources, action-sets</allowed-children>
        </node>
      
        <!-- Components definition : fills selectors on the TreeProcessor -->
        <node name="components" builder="org.apache.cocoon.treeprocessor.sitemap.ComponentsNodeBuilder">
          <selector section="matchers"     elements="matcher"
                    role="org.apache.cocoon.matching.MatcherSelector"/>
          <selector section="selectors"    elements="selector"
                    role="org.apache.cocoon.selection.SelectorSelector"/>
          <selector section="actions"      elements="action"
                    role="org.apache.cocoon.acting.ActionSelector"/>
          <selector section="generators"   elements="generator"
                    role="org.apache.cocoon.generation.GeneratorSelector"/>
          <selector section="transformers" elements="transformer"
                    role="org.apache.cocoon.transformation.TransformerSelector"/>
          <selector section="serializers"  elements="serializer"
                    role="org.apache.cocoon.serialization.SerializerSelector"/>
          <selector section="readers"      elements="reader"
                    role="org.apache.cocoon.reading.ReaderSelector"/>
        </node>
        
        <!--node name="action" builder="org.apache.cocoon.treeprocessor.sitemap.ActionNodeBuilder">
          <forbidden-children>sitemap, components, pipeline, error-handler</forbidden-children>
        </node-->
        
        <node name="generator" builder="org.apache.cocoon.treeprocessor.sitemap.GeneratorNodeBuilder"/>
      </nodes>
      
    </language>
  
  </tree-processor>
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ActionNode.java
  
  Index: ActionNode.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.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.treeprocessor.AbstractParentProcessingNode;
  import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  
  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/08 14:39:24 $
   */
  
  public class ActionNode extends AbstractParentProcessingNode {
      
      /** The action name */
      protected String name;
      
      /** The 'src' attribute */
      protected ListOfMapsResolver source;
  
      /** Selector where to get Actions from */
      protected ComponentSelector selector;
      
      /** Pre-selected action, if it's ThreadSafe */
      protected Action threadSafeAction;
  
      public ActionNode(String name, String source) throws PatternException {
          this.name = name;
          this.source = ListOfMapsResolver.getResolver(source);
      }
      
      public void setSelector(ComponentSelector selector) throws ComponentException {
          
          this.selector = selector;
          
          // Is it a ThreadSafe action ?
          Action action = (Action)selector.select(name);
          if (action instanceof ThreadSafe) {
              // Yes : keep it.
              this.threadSafeAction = action;
          } else {
              this.threadSafeAction = null;
              selector.release(action);
          }
      }
      
      public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
        throws Exception {
          
          // Extract required data from the object model
          Map objectModel = env.getObjectModel();
          
          // Execute the action
          Action action = this.threadSafeAction;
          if (action == null) {
              action = (Action)this.selector.select(this.name);
          }
  
          Map actionResult;
          try {
              actionResult = action.act(
                  getRedirector(objectModel),
                  getSourceResolver(objectModel),
                  objectModel,
                  source.resolve(listOfMaps),
                  ListOfMapsResolver.buildParameters(this.parameters, listOfMaps)
              );
          } finally {
              if (this.threadSafeAction == null) {
                  this.selector.release(action);
              }
          }
          
          if (actionResult == null) {
              // Action failed
              return false;
          } else {
              // Action succeeded : process children
              return this.invokeChildren(env, pipeline, eventPipeline, listOfMaps, actionResult);
          }
      }
      
      public void dispose() {
          super.dispose();
          if (this.threadSafeAction != null) {
              this.selector.release(this.threadSafeAction);
          }
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/ComponentsNodeBuilder.java
  
  Index: ComponentsNodeBuilder.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.excalibur.component.ExcaliburComponentSelector;
  import org.apache.avalon.excalibur.component.DefaultRoleManager;
  
  import org.apache.avalon.excalibur.logger.LogKitManager;
  import org.apache.avalon.excalibur.logger.LogKitManageable;
  
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.context.Contextualizable;
  
  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.configuration.DefaultConfiguration;
  
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentSelector;
  
  import org.apache.cocoon.treeprocessor.AbstractProcessingNodeBuilder;
  import org.apache.cocoon.treeprocessor.ProcessingNode;
  
  import org.apache.cocoon.util.ClassUtils;
  
  import java.util.Map;
  import java.util.HashMap;
  
  /**
   * Handles &lt;map:components&gt;. It doesn't actually create a <code>ProcessingNode</code>,
   * 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.1 $ $Date: 2001/11/08 14:39:24 $
   */
  
  public class ComponentsNodeBuilder extends AbstractProcessingNodeBuilder implements
    Composable, Configurable, Contextualizable, LogKitManageable {
  
      /** The hint name to use to select the default component in a selector */
      public static final String DEFAULT_HINT = "!default!";
      
      private Context context;
      
      private LogKitManager logKit;
  
      private ComponentManager parentManager;
      
      protected ComponentManager manager;
      
      /** Association of element names to section names */
      protected Map sectionElements = new HashMap();
      
      /** Association of role names to section names */
      protected Map sectionRoles = new HashMap();
      
      public void contextualize(Context context) {
          this.context = context;
      }
      
      public void setLogKitManager(LogKitManager logKit) {
          this.logKit = logKit;
      }
  
      public void compose(ComponentManager manager) throws ComponentException {
          this.manager = manager;
      }
  
      public void configure(Configuration config) throws ConfigurationException {
          
          // Iterate on all "section" children
          Configuration[] sectionsConfig = config.getChildren("selector");
          for (int i = 0; i < sectionsConfig.length; i++) {
              
              Configuration sectionConfig = sectionsConfig[i];
              
              // "section" : name of the section
              String name = sectionConfig.getAttribute("section");
              
              // "elements" : name of elements in the section
              sectionElements.put(name, sectionConfig.getAttribute("elements"));
              
              // "role" : role of the Selector in the component manager
              sectionRoles.put(name, sectionConfig.getAttribute("role"));
              
          }
      }
      
      /**
       * Build a <code>ComponentSelector</code> for each component section and add it
       * each component contained in the section. Selectors are also published on the
       * <code>TreeBuilder</code> for use by other nodes.
       */
      public void buildNode(Configuration config, Map buildModel) throws Exception {
          
          // Iterate on all sections
          Configuration[] sections = config.getChildren();
          for (int i = 0; i < sections.length; i++) {
              
              Configuration section = sections[i];
              checkNamespace(section);
              String sectionName = section.getName();
              
              // Is it a known section ?
              String elementName = (String)this.sectionElements.get(sectionName);
              if (elementName == null) {
                  // Unknown section
                  throw new ConfigurationException("Unknown section named '" + sectionName +
                      "', at " + section.getLocation());
              }
              
              // default component
              String defaultElement = section.getAttribute("default", null);
              if (defaultElement == null) {
                  getLogger().debug("Component section '" + sectionName + "' has no default");
              }
                     
              // Create the selector for this section
              getLogger().debug("Creating component selector for " + sectionName);
              
              // Prepare an empty role manager
              DefaultRoleManager emptyRoleManager = new DefaultRoleManager();
              emptyRoleManager.setLogger(getLogger());
              try {
                  emptyRoleManager.configure(new DefaultConfiguration("role-list", "-"));
              } catch(ConfigurationException ce) {
                  throw new ComponentException("Cannot configure role manager", ce);
              }
              
              ExcaliburComponentSelector selector = new ExcaliburComponentSelector();
              selector.setLogger(getLogger());
              selector.contextualize(this.context);
              selector.setRoleManager(emptyRoleManager);
              selector.setLogKitManager(this.logKit);
              selector.compose(this.manager);
              // selector.configure( - no configuration - );
  
  
              // Iterate on all components
              Configuration[] elements = section.getChildren();
              for (int j = 0; i < elements.length; j++) {
                  
                  Configuration element = elements[i];
                  checkNamespace(element);
                  
                  // Is it the right name ?
                  if (! element.getName().equals(elementName)) {
                      throw new ConfigurationException("Unknown element named '" + element.getName() +
                          ", at " + element.getLocation());
                  }
                  
                  // Get the element name
                  String name = element.getAttribute("name");
                  
                  // Register the class
                  String className = element.getAttribute("src");
                  Class clazz;
                  try {
                      clazz = ClassUtils.loadClass(className);
                  } catch(Exception e) {
                      throw new ConfigurationException("Cannot load class '" + className +
                          "' for " + elementName + " '" + name + "', at " + element.getLocation(), e);
                  }
                  
                  // Register the component
                  selector.addComponent(name, clazz, element);
                  if (name.equals(defaultElement)) {
                      // Also register as default component
                      selector.addComponent(DEFAULT_HINT, clazz, element);
                      
                      // Clear default to mark it as found
                      defaultElement = null;
                  }
              } // end for elements
              
              if (defaultElement != null) {
                  // default not found
                  throw new ConfigurationException("Default element '" + defaultElement +
                      "' does not exist, at " + section.getLocation());
              }
              
              selector.initialize();
              
              // Publish the selector
              this.builder.addComponentInstance((String)this.sectionRoles.get(sectionName), selector);
              
          } // end for sections
      }
      
      /**
       * Always return <code>null</code>.
       */
      public ProcessingNode getNode() {
          // Nothing to do at runtime.
          return null;
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/GeneratorNode.java
  
  Index: GeneratorNode.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.treeprocessor.AbstractProcessingNode;
  import org.apache.cocoon.treeprocessor.ListOfMapsResolver;
  
  import java.util.List;
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:24 $
   */
  
  public class GeneratorNode extends AbstractProcessingNode {
  
      protected String generatorName;
      
      protected ListOfMapsResolver source;
  
      public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
        throws Exception {
          
          eventPipeline.setGenerator(
              this.generatorName,
              source.resolve(listOfMaps),
              ListOfMapsResolver.buildParameters(this.parameters, listOfMaps)
          );
          
          return true;
      }
      
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/GeneratorNodeBuilder.java
  
  Index: GeneratorNodeBuilder.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.ProcessingNode;
  
  import java.util.Map;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:24 $
   */
  
  public class GeneratorNodeBuilder extends AbstractProcessingNodeBuilder {
  
      public void buildNode(Configuration config, Map buildModel) {
      }
      public ProcessingNode getNode() {
          return null;
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/PipelineNode.java
  
  Index: PipelineNode.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.activity.Disposable;
  
  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 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/08 14:39:24 $
   */
  
  public class PipelineNode extends AbstractParentProcessingNode {
  
      public boolean invoke(Environment env, StreamPipeline pipeline, EventPipeline eventPipeline, List listOfMaps)
        throws Exception {
          
          boolean success = false;
          
          // Try each of the children until one is successfull.
          try {
              
              for (int i = 0; i < childNodes.length; i++) {
                  if (childNodes[i].invoke(env, pipeline, eventPipeline, listOfMaps)) {
                      success = true;
                      break;
                  }
              }    
          } catch(Exception e) {
              // FIXME Error pipelines.
              getLogger().debug("Error while processing pipeline", e);
              throw e;
          }
          
          return success;
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SitemapNode.java
  
  Index: SitemapNode.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.ComponentManager;
  import org.apache.avalon.framework.component.Composable;
  
  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.SitemapRedirector;
  
  import org.apache.cocoon.treeprocessor.AbstractParentProcessingNode;
  import org.apache.cocoon.treeprocessor.EnvironmentSourceResolver;
  
  import java.util.List;
  import java.util.Map;
  
  /**
   * The root node of a sitemap.
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:24 $
   */
  
  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;
      
      public static boolean isInternalRequest(Environment env) {
          return env.getAttribute(INTERNAL_ATTR) != null;
      }
      
      public static Redirector getRedirector(Environment env) {
          return (Redirector)env.getAttribute(REDIRECTOR_ATTR);
      }
      
      /**
       * Keep the component manager used everywhere in the tree so that we can
       * cleanly dispose it.
       */
      public void compose(ComponentManager manager) {
          this.manager = manager;
      }
      
      /**
       * Process the environment. Also adds an <code>EnvironmentSourceResolver</code>
       * 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)
        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);
              }
          }
      }
  
      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.
          EnvironmentSourceResolver resolver = new EnvironmentSourceResolver(this.manager, env);
          
          Map objectModel = env.getObjectModel();
          
          Object oldResolver = objectModel.get(OBJECT_SOURCE_RESOLVER);
          Object oldRedirector = env.getAttribute(REDIRECTOR_ATTR);
  
          objectModel.put(OBJECT_SOURCE_RESOLVER, resolver);
          env.setAttribute(REDIRECTOR_ATTR, new SitemapRedirector(env));
  
          boolean success = true;
          try {
              // FIXME : is there any useful information that can be passed as top-level parameters,
              //         such as the URI of the mount point ?
              for (int i = 0; i < childNodes.length; i++) {
                  if (! childNodes[i].invoke(env, pipeline, eventPipeline, listOfMaps)) {
                      success = false;
                      break;
                  }
              }
              
              //TODO : check redirector
              
              return success;
              
          } finally {
  
              // Restore old redirector and resolver
              env.setAttribute(REDIRECTOR_ATTR, oldRedirector);
              objectModel.put(OBJECT_SOURCE_RESOLVER, oldResolver);
              
              // Dispose the resolver
              resolver.dispose();
          }
      }
      
      /**
       * Dispose all children and the component manager.
       */
      public void dispose() {
          this.disposeChildren();
          if (this.manager instanceof Disposable) {
              ((Disposable)this.manager).dispose();
          }
      }
  }
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/treeprocessor/sitemap/SitemapNodeBuilder.java
  
  Index: SitemapNodeBuilder.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.Composable;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.configuration.Configuration;
  
  import org.apache.cocoon.treeprocessor.ProcessingNode;
  import org.apache.cocoon.treeprocessor.AbstractParentProcessingNodeBuilder;
  
  import java.util.Map;
  
  /**
   *
   * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/08 14:39:24 $
   */
  
  public class SitemapNodeBuilder extends AbstractParentProcessingNodeBuilder implements Composable {
  
      protected ComponentManager manager;
      
      protected SitemapNode node;
      
      public void compose(ComponentManager manager) throws ComponentException {
          this.manager = manager;
      }
      
      public void buildNode(Configuration config, Map buildModel) throws Exception {
          node = new SitemapNode();
          node.compose(this.manager);
      }
      
      public ProcessingNode getNode() throws Exception {
          return this.node;
      }
  
  }
  
  

----------------------------------------------------------------------
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