coliver 2003/07/12 12:39:41
Modified: src/blocks/velocity/java/org/apache/cocoon/generation
VelocityGenerator.java
Log:
Moved from scratchpad
Revision Changes Path
1.7 +730 -250
cocoon-2.1/src/blocks/velocity/java/org/apache/cocoon/generation/VelocityGenerator.java
Index: VelocityGenerator.java
===================================================================
RCS file:
/home/cvs/cocoon-2.1/src/blocks/velocity/java/org/apache/cocoon/generation/VelocityGenerator.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- VelocityGenerator.java 3 Jul 2003 11:36:10 -0000 1.6
+++ VelocityGenerator.java 12 Jul 2003 19:39:41 -0000 1.7
@@ -1,4 +1,4 @@
-/*
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
============================================================================
The Apache Software License, Version 1.1
@@ -50,46 +50,90 @@
*/
package org.apache.cocoon.generation;
+import java.beans.PropertyDescriptor;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.*;
+
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.component.Component;
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.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.ResourceNotFoundException;
+import org.apache.cocoon.components.flow.FlowHelper;
+import org.apache.cocoon.components.flow.WebContinuation;
import org.apache.cocoon.environment.ObjectModelHelper;
import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.Response;
import org.apache.cocoon.environment.Session;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.commons.collections.ExtendedProperties;
+import org.apache.commons.jxpath.DynamicPropertyHandler;
+import org.apache.commons.jxpath.JXPathBeanInfo;
+import org.apache.commons.jxpath.JXPathIntrospector;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.xml.sax.SAXParser;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.log.LogSystem;
+import org.apache.velocity.util.introspection.Info;
+import org.apache.velocity.util.introspection.UberspectImpl;
+import org.apache.velocity.util.introspection.VelMethod;
+import org.apache.velocity.util.introspection.VelPropertyGet;
+import org.apache.velocity.util.introspection.VelPropertySet;
+import org.mozilla.javascript.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import org.xml.sax.SAXParseException;
/**
* <p>Cocoon [EMAIL PROTECTED] Generator} that produces dynamic XML SAX events
* from a Velocity template file.</p>
+ * If called from a Flowscript, the immediate properties of the context object from
the Flowscript are available in the Velocity context.
+ * In that case, the current Web Continuation from the Flowscript
+ * is also available as a variable named <code>continuation</code>. You would
+ * typically access its <code>id</code>:
+ * <p><pre>
+ * <form action="$continuation.id">
+ * </pre></p>
+ * <p>You can also reach previous continuations by using the
<code>getContinuation()</code> function:</p>
+ * <p><pre>
+ * <form action="$continuation.getContinuation(1).id}" >
+ * </pre></p>
+ *
+ * In addition the following implicit objects are always available in
+ * the Velocity context:
+ * <p>
+ * <dl>
+ * <dt><code>request</code>
(<code>org.apache.cocoon.environment.Request</code>)</dt>
+ * <dd>The Cocoon current request</dd>
+ *
+ * <dt><code>response</code>
(<code>org.apache.cocoon.environment.Response</code>)</dt>
+ * <dd>The Cocoon response associated with the current request</dd>
+ *
+ * <dt><code>session</code>
(<code>org.apache.cocoon.environment.Session</code>)</dt>
+ * <dd>The Cocoon session associated with the current request</dd>
+ *
+ * <dt><code>context</code>
(<code>org.apache.cocoon.environment.Context</code>)</dt>
+ * <dd>The Cocoon context associated with the current request</dd>
+ *
+ * <dt><code>parameters</code>
(<code>org.apache.avalon.framework.parameters.Parameters</code>)</dt>
+ * <dd>Any parameters passed to the generator in the pipeline</dd>
+ * </dl>
+ * </p>
+ *
*
* <h2>Sitemap Configuration</h2>
*
@@ -131,49 +175,460 @@
* element. The prefix '<name>.resource.loader.' is
* automatically added to the property name.</dd>
*
- * <dt><export-object key="objectMapKey" name="velocityContextName"
[scope=request/session/sitemap]/> (optional; 0..n)</dt>
- * <dd>Export the object specified by <em>key</em> from the <em>request, session,
sitemap</em> to
- * the Velocity context of the template. The object can be accessed
- * from the template as <em>name</em>.
- * Be careful to use a valid VTL variable name.</dd>
- * </dl>
- * </p>
- *
- * <p>
- * Default Java objects exported to the Velocity context:
- *
- * <dl>
- * <dt>request (org.apache.cocoon.environment.Request)</dt>
- * <dd>The Cocoon current request</dd>
- *
- * <dt>template (java.lang.String)</dt>
- * <dd>The path of the template file currently being evaluated</dd>
- *
- * <dt>response (org.apache.cocoon.environment.Response)</dt>
- * <dd>The Cocoon response associated with the current request</dd>
- *
- * <dt>context (org.apache.cocoon.environment.Context)</dt>
- * <dd>The Cocoon context associated with the current request</dd>
- *
- * <dt>parameters (org.apache.avalon.framework.parameters.Parameters)</dt>
- * <dd>Any parameters passed to the generator in the pipeline</dd>
- * </dl>
- * </p>
- *
- * <p> Additional Java objects can be exported from the Cocoon object
- * map to the Velocity context by adding one or more <export-object
- * key="objectMapKey" name="velocityContextName"/> child elements
- * to the generator configuration in the sitemap.
- * Additionally a scope=[request, session, sitemap] can be set in which
- * lookup of the object to export is performed.</p>
- *
- * @author <a href="mailto:[EMAIL PROTECTED]">Davanum Srinivas</a>
- * @author <a href="mailto:[EMAIL PROTECTED]">Michael McKibben</a>
- * @author <a href="mailto:[EMAIL PROTECTED]">Michael Homeijer</a>
* @version CVS $Id$
*/
public class VelocityGenerator extends ComposerGenerator
- implements Initializable, Configurable, LogSystem {
+ implements Initializable, Configurable, LogSystem {
+
+ /**
+ * <p>Velocity context implementation specific to the Servlet environment.</p>
+ *
+ * <p>It provides the following special features:</p>
+ * <ul>
+ * <li>puts the request, response, session, and servlet context objects
+ * into the Velocity context for direct access, and keeps them
+ * read-only</li>
+ * <li>supports a read-only toolbox of view tools</li>
+ * <li>auto-searches servlet request attributes, session attributes and
+ * servlet context attribues for objects</li>
+ * </ul>
+ *
+ * <p>The [EMAIL PROTECTED] #internalGet(String key)} method implements the
following search order
+ * for objects:</p>
+ * <ol>
+ * <li>servlet request, servlet response, servlet session, servlet
context</li>
+ * <li>toolbox</li>
+ * <li>local hashtable of objects (traditional use)</li>
+ * <li>servlet request attribues, servlet session attribute, servlet context
+ * attributes</li>
+ * </ol>
+ *
+ * <p>The purpose of this class is to make it easy for web designer to work
+ * with Java servlet based web applications. They do not need to be concerned
+ * with the concepts of request, session or application attributes and the
+ * live time of objects in these scopes.</p>
+ *
+ * <p>Note that the put() method always puts objects into the local hashtable.
+ * </p>
+ *
+ * <p>Acknowledge: the source code is borrowed from the jakarta-velocity-tools
+ * project with slight modifications.</p>
+ *
+ * @author <a href="mailto:[EMAIL PROTECTED]">Albert Kwong</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Gabe Sidler</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Albert Kwong</a>
+ */
+ public static class ChainedContext extends VelocityContext
+ {
+
+ /**
+ * A local reference to the current servlet request.
+ */
+ private Request request;
+
+ /**
+ * A local reference to the current servlet response.
+ */
+ private Response response;
+
+ /**
+ * A local reference to the servlet session.
+ */
+ private Session session;
+
+ /**
+ * A local reference to the servlet context.
+ */
+ private org.apache.cocoon.environment.Context application;
+
+ /**
+ * A local reference to pipeline parameters.
+ */
+ private Parameters parameters;
+
+ /**
+ * Key to the HTTP request object.
+ */
+ public static final String REQUEST = "request";
+
+ /**
+ * Key to the HTTP response object.
+ */
+ public static final String RESPONSE = "response";
+
+ /**
+ * Key to the HTTP session object.
+ */
+ public static final String SESSION = "session";
+
+ /**
+ * Key to the servlet context object.
+ */
+ public static final String APPLICATION = "context";
+
+ /**
+ * Key to the servlet context object.
+ */
+ public static final String PARAMETERS = "parameters";
+
+
+ /**
+ * Default constructor.
+ */
+ public ChainedContext(org.apache.velocity.context.Context ctx,
+ Request request,
+ Response response,
+ org.apache.cocoon.environment.Context application,
+ Parameters parameters)
+ {
+ super(null, ctx);
+ this.request = request;
+ this.response = response;
+ this.session = request.getSession(false);
+ this.application = application;
+ this.parameters = parameters;
+ }
+
+
+ /**
+ * <p>Looks up and returns the object with the specified key.</p>
+ *
+ * <p>See the class documentation for more details.</p>
+ *
+ * @param key the key of the object requested
+ *
+ * @return the requested object or null if not found
+ */
+ public Object internalGet( String key )
+ {
+ // make the four scopes of the Apocalypse Read only
+ if ( key.equals( REQUEST ))
+ {
+ return request;
+ }
+ else if( key.equals(RESPONSE) )
+ {
+ return response;
+ }
+ else if ( key.equals(SESSION) )
+ {
+ return session;
+ }
+ else if ( key.equals(APPLICATION))
+ {
+ return application;
+ }
+ else if ( key.equals(PARAMETERS))
+ {
+ return parameters;
+ }
+
+ Object o = null;
+
+ // try the local hashtable
+ o = super.internalGet( key );
+
+ // if not found, wander down the scopes...
+ if (o == null)
+ {
+ o = request.getAttribute( key );
+
+ if ( o == null )
+ {
+ if ( session != null )
+ {
+ o = session.getAttribute( key );
+ }
+
+ if ( o == null )
+ {
+ o = application.getAttribute( key );
+ }
+ }
+ }
+
+ return o;
+ }
+
+
+ } // ChainedContext
+
+ /**
+ * Velocity Introspector that supports Rhino JavaScript objects
+ * as well as Java Objects
+ *
+ */
+ public static class JSIntrospector extends UberspectImpl {
+
+ public static class JSMethod implements VelMethod {
+
+ Scriptable scope;
+ String name;
+
+ public JSMethod(Scriptable scope, String name) {
+ this.scope = scope;
+ this.name = name;
+ }
+
+ public Object invoke(Object thisArg, Object[] args)
+ throws Exception {
+ org.mozilla.javascript.Context cx =
org.mozilla.javascript.Context.enter();
+ try {
+ Object result;
+ Scriptable thisObj;
+ if (!(thisArg instanceof Scriptable)) {
+ thisObj = org.mozilla.javascript.Context.toObject(thisArg,
scope);
+ } else {
+ thisObj = (Scriptable)thisArg;
+ }
+ result = ScriptableObject.getProperty(thisObj, name);
+ Object[] newArgs = null;
+ if (args != null) {
+ newArgs = new Object[args.length];
+ for (int i = 0; i < args.length; i++) {
+ newArgs[i] = args[i];
+ if (args[i] != null &&
+ !(args[i] instanceof Number) &&
+ !(args[i] instanceof Boolean) &&
+ !(args[i] instanceof String) &&
+ !(args[i] instanceof Scriptable)) {
+ newArgs[i] =
org.mozilla.javascript.Context.toObject(args[i], scope);
+ }
+ }
+ }
+ result = ScriptRuntime.call(cx, result, thisObj,
+ newArgs, scope);
+ if (result == Undefined.instance ||
+ result == ScriptableObject.NOT_FOUND) {
+ result = null;
+ } else while (result instanceof Wrapper) {
+ result = ((Wrapper)result).unwrap();
+ }
+ return result;
+ } catch (JavaScriptException e) {
+ throw new java.lang.reflect.InvocationTargetException(e);
+ } finally {
+ org.mozilla.javascript.Context.exit();
+ }
+ }
+
+ public boolean isCacheable() {
+ return false;
+ }
+
+ public String getMethodName() {
+ return name;
+ }
+
+ public Class getReturnType() {
+ return Object.class;
+ }
+
+ }
+
+ public static class JSPropertyGet implements VelPropertyGet {
+
+ Scriptable scope;
+ String name;
+
+ public JSPropertyGet(Scriptable scope, String name) {
+ this.scope = scope;
+ this.name = name;
+ }
+
+ public Object invoke(Object thisArg) throws Exception {
+ org.mozilla.javascript.Context.enter();
+ try {
+ Scriptable thisObj;
+ if (!(thisArg instanceof Scriptable)) {
+ thisObj = org.mozilla.javascript.Context.toObject(thisArg,
scope);
+ } else {
+ thisObj = (Scriptable)thisArg;
+ }
+ Object result = ScriptableObject.getProperty(thisObj, name);
+ if (result == Undefined.instance ||
+ result == ScriptableObject.NOT_FOUND) {
+ result = null;
+ } else while (result instanceof Wrapper) {
+ result = ((Wrapper)result).unwrap();
+ }
+ return result;
+ } finally {
+ org.mozilla.javascript.Context.exit();
+ }
+ }
+
+ public boolean isCacheable() {
+ return false;
+ }
+
+ public String getMethodName() {
+ return name;
+ }
+
+ }
+
+ public static class JSPropertySet implements VelPropertySet {
+
+ Scriptable scope;
+ String name;
+
+ public JSPropertySet(Scriptable scope, String name) {
+ this.scope = scope;
+ this.name = name;
+ }
+
+ public Object invoke(Object thisArg, Object rhs) throws Exception {
+ org.mozilla.javascript.Context.enter();
+ try {
+ Scriptable thisObj;
+ Object arg = rhs;
+ if (!(thisArg instanceof Scriptable)) {
+ thisObj = org.mozilla.javascript.Context.toObject(thisArg,
scope);
+ } else {
+ thisObj = (Scriptable)thisArg;
+ }
+ if (arg != null &&
+ !(arg instanceof Number) &&
+ !(arg instanceof Boolean) &&
+ !(arg instanceof String) &&
+ !(arg instanceof Scriptable)) {
+ arg = org.mozilla.javascript.Context.toObject(arg, scope);
+ }
+ ScriptableObject.putProperty(thisObj, name, arg);
+ return rhs;
+ } finally {
+ org.mozilla.javascript.Context.exit();
+ }
+ }
+
+ public boolean isCacheable() {
+ return false;
+ }
+
+ public String getMethodName() {
+ return name;
+ }
+ }
+
+ public static class NativeArrayIterator implements Iterator {
+
+ NativeArray arr;
+ int index;
+
+ public NativeArrayIterator(NativeArray arr) {
+ this.arr = arr;
+ this.index = 0;
+ }
+
+ public boolean hasNext() {
+ return index < (int)arr.jsGet_length();
+ }
+
+ public Object next() {
+ org.mozilla.javascript.Context.enter();
+ try {
+ Object result = arr.get(index++, arr);
+ if (result == Undefined.instance ||
+ result == ScriptableObject.NOT_FOUND) {
+ result = null;
+ } else while (result instanceof Wrapper) {
+ result = ((Wrapper)result).unwrap();
+ }
+ return result;
+ } finally {
+ org.mozilla.javascript.Context.exit();
+ }
+ }
+
+ public void remove() {
+ arr.delete(index);
+ }
+ }
+
+ public static class ScriptableIterator implements Iterator {
+
+ Scriptable scope;
+ Object[] ids;
+ int index;
+
+ public ScriptableIterator(Scriptable scope) {
+ this.scope = scope;
+ this.ids = scope.getIds();
+ this.index = 0;
+ }
+
+ public boolean hasNext() {
+ return index < ids.length;
+ }
+
+ public Object next() {
+ org.mozilla.javascript.Context.enter();
+ try {
+ Object result =
+ ScriptableObject.getProperty(scope,
+ ids[index++].toString());
+ if (result == Undefined.instance ||
+ result == ScriptableObject.NOT_FOUND) {
+ result = null;
+ } else while (result instanceof Wrapper) {
+ result = ((Wrapper)result).unwrap();
+ }
+ return result;
+ } finally {
+ org.mozilla.javascript.Context.exit();
+ }
+ }
+
+ public void remove() {
+ org.mozilla.javascript.Context.enter();
+ try {
+ scope.delete(ids[index].toString());
+ } finally {
+ org.mozilla.javascript.Context.exit();
+ }
+ }
+ }
+
+ public Iterator getIterator(Object obj, Info i)
+ throws Exception {
+ if (!(obj instanceof Scriptable)) {
+ return super.getIterator(obj, i);
+ }
+ if (obj instanceof NativeArray) {
+ return new NativeArrayIterator((NativeArray)obj);
+ }
+ return new ScriptableIterator((Scriptable)obj);
+ }
+
+ public VelMethod getMethod(Object obj, String methodName,
+ Object[] args, Info i)
+ throws Exception {
+ if (!(obj instanceof Scriptable)) {
+ return super.getMethod(obj, methodName, args, i);
+ }
+ return new JSMethod((Scriptable)obj, methodName);
+ }
+
+ public VelPropertyGet getPropertyGet(Object obj, String identifier,
+ Info i)
+ throws Exception {
+ if (!(obj instanceof Scriptable)) {
+ return super.getPropertyGet(obj, identifier, i);
+ }
+ return new JSPropertyGet((Scriptable)obj, identifier);
+ }
+
+ public VelPropertySet getPropertySet(Object obj, String identifier,
+ Object arg, Info i)
+ throws Exception {
+ if (!(obj instanceof Scriptable)) {
+ return super.getPropertySet(obj, identifier, arg, i);
+ }
+ return new JSPropertySet((Scriptable)obj, identifier);
+ }
+ }
/**
* Velocity [EMAIL PROTECTED]
org.apache.velocity.runtime.resource.loader.ResourceLoader}
@@ -184,9 +639,9 @@
* @see org.apache.velocity.runtime.resource.loader.ResourceLoader
*/
public static class TemplateLoader
- extends org.apache.velocity.runtime.resource.loader.ResourceLoader {
+ extends org.apache.velocity.runtime.resource.loader.ResourceLoader {
- private Context resolverContext;
+ private org.apache.avalon.framework.context.Context resolverContext;
/**
* Initialize this resource loader. The 'context' property is
@@ -195,12 +650,15 @@
* pipeline.
*
* @param config the properties to configure this resource.
- * of type [EMAIL PROTECTED] Context}.
+ * @throws IllegalArgumentException thrown if the required
+ * 'context' property is not set.
+ * @throws ClassCastException if the 'context' property is not
+ * of type [EMAIL PROTECTED]
org.apache.avalon.framework.context.Context}.
* @see org.apache.velocity.runtime.resource.loader.ResourceLoader#init
*/
public void init(ExtendedProperties config) {
- this.resolverContext = (Context) config.get("context");
- if (this.resolverContext==null) {
+ this.resolverContext = (org.apache.avalon.framework.context.Context)
config.get("context");
+ if (this.resolverContext == null) {
throw new IllegalArgumentException("Runtime Cocoon resolver context
not specified in resource loader configuration.");
}
}
@@ -208,54 +666,43 @@
/**
* @param systemId the path to the resource
* @see
org.apache.velocity.runtime.resource.loader.ResourceLoader#getResourceStream
- *
*/
public InputStream getResourceStream(String systemId)
- throws org.apache.velocity.exception.ResourceNotFoundException {
+ throws org.apache.velocity.exception.ResourceNotFoundException {
try {
return resolveSource(systemId).getInputStream();
} catch (org.apache.velocity.exception.ResourceNotFoundException ex) {
throw ex;
} catch (Exception ex) {
- throw new
org.apache.velocity.exception.ResourceNotFoundException("Unable to resolve source: "+
- ex);
+ throw new
org.apache.velocity.exception.ResourceNotFoundException("Unable to resolve source: " +
ex);
}
}
/**
* @see
org.apache.velocity.runtime.resource.loader.ResourceLoader#isSourceModified
- *
- * @param resource
- *
*/
public boolean
isSourceModified(org.apache.velocity.runtime.resource.Resource resource) {
long lastModified = 0;
-
try {
lastModified = resolveSource(resource.getName()).getLastModified();
} catch (Exception ex) {
- super.rsvc.warn("Unable to determine last modified for resource: "+
- resource.getName()+": "+ex);
+ super.rsvc.warn("Unable to determine last modified for resource: "
+ + resource.getName() + ": " + ex);
}
- return (lastModified>0)
- ? lastModified!=resource.getLastModified() : true;
+ return lastModified > 0 ? lastModified != resource.getLastModified() :
true;
}
/**
* @see
org.apache.velocity.runtime.resource.loader.ResourceLoader#getLastModified
- *
- * @param resource
- *
*/
public long getLastModified(org.apache.velocity.runtime.resource.Resource
resource) {
long lastModified = 0;
-
try {
lastModified = resolveSource(resource.getName()).getLastModified();
} catch (Exception ex) {
- super.rsvc.warn("Unable to determine last modified for resource: "+
- resource.getName()+": "+ex);
+ super.rsvc.warn("Unable to determine last modified for resource: "
+ + resource.getName() + ": " + ex);
}
return lastModified;
@@ -266,12 +713,9 @@
* recycled later.
*
* @param systemId the path to the resource
- *
*/
- private Source resolveSource(String systemId)
- throws org.apache.velocity.exception.ResourceNotFoundException {
+ private Source resolveSource(String systemId) throws
org.apache.velocity.exception.ResourceNotFoundException {
Map sourceCache;
-
try {
sourceCache = (Map)
this.resolverContext.get(CONTEXT_SOURCE_CACHE_KEY);
} catch (ContextException ignore) {
@@ -279,17 +723,14 @@
}
Source source = (Source) sourceCache.get(systemId);
-
- if (source==null) {
+ if (source == null) {
try {
SourceResolver resolver = (SourceResolver)
this.resolverContext.get(CONTEXT_RESOLVER_KEY);
-
source = resolver.resolveURI(systemId);
} catch (ContextException ex) {
throw new
org.apache.velocity.exception.ResourceNotFoundException("No Cocoon source resolver
associated with current request.");
} catch (Exception ex) {
- throw new
org.apache.velocity.exception.ResourceNotFoundException("Unable to resolve source: "+
- ex);
+ throw new
org.apache.velocity.exception.ResourceNotFoundException("Unable to resolve source: " +
ex);
}
}
@@ -299,35 +740,21 @@
}
/**
- * Holder object for controlling Cocoon objects exported to the
- * Velocity Context.
- */
- private static class ObjectExport {
- private String key;
- private String sourcemap;
- private String name;
- }
-
- /**
* Key to lookup the [EMAIL PROTECTED] SourceResolver} from the context of
- * the resource loader.
+ * the resource loader
*/
final private static String CONTEXT_RESOLVER_KEY = "resolver";
/**
* Key to lookup the source cache [EMAIL PROTECTED] Map} from the context of
- * the resource loader.
+ * the resource loader
*/
final private static String CONTEXT_SOURCE_CACHE_KEY = "source-cache";
- private static final String VALUE_SITEMAP = "sitemap";
- private static final String VALUE_SESSION = "session";
- private static final String VALUE_REQUEST = "request";
-
- private List objectExports;
private VelocityEngine tmplEngine;
+ private boolean tmplEngineInitialized;
private DefaultContext resolverContext;
- private VelocityContext velocityContext;
+ private Context velocityContext;
private boolean activeFlag;
/**
@@ -337,38 +764,36 @@
* @param configuration the class configurations.
* @see org.apache.avalon.framework.configuration.Configurable#configure
*/
- public void configure(Configuration configuration)
- throws ConfigurationException {
+ public void configure(Configuration configuration) throws
ConfigurationException {
this.resolverContext = new DefaultContext();
- this.objectExports = new ArrayList();
this.tmplEngine = new VelocityEngine();
+
+ // Set up a JavaScript introspector for the Cocoon flow layer
+
this.tmplEngine.setProperty(org.apache.velocity.runtime.RuntimeConstants.UBERSPECT_CLASSNAME,
+ JSIntrospector.class.getName());
this.tmplEngine.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, this);
// First set up our default 'cocoon' resource loader
this.tmplEngine.setProperty("cocoon.resource.loader.class",
TemplateLoader.class.getName());
this.tmplEngine.setProperty("cocoon.resource.loader.cache",
- configuration.getAttribute("usecache",
- "false"));
+ configuration.getAttribute("usecache",
"false"));
this.tmplEngine.setProperty("cocoon.resource.loader.modificationCheckInterval",
- configuration.getAttribute("checkInterval",
- "0"));
+ configuration.getAttribute("checkInterval",
"0"));
this.tmplEngine.setProperty("cocoon.resource.loader.context",
this.resolverContext);
// Read in any additional properties to pass to the VelocityEngine during
initialization
Configuration[] properties = configuration.getChildren("property");
-
- for (int i = 0; i<properties.length; ++i) {
+ for (int i = 0; i < properties.length; ++i) {
Configuration c = properties[i];
String name = c.getAttribute("name");
// Disallow setting of certain properties
- if (name.startsWith("runtime.log") ||
- (name.indexOf(".resource.loader.")!=-1)) {
+ if (name.startsWith("runtime.log")
+ || name.indexOf(".resource.loader.") != -1) {
if (getLogger().isInfoEnabled()) {
- getLogger().info("ignoring disallowed property '"+name+
- "'.");
+ getLogger().info("ignoring disallowed property '" + name +
"'.");
}
continue;
}
@@ -378,11 +803,9 @@
// Now read in any additional Velocity resource loaders
List resourceLoaders = new ArrayList();
Configuration[] loaders = configuration.getChildren("resource-loader");
-
- for (int i = 0; i<loaders.length; ++i) {
+ for (int i = 0; i < loaders.length; ++i) {
Configuration loader = loaders[i];
String name = loader.getAttribute("name");
-
if (name.equals("cocoon")) {
if (getLogger().isInfoEnabled()) {
getLogger().info("'cocoon' resource loader already defined.");
@@ -390,58 +813,39 @@
continue;
}
resourceLoaders.add(name);
- String prefix = name+".resource.loader.";
+ String prefix = name + ".resource.loader.";
String type = loader.getAttribute("class");
-
- this.tmplEngine.setProperty(prefix+"class", type);
+ this.tmplEngine.setProperty(prefix + "class", type);
Configuration[] loaderProperties = loader.getChildren("property");
-
- for (int j = 0; j<loaderProperties.length; j++) {
+ for (int j = 0; j < loaderProperties.length; j++) {
Configuration c = loaderProperties[j];
String propName = c.getAttribute("name");
-
- this.tmplEngine.setProperty(prefix+propName,
- c.getAttribute("value"));
+ this.tmplEngine.setProperty(prefix + propName,
c.getAttribute("value"));
}
}
// Velocity expects resource loaders as CSV list
+ //
StringBuffer buffer = new StringBuffer("cocoon");
-
- for (Iterator it = resourceLoaders.iterator(); it.hasNext(); ) {
+ for (Iterator it = resourceLoaders.iterator(); it.hasNext();) {
buffer.append(',');
buffer.append((String) it.next());
}
tmplEngine.setProperty(Velocity.RESOURCE_LOADER, buffer.toString());
-
- // Read in additional objects to export from the object map
- Configuration[] exports = configuration.getChildren("export-object");
-
- for (int i = 0; i<exports.length; ++i) {
- Configuration c = exports[i];
- ObjectExport export = new ObjectExport();
-
- export.key = c.getAttribute("key");
- export.name = c.getAttribute("name");
- export.sourcemap = c.getAttribute("scope");
- this.objectExports.add(export);
- }
}
/**
* @see org.apache.avalon.framework.activity.Initializable#initialize
*/
public void initialize() throws Exception {
- this.tmplEngine.init();
+ //this.tmplEngine.init();
}
/**
* @see org.apache.cocoon.sitemap.SitemapModelComponent#setup
*/
- public void setup(SourceResolver resolver, Map objectModel, String src,
- Parameters params)
- throws ProcessingException, SAXException,
- IOException {
+ public void setup(SourceResolver resolver, Map objectModel, String src,
Parameters params)
+ throws ProcessingException, SAXException, IOException {
if (activeFlag) {
throw new IllegalStateException("setup called on recyclable sitemap
component before properly recycling previous state");
}
@@ -452,41 +856,135 @@
this.resolverContext.put(CONTEXT_RESOLVER_KEY, resolver);
this.resolverContext.put(CONTEXT_SOURCE_CACHE_KEY, new HashMap());
- // Initialize the Velocity context
- this.velocityContext = new VelocityContext();
- this.velocityContext.put("template", src);
- this.velocityContext.put("request",
- ObjectModelHelper.getRequest(objectModel));
- this.velocityContext.put("response",
- ObjectModelHelper.getResponse(objectModel));
- this.velocityContext.put("context",
- ObjectModelHelper.getContext(objectModel));
- this.velocityContext.put("parameters", params);
-
- // Export any additional objects to the Velocity context
- for (Iterator it = this.objectExports.iterator(); it.hasNext(); ) {
- ObjectExport export = (ObjectExport) it.next();
- // Object object = objectModel.get(export.key);
- Object object = this.searchBean(objectModel, export.key,
- export.sourcemap);
-
- if (object!=null) {
- this.velocityContext.put(export.name, object);
- if (getLogger().isDebugEnabled()) {
- getLogger().debug("exporting object under key '"+
- export.key+
- "' to velocity context with name '"+
- export.name+"'.");
- }
- } else if (getLogger().isInfoEnabled()) {
- getLogger().info("unable to export object under key '"+
- export.key+"' to velocity context.");
+ // FIXME: Initialize the Velocity context. Use objectModel to pass these
+ final Object bean = FlowHelper.getContextObject(objectModel);
+ if (bean != null) {
+
+ final WebContinuation kont = FlowHelper.getWebContinuation(objectModel);
+
+ // Hack? I use JXPath to determine the properties of the bean object
+ final JXPathBeanInfo bi =
JXPathIntrospector.getBeanInfo(bean.getClass());
+ DynamicPropertyHandler h = null;
+ final PropertyDescriptor[] props;
+ if (bi.isDynamic()) {
+ Class cl = bi.getDynamicPropertyHandlerClass();
+ try {
+ h = (DynamicPropertyHandler) cl.newInstance();
+ } catch (Exception exc) {
+ exc.printStackTrace();
+ h = null;
+ }
+ props = null;
+ } else {
+ h = null;
+ props = bi.getPropertyDescriptors();
}
+ final DynamicPropertyHandler handler = h;
+
+ this.velocityContext = new Context() {
+ public Object put(String key, Object value) {
+ if (key.equals("flowContext")
+ || key.equals("continuation")) {
+ return value;
+ }
+ if (handler != null) {
+ handler.setProperty(bean, key, value);
+ return value;
+ } else {
+ for (int i = 0; i < props.length; i++) {
+ if (props[i].getName().equals(key)) {
+ try {
+ return
props[i].getWriteMethod().invoke(bean, new Object[]{value});
+ } catch (Exception ignored) {
+ break;
+ }
+ }
+ }
+ return value;
+ }
+ }
+
+ public boolean containsKey(Object key) {
+ if (key.equals("flowContext")
+ || key.equals("continuation")) {
+ return true;
+ }
+ if (handler != null) {
+ String[] result = handler.getPropertyNames(bean);
+ for (int i = 0; i < result.length; i++) {
+ if (key.equals(result[i])) {
+ return true;
+ }
+ }
+ } else {
+ for (int i = 0; i < props.length; i++) {
+ if (key.equals(props[i].getName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public Object[] getKeys() {
+ Object[] result = null;
+ if (handler != null) {
+ result = handler.getPropertyNames(bean);
+ } else {
+ result = new Object[props.length];
+ for (int i = 0; i < props.length; i++) {
+ result[i] = props[i].getName();
+ }
+ }
+ Set set = new HashSet();
+ for (int i = 0; i < result.length; i++) {
+ set.add(result[i]);
+ }
+ set.add("flowContext");
+ set.add("continuation");
+ result = new Object[set.size()];
+ set.toArray(result);
+ return result;
+ }
+
+ public Object get(String key) {
+ if (key.equals("flowContext")) {
+ return bean;
+ }
+ if (key.equals("continuation")) {
+ return kont;
+ }
+ if (handler != null) {
+ return handler.getProperty(bean, key.toString());
+ } else {
+ for (int i = 0; i < props.length; i++) {
+ if (props[i].getName().equals(key)) {
+ try {
+ return
props[i].getReadMethod().invoke(bean, null);
+ } catch (Exception ignored) {
+ break;
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ public Object remove(Object key) {
+ // not implemented
+ return key;
+ }
+ };
}
-
+ this.velocityContext =
+ new ChainedContext (this.velocityContext,
+ ObjectModelHelper.getRequest(objectModel),
+ ObjectModelHelper.getResponse(objectModel),
+ ObjectModelHelper.getContext(objectModel),
+ params);
+ this.velocityContext.put("template", src);
this.activeFlag = true;
}
-
/**
* Free up the VelocityContext associated with the pipeline, and
* release any Source objects resolved by the resource loader.
@@ -499,11 +997,11 @@
// Recycle all the Source objects resolved/used by our resource loader
try {
Map sourceCache = (Map)
this.resolverContext.get(CONTEXT_SOURCE_CACHE_KEY);
-
- for (Iterator it = sourceCache.values().iterator(); it.hasNext(); ) {
+ for (Iterator it = sourceCache.values().iterator(); it.hasNext();) {
this.resolver.release((Source) it.next());
}
- } catch (ContextException ignore) {}
+ } catch (ContextException ignore) {
+ }
this.velocityContext = null;
super.recycle();
@@ -515,127 +1013,109 @@
* @see org.apache.cocoon.generation.Generator#generate
*/
public void generate()
- throws IOException, SAXException, ProcessingException {
+ throws IOException, SAXException, ProcessingException {
// Guard against calling generate before setup.
- if ( !activeFlag) {
+ if (!activeFlag) {
throw new IllegalStateException("generate called on sitemap component
before setup.");
}
SAXParser parser = null;
-
+ StringWriter w = new StringWriter();
try {
parser = (SAXParser) this.manager.lookup(SAXParser.ROLE);
if (getLogger().isDebugEnabled()) {
- getLogger().debug("Processing File: "+super.source);
+ getLogger().debug("Processing File: " + super.source);
+ }
+ if (!tmplEngineInitialized) {
+ tmplEngine.init();
+ tmplEngineInitialized = true;
}
-
/* lets render a template */
-
- StringWriter w = new StringWriter();
-
this.tmplEngine.mergeTemplate(super.source, velocityContext, w);
- InputSource xmlInput = new InputSource(new StringReader(w.toString()));
-
+ InputSource xmlInput =
+ new InputSource(new StringReader(w.toString()));
+ xmlInput.setSystemId(super.source);
parser.parse(xmlInput, this.xmlConsumer);
} catch (IOException e) {
getLogger().warn("VelocityGenerator.generate()", e);
- throw new ResourceNotFoundException("Could not get Resource for
VelocityGenerator",
- e);
+ throw new ResourceNotFoundException("Could not get Resource for
VelocityGenerator", e);
+ } catch (SAXParseException e) {
+ int line = e.getLineNumber();
+ int column = e.getColumnNumber();
+ if (line <= 0) {
+ line = Integer.MAX_VALUE;
+ }
+ BufferedReader reader =
+ new BufferedReader(new StringReader(w.toString()));
+ String message = e.getMessage() +" In generated document:\n";
+ for (int i = 0; i < line; i++) {
+ String lineStr = reader.readLine();
+ if (lineStr == null) {
+ break;
+ }
+ message += lineStr + "\n";
+ }
+ if (column > 0) {
+ String columnIndicator = "";
+ for (int i = 1; i < column; i++) {
+ columnIndicator += " ";
+ }
+ columnIndicator += "^" + "\n";
+ message += columnIndicator;
+ }
+ SAXException pe = new SAXParseException(message,
+ e.getPublicId(),
+ "(Document generated from
template "+e.getSystemId() + ")",
+ e.getLineNumber(),
+ e.getColumnNumber(),
+ null);
+ getLogger().error("VelocityGenerator.generate()", pe);
+ throw pe;
} catch (SAXException e) {
getLogger().error("VelocityGenerator.generate()", e);
throw e;
} catch (ComponentException e) {
getLogger().error("Could not get parser", e);
- throw new ProcessingException("Exception in
VelocityGenerator.generate()",
- e);
+ throw new ProcessingException("Exception in
VelocityGenerator.generate()", e);
} catch (ProcessingException e) {
throw e;
} catch (Exception e) {
getLogger().error("Could not get parser", e);
- throw new ProcessingException("Exception in
VelocityGenerator.generate()",
- e);
+ throw new ProcessingException("Exception in
VelocityGenerator.generate()", e);
} finally {
this.manager.release((Component) parser);
}
}
+
/**
* This implementation does nothing.
*
* @see org.apache.velocity.runtime.log.LogSystem#init
- *
- * @param rs
*/
public void init(RuntimeServices rs) throws Exception {
}
- private Object searchBean(Map objectModel, String name,
- String sourcemap) {
- Request request = ObjectModelHelper.getRequest(objectModel);
- Object bean;
-
- // search all maps for the given bean
- if ((sourcemap==null) || VALUE_SITEMAP.equals(sourcemap)) {
- // System.out.println("Searching bean " + name+ " in "+VALUE_SITEMAP);
- bean = objectModel.get(name);
-
- if (bean!=null) {
- return bean;
- }
- }
-
- if ((sourcemap==null) || VALUE_REQUEST.equals(sourcemap)) {
- // System.out.println("Searching bean " + name+ " in "+ VALUE_REQUEST);
- bean = request.getAttribute(name);
-
- if (bean!=null) {
- return bean;
- }
- }
-
- if ((sourcemap==null) || VALUE_SESSION.equals(sourcemap)) {
- // System.out.println("Searching bean " + name+ " in "+VALUE_SESSION);
- Session session = request.getSession(false);
-
- if (session!=null) {
- bean = session.getAttribute(name);
-
- if (bean!=null) {
- return bean;
- }
- }
- }
-
- return null;
- }
-
/**
* Pass along Velocity log messages to our configured logger.
*
* @see org.apache.velocity.runtime.log.LogSystem#logVelocityMessage
- *
- * @param level
- * @param message
*/
public void logVelocityMessage(int level, String message) {
switch (level) {
- case LogSystem.WARN_ID :
+ case LogSystem.WARN_ID:
getLogger().warn(message);
break;
-
- case LogSystem.INFO_ID :
+ case LogSystem.INFO_ID:
getLogger().info(message);
break;
-
- case LogSystem.DEBUG_ID :
+ case LogSystem.DEBUG_ID:
getLogger().debug(message);
break;
-
- case LogSystem.ERROR_ID :
+ case LogSystem.ERROR_ID:
getLogger().error(message);
break;
-
default :
getLogger().info(message);
break;