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>
* <map:call function="..." language="..."/>
* </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>
* <map:call function="..."/>
* </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,
' 	

~`!@%^*()-+=[]{}\|,./?><', '')"/>
</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 <jpath:if> 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 <jpath:for-each> 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 <jpath:value-of> 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>
* <map:transform type="augment">
* <map:parameter name="mount" value="directory/to/be/appended"/>
* </map:transform>
* </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]