ovidiu      02/05/19 12:19:39

  Modified:    src/java/org/apache/cocoon cocoon.roles
               src/java/org/apache/cocoon/components/treeprocessor
                        treeprocessor-builtins.xml
               src/java/org/apache/cocoon/components/treeprocessor/sitemap
                        CallNodeBuilder.java
  Added:       src/java/org/apache/cocoon/components/flow
                        AbstractInterpreter.java ContinuationsManager.java
                        ContinuationsManagerImpl.java Interpreter.java
                        InterpreterSelector.java ScriptSource.java
                        WebContinuation.java flow.xconf
               src/java/org/apache/cocoon/components/flow/javascript
                        JSCocoon.java JSGlobal.java JSLog.java
                        JSWebContinuation.java JavaScriptInterpreter.java
                        system.js
               src/java/org/apache/cocoon/components/language/markup/xsp/java
                        jpath.xsl
               src/java/org/apache/cocoon/components/treeprocessor/sitemap
                        CallFunctionNode.java ContinueNode.java
                        ContinueNodeBuilder.java ScriptNode.java
                        ScriptNodeBuilder.java
               src/java/org/apache/cocoon/transformation
                        AugmentTransformer.java
  Log:
  Added the continuations based control flow layer from scratchpad/schecoon/.
  
  Revision  Changes    Path
  1.28      +13 -0     xml-cocoon2/src/java/org/apache/cocoon/cocoon.roles
  
  Index: cocoon.roles
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/src/java/org/apache/cocoon/cocoon.roles,v
  retrieving revision 1.27
  retrieving revision 1.28
  diff -u -r1.27 -r1.28
  --- cocoon.roles      6 May 2002 13:22:30 -0000       1.27
  +++ cocoon.roles      19 May 2002 19:19:38 -0000      1.28
  @@ -210,6 +210,19 @@
           shorthand="autoincrement-modules"
           
