coliver 2003/02/23 11:20:25
Modified: src/java/org/apache/cocoon/components/flow/javascript
system.js ScriptablePointer.java
ScriptablePropertyPointer.java
ScriptablePropertyHandler.java
Log:
'Updated to support using the Cocoon flow layer with XMLForm'
Revision Changes Path
1.7 +264 -0
xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/system.js
Index: system.js
===================================================================
RCS file:
/home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/system.js,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- system.js 9 Jan 2003 03:41:37 -0000 1.6
+++ system.js 23 Feb 2003 19:20:25 -0000 1.7
@@ -112,3 +112,267 @@
cocoon.outputModuleRollback(type);
}
}
+
+//
+// XMLForm Support
+//
+
+/**
+ * Creates a new JavaScript wrapper of a Form object
+ * see org.apache.cocoon.components.xmlform.Form
+ * @param id form id
+ * @param validatorNS Namespace of validator
+ * @param validatorDoc Validator document
+ */
+
+function XForm(id, validatorNS, validatorDoc) {
+ cocoon.createSession();
+ this.id = id;
+ this.lastContinuation = null;
+ XForm.forms[id] = this;
+ this.validatorNS = validatorNS;
+ this.validatorDoc = validatorDoc;
+ this.dead = false;
+}
+
+/**
+ * Global variable that stores XForm instances by id
+ */
+XForm.forms = {};
+
+/**
+ * Return the model object of this form
+ * @return a Java bean, JavaScript, DOM, or JDOM object
+ */
+XForm.prototype.getModel = function() {
+ return this.form.getModel();
+}
+
+/**
+ * Set the model object of this form
+ * @param model Any Java bean, JavaScript, DOM, or JDOM object
+ */
+XForm.prototype.setModel = function(model) {
+ this.form =
+ new Packages.org.apache.cocoon.components.xmlform.Form(this.id,
+ model);
+ this.form.setAutoValidate(false);
+ if (this.validatorNS != undefined && this.validatorDoc != undefined) {
+ this._setValidator(this.validatorNS, this.validatorDoc);
+ }
+}
+
+/**
+ * Creates a new web continuation
+ * @param lastCont previous web continuation
+ * @param timeToLive expiration time for this continuation
+ * @return a new WebContinuation instance
+ */
+XForm.prototype.start = function(lastCont, timeToLive) {
+ var k = new Continuation();
+ var kont = new WebContinuation(cocoon, k,
+ lastCont, timeToLive);
+ return kont;
+}
+
+/**
+ * Adds a violation to this form
+ * @param xpath xpath expression of field that contains invalid data
+ * @param message error message
+ */
+XForm.prototype.addViolation = function(xpath, message) {
+ var violation =
+ new Packages.org.apache.cocoon.components.validation.Violation();
+ violation.path = xpath;
+ violation.message = message;
+ var list = new java.util.LinkedList();
+ list.add(violation);
+ try {
+ this.form.addViolations(list);
+ } catch (e) {
+ print(e);
+ if (e instanceof java.lang.Throwable) {
+ e.printStackTrace();
+ }
+ }
+}
+
+/**
+ * Computes the value of an xpath expression against the model of this form
+ * @param expr xpath expression
+ * @return result of computing <code>expr</code>
+ */
+XForm.prototype.xpath = function(expr) {
+ var ctx = new
Packages.org.apache.commons.jxpath.JXPathContext.newContext(this.form.getModel());
+ return ctx.getValue(expr);
+}
+
+XForm.prototype._sendView = function(uri, lastCont, timeToLive) {
+ var k = new Continuation();
+ var kont = new WebContinuation(cocoon, k, lastCont, timeToLive);
+ var bizData = this.form.getModel();
+ if (bizData == undefined) {
+ bizData = null;
+ }
+ cocoon.forwardTo("cocoon://" + cocoon.environment.getURIPrefix() + uri,
+ bizData, kont);
+ this.lastContinuation = kont;
+ suicide();
+}
+
+/**
+ * Sends view to presentation pipeline and waits for subsequent submission.
+ * Automatically resends view if validation fails.
+ * Creates two continuations: one immediately before the page submission
+ * and one immediately after. These are used to implement automated support
+ * for back/forward navigation in the form. When you move forward in the
+ * form the second continuation is invoked. When you move back from the
+ * following page the first continuation is invoked.
+ * @param phase view to send (and phase to validate)
+ * @param uri presentation pipeline resource identifier
+ * @param validator optional function invoked to perform validation
+ */
+XForm.prototype.sendView = function(phase, uri, validator) {
+ var lastCont = this.lastContinuation;
+ this.form.clearViolations();
+ var view = this.form.getFormView(cocoon.environment.objectModel);
+ while (true) {
+ var k = this.start(lastCont);
+ if (cocoon.request == null) {
+ // this continuation has been invalidated
+ this.dead = true;
+ handleInvalidContinuation();
+ suicide();
+ }
+ cocoon.request.setAttribute("view", view);
+ try {
+ this.form.save(cocoon.environment.objectModel, "request");
+ } catch (e if (e instanceof java.lang.IllegalStateException)) {
+ if (cocoon.session.getAttribute(this.id) != null) {
+ // someone else has taken my session
+ this.dead = true;
+ cocoon.removeSession();
+ handleInvalidContinuation();
+ suicide();
+ }
+ throw e;
+ }
+ this._sendView(uri, k);
+ if (this.dead || cocoon.request == null) {
+ // this continuation has been invalidated
+ handleInvalidContinuation();
+ suicide();
+ }
+ this.form.populate(cocoon.environment.objectModel);
+ if (validator != undefined) {
+ validator(this);
+ }
+ this.form.validate(phase);
+ if (this.form.violationsAsSortedSet == null ||
+ this.form.violationsAsSortedSet.size() == 0) {
+ break;
+ }
+ }
+}
+
+XForm.prototype._setValidator = function(schNS, schDoc) {
+ // if validator params are not specified, then
+ // there is no validation by default
+ if (schNS == null || schDoc == null ) return null;
+ var resolver = cocoon.environment;
+ var schemaSrc = resolver.resolveURI( schDoc );
+ try {
+ var is =
Packages.org.apache.cocoon.components.source.SourceUtil.getInputSource(schemaSrc);
+ var schf =
Packages.org.apache.cocoon.components.validation.SchemaFactory.lookup ( schNS );
+ var sch = schf.compileSchema ( is );
+ this.form.setValidator(sch.newValidator());
+ } finally {
+ resolver.release(schemaSrc);
+ }
+}
+
+/**
+ * Sends view to presentation pipeline but doesn't wait for submission
+ * @param view view to send
+ * @param uri presentation pipeline uri
+ */
+
+XForm.prototype.finish = function(view, uri) {
+ try {
+ this.form.save(cocoon.environment.objectModel, "request");
+ } catch (e if (e instanceof java.lang.IllegalStateException)) {
+ if (cocoon.session.getAttribute(this.id) != null) {
+ // someone else has taken my session
+ this.dead = true;
+ cocoon.removeSession();
+ handleInvalidContinuation();
+ suicide();
+ }
+ throw e;
+ }
+ cocoon.forwardTo("cocoon://" + cocoon.environment.getURIPrefix() + uri,
+ this.form.getModel(), null);
+ delete XForm.forms[this.id]; // delete myself
+ this.dead = true;
+ cocoon.removeSession();
+ if (this.lastContinuation != null) {
+ this.lastContinuation.invalidate();
+ this.lastContinuation = null;
+ }
+
+}
+
+/**
+ * Entry point to a flow-based XMLForm application. Replaces the functionality
+ * of XMLForm actions.
+ * @param application Name of a JavaScript function that represents the page flow
for a form
+ * @param id form id
+ * @param validator_ns XML namespace of validator
+ * @param validator_doc validator document
+ */
+
+function xmlForm(application, id, validator_ns, validator_doc) {
+ function getCommand() {
+ if (cocoon.request == null) {
+ return undefined;
+ }
+ var enum_ = cocoon.request.parameterNames;
+ var command = undefined;
+ while (enum_.hasMoreElements()) {
+ var paramName = enum_.nextElement();
+ // search for the command
+ if
(paramName.startsWith(Packages.org.apache.cocoon.Constants.ACTION_PARAM_PREFIX)) {
+ command =
+
paramName.substring(Packages.org.apache.cocoon.Constants.ACTION_PARAM_PREFIX.length(),
paramName.length());
+ break;
+ }
+ }
+ // command encodes the continuation id for "back" or "next" actions
+ return command;
+ }
+ var command = getCommand();
+ if (command != undefined) {
+ var xform = XForm.forms[id];
+ if (xform != undefined) {
+ // invoke a continuation
+ var continuationsMgr =
+
cocoon.componentManager.lookup(Packages.org.apache.cocoon.components.flow.ContinuationsManager.ROLE);
+ var wk = continuationsMgr.lookupWebContinuation(command);
+ cocoon.componentManager.release(continuationsMgr);
+ if (wk != null) {
+ var jswk = wk.userObject;
+ xform.form.clearViolations();
+ jswk.continuation(jswk);
+ } else {
+ handleInvalidContinuation(command);
+ }
+ }
+ }
+ // Just start a new instance of the application
+ cocoon.session.removeAttribute(id);
+ this[application](new XForm(id, validator_ns, validator_doc));
+}
+
+
+
+
1.2 +15 -0
xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/ScriptablePointer.java
Index: ScriptablePointer.java
===================================================================
RCS file:
/home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/ScriptablePointer.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- ScriptablePointer.java 20 Feb 2003 18:22:43 -0000 1.1
+++ ScriptablePointer.java 23 Feb 2003 19:20:25 -0000 1.2
@@ -47,6 +47,7 @@
import org.mozilla.javascript.*;
import org.apache.commons.jxpath.ri.QName;
import org.apache.commons.jxpath.*;
+import org.apache.commons.jxpath.ri.model.dynamic.*;
import org.apache.commons.jxpath.ri.model.beans.*;
import org.apache.commons.jxpath.ri.model.*;
import java.util.Locale;
@@ -65,6 +66,13 @@
node = object;
}
+ public boolean isCollection() {
+ if (node instanceof NativeArray) {
+ return true;
+ }
+ return false;
+ }
+
public ScriptablePointer(QName name,
Scriptable object,
Locale locale) {
@@ -84,6 +92,13 @@
public PropertyPointer getPropertyPointer(){
return new ScriptablePropertyPointer(this, handler);
+ }
+
+ public void setValue(Object value){
+ System.out.println("attempt to set " + getName() + " to " + value);
+ System.out.println("node="+node + "->"
+java.util.Arrays.asList(node.getIds()));
+ getParent().setValue(value);
+ //handler.setProperty(node, getName().toString(), value);
}
}
1.2 +110 -15
xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/ScriptablePropertyPointer.java
Index: ScriptablePropertyPointer.java
===================================================================
RCS file:
/home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/ScriptablePropertyPointer.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- ScriptablePropertyPointer.java 20 Feb 2003 18:22:43 -0000 1.1
+++ ScriptablePropertyPointer.java 23 Feb 2003 19:20:25 -0000 1.2
@@ -47,10 +47,11 @@
import org.mozilla.javascript.*;
import org.apache.commons.jxpath.ri.QName;
import org.apache.commons.jxpath.*;
-import org.apache.commons.jxpath.ri.model.beans.*;
+import org.apache.commons.jxpath.ri.model.dynamic.*;
import org.apache.commons.jxpath.ri.model.*;
-import org.apache.commons.jxpath.util.*;
import java.util.Locale;
+import java.util.LinkedList;
+import java.util.List;
public class ScriptablePropertyPointer extends DynamicPropertyPointer {
@@ -66,6 +67,9 @@
Object obj = getBaseValue();
if (obj instanceof Scriptable) {
Scriptable node = (Scriptable)obj;
+ if (node instanceof NativeArray) {
+ return (int)((NativeArray)node).jsGet_length();
+ }
if (ScriptableObject.hasProperty(node, "length")) {
Object val = ScriptableObject.getProperty(node, "length");
if (val instanceof Number) {
@@ -76,32 +80,123 @@
return super.getLength();
}
- public Object getNodeValue() {
- Object value;
+ public Object getValue() {
+ Object val = getNode();
+ if (val instanceof NativeArray) {
+ NativeArray arr = (NativeArray)val;
+ List list = new LinkedList();
+ int len = (int)arr.jsGet_length();
+ for (int i = 0; i < len; i++) {
+ Object obj = arr.get(i, arr);
+ if (obj == Undefined.instance) {
+ obj = null;
+ }
+ list.add(obj);
+ }
+ return list;
+ }
+ return super.getValue();
+ }
+
+ public void setValue(Object value){
if (index == WHOLE_COLLECTION){
- value = handler.getProperty(getBean(), getPropertyName());
+ handler.setProperty(getBean(), getPropertyName(), value);
} else {
- value = handler.getProperty(getBean(), getPropertyName());
- if (value instanceof Scriptable &&
- ScriptableObject.hasProperty((Scriptable)value, index)) {
- value = ScriptableObject.getProperty((Scriptable)value, index);
+ Object val = handler.getProperty(getBean(), getPropertyName());
+ if (val instanceof Scriptable) {
+ ScriptableObject.putProperty((Scriptable)val, index, value);
} else {
- return super.getNodeValue();
+ super.setValue(value);
}
}
- return value;
}
- public void setValue(Object value){
+ public void remove(){
if (index == WHOLE_COLLECTION){
- handler.setProperty(getBean(), getPropertyName(), value);
+ handler.setProperty(getBean(), "length", new Integer(0));
} else {
Object val = handler.getProperty(getBean(), getPropertyName());
if (val instanceof Scriptable) {
- ScriptableObject.putProperty((Scriptable)val, index, value);
+ try {
+ ScriptableObject.deleteProperty((Scriptable)val, index);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
} else {
- super.setValue(value);
+ super.remove();
+ }
+ }
+ }
+
+ public boolean isCollection() {
+ Object obj = getBaseValue();
+ if (obj instanceof NativeArray) {
+ return true;
+ }
+ return super.isCollection();
+ }
+
+ public String asPath(){
+ Object obj = getBaseValue();
+ if (!(obj instanceof Scriptable)) {
+ return super.asPath();
+ }
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(getParent().asPath());
+ if (buffer.length() == 0){
+ buffer.append("/.");
+ }
+ else if (buffer.charAt(buffer.length() - 1) == '/'){
+ buffer.append('.');
+ }
+ buffer.append("[EMAIL PROTECTED] = '");
+ buffer.append(escape(getPropertyName()));
+ buffer.append("']");
+ if (index != WHOLE_COLLECTION && (obj instanceof NativeArray)) {
+ buffer.append('[').append(index+1).append(']');
+ }
+ return buffer.toString();
+ }
+
+ private String escape(String string){
+ int index = string.indexOf('\'');
+ while (index != -1){
+ string = string.substring(0, index) + "'" + string.substring(index
+ 1);
+ index = string.indexOf('\'');
+ }
+ index = string.indexOf('\"');
+ while (index != -1){
+ string = string.substring(0, index) + """ + string.substring(index
+ 1);
+ index = string.indexOf('\"');
+ }
+ return string;
+ }
+
+ public Object getImmediateNode() {
+ System.out.println("get immediate node: " + getBean() + ": " +
+ getPropertyName() + ": " +
+ index);
+ Object value;
+ if (index == WHOLE_COLLECTION) {
+ value = handler.getProperty(getBean(), getPropertyName());
+ }
+ else {
+ value = handler.getProperty(getBean(), getPropertyName());
+ if (value instanceof Scriptable) {
+ System.out.println("getting indexed property: " +
+ getPropertyName() + ": " +
+ index);
+ value = ScriptableObject.getProperty((Scriptable)value, index);
+ if (value == ScriptableObject.NOT_FOUND) {
+ value = null;
+ } else if (value instanceof Wrapper) {
+ value = ((Wrapper)value).unwrap();
+ }
+ } else {
+ return super.getImmediateNode();
}
}
+ return value;
}
+
}
1.2 +33 -7
xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/ScriptablePropertyHandler.java
Index: ScriptablePropertyHandler.java
===================================================================
RCS file:
/home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/flow/javascript/ScriptablePropertyHandler.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- ScriptablePropertyHandler.java 20 Feb 2003 18:22:43 -0000 1.1
+++ ScriptablePropertyHandler.java 23 Feb 2003 19:20:25 -0000 1.2
@@ -46,25 +46,44 @@
package org.apache.cocoon.components.flow.javascript;
import org.apache.commons.jxpath.DynamicPropertyHandler;
import org.mozilla.javascript.*;
+import java.util.List;
+import java.util.LinkedList;
public class ScriptablePropertyHandler implements DynamicPropertyHandler {
public Object getProperty(Object obj, String propertyName) {
Context cx = null;
+ System.out.println("getProperty: " + propertyName);
try {
cx = Context.enter();
Scriptable s = (Scriptable)obj;
+ System.out.println("s="+s);
Object result = ScriptableObject.getProperty(s, propertyName);
if (result == ScriptableObject.NOT_FOUND) {
- result = null;
+ result = ScriptableObject.getProperty(s, "get" +
propertyName.substring(0, 1).toUpperCase() + (propertyName.length() > 1 ?
propertyName.substring(1) : ""));
+ if (result != ScriptableObject.NOT_FOUND &&
+ result instanceof Function) {
+ try {
+ result = ((Function)result).call(cx,
+
ScriptableObject.getTopLevelScope(s), s, new Object[] {});
+ } catch (JavaScriptException exc) {
+ exc.printStackTrace();
+ result = Undefined.instance;
+ }
+ }
+ if (result == Undefined.instance ||
+ result == Scriptable.NOT_FOUND) {
+ result = null;
+ }
} else if (result instanceof Wrapper) {
result = ((Wrapper)result).unwrap();
} else if (result == Undefined.instance) {
result = null;
}
+ System.out.println("getProperty: " + propertyName + " = " + result);
return result;
} finally {
- cx.exit();
+ Context.exit();
}
}
@@ -84,7 +103,7 @@
}
return result;
} finally {
- cx.exit();
+ Context.exit();
}
}
@@ -93,12 +112,19 @@
Context cx = null;
try {
cx = Context.enter();
+ System.out.println("setProperty: " + propertyName +
+ " = " + Context.toObject(value, (Scriptable)obj));
+ if (!(value == null
+ || value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean)) {
+ value = Context.toObject(value,
+ (Scriptable)obj);
+ }
ScriptableObject.putProperty((Scriptable)obj,
- propertyName,
- Context.toObject(value,
- (Scriptable)obj));
+ propertyName, value);
} finally {
- cx.exit();
+ Context.exit();
}
}
}