default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector"/>
   
  +
  +  <!-- Control flow layer: the interpreters selector and continuations
  +       manager
  +    -->
  +
  +  <role name="org.apache.cocoon.components.flow.Interpreter"
  +        default-class="org.apache.cocoon.components.flow.InterpreterSelector"
  +        shorthand="flow-interpreters"/>
  +
  +  <role name="org.apache.cocoon.components.flow.ContinuationsManager"
  +        default-class="org.apache.cocoon.components.flow.ContinuationsManagerImpl"
  +        shorthand="continuations"/>
  +
     <!-- DEPRECATED, use the xml-parser instead ! -->
     <role name="org.apache.cocoon.components.resolver.Resolver"
           shorthand="resolver"
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/AbstractInterpreter.java
  
  Index: AbstractInterpreter.java
  ===================================================================
  package org.apache.cocoon.components.flow;
  
  import java.lang.System;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Iterator;
  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.framework.component.Composable;
  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.Constants;
  import org.apache.cocoon.components.source.SourceFactory;
  import org.apache.cocoon.components.treeprocessor.sitemap.PipelinesNode;
  import org.apache.cocoon.environment.Context;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.ForwardRedirector;
  
  /**
   * Abstract superclass for various scripting languages used by Cocoon
   * for flow control. Defines some useful behavior like the ability to
   * reload script files if they get modified (useful when doing
   * development), and passing the control to Cocoon's sitemap for
   * result page generation.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since March 15, 2002
   */
  public abstract class AbstractInterpreter extends AbstractLoggable
    implements Component, Composable, Contextualizable, Interpreter, ThreadSafe
  {
    /**
     * Hash table of source locations -> ScriptSource instances
     */
    protected HashMap scripts = new HashMap();
  
    /**
     * List of source locations that need to be resolved.
     */
    protected ArrayList needResolve = new ArrayList();
  
    /**
     * When was the last time we checked for script modifications. Used
     * only if {@link reloadScripts} is true.
     */
    protected long lastTimeCheck = 0;
  
    protected org.apache.cocoon.environment.Context context;
    protected ComponentManager manager;
    protected ContinuationsManager continuationsMgr;
  
    /**
     * Whether reloading of scripts should be done. Specified through
     * the "reload-scripts" attribute in <code>flow.xmap</code>.
     */
    protected boolean reloadScripts;
  
    /**
     * Interval between two checks for modified script files. Specified
     * through the "check-time" XML attribute in <code>flow.xmap</code>.
     */
    protected long checkTime;
  
    public void compose(ComponentManager manager)
      throws ComponentException
    {
      this.manager = manager;
  //     System.out.println("AbstractInterpreter: ComponentManager = " + manager);
      // FIXME: Why is the manager null here?
      if (manager != null)
        continuationsMgr
          = (ContinuationsManager)manager.lookup(ContinuationsManager.ROLE);
    }
  
    public void contextualize(org.apache.avalon.framework.context.Context context)
      throws ContextException
    {
      this.context = (Context)context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
    }
  
    public void setReloadScripts(boolean yn)
    {
      reloadScripts = yn;
    }
  
    public void setCheckTime(long time)
    {
      checkTime = time;
    }
  
    /**
     * Registers a source file with the interpreter. Using this method
     * an implementation keeps track of all the script files which are
     * compiled. This allows them to reload the script files which get
     * modified on the file system.
     *
     * <p>The parsing/compilation of a script file by an interpreter
     * happens in two phases. In the first phase the file's location is
     * registered in the <code>needResolve</code> array.
     *
     * <p>The second is possible only when a Cocoon
     * <code>Environment</code> is passed to the Interpreter. This
     * allows the file location to be resolved using Cocoon's
     * <code>SourceFactory</code> class.
     *
     * <p>Once a file's location can be resolved, it is removed from the
     * <code>needResolve</code> array and placed in the
     * <code>scripts</code> hash table. The key in this hash table is
     * the file location string, and the value is a
     * DelayedRefreshSourceWrapper instance which keeps track of when
     * the file needs to re-read.
     *
     * @param source the location of the script
     *
     * @see org.apache.cocoon.components.source.SourceFactory
     * @see org.apache.cocoon.environment.Environment
     * @see org.apache.cocoon.components.source.DelayedRefreshSourceWrapper
     */
    public void register(String source)
    {
      synchronized(this) {
        needResolve.add(source);
      }
    }
  
    /**
     * Unregister the source file. Called from <code>ScriptNode</code>
     * when a tree corresponding to a sitemap is decommissioned.
     *
     * @param source a <code>String</code> value
     */
    public void unregister(String source)
    {
      synchronized(this) {
        scripts.remove(source);
        int index = needResolve.indexOf(source);
        if (index != -1)
          needResolve.remove(index);
      }
    }
  
    /**
     * Reloads any modified script files.
     *
     * <p>It checks to see if any of the files already read in (those
     * present in the <code>scripts</code> hash map) have been
     * modified.
     *
     * <p>It also checks to see if any script files have been registered
     * with the interpreter since the last call to
     * <code>checkForModifiedScripts</code>. These files are stored in
     * the temporary array <code>needResolve</code>. If any such files
     * are found, they are read in.
     *
     * @param environment an <code>Environment</code> value
     */
    public void checkForModifiedScripts(Environment environment)
      throws Exception
    {
      if (reloadScripts
          && System.currentTimeMillis() >= lastTimeCheck + checkTime) {
        // FIXME: should we worry about synchronization?
        Iterator iter = scripts.values().iterator();
        while (iter.hasNext()) {
          ScriptSource src = (ScriptSource)iter.next();
          if (src.getLastModified() > lastTimeCheck) {
            try {
              src.refresh(environment);
            }
            catch (Exception ex) {
              System.out.println("Error reading script " + src.getSourceName()
                                 + ", ignoring!");
            }
          }
        }
      }
  
      // FIXME: remove the need for synchronization
      synchronized (this) {
        int size = needResolve.size();
        for (int i = 0; i < size; i++) {
          String source = (String)needResolve.get(0);
          ScriptSource src = new ScriptSource(this, source);
          scripts.put(source, src);
          needResolve.remove(0);
          try {
            src.refresh(environment);
          }
          catch (Exception ex) {
            System.out.println("Error reading script " + source + ", ignoring!");
          }
        }
      }
  
      // Update the time of the last check. If an exception occurs, this
      // is not executed, so the next request will force a reparse of
      // the script files because of an old time stamp.
      lastTimeCheck = System.currentTimeMillis();
    }
  
    public void forwardTo(String uri, Object bizData,
                          WebContinuation continuation,
                          Environment environment)
      throws Exception
    {
      environment.setAttribute("bean-dict", bizData);
      if (continuation != null)
        environment.setAttribute("kont", continuation);
  
      try {
        PipelinesNode.getRedirector(environment)
          .redirect(false, "cocoon:/" + uri);
      }
      finally {
        environment.removeAttribute("bean-dict");
        if (continuation != null)
          environment.removeAttribute("kont");
      }
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/ContinuationsManager.java
  
  Index: ContinuationsManager.java
  ===================================================================
  package org.apache.cocoon.components.flow;
  
  /**
   * The interface of the Continuations manager.
   *
   * The continuation manager maintains a forrest of {@link
   * WebContinuation} trees. Each tree defines the flow of control for a
   * user within the application.
   * 
   * A <code>WebContinuation</code> is created for a continuation object
   * from the scripting language used. A continuation object in the
   * implementation of the scripting language is an opaque object
   * here. It is only stored inside the <code>WebContinuation</code>,
   * without being interpreted in any way.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since March 19, 2002
   * @see WebContinuation
   */
  public interface ContinuationsManager
  {
    public final String ROLE = 
"org.apache.cocoon.components.flow.ContinuationsManager";
  
    /**
     * Create a <code>WebContinuation</code> object given a native
     * continuation object and its parent. If the parent continuation is
     * null, the <code>WebContinuation</code> returned becomes the root
     * of a tree in the forrest.
     *
     * @param kont an <code>Object</code> value
     * @param parentKont a <code>WebContinuation</code> value
     * @param timeToLive an <code>int</code> value indicating how long
     * in seconds this continuation will live in the server if not
     * accessed
     * @return a <code>WebContinuation</code> value
     * @see WebContinuation
     */
    public WebContinuation createWebContinuation(Object kont,
                                                 WebContinuation parentKont,
                                                 int timeToLive);
  
    /**
     * Invalidates a <code>WebContinuation</code>. This effectively
     * means that the continuation object associated with it will no
     * longer be accessible from Web pages. Invalidating a
     * <code>WebContinuation</code> invalidates all the
     * <code>WebContinuation</code>s which are children of it.
     *
     * @param k a <code>WebContinuation</code> value
     */
    public void invalidateWebContinuation(WebContinuation k);
  
    /**
     * Given a <code>WebContinuation</code> id, retrieve the associated
     * <code>WebContinuation</code> object.
     *
     * @param id a <code>String</code> value
     * @return a <code>WebContinuation</code> object, or null if no such
     * <code>WebContinuation</code> could be found.
     */
    public WebContinuation lookupWebContinuation(String id);
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/ContinuationsManagerImpl.java
  
  Index: ContinuationsManagerImpl.java
  ===================================================================
  package org.apache.cocoon.components.flow;
  
  import java.security.SecureRandom;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.SortedSet;
  import java.util.TreeSet;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  /**
   * The default implementation of {@link ContinuationsManager}.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since March 19, 2002
   * @see ContinuationsManager
   */
  public class ContinuationsManagerImpl
    extends AbstractLoggable
    implements ContinuationsManager, Component, Configurable, ThreadSafe
  {
    static final int CONTINUATION_ID_LENGTH = 20;
  
    protected SecureRandom random = null;
    protected byte[] bytes;
  
    /**
     * How long does a continuation exist in memory since the last
     * access? The time is in seconds, and the default is 3600 (1 hour).
     */
    protected int defaultTimeToLive;
  
    /**
     * Maintains the forrest of <code>WebContinuation</code> trees.
     */
    protected Set forrest = Collections.synchronizedSet(new HashSet());
  
    /**
     * Association between <code>WebContinuation</code> ids and the
     * corresponding <code>WebContinuation</code> object.
     */
    protected Map idToWebCont = Collections.synchronizedMap(new HashMap());
  
    /**
     * Sorted set of <code>WebContinuation</code> instances, based on
     * their expiration time. This is used by the background thread to
     * invalidate continuations.
     */
    protected SortedSet expirations = new TreeSet();
  
    public ContinuationsManagerImpl()
      throws Exception
    {
      random = SecureRandom.getInstance("SHA1PRNG");
      random.setSeed(System.currentTimeMillis());
      bytes = new byte[CONTINUATION_ID_LENGTH];
    }
  
    public void configure(Configuration config)
    {
      defaultTimeToLive = config.getAttributeAsInteger("time-to-live", 3600);
    }
  
    public WebContinuation createWebContinuation(Object kont,
                                                 WebContinuation parentKont,
                                                 int timeToLive)
    {
      int ttl = (timeToLive == 0 ? defaultTimeToLive : timeToLive);
      WebContinuation wk = new WebContinuation(kont, parentKont, this, ttl);
  
      if (parentKont == null)
        forrest.add(wk);
  
      expirations.add(wk);
  
      // No need to add the WebContinuation in idToWebCont as it was
      // already done during its construction.
  
      return wk;
    }
  
    public void invalidateWebContinuation(WebContinuation wk)
    {
      WebContinuation parent = wk.getParentContinuation();
      if (parent == null)
          forrest.remove(wk);
      else {
        List parentKids = parent.getChildren();
        parentKids.remove(wk);
      }
  
      _invalidate(wk);
    }
  
    protected void _invalidate(WebContinuation wk)
    {
      idToWebCont.remove(wk.getId());
      expirations.remove(wk);
      
      // Invalidate all the children continuations as well
      List children = wk.getChildren();
      int size = children.size();
      for (int i = 0; i < size; i++)
        _invalidate((WebContinuation)children.get(i));
    }
  
    public WebContinuation lookupWebContinuation(String id)
    {
      return (WebContinuation)idToWebCont.get(id);
    }
  
    /**
     * Generate a unique identifier for a
     * <code>WebContinuation</code>. The identifier is generated using a
     * cryptographically strong algorithm to prevent people to generate
     * their own identifiers.
     *
     * <p>It has the side effect of interning the continuation object in
     * the <code>idToWebCont</code> hash table.
     *
     * @param wk a <code>WebContinuation</code> object for which the
     * identifier should be generated.
     * @return the <code>String</code> identifier of the
     * <code>WebContinuation</code>
     */
    public String generateContinuationId(WebContinuation wk)
    {
      char[] result = new char[bytes.length * 2];
      String continuationId = null;
      
      while (true) {
        random.nextBytes(bytes);
      
        for (int i = 0; i < CONTINUATION_ID_LENGTH; i++) {
          byte ch = bytes[i];      
          result[2 * i] = Character.forDigit(Math.abs(ch >> 4), 16);
          result[2 * i + 1] = Character.forDigit(Math.abs(ch & 0x0f), 16);
        }
        continuationId = new String(result);
        synchronized(idToWebCont) {
          if (!idToWebCont.containsKey(continuationId)) {
            idToWebCont.put(continuationId, wk);
            break;
          }
        }
      }
  
      return continuationId;
    }
  
    public void displayAllContinuations()
    {
      Iterator iter = forrest.iterator();
      while (iter.hasNext())
        ((WebContinuation)iter.next()).display();
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/Interpreter.java
  
  Index: Interpreter.java
  ===================================================================
  package org.apache.cocoon.components.flow;
  
  import java.util.List;
  import java.util.Map;
  import org.apache.cocoon.components.treeprocessor.CategoryNode;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.Source;
  
  /**
   * The interface to the flow scripting languages. This interface is
   * for a component, which implements the appropriate language to be
   * used for describing the flow. A system could have multiple
   * components that implement this interface, each of them for a
   * different scripting language.
   *
   * <p>A flow script defines what is the page flow in an interactive
   * Web application. Usually the flow is defined in a high level
   * programming language which provides the notion of continuations,
   * which allows for the flow of the application to be described as a
   * simple procedural program, without having to think about the
   * application as a finite state machine which changes its internal
   * state on each HTTP request from the client browser.
   *
   * <p>However an implementation may choose to use its own
   * representation of an application, which may include XML
   * representations of finite state machines. Note: this API has no
   * provision for such implementations.
   *
   * <p>The component represented by this interface is called in three
   * situations:
   *
   * <ul>
   *
   *  <li>
   *
   *    <p>From the sitemap, to invoke a top level function defined in a
   *    * given implementation language of the flow. This is done from
   *    the * sitemap using the construction:
   *
   *    <pre>
   *      &lt;map:call function="..." language="..."/&gt;
   *    </pre>
   *
   *    <p>The <code>language</code> attribute can be ignored if the *
   *    default language is used.
   *
   *  <li>
   *
   *    <p>From the sitemap, to continue a previously started
   *    computation. A previously started computation is saved in the
   *    form of a continuation inside the flow implementation language.
   *
   *    <p>This case is similar with the above one, but the function
   *    invoked has a special name, specific to each language
   *    implementation. See the language implementation for more
   *    information on the function name and the arguments it receives.
   *
   *  <li>
   *
   *    <p>From a program in the flow layer. This is done to invoke a
   *    pipeline defined in the sitemap, to generate the response of the
   *    request.
   *
   * </ul>
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since March 11, 2002
   * @see InterpreterSelector
   */
  public interface Interpreter
  {
    public static class Argument
    {
      public String name;
      public String value;
  
      public Argument(String name, String value)
      {
        this.name = name;
        this.value = value;
      }
  
      public String toString()
      {
        return name + ": " + value;
      }
    }
  
    public static String ROLE = "org.apache.cocoon.components.flow.Interpreter";
  
    /**
     * Load a script given its location.
     *
     * @param environment an <code>Environment</code> value
     * @param source a <code>String</code> value
     * @return a <code>Source</code> value
     * @exception Exception if an error occurs
     */
    Source readScript(Environment environment, String source)
      throws Exception;
  
    /**
     * This method is called from the sitemap, using the syntax
     *
     * <pre>
     *   &lt;map:call function="..."/&gt;
     * </pre>
     *
     * The method will execute the named function, which must be defined
     * in the given language. There is no assumption made on how various
     * arguments are passed to the function.
     *
     * <p>The <code>params</code> argument is a <code>List</code> object
     * that contains <code>Interpreter.Argument</code> instances,
     * representing the parameters to be passed to the called
     * function. An <code>Argument</code> instance is a key-value pair,
     * where the key is the name of the parameter, and the value is its
     * desired value. Most languages will ignore the name value and
     * simply pass to the function, in a positional order, the values of
     * the argument. Some languages however can pass the arguments in a
     * different order than the original prototype of the function. For
     * these languages the ability to associate the actual argument with
     * a formal parameter using its name is essential.
     *
     * <p>A particular language implementation may decide to put the
     * environment, request, response etc. objects in the dynamic scope
     * available to the function at the time of the call. Other
     * implementations may decide to pass these as arguments to the
     * called function.
     *
     * <p>The current implementation assumes the sitemap implementation
     * is TreeProcessor.
     *
     * @param funName a <code>String</code> value, the name of the
     * function to call
     * @param params a <code>List</code> object whose components are
     * CallFunctionNode.Argument instances. The interpretation of the
     * parameters is left to the actual implementation of the
     * interpreter.
     * @param env an <code>Environment</code> value
     */
    void callFunction(String funName, List params, Environment env)
      throws Exception;
  
    /**
     * Forward the request to a Cocoon pipeline.
     *
     * @param URI a <code>String</code>, the URI of the forwarded request
     * @param bizData an <code>Object</code>, the business data object
     * to be made available to the forwarded pipeline
     * @param continuation a <code>WebContinuation</code>, the
     * continuation to be called to resume the processing
     * @param environment an <code>Environment</code>, the Cocoon environment
     * invocation context.
     * @exception Exception if an error occurs
     */
    void forwardTo(String uri, Object bizData, WebContinuation continuation,
                   Environment environment)
      throws Exception;
  
    /**
     * Continues a previously started processing. The continuation
     * object where the processing should start from is indicated by the
     * <code>continuationId</code> string.
     *
     * @param continuationId a <code>String</code> value
     *
     * @param params a <code>List</code> value, containing the
     * parameters to be passed when invoking the continuation. As
     * opposed to the parameters passed by <code>callFunction</code>,
     * these parameters will only become available in the language's
     * environment, if at all.
     *
     * @param environment an <code>Environment</code> value
     * @exception Exception if an error occurs
     */
    void handleContinuation(String continuationId, List params,
                            Environment environment)
      throws Exception;
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/InterpreterSelector.java
  
  Index: InterpreterSelector.java
  ===================================================================
  package org.apache.cocoon.components.flow;
  
  import org.apache.avalon.excalibur.component.ExcaliburComponentSelector;
  import org.apache.avalon.framework.component.ComponentException;
  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;
  
  public class InterpreterSelector extends ExcaliburComponentSelector
    implements Configurable, ThreadSafe
  {
    String defaultLanguage;
  
    public void configure(Configuration config)
      throws ConfigurationException
    {
      super.configure(config);
  
      defaultLanguage = config.getAttribute("default", null);
      boolean reloadScripts = config.getAttributeAsBoolean("reload-scripts", false);
      long checkTime = config.getAttributeAsLong("check-time", 1000L);
  
      // Finish the initialization of the already created components
      Configuration[] configurations = config.getChildren("component-instance");
      if (configurations.length == 0)
        throw new ConfigurationException("No languages defined!");
  
      for (int i = 0; i < configurations.length; i++) {
        Configuration conf = configurations[i];
        String hint = conf.getAttribute("name").trim();
        Interpreter interp;
  
        try {
          interp = (Interpreter)this.select(hint);
        }
        catch (ComponentException ex) {
          throw new ConfigurationException("Could not find component for hint "
                                           + hint + ": " + ex.toString());
        }
  
        if (interp instanceof AbstractInterpreter) {
          ((AbstractInterpreter)interp).setReloadScripts(reloadScripts);
          ((AbstractInterpreter)interp).setCheckTime(checkTime);
        }
  
        if (i == 0 && defaultLanguage == null)
          defaultLanguage = hint;
      }
    }
  
    public String getDefaultLanguage()
    {
      return defaultLanguage;
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/ScriptSource.java
  
  Index: ScriptSource.java
  ===================================================================
  package org.apache.cocoon.components.flow;
  
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.ModifiableSource;
  import org.apache.cocoon.environment.Source;
  
  /**
   * Representation of a source in a scripting language. Loosely modeled
   * after Source and ModifiableSource, but doesn't carry all the XML
   * baggage which is not needed here.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since March 16, 2002
   */
  public class ScriptSource
  {
    Interpreter interp;
    String sourceName;
    Source source;
    boolean isModifiable;
  
    public ScriptSource(Interpreter interp, String sourceName)
    {
      this.interp = interp;
      this.sourceName = sourceName;
    }
  
    public String getSourceName()
    {
      return sourceName;
    }
  
    public long getLastModified()
    {
      if (isModifiable)
        ((ModifiableSource)source).refresh();
  
      return source == null ? 0 : source.getLastModified();
    }
  
    public void refresh(Environment environment)
      throws Exception
    {
      source = interp.readScript(environment, sourceName);
      isModifiable = source instanceof ModifiableSource;
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/WebContinuation.java
  
  Index: WebContinuation.java
  ===================================================================
  package org.apache.cocoon.components.flow;
  
  import java.lang.Comparable;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.List;
  
  /**
   * Representation of continuations in a Web environment.
   *
   * <p>Because a user may click on the back button of the browser and
   * restart a saved computation in a continuation, each
   * <code>WebContinuation</code> becomes the parent of a subtree of
   * continuations.
   *
   * <p>If there is no parent <code>WebContinuation</code>, the created
   * continuation becomes the root of a tree of
   * <code>WebContinuation</code>s.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since March 19, 2002
   */
  public class WebContinuation
    implements Comparable
  {
    /**
     * The continuation this object represents.
     */
    protected Object continuation;
  
    /**
     * The parent <code>WebContinuation</code> from which processing
     * last started. If null, there is no parent continuation
     * associated, and this is the first one to be created in a
     * processing. In this case this <code>WebContinuation</code>
     * instance becomes the root of the tree maintained by the
     * <code>ContinuationsManager</code>.
     *
     * @see ContinuationsManager
     */
    protected WebContinuation parentContinuation;
  
    /**
     * The children continuations. These are continuations created by
     * resuming the processing from the point stored by
     * <code>continuation</code>.
     */
    protected List children = new ArrayList();
  
    /**
     * The continuation id used to represent this instance in Web pages.
     */
    protected String id;
  
    /**
     * A user definable object. This is present for convenience, to
     * store any information associated with this
     * <code>WebContinuation</code> a particular implementation might
     * need.
     */
    protected Object userObject;
  
    /**
     * When was this continuation accessed last time. Each time the
     * continuation is accessed, this time is set to the time of the
     * access.
     */
    protected long lastAccessTime;
  
    /**
     * Indicates how long does this continuation will live (in
     * seconds). The continuation will be removed once the current time
     * is bigger than <code>lastAccessTime + timeToLive</code>.
     */
    protected int timeToLive;
  
    /**
     * Create a <code>WebContinuation</code> object. Saves the object in
     * the hash table of continuations maintained by
     * <code>manager</code> (this is done as a side effect of obtaining
     * and identifier from it).
     *
     * @param continuation an <code>Object</code> value
     * @param parentContinuation a <code>WebContinuation</code> value
     * @param manager a <code>ContinuationsManagerImpl</code> value
     */
    public WebContinuation(Object continuation,
                           WebContinuation parentContinuation,
                           ContinuationsManagerImpl manager,
                           int timeToLive)
    {
      this.continuation = continuation;
      this.parentContinuation = parentContinuation;
      id = manager.generateContinuationId(this);
      this.timeToLive = timeToLive;
      if (parentContinuation != null)
        this.parentContinuation.children.add(this);
    }
  
    /**
     * Return the continuation object.
     *
     * @return an <code>Object</code> value
     */
    public Object getContinuation()
    {
      updateLastAccessTime();
      return continuation;
    }
  
    /**
     * Return the ancestor continuation situated <code>level</code>s
     * above the current continuation. The current instance is
     * considered to be at level 0. The parent continuation of the
     * receiving instance at level 1, its parent is at level 2 relative
     * to the receiving instance. If <code>level</code> is bigger than
     * the depth of the tree, the root of the tree is returned.
     *
     * @param level an <code>int</code> value
     * @return a <code>WebContinuation</code> value
     */
    public WebContinuation getContinuation(int level)
    {
      if (level <= 0) {
        updateLastAccessTime();
        return this;
      }
      else if (parentContinuation == null)
        return this;
      else
        return parentContinuation.getContinuation(level - 1);
    }
  
    /**
     * Return the parent <code>WebContinuation</code>. Equivalent with
     * <code>getContinuation(1)</code>.
     *
     * @return a <code>WebContinuation</code> value
     */
    public WebContinuation getParentContinuation()
    {
      return parentContinuation;
    }
  
    /**
     * Return the children <code>WebContinuation</code> which were
     * created as a result of resuming the processing from the current
     * <code>continuation</code>.
     *
     * @return a <code>List</code> value
     */
    public List getChildren()
    {
      return children;
    }
  
    /**
     * Returns the string identifier of this
     * <code>WebContinuation</code>.
     *
     * @return a <code>String</code> value
     */
    public String getId()
    {
      return id;
    }
  
    /**
     * Sets the user object associated with this instance.
     *
     * @param obj an <code>Object</code> value
     */
    public void setUserObject(Object obj)
    {
      this.userObject = obj;
    }
  
    /**
     * Obtains the user object associated with this instance.
     *
     * @return an <code>Object</code> value
     */
    public Object getUserObject()
    {
      return userObject;
    }
  
    /**
     * Returns the hash code of the associated identifier.
     *
     * @return an <code>int</code> value
     */
    public int hashCode()
    {
      return id.hashCode();
    }
  
    /**
     * True if the identifiers are the same, false otherwise.
     *
     * @param another an <code>Object</code> value
     * @return a <code>boolean</code> value
     */
    public boolean equals(Object another)
    {
      if (another instanceof WebContinuation)
        return id.equals(((WebContinuation)another).id);
      return false;
    }
  
    /**
     * Compares the expiration time of this instance with that of the
     * WebContinuation passed as argument.
     *
     * <p><b>Note:</b> this class has a natural ordering that is
     * inconsistent with <code>equals</code>.</p>.
     *
     * @param other an <code>Object</code> value, which should be a
     * <code>WebContinuation</code> instance
     * @return an <code>int</code> value
     */
    public int compareTo(Object other)
    {
      WebContinuation wk = (WebContinuation)other;
      return (int)((lastAccessTime + timeToLive)
                   - (wk.lastAccessTime + wk.timeToLive));
    }
  
    /**
     * Debugging method.
     *
     * <p>Assumes the receiving instance as the root of a tree and
     * displays the tree of continuations.
     */
    public void display()
    {
      display(0);
    }
  
    /**
     * Debugging method.
     *
     * <p>Displays the receiving instance as if it is at the
     * <code>indent</code> depth in the tree of continuations. Each
     * level is indented 2 spaces.
     *
     * @param depth an <code>int</code> value
     */
    protected void display(int depth)
    {
      StringBuffer buf = new StringBuffer();
      for (int i = 0; i < depth; i++)
        buf.append("  ");
      String spaces = buf.toString();
  
      System.out.print(spaces); System.out.println("WebContinuation " + id);
  
      int size = children.size();
      depth++;
      for (int i = 0; i < size; i++)
        ((WebContinuation)children.get(i)).display(depth);
    }
  
    /**
     * Update the continuation in the 
     */
    protected void updateLastAccessTime()
    {
      lastAccessTime = (new Date()).getTime();
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/flow.xconf
  
  Index: flow.xconf
  ===================================================================
  <?xml version="1.0"?>
  
  <xconf xpath="/cocoon"
         unless="comment()[contains(., 'Flow interpreter')]">
  
    <!-- Flow interpreter support.
  
      The attributes recognized by the <flow-interpreters> element are:
  
        default (string value):
  
           the default interpreted language assumed for <map:script>
           elements which do not specify a "language" attribute. If not
           present, the first language that's described within the
           <flow-interpreters> element is assumed to be the default
           language.
  
        reload-scripts (boolean value, default false):
  
           whether to check if the scripts source files are
           modified. Checking for modification is an expensive
           operation, so leave it disabled in a production
           environment. If not present it is assumed to be "false". When
           "true" *all* script files are checked for modification on
           each function invocation done using <map:call
           function="...">, but not more frequent than the value of
           "check-time" (see below).
  
        check-time (long value, default 1000):
  
           time in miliseconds between the checks for the last
           modification date of script files.
  
      Within <flow-interpreters> only <component-instance> elements are
      recognized. The attributes recognized by this element are "name"
      and "class". "name" specifies the name of a scripting language,
      and "class" defines the Java class that implements it. See
      org.apache.cocoon.components.flow.Interpreter for the Cocoon
      interface with an scripting language interpreter.
  
    -->
  
    <flow-interpreters default="JavaScript"
                       reload-scripts="true"
                       check-time="2000"
                       logger="flow">
  
      <component-instance name="JavaScript"
                          
class="org.apache.cocoon.components.flow.javascript.JavaScriptInterpreter">
        
<load-on-startup>resource://org/apache/cocoon/components/flow/javascript/system.js</load-on-startup>
      </component-instance>
  
  <!--
    Temporarily disable Scheme, until full support is completed
  -->
  
  <!--
      <component-instance name="Scheme"
                          
class="org.apache.cocoon.components.flow.scheme.SchemeInterpreter">
        
<load-on-startup>resource://org/apache/cocoon/components/flow/scheme/system.scm</load-on-startup>
        <heap>/WEB-INF/sisc.heap</heap>
      </component-instance>
  -->
  
    </flow-interpreters>
  
    <continuations time-to-live="3600"/>
  
  </xconf>
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/JSCocoon.java
  
  Index: JSCocoon.java
  ===================================================================
  package org.apache.cocoon.components.flow.javascript;
  
  import java.util.HashMap;
  import java.util.Map;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.cocoon.components.flow.AbstractInterpreter;
  import org.apache.cocoon.components.flow.ContinuationsManager;
  import org.apache.cocoon.components.flow.ContinuationsManagerImpl;
  import org.apache.cocoon.environment.Context;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.ObjectModelHelper;
  import org.apache.cocoon.environment.Request;
  import org.apache.cocoon.environment.Response;
  import org.apache.cocoon.environment.Session;
  import org.mozilla.javascript.NativeArray;
  import org.mozilla.javascript.ScriptRuntime;
  import org.mozilla.javascript.Scriptable;
  import org.mozilla.javascript.ScriptableObject;
  import org.mozilla.javascript.Undefined;
  import org.mozilla.javascript.Wrapper;
  
  /**
   * JavaScript interface to various Cocoon abstractions.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since March 16, 2002
   */
  public class JSCocoon extends ScriptableObject
  {
    protected AbstractInterpreter interp;
    protected Scriptable scope;
    protected NativeArray parameters;
    protected Environment environment;
    protected ComponentManager manager;
  
    public JSCocoon() {}
  
    public String getClassName()
    {
      return "Cocoon";
    }
  
    public void setScope(Scriptable scope)
    {
      this.scope = scope;
    }
  
    public Scriptable getScope()
    {
      return scope;
    }
  
    public void setParameters(NativeArray parameters)
    {
      this.parameters = parameters;
    }
  
    public void setInterpreter(AbstractInterpreter interp)
    {
      this.interp = interp;
    }
  
    public void setContext(ComponentManager manager, Environment environment)
    {
      this.manager = manager;
      this.environment = environment;
    }
  
    public void invalidateContext()
    {
      manager = null;
      environment = null;
    }
  
    public NativeArray jsGet_parameters()
    {
      return parameters;
    }
  
    public AbstractInterpreter jsGet_interpreter()
    {
      return interp;
    }
  
    public Environment jsGet_environment()
    {
      return environment;
    }
  
    public Request jsGet_request()
    {
      Map objectModel = environment.getObjectModel();
      return ObjectModelHelper.getRequest(objectModel);
    }
  
    public Response jsGet_response()
    {
      Map objectModel = environment.getObjectModel();
      return ObjectModelHelper.getResponse(objectModel);
    }
  
    public Session jsGet_session()
    {
      return jsGet_request().getSession();
    }
  
    public Context jsGet_context()
    {
      Map objectModel = environment.getObjectModel();
      return ObjectModelHelper.getContext(objectModel);
    }
  
    /**
     * Load the file specified as argument. Registers the file with the
     * interpreter and then forces its loading by calling {@link
     * AbstractInterpreter#checkForModifiedScripts}.
     *
     * @param filename a <code>String</code> value
     * @return an <code>Object</code> value
     * @exception Exception if an error occurs
     */
    public Object jsFunction_load(String filename)
      throws Exception
    {
      try {
        interp.register(filename);
        interp.checkForModifiedScripts(environment);
      }
      catch (Exception ex) {
        ex.printStackTrace();
        throw ex;
      }
      return null;
    }
  
    public String jsFunction_toString()
    {
      return "[object " + toString() + "]";
    }
  
    public void jsFunction_forwardTo(String uri, Object bizData, Object cont)
      throws Exception
    {
      if (bizData instanceof Wrapper)
        bizData = ((Wrapper)bizData).unwrap();
      else if (bizData instanceof Scriptable)
        bizData = jsobjectToMap((Scriptable)bizData);
  
      JSWebContinuation kont = (JSWebContinuation)cont;
  
      interp.forwardTo(uri, bizData, kont.getWebContinuation(), environment);
    }
  
    public void jsFunction_diplayAllContinuations()
      throws ComponentException
    {
      ContinuationsManager continuationsMgr
        = (ContinuationsManager)manager.lookup(ContinuationsManager.ROLE);
  
      try {
        if (continuationsMgr instanceof ContinuationsManagerImpl)
          ((ContinuationsManagerImpl)continuationsMgr).displayAllContinuations();
      }
      finally {
        manager.release((Component)continuationsMgr);
      }
    }
  
    // All right, this breaks the encapsulation, but I couldn't find any
    // better way to obtain the ComponentManager for a
    // JSWebContinuation.
    ComponentManager getComponentManager()
    {
      return manager;
    }
  
    public static Map jsobjectToMap(Scriptable jsobject)
    {
      HashMap hash = new HashMap();
      Object[] ids = jsobject.getIds();
      for (int i = 0; i < ids.length; i++) {
        String key = ScriptRuntime.toString(ids[i]);
        Object value = jsobject.get(key, jsobject);
        if (value == Undefined.instance)
          value = null;
        else
          value = ScriptRuntime.toPrimitive(value);
        hash.put(key, value);
      }
      return hash;
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/JSGlobal.java
  
  Index: JSGlobal.java
  ===================================================================
  package org.apache.cocoon.components.flow.javascript;
  
  import org.mozilla.javascript.Context;
  import org.mozilla.javascript.tools.shell.Global;
  
  /**
   * Functions and variables in the global JavaScript scope.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since March 17, 2002
   */
  public class JSGlobal extends Global
  {
    public JSGlobal(Context cx)
    {
      super(cx);
    }
  
    public String getClassName()
    {
      return "Global";
    }
  
    public static void print(String message)
    {
      System.out.println(message);
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/JSLog.java
  
  Index: JSLog.java
  ===================================================================
  package org.apache.cocoon.components.flow.javascript;
  
  import org.apache.log.Logger;
  import org.mozilla.javascript.ScriptableObject;
  
  /**
   * JavaScript interface to the Cocoon log facility.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since March 16, 2002
   */
  public class JSLog extends ScriptableObject
  {
    Logger logger;
  
    public JSLog() {}
  
    public String getClassName()
    {
      return "Log";
    }
  
    public void setLogger(Logger logger)
    {
      this.logger = logger;
    }
  
    public void jsFunction_debug(String message)
    {
      logger.debug(message);
    }
  
    public void jsFunction_info(String message)
    {
      logger.info(message);
    }
  
    public void jsFunction_warn(String message)
    {
      logger.warn(message);
    }
  
    public void jsFunction_error(String message)
    {
      logger.error(message);
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/JSWebContinuation.java
  
  Index: JSWebContinuation.java
  ===================================================================
  package org.apache.cocoon.components.flow.javascript;
  
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.cocoon.components.flow.ContinuationsManager;
  import org.apache.cocoon.components.flow.WebContinuation;
  import org.mozilla.javascript.Context;
  import org.mozilla.javascript.Function;
  import org.mozilla.javascript.Scriptable;
  import org.mozilla.javascript.ScriptableObject;
  import org.mozilla.javascript.Undefined;
  
  public class JSWebContinuation extends ScriptableObject
  {
    protected JSCocoon cocoon;
    protected WebContinuation wk;
    protected ContinuationsManager continuationsMgr;
  
    public JSWebContinuation() {}
  
    public String getClassName()
    {
      return "WebContinuation";
    }
  
    public JSCocoon getJSCocoon()
    {
      return cocoon;
    }
  
    public WebContinuation getWebContinuation()
    {
      return wk;
    }
  
    public static Scriptable jsConstructor(Context cx, Object[] args,
                                           Function ctorObj, 
                                           boolean inNewExpr)
      throws Exception
    {
      JSCocoon cocoon = (JSCocoon)args[0];
      ComponentManager manager = cocoon.getComponentManager();
  
      ContinuationsManager contMgr
        = (ContinuationsManager)manager.lookup(ContinuationsManager.ROLE);
  
      Object kont = args[1];
      JSWebContinuation pjswk = (JSWebContinuation)args[2];
      WebContinuation pwk = (pjswk == null ? null : pjswk.wk);
  
      int ttl;
  
      if (args[3] == Undefined.instance)
        ttl = 0;
      else {
        Number timeToLive = (Number)args[3];
        ttl = (timeToLive == null ? 0 : timeToLive.intValue());
      }
  
      JSWebContinuation jswk = new JSWebContinuation();
      WebContinuation wk
        = contMgr.createWebContinuation(kont, pwk, ttl);
      wk.setUserObject(jswk);
  
      jswk.cocoon = cocoon;
      jswk.wk = wk;
      jswk.continuationsMgr = contMgr;
  
      return jswk;
    }
  
    public String jsGet_id()
    {
      return wk.getId();
    }
  
    public Object jsGet_continuation()
    {
      return wk.getContinuation();
    }
  
    public void jsFunction_invalidate()
    {
      continuationsMgr.invalidateWebContinuation(wk);
    }
  
    public void jsFunction_display()
    {
      wk.display();
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/JavaScriptInterpreter.java
  
  Index: JavaScriptInterpreter.java
  ===================================================================
  package org.apache.cocoon.components.flow.javascript;
  
  import java.io.BufferedReader;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.Reader;
  import java.util.List;
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.cocoon.components.flow.Interpreter;
  import org.apache.cocoon.components.flow.AbstractInterpreter;
  import org.apache.cocoon.components.flow.WebContinuation;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.Source;
  import org.mozilla.javascript.Context;
  import org.mozilla.javascript.Function;
  import org.mozilla.javascript.NativeArray;
  import org.mozilla.javascript.PropertyException;
  import org.mozilla.javascript.ScriptRuntime;
  import org.mozilla.javascript.Scriptable;
  import org.mozilla.javascript.ScriptableObject;
  
  /**
   * Interface with the JavaScript interpreter.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since March 25, 2002
   */
  public class JavaScriptInterpreter extends AbstractInterpreter
    implements Configurable, Initializable
  {
    // This is the only optimization level that supports continuations
    // in the Christoper Oliver's Rhino JavaScript implementation
    static int OPTIMIZATION_LEVEL = -2;
  
    JSGlobal scope;
  
    public void configure(Configuration config)
    {
      String loadOnStartup
        = config.getChild("load-on-startup", true).getValue(null);
      if (loadOnStartup != null)
        register(loadOnStartup);
    }
  
    public void initialize()
      throws Exception
    {
      Context context = Context.enter();
      context.setOptimizationLevel(OPTIMIZATION_LEVEL);
  
      try {
        scope = new JSGlobal(context);
  
        // Register some handy classes with JavaScript, so we can make
        // use of them from the flow layer.
  
        // Access to the Cocoon log
        ScriptableObject.defineClass(scope, JSLog.class);
  
        // Access to Cocoon internal objects
        ScriptableObject.defineClass(scope, JSCocoon.class);
  
        // Wrapper for WebContinuation
        ScriptableObject.defineClass(scope, JSWebContinuation.class);
  
        // Define some functions on the top level scope
        String[] names = { "print" };
        try {
          ((ScriptableObject)scope)
            .defineFunctionProperties(names, JSGlobal.class,
                                      ScriptableObject.DONTENUM);
        }
        catch (PropertyException e) {
          throw new Error(e.getMessage());
        }
  
        // Define some global variables in JavaScript
        Object args[] = {};
        Scriptable log = context.newObject(scope, "Log", args);
        ((JSLog)log).setLogger(getLogger());
        scope.put("log", scope, log);
  
      }
      catch (Exception e) {
        context.exit();
        System.out.println("problem initializing JavaScriptInterpreter: ");
        e.printStackTrace();
        throw e;
      }
    }
  
    protected Scriptable enterContext(Environment environment)
      throws Exception
    {
      Context context = Context.enter();
      context.setOptimizationLevel(OPTIMIZATION_LEVEL);
      context.setCompileFunctionsWithDynamicScope(true);
      Scriptable thrScope = context.newObject(scope);
  
      thrScope.setPrototype(scope);
      // We want 'thrScope' to be a new top-level scope, so set its
      // parent scope to null. This means that any variables created
      // by assignments will be properties of "thrScope".
      thrScope.setParentScope(null);
  
      // Put in the thread scope the Cocoon object, which gives access
      // to the interpreter object, and some Cocoon objects. See
      // JSCocoon for more details.
      Object args[] = {};
      Scriptable cocoon = context.newObject(scope, "Cocoon", args);
      ((JSCocoon)cocoon).setInterpreter(this);
      ((JSCocoon)cocoon).setContext(manager, environment);
      ((JSCocoon)cocoon).setScope(thrScope);
      thrScope.put("cocoon", thrScope, cocoon);
  
      return thrScope;
    }
  
    /**
     * Remove the Cocoon object from the JavaScript thread scope so it
     * can be garbage collected, together with all the objects it
     * contains.
     */
    protected void exitContext(Scriptable thrScope)
    {
      JSCocoon cocoon = (JSCocoon)thrScope.get("cocoon", thrScope);
      cocoon.invalidateContext();
      Context.getCurrentContext().exit();
    }
  
    public Source readScript(Environment environment, String sourceName)
      throws Exception
    {
      Scriptable thrScope = null;
      Source source = null;
  
      System.out.println("Reading file " + sourceName);
  
      try {
        thrScope = enterContext(environment);
        source = environment.resolve(sourceName);
        InputStream inputStream = source.getInputStream();
        Reader reader = new BufferedReader(new InputStreamReader(inputStream));
        Context ctx = Context.getCurrentContext();
        ctx.evaluateReader(scope, reader, sourceName, 1, null);
      }
      catch (Exception ex) {
        ex.printStackTrace();
        throw ex;
      }
      finally {
        exitContext(thrScope);
      }
      return source;
    }
  
    /**
     * Calls a JavaScript function, passing <code>params</code> as its
     * arguments. In addition to this, it makes available the parameters
     * through the <code>cocoon.parameters</code> JavaScript array
     * (indexed by the parameter names).
     *
     * @param funName a <code>String</code> value
     * @param params a <code>List</code> value
     * @param environment an <code>Environment</code> value
     * @exception Exception if an error occurs
     */
    public void callFunction(String funName, List params,
                             Environment environment)
      throws Exception
    {
      Scriptable thrScope = null;
  
      checkForModifiedScripts(environment);
  
      try {
        thrScope = enterContext(environment);
        JSCocoon cocoon = (JSCocoon)thrScope.get("cocoon", thrScope);
  
        Object callFunction = scope.get("callFunction", thrScope);
        if (callFunction == Scriptable.NOT_FOUND)
            throw new RuntimeException("Cannot find 'callFunction' "
                                       + "(system.js not loaded?)");
  
        Object fun = scope.get(funName, thrScope);
        if (fun == Scriptable.NOT_FOUND)
          throw new RuntimeException("'" + funName + "' is undefined!");
        if (!(fun instanceof Function))
          throw new RuntimeException("'" + funName + "' is not a function!");
  
        Context cx = Context.getCurrentContext();
  
        int size = (params != null ? params.size() : 0);
        Object[] funArgs = new Object[size];
        NativeArray funArgsArray = new NativeArray(funArgs);
        NativeArray parameters = new NativeArray(size);
  
        if (size != 0) {
          for (int i = 0; i < size; i++) {
            Interpreter.Argument arg = (Interpreter.Argument)params.get(i);
            funArgs[i] = ScriptRuntime.toObject(cx, thrScope, arg.value);
            parameters.put(arg.name, parameters, arg.value);
          }
        }
  
        cocoon.setParameters(parameters);
        Object callFunArgs[] = { fun, funArgsArray };
        ((Function) callFunction).call(cx, thrScope, thrScope, callFunArgs);
      }
      finally {
        exitContext(thrScope);
      }
    }
  
    public void handleContinuation(String id, List params,
                                   Environment environment)
      throws Exception
    {
      WebContinuation wk = continuationsMgr.lookupWebContinuation(id);
  
      if (wk == null)
        throw new RuntimeException("No continuation with id " + id);
  
      Context context = Context.enter();
      context.setOptimizationLevel(OPTIMIZATION_LEVEL);
      context.setCompileFunctionsWithDynamicScope(true);
  
      // Obtain the JS continuation object from it, and setup the
      // JSCocoon object associated in the dynamic scope of the saved
      // continuation with the environment and context objects.
      JSWebContinuation jswk = (JSWebContinuation)wk.getUserObject();
      JSCocoon cocoon = jswk.getJSCocoon();
      cocoon.setContext(manager, environment);
      Scriptable kScope = cocoon.getScope();
  
      // We can now resume the processing from the state saved by the
      // continuation object. Setup the JavaScript Context object.
      Object handleContFunction = scope.get("handleContinuation", kScope);
      if (handleContFunction == Scriptable.NOT_FOUND)
        throw new RuntimeException("Cannot find 'handleContinuation' "
                                   + "(system.js not loaded?)");
  
      Object args[] = { jswk };
  
      int size = (params != null ? params.size() : 0);
      NativeArray parameters = new NativeArray(size);
  
      if (size != 0) {
        for (int i = 0; i < size; i++) {
          Interpreter.Argument arg = (Interpreter.Argument)params.get(i);
          parameters.put(arg.name, parameters, arg.value);
        }
      }
  
      cocoon.setParameters(parameters);
  
      try {
        ((Function)handleContFunction).call(context, kScope, kScope, args);
      }
      finally {
        context.exit();
      }
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/system.js
  
  Index: system.js
  ===================================================================
  // system.js
  //
  // JavaScript definitions
  //
  // Author: Ovidiu Predescu <[EMAIL PROTECTED]>
  // Date: March 19, 2002
  //
  
  var suicide;
  
  var lastContinuation = null;
  
  function callFunction(func, args)
  {
    suicide = new Continuation();
    return func.apply(this, args);
  }
  
  function sendPage(uri, bizData, timeToLive)
  {
    var kont = _sendPage(uri, bizData, timeToLive);
    lastContinuation = kont;
    return kont;
  }
  
  function _sendPage(uri, bizData, timeToLive)
  {
    var k = new Continuation();
    var kont = new WebContinuation(cocoon, k, lastContinuation, timeToLive);
    cocoon.forwardTo(uri, bizData, kont);
    suicide();
  }
  
  function sendPageAndContinue(uri, bizData)
  {
    cocoon.forwardTo(uri, bizData, null);
  }
  
  function handleContinuation(kont)
  {
    kont.continuation(kont);
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/language/markup/xsp/java/jpath.xsl
  
  Index: jpath.xsl
  ===================================================================
  <?xml version="1.0" encoding="utf-8"?>
  
  <!--
    Author: Ovidiu Predescu "[EMAIL PROTECTED]"
  
    Date: February 15, 2002
   -->
  
  <xsl:stylesheet
    version="1.0"
    xmlns:xsp="http://apache.org/xsp";
    xmlns:jpath="http://apache.org/xsp/jpath/1.0";
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  
    <xsl:template match="xsp:page">
      <xsp:page>
        <xsl:apply-templates select="@*"/>
        <xsp:structure>
          <xsp:include>java.util.List</xsp:include>
          <xsp:include>java.util.Iterator</xsp:include>
          <xsp:include>org.apache.cocoon.environment.Environment</xsp:include>
          <xsp:include>org.apache.cocoon.components.flow.WebContinuation</xsp:include>
          <xsp:include>org.apache.commons.jxpath.JXPath</xsp:include>
          <xsp:include>org.apache.commons.jxpath.JXPathContext</xsp:include>
          <xsp:include>org.apache.commons.jxpath.CompiledExpression</xsp:include>
        </xsp:structure>
  
        <xsp:logic>
        </xsp:logic>
  
        <xsp:init-page>
          Object bean = ((Environment)resolver).getAttribute("bean-dict");
          WebContinuation kont
            = (WebContinuation)((Environment)resolver).getAttribute("kont");
          JXPathContext jxpathContext = JXPathContext.newContext(bean);
          // Generate the compiled representation of the JXPath
          // expressions used by this page.
          <xsl:apply-templates mode="compile"/>
        </xsp:init-page>
  
        <xsl:apply-templates/>
      </xsp:page>
    </xsl:template>
  
    <xsl:template match="jpath:if | jpath:when" mode="compile">
      <xsl:variable name="var-name">
        <xsl:call-template name="get-var-name">
          <xsl:with-param name="expr" select="@test"/>
        </xsl:call-template>
      </xsl:variable>
      CompiledExpression <xsl:value-of select="$var-name"/>
        = jxpathContext.compile("<xsl:value-of select="@test"/>");
      <xsl:apply-templates select="jpath:*" mode="compile"/>
    </xsl:template>
  
    <xsl:template match="jpath:for-each | jpath:value-of" mode="compile">
      <xsl:variable name="var-name">
        <xsl:call-template name="get-var-name">
          <xsl:with-param name="expr" select="@select"/>
        </xsl:call-template>
      </xsl:variable>
      CompiledExpression <xsl:value-of select="$var-name"/>
        = jxpathContext.compile("<xsl:value-of select="@select"/>");
      <xsl:apply-templates select="jpath:*" mode="compile"/>
    </xsl:template>
  
    <xsl:template match="*|@*|text()|processing-instruction()" mode="compile">
      <xsl:apply-templates mode="compile"/>
    </xsl:template>
  
    <xsl:template name="get-var-name">
      <xsl:param name="expr"/>
      jxpath_<xsl:value-of select="translate($expr, 
'&#x20;&#x9;&#xA;&#xD;~`!@%^*()-+=[]{}\|,./?&gt;&lt;', '')"/>
    </xsl:template>
  
    <xsl:template match="jpath:if">
      <xsl:choose>
        <xsl:when test="@test">
          <xsp:logic>
            if (<xsl:call-template name="get-var-name">
                  <xsl:with-param name="expr" select="@test"/>
                </xsl:call-template>
                .getValue(jxpathContext) != null) {
              <xsl:apply-templates/>
            }
          </xsp:logic>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message terminate="yes">
            <xsl:text>Required 'test' attribute in &lt;jpath:if&gt; is 
missing!</xsl:text>
          </xsl:message>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>
  
  
    <xsl:template match="jpath:choose">
      <xsp:logic>
        if (0) {
        }
        <xsl:apply-templates select="jpath:when|jpath:otherwise"/>
      </xsp:logic>
    </xsl:template>
  
  
    <xsl:template match="jpath:when">
      <xsp:logic>
        else if (<xsl:call-template name="get-var-name">
                  <xsl:with-param name="expr" select="@test"/>
                </xsl:call-template>
                .getValue(jxpathContext) != null) {
          <xsl:apply-templates/>
        }
      </xsp:logic>
    </xsl:template>
  
  
    <xsl:template match="jpath:otherwise">
      <xsp:logic>
        else {
          <xsl:apply-templates/>
        }
      </xsp:logic>
    </xsl:template>
  
    <xsl:template match="jpath:for-each">
      <xsl:variable name="old-context">
        oldJPathContext<xsl:value-of select="count(ancestor-or-self::*)"/>
      </xsl:variable>
  
      <xsl:choose>
        <xsl:when test="@select">
          <xsp:logic>
            {
              List selection = <xsl:call-template name="get-var-name">
                                 <xsl:with-param name="expr" select="@test"/>
                               </xsl:call-template>
                               .eval(jxpathContext);
              if (selection != null) {
                Iterator iter = selection.iterator();
                JPathContext <xsl:value-of select="$old-context"/> = jxpathContext;
                while (iter.hasNext()) {
                  Object current = iter.next();
                  jxpathContext = JPathContext.newContext(current);
          </xsp:logic>
  
          <xsl:apply-templates/>
  
          <xsp:logic>
                }
                jxpathContext = <xsl:value-of select="$old-context"/>;
              }
            }
          </xsp:logic>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message terminate="yes">
            <xsl:text>Required 'select' attribute in &lt;jpath:for-each&gt; is 
missing!</xsl:text>
          </xsl:message>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>
  
    <xsl:template match="jpath:value-of">
      <xsp:expr>
        <xsl:choose>
          <xsl:when test="@select">
            <xsl:call-template name="get-var-name">
              <xsl:with-param name="expr" select="@select"/>
            </xsl:call-template>
            .getValue(jxpathContext)
          </xsl:when>
          <xsl:otherwise>
            <xsl:message terminate="yes">
              <xsl:text>Required 'select' attribute in &lt;jpath:value-of&gt; is 
missing!</xsl:text>
            </xsl:message>
          </xsl:otherwise>
        </xsl:choose>
      </xsp:expr>
    </xsl:template>
  
    <xsl:template match="jpath:continuation">
      <xsl:variable name="level">
        <xsl:choose>
          <xsl:when test="@select">
            <xsl:value-of select="@select"/>
          </xsl:when>
          <xsl:otherwise>0</xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      <xsp:expr>
        kont.getContinuation(<xsl:value-of select="$level"/>).getId()
      </xsp:expr>
    </xsl:template>
  
    <xsl:template match="@*|*|text()|processing-instruction()">
      <!-- Catch all template. Just pass along unmodified everything we
           don't handle. -->
      <xsl:copy>
        <xsl:apply-templates select="@*|*|text()|processing-instruction()"/>
      </xsl:copy>
    </xsl:template>
  
  
  </xsl:stylesheet>
  
  
  
  
  1.3       +4 -0      
xml-cocoon2/src/java/org/apache/cocoon/components/treeprocessor/treeprocessor-builtins.xml
  
  Index: treeprocessor-builtins.xml
  ===================================================================
  RCS file: 
/home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/treeprocessor/treeprocessor-builtins.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- treeprocessor-builtins.xml        17 Mar 2002 21:55:22 -0000      1.2
  +++ treeprocessor-builtins.xml        19 May 2002 19:19:39 -0000      1.3
  @@ -149,6 +149,10 @@
   
         <node name="serialize" 
builder="org.apache.cocoon.components.treeprocessor.sitemap.SerializeNodeBuilder"/>
   
  +      <node name="script" 
builder="org.apache.cocoon.components.treeprocessor.sitemap.ScriptNodeBuilder"/>
  +
  +      <node name="continue" 
builder="org.apache.cocoon.components.treeprocessor.sitemap.ContinueNodeBuilder"/>
  +
         <node name="handle-errors" 
builder="org.apache.cocoon.components.treeprocessor.sitemap.HandleErrorsNodeBuilder"/>
   
       </nodes>
  
  
  
  1.3       +35 -12    
xml-cocoon2/src/java/org/apache/cocoon/components/treeprocessor/sitemap/CallNodeBuilder.java
  
  Index: CallNodeBuilder.java
  ===================================================================
  RCS file: 
/home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/treeprocessor/sitemap/CallNodeBuilder.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- CallNodeBuilder.java      13 Mar 2002 17:42:22 -0000      1.2
  +++ CallNodeBuilder.java      19 May 2002 19:19:39 -0000      1.3
  @@ -50,22 +50,25 @@
   */
   package org.apache.cocoon.components.treeprocessor.sitemap;
   
  +
  +
  +import java.util.*;
  +import org.apache.avalon.framework.configuration.Configurable;
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
  -
   import org.apache.cocoon.components.treeprocessor.AbstractProcessingNodeBuilder;
   import org.apache.cocoon.components.treeprocessor.CategoryNode;
   import org.apache.cocoon.components.treeprocessor.CategoryNodeBuilder;
  -import org.apache.cocoon.components.treeprocessor.MapStackResolver;
   import org.apache.cocoon.components.treeprocessor.LinkedProcessingNodeBuilder;
  +import org.apache.cocoon.components.treeprocessor.MapStackResolver;
   import org.apache.cocoon.components.treeprocessor.ProcessingNode;
  -
  -import java.util.*;
  +import org.apache.cocoon.components.treeprocessor.sitemap.CallFunctionNode;
  +import org.apache.cocoon.components.treeprocessor.sitemap.CallNode;
   
   /**
    *
    * @author <a href="mailto:[EMAIL PROTECTED]";>Sylvain Wallez</a>
  - * @version CVS $Id: CallNodeBuilder.java,v 1.2 2002/03/13 17:42:22 ovidiu Exp $
  + * @version CVS $Id: CallNodeBuilder.java,v 1.3 2002/05/19 19:19:39 ovidiu Exp $
    */
   
   public class CallNodeBuilder extends AbstractProcessingNodeBuilder
  @@ -73,25 +76,45 @@
   
       protected ProcessingNode node;
       protected String resourceName;
  +    protected String functionName;
   
  -    public ProcessingNode buildNode(Configuration config) throws Exception {
  +    public ProcessingNode buildNode(Configuration config)
  +        throws Exception
  +    {
  +        this.resourceName = config.getAttribute("resource", null);
  +        this.functionName = config.getAttribute("function", null);
  +
  +        if (resourceName == null && functionName == null)
  +            throw new ConfigurationException("<map:call> must have either a 
'resource' or a 'function' attribute!");
  +
  +        if (resourceName != null)
  +            this.node = new CallNode();
  +        else
  +            this.node = new CallFunctionNode(functionName);
   
  -        this.resourceName = config.getAttribute("resource");
  -        this.node = new CallNode();
           this.treeBuilder.setupNode(this.node, config);
  +        if (node instanceof Configurable)
  +            ((Configurable)this.node).configure(config);
   
           return this.node;
       }
   
  -    public void linkNode() throws Exception {
  -        CategoryNode resources = 
CategoryNodeBuilder.getCategoryNode(this.treeBuilder, "resources");
  +    public void linkNode()
  +        throws Exception
  +    {
  +        CategoryNode resources
  +            = CategoryNodeBuilder.getCategoryNode(this.treeBuilder, "resources");
   
           if (resources == null) {
  -            String msg = "This sitemap contains no resources. Cannot call at " + 
this.node.getLocation();
  +            String msg = "This sitemap contains no resources. Cannot call at "
  +                + this.node.getLocation();
               getLogger().error(msg);
               throw new ConfigurationException(msg);
           }
   
  -        ((CallNode)this.node).setResource(resources, this.resourceName);
  +        if (resourceName != null)
  +            ((CallNode)this.node).setResource(resources, this.resourceName);
  +        else
  +            ((CallFunctionNode)this.node).setResources(resources);
       }
   }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/treeprocessor/sitemap/CallFunctionNode.java
  
  Index: CallFunctionNode.java
  ===================================================================
  package org.apache.cocoon.components.treeprocessor.sitemap;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.Map;
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.component.Component;
  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.configuration.ConfigurationException;
  import org.apache.cocoon.components.flow.Interpreter;
  import org.apache.cocoon.components.flow.InterpreterSelector;
  import org.apache.cocoon.components.treeprocessor.AbstractProcessingNode;
  import org.apache.cocoon.components.treeprocessor.CategoryNode;
  import org.apache.cocoon.components.treeprocessor.InvokeContext;
  import org.apache.cocoon.components.treeprocessor.MapStackResolver;
  import org.apache.cocoon.components.treeprocessor.ParameterizableProcessingNode;
  import org.apache.cocoon.components.treeprocessor.ProcessingNode;
  import org.apache.cocoon.components.treeprocessor.sitemap.PipelinesNode;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.Redirector;
  import org.apache.cocoon.sitemap.PatternException;
  
  public class CallFunctionNode extends AbstractProcessingNode
    implements Configurable, Composable, Initializable
  {
    protected String functionName;
    protected List parameters;
    protected MapStackResolver resourceResolver;
    protected ComponentManager manager;
    protected CategoryNode resources;
    protected String language;
  
    public static List resolveList(List expressions, List mapStack)
      throws PatternException 
    {
      int size;
      if (expressions == null || (size = expressions.size()) == 0)
        return Collections.EMPTY_LIST;
  
      List result = new ArrayList(size);
  
      for (int i = 0; i < size; i++) {
        Interpreter.Argument arg = (Interpreter.Argument)expressions.get(i);
        String value = MapStackResolver.getResolver(arg.value).resolve(mapStack);
        result.add (new Interpreter.Argument(arg.name, value));
      }
  
      return result;
    }
  
    public CallFunctionNode(String funName)
    {
      functionName = funName;
    }
  
    public void setResources(CategoryNode resources)
      throws Exception
    {
      this.resources = resources;
    }
  
    /**
     * Obtain the configuration specific to this node type and update
     * the internal state.
     *
     * @param config a <code>Configuration</code> value
     * @exception ConfigurationException if an error occurs
     */
    public void configure(Configuration config)
      throws ConfigurationException
    {
      language = config.getAttribute("language", null);
  
      parameters = new ArrayList();
  
      Configuration[] params = config.getChildren("parameter");
      for (int i = 0; i < params.length; i++) {
        Configuration param = params[i];
        String name = param.getAttribute("name", null);
        String value = param.getAttribute("value", null);
        parameters.add(new Interpreter.Argument(name, value));
      }
  
      try {
        if (MapStackResolver.needsResolve(functionName)) {
          // Will always be resolved at invoke time
          this.resourceResolver = MapStackResolver.getResolver(functionName);
        }
      }
      catch (PatternException ex) {
        throw new ConfigurationException(ex.toString());
      }
    }
  
    public void compose(ComponentManager manager)
    {
      this.manager = manager;
    }
  
    public void initialize()
      throws Exception
    {
      InterpreterSelector selector
        = (InterpreterSelector)manager.lookup(Interpreter.ROLE);
      if (language == null)
        language = selector.getDefaultLanguage();
    }
  
    public boolean invoke(Environment env, InvokeContext context)
        throws Exception
    {
      List params = null;
  
      // Resolve parameters
      if (this.parameters != null)
        params = resolveList(this.parameters, context.getMapStack());
  
      String name = functionName;
      if (resourceResolver != null) {
        // Need to resolve the function name at runtime
        name = resourceResolver.resolve(context.getMapStack());
      }
  
      InterpreterSelector selector
        = (InterpreterSelector)manager.lookup(Interpreter.ROLE);
      if (language == null)
        language = selector.getDefaultLanguage();
      
      // Obtain the Interpreter instance for this language
      Interpreter interpreter = (Interpreter)selector.select(language);
  
      // Obtain the redirector
      Redirector redirector = PipelinesNode.getRedirector(env);
  
      try {
        interpreter.callFunction(name, params, env /*, redirector*/);
      }
      finally {
        selector.release((Component)interpreter);
      }
      return true;
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/treeprocessor/sitemap/ContinueNode.java
  
  Index: ContinueNode.java
  ===================================================================
  package org.apache.cocoon.components.treeprocessor.sitemap;
  
  import java.util.ArrayList;
  import java.util.List;
  import org.apache.avalon.framework.component.Component;
  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.configuration.ConfigurationException;
  import org.apache.cocoon.components.flow.ContinuationsManager;
  import org.apache.cocoon.components.flow.Interpreter;
  import org.apache.cocoon.components.flow.InterpreterSelector;
  import org.apache.cocoon.components.treeprocessor.AbstractProcessingNode;
  import org.apache.cocoon.components.treeprocessor.InvokeContext;
  import org.apache.cocoon.components.treeprocessor.MapStackResolver;
  import org.apache.cocoon.components.treeprocessor.sitemap.PipelinesNode;
  import org.apache.cocoon.environment.Environment;
  import org.apache.cocoon.environment.Redirector;
  import org.apache.cocoon.sitemap.PatternException;
  
  public class ContinueNode
    extends AbstractProcessingNode
    implements Configurable, Composable
  {
    protected String continuationId;
    protected List parameters;
    protected MapStackResolver continuationIdResolver;
    protected ComponentManager manager;
  
    public ContinueNode(String contId)
    {
      this.continuationId = contId;
    }
  
    public void configure(Configuration config)
      throws ConfigurationException
    {
      parameters = new ArrayList();
  
      Configuration[] params = config.getChildren("parameter");
      for (int i = 0; i < params.length; i++) {
        Configuration param = params[i];
        String name = param.getAttribute("name", null);
        String value = param.getAttribute("value", null);
        parameters.add(new Interpreter.Argument(name, value));
      }
  
      try {
        // The continuation id should would need to be resolved at all
        // times, but who knows...
        if (MapStackResolver.needsResolve(continuationId)) {
          this.continuationIdResolver
            = MapStackResolver.getResolver(continuationId);
        }
      }
      catch (PatternException ex) {
        throw new ConfigurationException(ex.toString());
      }
    }
  
    public void compose(ComponentManager manager)
    {
      this.manager = manager;
    }
  
    public boolean invoke(Environment env, InvokeContext context)
        throws Exception
    {
      List params = null;
  
      // Resolve parameters
      if (this.parameters != null)
        params = CallFunctionNode.resolveList(this.parameters,
                                              context.getMapStack());
  
      String contId = continuationId;
  
      if (continuationIdResolver != null) {
        contId = continuationIdResolver.resolve(context.getMapStack());
      }
  
      InterpreterSelector selector
        = (InterpreterSelector)manager.lookup(Interpreter.ROLE);
  
      // FIXME: How to detect the language associated with the
      // continuation object? Use the default language for now, but it
      // should be fixed ASAP.
      String language = selector.getDefaultLanguage();
  
      // Obtain the Interpreter instance for this language
      Interpreter interpreter = (Interpreter)selector.select(language);
      
      // Obtain the redirector
      Redirector redirector = PipelinesNode.getRedirector(env);
  
      try {
        interpreter.handleContinuation(contId, params, env /*, redirector*/);
      }
      finally {
        selector.release((Component)interpreter);
      }
  
      return true;
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/treeprocessor/sitemap/ContinueNodeBuilder.java
  
  Index: ContinueNodeBuilder.java
  ===================================================================
  package org.apache.cocoon.components.treeprocessor.sitemap;
  
  import org.apache.avalon.excalibur.component.ExcaliburComponentSelector;
  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.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.cocoon.components.flow.Interpreter;
  import org.apache.cocoon.components.language.generator.GeneratorSelector;
  import org.apache.cocoon.components.treeprocessor.AbstractProcessingNodeBuilder;
  import org.apache.cocoon.components.treeprocessor.CategoryNode;
  import org.apache.cocoon.components.treeprocessor.CategoryNodeBuilder;
  import org.apache.cocoon.components.treeprocessor.LinkedProcessingNodeBuilder;
  import org.apache.cocoon.components.treeprocessor.MapStackResolver;
  import org.apache.cocoon.components.treeprocessor.ProcessingNode;
  
  public class ContinueNodeBuilder extends AbstractProcessingNodeBuilder
    implements Composable
  {
    protected ContinueNode node;
    protected ComponentManager manager;
  
    public void compose(ComponentManager manager)
    {
      this.manager = manager;
    }
  
    public ProcessingNode buildNode(Configuration config)
      throws Exception
    {
      String contId = config.getAttribute("with");
  
      this.node = new ContinueNode(contId);
      this.treeBuilder.setupNode(this.node, config);
      if (node instanceof Configurable)
        ((Configurable)this.node).configure(config);
  
      return this.node;
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/treeprocessor/sitemap/ScriptNode.java
  
  Index: ScriptNode.java
  ===================================================================
  package org.apache.cocoon.components.treeprocessor.sitemap;
  
  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.component.Composable;
  import org.apache.avalon.framework.context.ContextException;
  import org.apache.avalon.framework.context.Contextualizable;
  import org.apache.cocoon.Constants;
  import org.apache.cocoon.components.flow.Interpreter;
  import org.apache.cocoon.components.flow.AbstractInterpreter;
  import org.apache.cocoon.components.treeprocessor.AbstractProcessingNode;
  import org.apache.cocoon.components.treeprocessor.InvokeContext;
  import org.apache.cocoon.environment.Context;
  import org.apache.cocoon.environment.Environment;
  
  public class ScriptNode extends AbstractProcessingNode
    implements Composable, Contextualizable
  {
    ComponentManager manager;
    String source;
    String language;
    Context context;
  
    public ScriptNode(String source, String language)
    {
      this.source = source;
      this.language = language;
    }
    
    /**
     * This method should never be called by the TreeProcessor, since a
     * <map:script> element should not be in an "executable" sitemap
     * node.
     *
     * @param env an <code>Environment</code> value
     * @param context an <code>InvokeContext</code> value
     * @return a <code>boolean</code> value
     * @exception Exception if an error occurs
     */
    public boolean invoke(Environment env, InvokeContext context)
      throws Exception
    {
      return true;
    }
  
    public void contextualize(org.apache.avalon.framework.context.Context context)
      throws ContextException
    {
      this.context = (Context)context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
    }
  
    /**
     *
     * Load the script specified by this node.
     *
     * @param manager a <code>ComponentManager</code> value
     */
    public void compose(ComponentManager manager)
      throws ComponentException
    {
      this.manager = manager;
  
      try {
        ComponentSelector selector
          = (ComponentSelector)manager.lookup(Interpreter.ROLE);
        // Obtain the Interpreter instance for this language
        Interpreter interpreter = (Interpreter)selector.select(language);
        if (interpreter instanceof AbstractInterpreter)
          ((AbstractInterpreter)interpreter).register(source);
        // else FIXME: what to do if interpreter doesn't inherit from
        // AbstractInterpreter
      }
      catch (Exception ex) {
        throw new ComponentException("ScriptNode: Couldn't read the source file "
                                     + source + " in language " + language
                                     + ":" + ex);
      }
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/components/treeprocessor/sitemap/ScriptNodeBuilder.java
  
  Index: ScriptNodeBuilder.java
  ===================================================================
  package org.apache.cocoon.components.treeprocessor.sitemap;
  
  import org.apache.avalon.excalibur.component.ExcaliburComponentSelector;
  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.components.flow.Interpreter;
  import org.apache.cocoon.components.language.generator.GeneratorSelector;
  import org.apache.cocoon.components.treeprocessor.AbstractProcessingNodeBuilder;
  import org.apache.cocoon.components.treeprocessor.CategoryNode;
  import org.apache.cocoon.components.treeprocessor.CategoryNodeBuilder;
  import org.apache.cocoon.components.treeprocessor.LinkedProcessingNodeBuilder;
  import org.apache.cocoon.components.treeprocessor.MapStackResolver;
  import org.apache.cocoon.components.treeprocessor.ProcessingNode;
  
  public class ScriptNodeBuilder extends AbstractProcessingNodeBuilder
    implements Composable
  {
    protected ScriptNode node;
    protected ComponentManager manager;
  
    public void compose(ComponentManager manager)
    {
      this.manager = manager;
    }
  
    public ProcessingNode buildNode(Configuration config)
      throws Exception
    {
      String source = config.getAttribute("src");
      String language = config.getAttribute("language", "JavaScript");
  
      this.node = new ScriptNode(source, language);
      this.treeBuilder.setupNode(this.node, config);
  
      return this.node;
    }
  }
  
  
  
  1.1                  
xml-cocoon2/src/java/org/apache/cocoon/transformation/AugmentTransformer.java
  
  Index: AugmentTransformer.java
  ===================================================================
  package org.apache.cocoon.transformation;
  
  import org.apache.avalon.excalibur.pool.Poolable;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.cocoon.ProcessingException;
  import org.apache.cocoon.environment.Request;
  import org.apache.cocoon.transformation.AbstractTransformer;
  import org.apache.cocoon.environment.SourceResolver;
  import org.apache.cocoon.components.language.markup.xsp.XSPRequestHelper;
  import org.apache.cocoon.environment.Request;
  import org.xml.sax.Attributes;
  import org.xml.sax.SAXException;
  import org.xml.sax.helpers.AttributesImpl;
  
  import java.io.IOException;
  import java.util.Map;
  
  /**
   * <p>Augments all <code>href</code> attributes with the full path to
   * the request.</p>
   *
   * <p>Usage in sitemap:</p>
   *
   * <pre>
   *    &lt;map:transform type="augment"&gt
   *      &lt;map:parameter name="mount" value="directory/to/be/appended"/&gt;
   *    &lt;/map:transform&gt;
   * </pre>
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Ovidiu Predescu</a>
   * @since October 10, 2001
   */
  public class AugmentTransformer
    extends AbstractTransformer
    implements Poolable
  {
    Map objectModel;
    Request request;
    String baseURI;
    
    public void setup(SourceResolver resolver,
                      Map objectModel,
                      String source,
                      Parameters parameters)
      throws ProcessingException, SAXException, IOException
    {
      this.objectModel = objectModel;
      request = (Request)objectModel.get("request");
  
      String mountPoint = (String)parameters.getParameter("mount", null);
      
      StringBuffer uribuf = new StringBuffer();
      boolean isSecure = request.isSecure();
      int port = request.getServerPort();
  
      if (isSecure)
        uribuf.append("https://";);
      else
        uribuf.append("http://";);
  
      uribuf.append(request.getServerName());
  
      if (isSecure) {
        if (port != 443) {
          uribuf.append(":").append(port);
        }
      }
      else {
        if (port != 80) {
          uribuf.append(":").append(port);
        }
      }
      if (mountPoint == null) {
        String requestedURI = request.getRequestURI();
        requestedURI = requestedURI.substring(0, requestedURI.lastIndexOf("/"));
        uribuf.append(requestedURI);
        uribuf.append("/");
      }
      else {
        uribuf.append(request.getContextPath());
        uribuf.append("/");
        uribuf.append(mountPoint);
      }
      baseURI = uribuf.toString();
    }
  
    public void startElement(String uri,
                             String name,
                             String qname,
                             Attributes attrs)
      throws SAXException
    {
      AttributesImpl newAttrs = null;
      
      for (int i = 0, size = attrs.getLength(); i < size; i++) {
        String attrName = attrs.getLocalName(i);
        if (attrName.equals("href")) {
          String value = attrs.getValue(i);
  
          // Don't touch the attribute if it's an absolute URL
          if (value.startsWith("http:") || value.startsWith("https:"))
            continue;
  
          if (newAttrs == null)
            newAttrs = new AttributesImpl(attrs);
  
          String newValue = baseURI + value;
          newAttrs.setValue(i, newValue);
        }
      }
  
      if (newAttrs == null)
        super.startElement(uri, name, qname, attrs);
      else
        super.startElement(uri, name, qname, newAttrs);
    }
  
    public void recycle()
    {
      this.objectModel = null;
    }
  }
  
  
  

----------------------------------------------------------------------
In case of troubles, e-mail:     [EMAIL PROTECTED]
To unsubscribe, e-mail:          [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to