You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by sy...@apache.org on 2005/04/14 14:32:34 UTC

svn commit: r161264 [1/2] - in cocoon: blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/ blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/ blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/ blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/ blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/v3/ blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/ blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/ blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/ blocks/core/forms/trunk/java/org/apache/cocoon/forms/samples/ blocks/core/forms/trunk/java/org/apache/cocoon/forms/util/ blocks/core/forms/trunk/samples/ blocks/core/forms/trunk/samples/flow/ blocks/core/forms/trunk/samples/forms/ blocks/core/forms/trunk/samples/forms/binding/ blocks/core/forms/trunk/samples/resources/ trunk/src/java/org/apache/cocoon/transformation/

Author: sylvain
Date: Thu Apr 14 05:32:26 2005
New Revision: 161264

URL: http://svn.apache.org/viewcvs?view=rev&rev=161264
Log:
Merge with 2.1, add initial implementation of transparent AJAX support for CForms

Added:
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js   (with props)
    cocoon/trunk/src/java/org/apache/cocoon/transformation/BrowserUpdateTransformer.java   (with props)
Modified:
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/JXPathBindingBase.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/MultiValueJXPathBinding.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/BeanConvertor.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/BeanConvertorBuilder.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/HtmlCleanerConfiguration.xml
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/BeanType.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/BeanTypeBuilder.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/v3/ScriptableWidget.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AggregateField.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AggregateFieldDefinition.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/BooleanField.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Field.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/FieldDefinition.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Form.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Messages.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/MultiValueField.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/MultiValueFieldDefinition.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Output.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Repeater.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Union.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Upload.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/jx-macros.xml
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/forms-lib.js
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/samples/Form2Bean.java
    cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/util/XMLAdapter.java
    cocoon/blocks/core/forms/trunk/samples/flow/binding_example.js
    cocoon/blocks/core/forms/trunk/samples/flow/bindings.js
    cocoon/blocks/core/forms/trunk/samples/flow/forms_flow_example.js
    cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-bind.xml
    cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-data.xml
    cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-def.xml
    cocoon/blocks/core/forms/trunk/samples/forms/carselector_template.xml
    cocoon/blocks/core/forms/trunk/samples/forms/datasource_chooser_template.xml
    cocoon/blocks/core/forms/trunk/samples/forms/dynamicrepeater_template.xml
    cocoon/blocks/core/forms/trunk/samples/forms/form2_bind_bean.xml
    cocoon/blocks/core/forms/trunk/samples/forms/form2_jx.xml
    cocoon/blocks/core/forms/trunk/samples/forms/form2_model.xml
    cocoon/blocks/core/forms/trunk/samples/forms/tasktree_template.xml
    cocoon/blocks/core/forms/trunk/samples/resources/forms-advanced-field-styling.xsl
    cocoon/blocks/core/forms/trunk/samples/resources/forms-calendar-styling.xsl
    cocoon/blocks/core/forms/trunk/samples/resources/forms-field-styling.xsl
    cocoon/blocks/core/forms/trunk/samples/resources/forms-page-styling.xsl
    cocoon/blocks/core/forms/trunk/samples/sitemap.xmap

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/JXPathBindingBase.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/JXPathBindingBase.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/JXPathBindingBase.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/JXPathBindingBase.java Thu Apr 14 05:32:26 2005
@@ -15,6 +15,7 @@
  */
 package org.apache.cocoon.forms.binding;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -24,7 +25,9 @@
 import org.apache.cocoon.forms.formmodel.Widget;
 import org.apache.cocoon.util.jxpath.DOMFactory;
 import org.apache.commons.jxpath.JXPathContext;
-import org.w3c.dom.Node;
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.model.beans.BeanPropertyPointer;
+import org.apache.commons.jxpath.util.TypeUtils;
 
 /**
  * Provides a base class for hooking up Binding implementations that use the
@@ -244,9 +247,7 @@
         if (!(objModel instanceof JXPathContext)) {
             jxpc = JXPathContext.newContext(objModel);
             jxpc.setLenient(true);
-            if (objModel instanceof Node) {
-                jxpc.setFactory(new DOMFactory());
-            }
+            jxpc.setFactory(new BindingJXPathFactory());
         } else {
             jxpc = (JXPathContext) objModel;
         }
@@ -264,5 +265,77 @@
 
     protected Logger getLogger() {
         return logger;
+    }
+    
+    /**
+     * JXPath factory that combines the DOMFactory and support for collections.
+     */
+    private static class BindingJXPathFactory extends DOMFactory {
+        
+        public boolean createObject(JXPathContext context, Pointer pointer, Object parent, String name, int index) {
+            if (createCollectionItem(context, pointer, parent, name, index)) {
+                return true;
+            // AG: If this is a bean, then the object is supposed to exists.  
+            } else if (pointer instanceof BeanPropertyPointer) {
+                return createBeanField(context, pointer, parent, name, index);
+            } else {
+                return super.createObject(context, pointer, parent, name, index);
+            }
+        }
+        
+        private boolean createCollectionItem(JXPathContext context, Pointer pointer, Object parent, String name, int index) {
+            // FIXME: don't clearly understand how this works.
+            // see http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=111148567029114&w=2
+            final Object o = context.getValue(name);
+            if (o == null) {
+                return false;
+            } 
+            if (o instanceof Collection) {
+                ((Collection)o).add(null);
+            } else if(o.getClass().isArray()) {
+                // not yet supported
+                return false;
+            } else {
+                return false;
+            }
+            return true;
+        }
+
+        // AG: Create the Object for the field as defined in the Bean.
+        // The value we will set here is not important. JXPath knows that it is UNITIALIZED.
+        // if we set it Pointer Value to null then the code will throw an exception.
+        //
+        // In short, there is no harm. The value will never show up.
+        // TODO: Manage other forms' types as Date, Bean and others not covered by this method.
+        private boolean createBeanField(JXPathContext context, Pointer pointer, Object parent, String name, int index) {
+            try {
+                Class clazz = parent.getClass().getDeclaredField(name).getType();
+                Object o = context.getValue(name);
+                if (o == null) {
+                    final Class[] parametersTypes = {String.class};
+                    final Object[] initArgs = {"0"};
+                    try {
+                        // AG: Here we service Booleans, Strings and Number() + his Direct know subclasses:
+                        // (BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short)
+                        // as well as other classes that use an String as Constructor parameter.
+                        o = clazz.getConstructor(parametersTypes).newInstance(initArgs);
+                    } catch (Exception e) {
+                        // AG: The class has not a constructor using a String as a parameter.
+                        // ie: Boolean(String), Integer(String), etc.
+                        // Lets try with a constructor with no parameters. ie: Number().
+                        o = clazz.newInstance();
+                    }
+                } else if (TypeUtils.canConvert(o, clazz)) {
+                    o = TypeUtils.convert(o, clazz);
+                }
+                if (o != null) {
+                    pointer.setValue(o);
+                    return true;  // OK. We have an initial Object of the right Class initialized.
+                }
+            } catch (Exception e) {
+                // TODO: Output info in logs.
+            }
+            return false;
+        }
     }
 }

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/MultiValueJXPathBinding.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/MultiValueJXPathBinding.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/MultiValueJXPathBinding.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/binding/MultiValueJXPathBinding.java Thu Apr 14 05:32:26 2005
@@ -104,24 +104,7 @@
         Object[] values = (Object[])widget.getValue();
 
         JXPathContext multiValueContext = jctx.getRelativeContext(jctx.createPath(this.multiValuePath));
-        multiValueContext.setFactory( new AbstractFactory() {
-            public boolean createObject(JXPathContext context, Pointer pointer,
-                                        Object parent, String name, int index) {
-                final Object o = context.getValue(name);
-                if( o == null ) {
-                    return false;
-                }
-                if( Collection.class.isAssignableFrom( o.getClass() ) ) {
-                    ((Collection)context.getValue(name)).add(null);
-                } else if( o.getClass().isArray() ) {
-                    // not yet supported
-                    return false;
-                } else {
-                    return false;
-                }
-                return true;
-            }
-        });
+
         // Delete all that is already present
         multiValueContext.removeAll(this.rowPath);
 

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/BeanConvertor.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/BeanConvertor.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/BeanConvertor.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/BeanConvertor.java Thu Apr 14 05:32:26 2005
@@ -37,7 +37,7 @@
  * </p>
  *
  * @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
- * @version $Id: BeanConvertor.java,v 1.3 2004/12/30 13:37:45 giacomo Exp $
+ * @version $Id$
  */
 public class BeanConvertor
     implements Convertor

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/BeanConvertorBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/BeanConvertorBuilder.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/BeanConvertorBuilder.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/BeanConvertorBuilder.java Thu Apr 14 05:32:26 2005
@@ -36,7 +36,7 @@
  * </p>
  *
  * @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
- * @version $Id: BeanConvertorBuilder.java,v 1.2 2004/12/27 13:30:48 giacomo Exp $
+ * @version $Id$
  */
 public class BeanConvertorBuilder
     implements ConvertorBuilder

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/HtmlCleanerConfiguration.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/HtmlCleanerConfiguration.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/HtmlCleanerConfiguration.xml (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/convertor/HtmlCleanerConfiguration.xml Thu Apr 14 05:32:26 2005
@@ -1,3 +1,4 @@
+<?xml version="1.0"?>
 <!--
   Copyright 1999-2004 The Apache Software Foundation
 

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/BeanType.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/BeanType.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/BeanType.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/BeanType.java Thu Apr 14 05:32:26 2005
@@ -22,7 +22,7 @@
  * The CForm type of a bean
  *
  * @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
- * @version $Id: BeanType.java,v 1.1 2004/12/21 14:37:32 giacomo Exp $
+ * @version $Id$
  */
 public class BeanType
     extends AbstractDatatype

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/BeanTypeBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/BeanTypeBuilder.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/BeanTypeBuilder.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/datatype/typeimpl/BeanTypeBuilder.java Thu Apr 14 05:32:26 2005
@@ -25,7 +25,7 @@
  * Builder for {@link BeanType}
  *
  * @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
- * @version $Id: BeanTypeBuilder.java,v 1.1 2004/12/21 14:37:32 giacomo Exp $
+ * @version $Id$
  */
 public class BeanTypeBuilder
     extends AbstractDatatypeBuilder

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js Thu Apr 14 05:32:26 2005
@@ -87,25 +87,32 @@
 /**
  * Manages the display of a form and its validation.
  *
- * This uses some additionnal propertied on the form object :
+ * This uses some additionnal properties on the form object :
  * - "locale" : the form locale (default locale is used if not set)
+ * - "cleanupHook": a function called after having sent the page displaying the form. This is equivalent
+ *       to the "fun" argument of sendPageAndWait(), which allows to perform some cleanup when the pipeline
+ *       has been processed. The function is called with a single parameter which is the form it is attached to.
+ * - "restoreHook": a function called before processing the form when it has been submitted by
+ *       the browser. This allows to restore some environment that is needed by the form processing.
+ *       The function is called with a single parameter which is the form it is attached to.
  *
  * On return, the calling code can check some properties to know the form result :
  * - "isValid" : true if the form was sucessfully validated
  * - "submitId" : the id of the widget that triggered the form submit (can be null)
  *
  * @parameter uri the page uri (like in cocoon.sendPageAndWait())
- * @parameter bizdata some business data for the view (like in cocoon.sendPageAndWait()).
+ * @parameter viewdata some data for the view (like in cocoon.sendPageAndWait()).
  *            The "{FormsPipelineConfig.CFORMSKEY}" and "locale" properties are added to this object.
+ * @parameter ttl the time to live of the continuation used to display the form
  */
-Form.prototype.showForm = function(uri, bizData, fun, ttl) {
+Form.prototype.showForm = function(uri, viewdata, ttl) {
 
-    if (bizData == undefined) bizData = new Object();
-    bizData[Packages.org.apache.cocoon.forms.transformation.FormsPipelineConfig.CFORMSKEY] = this.form;
+    if (viewdata == undefined) viewdata = new Object();
+    viewdata[Packages.org.apache.cocoon.forms.transformation.FormsPipelineConfig.CFORMSKEY] = this.form;
 
     if (this.locale == null)
         this.locale = java.util.Locale.getDefault();
-    bizData["locale"] = this.locale;
+    viewdata["locale"] = this.locale;
 
     // Keep the first continuation that will be created as the result of this function
     var result = null;
@@ -118,13 +125,24 @@
 
     if (comingBack) {
         // We come back to the bookmark: process the form
+        
+        if (cocoon.request.getParameter("cocoon-ajax-continue") != null) {
+            //TODO(SW): when exiting AJAX rountrips, better send a new continuation
+            //to ensure we really come back from a "100-continue" response (see below)
+            //and this isn't a forged request
+            return bookmark;
+        }
+        
+	    if (this.restoreHook) {
+	        this.restoreHook(this);
+	    }
         var formContext = new Packages.org.apache.cocoon.forms.FormContext(cocoon.request, this.locale);
 
-        // Prematurely add the bizData as in the object model so that event listeners can use it 	 
+        // Prematurely add the viewdata as in the object model so that event listeners can use it 	 
         // (the same is done by cocoon.sendPage()) 	 
         // FIXME : hack needed because FOM doesn't provide access to the object model 	 
         var objectModel = org.apache.cocoon.components.ContextHelper.getObjectModel(this.avalonContext); 	 
-        org.apache.cocoon.components.flow.FlowHelper.setContextObject(objectModel, bizData); 	 
+        org.apache.cocoon.components.flow.FlowHelper.setContextObject(objectModel, viewdata); 	 
 
         finished = this.form.process(formContext);
         if (finished) {
@@ -132,14 +150,26 @@
             var widget = this.form.getSubmitWidget();
             // Can be null on "normal" submit
             this.submitId = widget == null ? null : widget.getId();
+            
+            if (cocoon.request.getParameter("cocoon-ajax") != null) {
+                // Ask the client to load the page
+                cocoon.response.setHeader("X-Cocoon-Ajax", "continue");
+                cocoon.response.setHeader("Content-Length", "0");
+                cocoon.sendStatus(200); // Continue
+                FOM_Cocoon.suicide();
+            }
+            
             return bookmark;
         }
     }
     comingBack = true;
-    cocoon.sendPage(uri, bizData, bookmark);
-    if (fun && fun instanceof Function) {
-        fun();
+    cocoon.sendPage(uri, viewdata, bookmark);
+    
+    // Clean up after sending the page
+    if (this.cleanupHook) {
+        this.cleanupHook(this);
     }
+    
     FOM_Cocoon.suicide();
 }
 

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/v3/ScriptableWidget.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/v3/ScriptableWidget.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/v3/ScriptableWidget.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/v3/ScriptableWidget.java Thu Apr 14 05:32:26 2005
@@ -48,6 +48,7 @@
 import org.apache.cocoon.forms.validation.ValidationError;
 import org.apache.cocoon.forms.validation.ValidationErrorAware;
 import org.apache.cocoon.forms.validation.WidgetValidator;
+import org.apache.commons.lang.BooleanUtils;
 import org.mozilla.javascript.Context;
 import org.mozilla.javascript.Function;
 import org.mozilla.javascript.JavaScriptException;
@@ -390,7 +391,7 @@
             delegate.setValue(value);
         } else if (delegate instanceof BooleanField) {
             BooleanField field = (BooleanField)delegate;
-            field.setValue(Boolean.valueOf(Context.toBoolean(value)));
+            field.setValue(BooleanUtils.toBooleanObject(Context.toBoolean(value)));
         } else if (delegate instanceof Repeater) {
             Repeater repeater = (Repeater)delegate;
             if (value instanceof NativeArray) {

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java Thu Apr 14 05:32:26 2005
@@ -131,8 +131,8 @@
      * @return the form where this widget belongs to.  
      */
     public Form getForm() {
-        Widget myParent = getParent();
         if (this.form == null) {
+            Widget myParent = getParent();
             if (myParent == null) {
                 this.form = (Form)this;
             } else {
@@ -151,6 +151,9 @@
             throw new IllegalArgumentException("A widget state cannot be set to null");
         }
         this.state = state;
+
+        // Update the browser
+        getForm().addWidgetUpdate(this);
     }
 
     public WidgetState getCombinedState() {
@@ -419,8 +422,9 @@
      */
     public void generateSaxFragment(ContentHandler contentHandler, Locale locale)    
     throws SAXException {
-        if (getCombinedState().isDisplayingValues()) {
 
+        if (getCombinedState().isDisplayingValues()) {
+            // FIXME: we may want to strip out completely widgets that aren't updated when in AJAX mode
             String element = this.getXMLElementName();
             AttributesImpl attrs = getXMLElementAttributes();
             contentHandler.startElement(Constants.INSTANCE_NS, element, Constants.INSTANCE_PREFIX_COLON + element, attrs);
@@ -430,6 +434,13 @@
             generateItemSaxFragment(contentHandler, locale);
 
             contentHandler.endElement(Constants.INSTANCE_NS, element, Constants.INSTANCE_PREFIX_COLON + element);
+
+        } else {
+            // Generate a placeholder that can be used later by AJAX updates
+            AttributesImpl attrs = new AttributesImpl();
+            attrs.addCDATAAttribute("id", getRequestParameterName());
+            contentHandler.startElement(Constants.INSTANCE_NS, "placeholder", Constants.INSTANCE_PREFIX_COLON + "placeholder", attrs);
+            contentHandler.endElement(Constants.INSTANCE_NS, "placeholder", Constants.INSTANCE_PREFIX_COLON + "placeholder");
         }
     }
 

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AggregateField.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AggregateField.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AggregateField.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AggregateField.java Thu Apr 14 05:32:26 2005
@@ -71,6 +71,10 @@
     public final AggregateFieldDefinition getAggregateFieldDefinition() {
         return (AggregateFieldDefinition)getDefinition();
     }
+    
+    public void initialize() {
+        this.selectionList = getAggregateFieldDefinition().getSelectionList();
+    }
 
     public void addChild(Widget widget) {
     	if (!(widget instanceof Field)) {

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AggregateFieldDefinition.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AggregateFieldDefinition.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AggregateFieldDefinition.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/AggregateFieldDefinition.java Thu Apr 14 05:32:26 2005
@@ -117,8 +117,6 @@
 
     public Widget createInstance() {
         AggregateField aggregateField = new AggregateField(this);
-        // Set the initial selection list, if any
-        aggregateField.setSelectionList(getSelectionList());
 
         Iterator fieldDefinitionIt = container.getWidgetDefinitions().iterator();
         while (fieldDefinitionIt.hasNext()) {

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/BooleanField.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/BooleanField.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/BooleanField.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/BooleanField.java Thu Apr 14 05:32:26 2005
@@ -99,6 +99,7 @@
      */
     public void setValidationError(ValidationError error) {
         this.validationError = error;
+        getForm().addWidgetUpdate(this);
     }
 
     /**
@@ -142,7 +143,9 @@
         Object oldValue = value;
         value = (Boolean)object;
         if (!value.equals(oldValue)) {
-            getForm().addWidgetEvent(new ValueChangedEvent(this, oldValue, value));
+            Form form = getForm();
+            form.addWidgetEvent(new ValueChangedEvent(this, oldValue, value));
+            form.addWidgetUpdate(this);
         }
     }
 

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Field.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Field.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Field.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Field.java Thu Apr 14 05:32:26 2005
@@ -134,6 +134,7 @@
         if (value != null) {
             setValue(value);
         }
+        this.selectionList = this.fieldDefinition.getSelectionList();
         super.initialize();
     }
 
@@ -197,6 +198,7 @@
             if (callListeners) {
                 getForm().addWidgetEvent(new ValueChangedEvent(this, oldValue, newValue));
             }
+            getForm().addWidgetUpdate(this);
         }
     }
 
@@ -251,6 +253,8 @@
                 // will lazily compute the new value only if needed.
                 getForm().addWidgetEvent(new DeferredValueChangedEvent(this, oldValue));
             }
+            
+            getForm().addWidgetUpdate(this);
         }
     }
 
@@ -270,8 +274,10 @@
         if (this.valueState == VALUE_PARSED) {
             doValidate();
             this.valueState = VALUE_DISPLAY_VALIDATION;
+            getForm().addWidgetUpdate(this);
         } else if (this.valueState == VALUE_PARSE_ERROR) {
             this.valueState = VALUE_DISPLAY_PARSE_ERROR;
+            getForm().addWidgetUpdate(this);
         }
 
         return this.validationError == null;
@@ -360,6 +366,7 @@
     public void setValidationError(ValidationError error) {
         this.validationError = error;
         this.valueState = VALUE_DISPLAY_VALIDATION;
+        getForm().addWidgetUpdate(this);
     }
 
     public boolean isRequired() {
@@ -427,6 +434,7 @@
             throw new RuntimeException("Tried to assign a SelectionList that is not associated with this widget's datatype.");
         }
         this.selectionList = selectionList;
+        getForm().addWidgetUpdate(this);
     }
 
     /**

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/FieldDefinition.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/FieldDefinition.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/FieldDefinition.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/FieldDefinition.java Thu Apr 14 05:32:26 2005
@@ -25,8 +25,6 @@
 
     public Widget createInstance() {
         Field field = new Field(this);
-        // Set the initial selection list, if any
-        field.setSelectionList(getSelectionList());
         return field;
     }
 

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Form.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Form.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Form.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Form.java Thu Apr 14 05:32:26 2005
@@ -15,7 +15,10 @@
  */
 package org.apache.cocoon.forms.formmodel;
 
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Locale;
+import java.util.Set;
 import java.util.StringTokenizer;
 
 import org.apache.cocoon.forms.FormContext;
@@ -26,6 +29,7 @@
 import org.apache.cocoon.forms.event.WidgetEvent;
 import org.apache.cocoon.forms.event.WidgetEventMulticaster;
 import org.apache.commons.collections.list.CursorableLinkedList;
+import org.apache.commons.lang.BooleanUtils;
 
 /**
  * A widget that serves as a container for other widgets, the top-level widget in
@@ -51,6 +55,10 @@
     //to read their value before events get fired.
     private boolean bufferEvents = false;
     private CursorableLinkedList events;
+    
+    // Widgets that need to be updated in the client when in AJAX mode
+    private Set updatedWidgets;
+
 
     public Form(FormDefinition definition) {
         super(definition);
@@ -101,6 +109,16 @@
             event.getSourceWidget().broadcastEvent(event);
         }
     }
+    
+    public void addWidgetUpdate(Widget widget) {
+        if (this.updatedWidgets != null) {
+            this.updatedWidgets.add(widget.getRequestParameterName());
+        }
+    }
+    
+    public Set getUpdatedWidgets() {
+        return this.updatedWidgets == null ? Collections.EMPTY_SET : this.updatedWidgets;
+    }
 
     /**
      * Fire the events that have been queued.
@@ -203,9 +221,16 @@
      * </ul>
      * This processing can be interrupted by the widgets (or their event listeners) by calling
      * {@link #endProcessing(boolean)}.
+     * <p>
+     * Note that this method is synchronized as a Form is not thread-safe. This should not be a
+     * bottleneck as such concurrent requests can only happen for a single user.
      */
-    public boolean process(FormContext formContext) {
-
+    public synchronized boolean process(FormContext formContext) {
+        // Is this an AJAX request?
+        if (formContext.getRequest().getParameter("cocoon-ajax") != null) {
+            this.updatedWidgets = new HashSet();
+        }
+        
         // Fire the binding phase events
         fireEvents();
 
@@ -273,7 +298,7 @@
      * @param redisplayForm indicates if the form should be redisplayed to the user.
      */
     public void endProcessing(boolean redisplayForm) {
-        this.endProcessing = Boolean.valueOf(!redisplayForm);
+        this.endProcessing = BooleanUtils.toBooleanObject(!redisplayForm);
     }
 
     /**

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Messages.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Messages.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Messages.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Messages.java Thu Apr 14 05:32:26 2005
@@ -70,6 +70,7 @@
      */
     public void addMessage(String message) {
         messages.add(new StringMessage(message));
+        getForm().addWidgetUpdate(this);
     }
 
     /**
@@ -81,6 +82,7 @@
      */
     public void addMessage(XMLizable message) {
         messages.add(message);
+        getForm().addWidgetUpdate(this);
     }
     
     /**

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/MultiValueField.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/MultiValueField.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/MultiValueField.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/MultiValueField.java Thu Apr 14 05:32:26 2005
@@ -67,6 +67,11 @@
         super(definition);
         this.definition = definition;
     }
+    
+    public void initialize() {
+        this.selectionList = this.definition.getSelectionList();
+        super.initialize();
+    }
 
     public WidgetDefinition getDefinition() {
         return definition;
@@ -170,6 +175,7 @@
         } else {
             throw new RuntimeException("Cannot set value of field \"" + getRequestParameterName() + "\" with an object of type " + value.getClass().getName());
         }
+        getForm().addWidgetUpdate(this);
     }
 
     public void setValues(Object[] values) {
@@ -179,6 +185,7 @@
                 throw new RuntimeException("Cannot set value of field \"" + getRequestParameterName() + "\" with an object of type " + values[i].getClass().getName());
         }
         this.values = values;
+        getForm().addWidgetUpdate(this);
     }
 
     /**
@@ -196,6 +203,7 @@
             throw new RuntimeException("Tried to assign a SelectionList that is not associated with this widget's datatype.");
         }
         this.selectionList = selectionList;
+        getForm().addWidgetUpdate(this);
     }
 
     /**
@@ -246,6 +254,7 @@
 
     public void setValidationError(ValidationError error) {
         this.validationError = error;
+        getForm().addWidgetUpdate(this);
     }
 
     public Datatype getDatatype() {

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/MultiValueFieldDefinition.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/MultiValueFieldDefinition.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/MultiValueFieldDefinition.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/MultiValueFieldDefinition.java Thu Apr 14 05:32:26 2005
@@ -23,8 +23,6 @@
 public class MultiValueFieldDefinition extends FieldDefinition {
     public Widget createInstance() {
         MultiValueField field =  new MultiValueField(this);
-        // Set the initial selection list, if any
-        field.setSelectionList(getSelectionList());
         return field;
     }
     

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Output.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Output.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Output.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Output.java Thu Apr 14 05:32:26 2005
@@ -98,5 +98,6 @@
                                        + ", received " + object.getClass() + ".");
         }
         value = object;
+        getForm().addWidgetUpdate(this);
     }
 }

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Repeater.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Repeater.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Repeater.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Repeater.java Thu Apr 14 05:32:26 2005
@@ -76,6 +76,7 @@
         RepeaterRow repeaterRow = new RepeaterRow(definition);
         rows.add(repeaterRow);
         repeaterRow.initialize();
+        getForm().addWidgetUpdate(this);
         return repeaterRow;
     }
     
@@ -87,6 +88,7 @@
             rows.add(index, repeaterRow);
         }
         repeaterRow.initialize();
+        getForm().addWidgetUpdate(this);
         return repeaterRow;
     }
 
@@ -104,8 +106,10 @@
     public Widget getChild(String id) {
         int rowIndex = -1;
         try {
-        	rowIndex = Integer.parseInt(id);
+            rowIndex = Integer.parseInt(id);
         } catch (NumberFormatException nfe) {
+            // Not a number
+            return null;
         }
         if (rowIndex < 0 || rowIndex >= getSize()) 
             return null;
@@ -146,6 +150,7 @@
      */
     public void removeRow(int index) {
         rows.remove(index);
+        getForm().addWidgetUpdate(this);
     }
     
     public void moveRowLeft(int index) {
@@ -156,6 +161,7 @@
             this.rows.set(index-1, this.rows.get(index));
             this.rows.set(index, temp);
         }
+        getForm().addWidgetUpdate(this);
     }
 
     public void moveRowRight(int index) {
@@ -166,6 +172,7 @@
             this.rows.set(index+1, this.rows.get(index));
             this.rows.set(index, temp);
         }
+        getForm().addWidgetUpdate(this);
     }
     
     /**
@@ -174,6 +181,7 @@
      */
     public void removeRows() {
         clear();
+        getForm().addWidgetUpdate(this);
     }
     
     /**
@@ -186,6 +194,7 @@
         for (int i = 0; i < this.definition.getInitialSize(); i++) {
             addRow();
         }
+        getForm().addWidgetUpdate(this);
     }
 
     /**

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Union.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Union.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Union.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Union.java Thu Apr 14 05:32:26 2005
@@ -19,6 +19,7 @@
 import org.apache.cocoon.forms.event.ValueChangedEvent;
 import org.apache.cocoon.forms.event.ValueChangedListener;
 import org.apache.cocoon.forms.event.ValueChangedListenerEnabled;
+import org.apache.commons.lang.ObjectUtils;
 
 /**
  * A discriminated union that references a discriminant value in another
@@ -66,7 +67,11 @@
         ((ValueChangedListenerEnabled)caseWidget).addValueChangedListener(
             new ValueChangedListener() {
                 public void valueChanged(ValueChangedEvent event) {
-                    Union.this.caseValue = (String)event.getNewValue();
+                    String newValue = (String)event.getNewValue();
+                    if (!ObjectUtils.equals(Union.this.caseValue, newValue)) {
+                        Union.this.caseValue = newValue;
+                        getForm().addWidgetUpdate(Union.this);
+                    }
                 }
             }
         );
@@ -106,7 +111,11 @@
                 widget.readFromRequest(formContext);
             }
         }
-        this.caseValue = newValue;
+        
+        if (!ObjectUtils.equals(this.caseValue, newValue)) {
+            this.caseValue = newValue;
+            getForm().addWidgetUpdate(this);
+        }
     }
 
     // TODO: Simplify this logic.

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Upload.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Upload.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Upload.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/formmodel/Upload.java Thu Apr 14 05:32:26 2005
@@ -26,6 +26,7 @@
 import org.apache.cocoon.servlet.multipart.Part;
 import org.apache.cocoon.xml.AttributesImpl;
 import org.apache.cocoon.xml.XMLUtils;
+import org.apache.commons.lang.ObjectUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
@@ -70,6 +71,7 @@
         } else {
             throw new RuntimeException("The value of an upload widget must be of type " + Part.class + ".");
         }
+        getForm().addWidgetUpdate(this);
     }
 
     public void readFromRequest(FormContext formContext) {
@@ -90,6 +92,7 @@
             requestPart.setDisposeWithRequest(false);
             this.part = requestPart;
             this.validationError = null;
+            getForm().addWidgetUpdate(this);
 
         // If it's not a part and not null, clear any existing value
         // We also check if we're the submit widget, as a result of clicking the "..." button
@@ -100,6 +103,7 @@
                 this.part = null;
             }
             this.validationError = null;
+            getForm().addWidgetUpdate(this);
         }
 
         // And keep the current state if the parameter doesn't exist or is null
@@ -108,26 +112,37 @@
     public boolean validate() {
         if (!getCombinedState().isValidatingValues())
             return true;
+        
+        ValidationError newError = null;
 
         if (this.part == null) {
             if (this.uploadDefinition.isRequired()) {
-                this.validationError = new ValidationError(new I18nMessage("general.field-required", Constants.I18N_CATALOGUE));
+                newError = new ValidationError(new I18nMessage("general.field-required", Constants.I18N_CATALOGUE));
+                getForm().addWidgetUpdate(this);
             }
         } else {
             String mimeTypes = this.uploadDefinition.getMimeTypes();
             if (mimeTypes != null) {
                 StringTokenizer tok = new StringTokenizer(this.uploadDefinition.getMimeTypes(), ", ");
-                this.validationError = new ValidationError(new I18nMessage("upload.invalid-type", Constants.I18N_CATALOGUE));
                 String contentType = this.part.getMimeType();
-                while (tok.hasMoreTokens()) {
+                boolean found = false;
+                while(tok.hasMoreTokens()) {
                     if (tok.nextToken().equals(contentType)) {
-                        this.validationError = null;
+                        found = true;
+                        break;
                     }
                 }
+                if (!found) {
+                    newError = new ValidationError(new I18nMessage("upload.invalid-type", Constants.I18N_CATALOGUE));
+                }
             } else {
-                this.validationError = null;
+                newError = null;
             }
         }
+        
+        if (!ObjectUtils.equals(this.validationError, newError)) {
+            setValidationError(newError);
+        }
 
         return validationError == null ? super.validate() : false;
     }
@@ -148,6 +163,7 @@
      */
     public void setValidationError(ValidationError error) {
         this.validationError = error;
+        getForm().addWidgetUpdate(this);
     }
 
     /**

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java Thu Apr 14 05:32:26 2005
@@ -20,12 +20,15 @@
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
+import org.apache.cocoon.environment.Request;
 import org.apache.cocoon.forms.Constants;
 import org.apache.cocoon.forms.formmodel.Form;
 import org.apache.cocoon.forms.formmodel.Repeater;
 import org.apache.cocoon.forms.formmodel.Widget;
 import org.apache.cocoon.forms.validation.ValidationError;
+import org.apache.cocoon.transformation.BrowserUpdateTransformer;
 import org.apache.cocoon.xml.AbstractXMLPipe;
 import org.apache.cocoon.xml.AttributesImpl;
 import org.apache.cocoon.xml.XMLConsumer;
@@ -44,6 +47,8 @@
     private XMLConsumer cocoonConsumer;
     private ArrayStack stack = new ArrayStack();
     private Map classes; // lazily created
+    private boolean ajaxMode;
+    private Set updatedWidgets;
 
     /**
      * Builds and helper object, given the generator's consumer.
@@ -51,15 +56,19 @@
      * @param consumer the generator's consumer
      * @return a helper object
      */
-    public static JXMacrosHelper createHelper(XMLConsumer consumer) {
-        return new JXMacrosHelper(consumer);
+    public static JXMacrosHelper createHelper(XMLConsumer consumer, Request request) {
+        return new JXMacrosHelper(consumer, request);
     }
 
-    public JXMacrosHelper(XMLConsumer consumer) {
+    public JXMacrosHelper(XMLConsumer consumer, Request request) {
         this.cocoonConsumer = consumer;
+        this.ajaxMode = request.getParameter("cocoon-ajax") != null;
     }
 
     public void startForm(Form form, Map attributes) throws SAXException {
+        
+        this.updatedWidgets = form.getUpdatedWidgets();
+        
         // build attributes
         AttributesImpl attrs = new AttributesImpl();
         Iterator iter = attributes.entrySet().iterator();
@@ -67,6 +76,8 @@
             Map.Entry entry = (Map.Entry)iter.next();
             attrs.addCDATAAttribute((String)entry.getKey(), (String)entry.getValue());
         }
+        
+        this.ajaxMode = this.ajaxMode && "true".equals(attributes.get("ajax"));
 
         this.cocoonConsumer.startPrefixMapping(Constants.INSTANCE_PREFIX, Constants.INSTANCE_NS);
         this.cocoonConsumer.startElement(Constants.INSTANCE_NS,
@@ -80,22 +91,9 @@
                                        "form-template",
                                        Constants.INSTANCE_PREFIX_COLON + "form-template");
         this.cocoonConsumer.endPrefixMapping(Constants.INSTANCE_PREFIX);
-    }
-
-    /**
-     * Flush the root element name that has been stored in
-     * {@link #generateWidget(Widget, Locale)}.
-     *
-     * @param obj the object that is terminated (widget or validation error)
-     * @throws SAXException
-     */
-    public void flushRoot(Object obj) throws SAXException {
-        Object stackObj = stack.pop();
-        if (stackObj != obj) {
-            throw new IllegalStateException("Flushing on wrong widget (expected " + stackObj +
-                                            ", got " + obj + ")");
-        }
-        ((RootBufferingPipe) stack.pop()).flushRoot();
+        
+        this.ajaxMode = false;
+        this.updatedWidgets = null;
     }
 
     /**
@@ -133,6 +131,12 @@
      * @throws SAXException
      */
     public void generateWidget(Widget widget, Locale locale) throws SAXException {
+        String id = widget.getRequestParameterName();
+        if (ajaxMode && this.updatedWidgets.contains(id)) {
+            AttributesImpl attr = new AttributesImpl();
+            attr.addCDATAAttribute("id", id);
+            this.cocoonConsumer.startElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace", attr);
+        }
         // Needs to be buffered
         RootBufferingPipe pipe = new RootBufferingPipe(this.cocoonConsumer);
         this.stack.push(pipe);
@@ -140,6 +144,26 @@
         widget.generateSaxFragment(pipe, locale);
     }
 
+    /**
+     * Flush the root element name that has been stored in
+     * {@link #generateWidget(Widget, Locale)}.
+     *
+     * @param obj the object that is terminated (widget or validation error)
+     * @throws SAXException
+     */
+    public void flushRoot(Widget widget) throws SAXException {
+        Object stackObj = stack.pop();
+        if (stackObj != widget) {
+            throw new IllegalStateException("Flushing on wrong widget (expected " + stackObj +
+                                            ", got " + widget + ")");
+        }
+        ((RootBufferingPipe) stack.pop()).flushRoot();
+        String id = widget.getRequestParameterName();
+        if (ajaxMode && this.updatedWidgets.contains(id)) {
+            this.cocoonConsumer.endElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace");
+        }
+    }
+
     public void generateWidgetLabel(Widget widget, String id) throws SAXException {
         getWidget(widget, id).generateLabel(this.cocoonConsumer);
     }
@@ -193,8 +217,25 @@
         return caseValue.equals(value != null ? value : "");
     }
 
-    public boolean isVisible(Widget widget) {
-        return widget.getCombinedState().isDisplayingValues();
+    public boolean isVisible(Widget widget) throws SAXException {
+        boolean visible = widget.getCombinedState().isDisplayingValues();
+        
+        if (!visible) {
+            // Generate a placeholder it not visible
+            String id = widget.getRequestParameterName();
+            AttributesImpl attrs = new AttributesImpl();
+            attrs.addCDATAAttribute("id", id);
+            this.cocoonConsumer.startElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace", attrs);
+            this.cocoonConsumer.startElement(Constants.INSTANCE_NS, "placeholder", Constants.INSTANCE_PREFIX_COLON + "placeholder", attrs);
+            this.cocoonConsumer.endElement(Constants.INSTANCE_NS, "placeholder", Constants.INSTANCE_PREFIX_COLON + "placeholder");
+            this.cocoonConsumer.endElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace");
+        }
+        
+        return visible;
+    }
+    
+    public boolean isModified(Widget widget) {
+        return this.updatedWidgets.contains(widget.getRequestParameterName());
     }
 
     /**

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/jx-macros.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/jx-macros.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/jx-macros.xml (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/generation/jx-macros.xml Thu Apr 14 05:32:26 2005
@@ -1,12 +1,13 @@
 <!-- An implementation of the CForms template engine as a JXTemplate tag library -->
     
 <jx:template xmlns:jx="http://apache.org/cocoon/templates/jx/1.0"
-             xmlns:fi="http://apache.org/cocoon/forms/1.0#instance">
+             xmlns:fi="http://apache.org/cocoon/forms/1.0#instance"
+             xmlns:bu="http://apache.org/cocoon/browser-update/1.0">
     <!--
         ft:form-template
     -->
     <jx:macro name="form-template" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
-      <jx:set var="cformsHelper" value="#{org.apache.cocoon.forms.generation.JXMacrosHelper.createHelper($cocoon/consumer)}"/>
+      <jx:set var="cformsHelper" value="#{org.apache.cocoon.forms.generation.JXMacrosHelper.createHelper($cocoon/consumer,$cocoon/request)}"/>
       <jx:set var="form" value="${CocoonFormsInstance}"/>
       <jx:if test="${cformsHelper.isVisible(form)}">
         <jx:set var="cformsDummy" value="${cformsHelper.startForm(form, macro.arguments)}"/>
@@ -138,9 +139,20 @@
       
       <jx:set var="widget" value="${cformsHelper.getWidget(widget, id)}"/>
       <jx:if test="${cformsHelper.isVisible(widget)}">
-        <fi:union id="${widget.getRequestParameterName()}">
-          <jx:evalBody/>
-        </fi:union>
+        <jx:choose>
+          <jx:when test="${cformsHelper.isModified(widget)}">
+            <bu:replace id="${widget.getRequestParameterName()}">
+              <fi:union id="${widget.getRequestParameterName()}">
+                <jx:evalBody/>
+              </fi:union>
+            </bu:replace>
+          </jx:when>
+          <jx:otherwise>
+            <fi:union id="${widget.getRequestParameterName()}">
+              <jx:evalBody/>
+            </fi:union>
+          </jx:otherwise>
+        </jx:choose>
       </jx:if>
     </jx:macro>
 
@@ -184,6 +196,37 @@
       <jx:if test="${cformsHelper.isVisible(widget)}">
         <jx:evalBody/>
       </jx:if>
+    </jx:macro>
+    
+    <jx:macro name="repeater" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
+      <jx:parameter name="id"/>
+      <jx:set var="repeater" value="${cformsHelper.getWidget(widget, id)}"/>
+      <jx:if test="${cformsHelper.isVisible(widget)}">
+        <jx:choose>
+          <jx:when test="${cformsHelper.isModified(repeater)}">
+            <bu:replace id="${repeater.getRequestParameterName()}">
+		     <fi:repeater-template id="${repeater.getRequestParameterName()}">
+		       <jx:evalBody/>
+		     </fi:repeater-template>
+            </bu:replace>
+          </jx:when>
+          <jx:otherwise>
+	        <fi:repeater-template id="${repeater.getRequestParameterName()}">
+	          <jx:evalBody/>
+	        </fi:repeater-template>
+          </jx:otherwise>
+        </jx:choose>
+        
+      </jx:if>
+    </jx:macro>
+    
+    <jx:macro name="repeater-rows" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
+        <jx:forEach varStatus="repeaterLoop" begin="0" end="${repeater.getSize() - 1}">
+          <jx:set var="widget" value="${repeater.getRow(repeaterLoop.index)}"/>
+          <jx:if test="${cformsHelper.isVisible(widget)}">
+            <jx:evalBody/>
+          </jx:if>
+        </jx:forEach>
     </jx:macro>
         
 </jx:template>

Added: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js?view=auto&rev=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js (added)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js Thu Apr 14 05:32:26 2005
@@ -0,0 +1,274 @@
+/*
+* Copyright 1999-2004 The Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+/**
+ * Runtime JavaScript library for Cocoon forms.
+ *
+ * @version $Id$
+ */
+
+//-------------------------------------------------------------------------------------------------
+/**
+ * The CForms class holds all forms-related properties and methods.
+ */
+
+CForms = {
+    ajax : false // default mode is full page update
+};
+
+/**
+ * Submits a form. If ajax mode is on and the browser is ajax-aware, the page isn't reloaded
+ */
+CForms.submitForm = function(element, name) {
+    if (name == undefined) {
+        name = element.name;
+    }
+    
+    var form = forms_getForm(element);
+    if (form == null) {
+        alert("Cannot find form for " + element);
+    } else {
+        form["forms_submit_id"].value = name;
+        // FIXME: programmatically submitting the form doesn't trigger onsubmit ? (both in IE and Moz)
+        forms_onsubmit();
+        
+        // Provide feedback that something is happening.
+        document.body.style.cursor = "wait";
+        
+        var req = CForms.newXMLHttpRequest();
+        if (req) {
+            req.open("POST", form.action, true);
+            req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+            // onreadystatechange must be all lower case (?)
+            req.onreadystatechange = function() {
+                if (req.readyState == 4) {
+                    CForms._handleBrowserUpdate(form, req);
+                }
+            }
+            req.send(CForms._buildQueryString(form, name));
+            // Reset submit-id
+            form["forms_submit_id"].value = '';
+            forms_onsubmitHandlers = new Array();
+            
+        } else {
+            // Non ajax-aware browser : regular submit
+            form.submit();
+        }
+    }
+}
+
+// Override the default forms_submitForm
+forms_submitForm = CForms.submitForm;
+
+/**
+ * Create an XHR if the browser supports it.
+ */
+CForms.newXMLHttpRequest = function () {
+    if (this.ajax) {
+	    if (window.XMLHttpRequest)
+	        return new XMLHttpRequest;
+	    else if (window.ActiveXObject)
+	        return new ActiveXObject("Microsoft.XMLHTTP");
+	}
+}
+
+/**
+ * Build a query string with all form inputs
+ */
+CForms._buildQueryString = function(form, submitId) {
+    // Indicate to the server that we're in ajax mode
+    var result = "cocoon-ajax=true";
+    // Iterate on all form controls
+    for (var i = 0; i < form.elements.length; i++) {
+        input = form.elements[i];
+        if (input.type != "submit" && input.type != "image") {
+            // Skip buttons
+            result += "&" + encodeURIComponent(input.name) + "=" + encodeURIComponent(input.value);
+        }
+    }
+    return result;
+}
+
+/**
+ * Handle the server response
+ */
+CForms._handleBrowserUpdate = function(form, request) {
+    // Restore normal cursor
+    document.body.style.cursor = "auto";
+    if (request.status == 200) {
+        var xca;
+        try { // An exception is thrown if header doesn't exist (at least in Moz)
+            xca = request.getResponseHeader("X-Cocoon-Ajax");
+        } catch (e) {
+            // header doesn't exist
+        }
+	    if (xca == "continue") {
+	        // Interaction with this page is finished: redirect the browser to the form's action URL
+	        // Get the continuation-id, if any.
+	        var contParam = '?cocoon-ajax-continue=true';
+	        if (form.elements["continuation-id"]) {
+	            contParam = "&continuation-id=" + form.elements["continuation-id"].value;
+	        }
+	        window.location.href = form.action + contParam;
+	    } else {
+	        // Handle browser update directives
+	        var doc = request.responseXML;
+	        if (!doc) {
+	            alert("no xml answer");
+	            return;
+	        }
+        
+            BrowserUpdate.processResponse(doc);
+        }
+    } else {
+        alert("request failed : " + request.status);
+    }
+}
+
+//-------------------------------------------------------------------------------------------------
+
+DOMUtils = {
+    // Stupid IE doesn't have these constants
+    ELEMENT_NODE : 1,
+    ATTRIBUTE_NODE : 2,
+    TEXT_NODE : 3,
+    CDATA_SECTION_NODE : 4,
+	ENTITY_REFERENCE_NODE : 5,
+    ENTITY_NODE : 6,
+    PROCESSING_INSTRUCTION_NODE : 7,
+    COMMENT_NODE : 8,
+    DOCUMENT_NODE : 9,
+    DOCUMENT_TYPE_NODE : 10,
+    DOCUMENT_FRAGMENT_NODE : 11
+}
+
+/**
+ * Get the first child element of an element, ignoring text nodes
+ */
+DOMUtils.firstChildElement = function(element) {
+    var nodes = element.childNodes;
+    for (var i = 0; i < nodes.length; i++) {
+        var node = nodes[i];
+        if (node.nodeType == this.ELEMENT_NODE) {
+            return node;
+        }
+    }
+}
+
+/**
+ * Imports an element into a document, taking care of using the correct implementation
+ * so that the browser interprets it as displayable XML
+ */
+DOMUtils.importNode = function(node, targetDoc) {
+    if(node.xml) {
+        // IE
+        var div = targetDoc.createElement("DIV");
+        div.innerHTML = node.xml;
+        return this.firstChildElement(div);
+    } else {
+        return DOMUtils._importNode(node, targetDoc);
+    }
+}
+
+/**
+ * DOM implementation of importNode, recursively creating nodes
+ */
+DOMUtils._importNode = function(node, targetDoc) {
+    switch(node.nodeType) {
+        case this.ELEMENT_NODE:
+            var element = targetDoc.createElement(node.nodeName);
+            //var element = targetDoc.createElementNS(node.namespaceURI, node.nodeName);
+            var attrs = node.attributes;
+            for (var i = 0; i < attrs.length; i++) {
+                attr = attrs[i];
+                element.setAttribute(attr.nodeName, attr.nodeValue);
+                //element.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.nodeValue);
+            }
+            var children = node.childNodes;
+            for (var j = 0; j < children.length; j++) {
+                var imported = this.importNode(children[j], targetDoc);
+                if (imported) element.appendChild(imported);
+            }
+            return element;
+        break;
+        
+        case this.TEXT_NODE:
+            return targetDoc.createTextNode(node.nodeValue);
+        break;
+        
+        case this.CDATA_SECTION_NODE:
+            return targetDoc.createTextNode(node.nodeValue);
+        break;
+    }
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// General purpose AJAX infrastructure to handle a BU ("browser update") response
+//
+// To implement a new instruction, add the corresponding function as a property of
+// BrowserUpdate.handlers.
+//-------------------------------------------------------------------------------------------------
+
+BrowserUpdate = {};
+
+BrowserUpdate.processResponse = function(doc) {
+    var nodes = doc.documentElement.childNodes;
+    for (var i = 0; i < nodes.length; i++) {
+        var node = nodes[i];
+        if (node.nodeType == DOMUtils.ELEMENT_NODE) {
+            var handler;
+            if (node.localName) {
+                handler = node.localName;
+            } else {
+                // No DOM2 support (IE6)
+                handler = node.nodeName.replace(/.*:/, "");
+            }
+            var handlerFunc = BrowserUpdate.handlers[handler];
+            if (handlerFunc) {
+                handlerFunc(node);
+            } else {
+                alert("no handler found for " + handler);
+            }
+        }
+    }
+}
+BrowserUpdate.handlers = {
+	replace :  function(element) {
+	    var id = element.getAttribute("id");
+	    if (!id) {
+	       alert("no id found on update element");
+	       return;
+	    }    
+	    // Get the first child element (the first child not may be some text!)
+	    var firstChild = DOMUtils.firstChildElement(element);
+	
+	    var oldElement = document.getElementById(id);
+	    
+	    if (!oldElement) {
+	        alert("no element '" + id + "' in source document");
+	        return;
+	    }
+	
+	    var newElement = DOMUtils.importNode(firstChild, document);
+	    
+	    // Warn: it's replace(new, old)!!
+	    oldElement.parentNode.replaceChild(newElement, oldElement);
+	    // Ensure the new node has the correct id
+	    newElement.setAttribute("id", id);
+	}
+}
+

Propchange: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/forms-lib.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/forms-lib.js?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/forms-lib.js (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/forms-lib.js Thu Apr 14 05:32:26 2005
@@ -52,7 +52,7 @@
  * Submit the form containing an element, also storing in the hidden
  * 'forms_submit_id' field the name of the element which triggered the submit.
  */
-function forms_submitForm(element, name) {
+function oldforms_submitForm(element, name) {
     if (name == undefined) {
         name = element.name;
     }

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/samples/Form2Bean.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/samples/Form2Bean.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/samples/Form2Bean.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/samples/Form2Bean.java Thu Apr 14 05:32:26 2005
@@ -36,6 +36,7 @@
     private int aNumber;
     private boolean choose;
     private Sex sex;
+    private Boolean enable;
 
     private Collection contacts = new ArrayList();
     private Collection drinks = new ArrayList();
@@ -119,6 +120,14 @@
 
     public void setChoose(boolean choose) {
         this.choose = choose;
+    }
+    
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
     }
 
     public Collection getDrinks() {

Modified: cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/util/XMLAdapter.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/util/XMLAdapter.java?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/util/XMLAdapter.java (original)
+++ cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/util/XMLAdapter.java Thu Apr 14 05:32:26 2005
@@ -80,6 +80,8 @@
     private Locale locale;
     /** Is a <code>MultiValueField</code> handled? */
     private boolean isMultiValueItem = false;
+    /** The buffer used to receive character events */
+    private StringBuffer textBuffer;
 
 
     /**
@@ -133,6 +135,7 @@
      */
     public void startElement(String uri, String loc, String raw, Attributes a)
     throws SAXException {
+        handleText();
         if (this.currentWidget == null) {
             // The name of the root element is ignored
             this.currentWidget = this.widget;
@@ -175,6 +178,7 @@
      */
     public void endElement(String uri, String loc, String raw)
     throws SAXException {
+        handleText();
         if (this.currentWidget == null)
             throw new SAXException("Wrong state");
 
@@ -208,11 +212,45 @@
      */
     public void characters(char ch[], int start, int len)
     throws SAXException {
-        String input = new String(ch, start, len);
-        if ("".equals(input.trim()))
+        // Buffer text, as a single text node can be sent in several chunks.
+        if (this.textBuffer == null) {
+            this.textBuffer = new StringBuffer();
+        } 
+        this.textBuffer.append(ch, start, len);
+    }
+    
+    /**
+     * Handle text nodes, if any. Called on every potential text node boundary,
+     * i.e. start and end element events.
+     * 
+     * @throws SAXException
+     */
+    private void handleText() throws SAXException {
+        if (this.textBuffer == null)
+            return;
+        
+        String input = this.textBuffer.toString().trim();
+        this.textBuffer = null; // clear buffer
+        if (input.length() == 0)
             return;
 
-        if (this.currentWidget instanceof DataWidget) {
+        if (this.currentWidget instanceof MultiValueField && isMultiValueItem) {
+            MultiValueField field = (MultiValueField)this.currentWidget;
+            Datatype type = field.getDatatype();
+            ConversionResult conv =
+                type.convertFromString(input, this.locale);
+            if (conv.isSuccessful()) {
+                Object[] values = (Object[])field.getValue();
+                int valLen = values == null ? 0 : values.length;
+                Object[] newValues = new Object[valLen + 1];
+                for (int i = 0; i < valLen; i++)
+                    newValues[i] = values[i];
+                newValues[valLen] = conv.getResult();
+                field.setValues(newValues);
+            } else
+                throw new SAXException("Could not convert: " + input +
+                                       " to " + type.getTypeClass());
+        } else if (this.currentWidget instanceof DataWidget) {
             DataWidget data = (DataWidget)this.currentWidget;
             Datatype type = data.getDatatype();
             ConversionResult conv =
@@ -231,24 +269,9 @@
                 this.currentWidget.setValue(Boolean.FALSE);
             else
                 throw new SAXException("Unkown boolean: " + input);
-        } else if (this.currentWidget instanceof MultiValueField && isMultiValueItem) {
-            MultiValueField field = (MultiValueField)this.currentWidget;
-            Datatype type = field.getDatatype();
-            ConversionResult conv =
-                type.convertFromString(input, this.locale);
-            if (conv.isSuccessful()) {
-                Object[] values = (Object[])field.getValue();
-                int valLen = values == null ? 0 : values.length;
-                Object[] newValues = new Object[valLen + 1];
-                for (int i = 0; i < valLen; i++)
-                    newValues[i] = values[i];
-                newValues[valLen] = conv.getResult();
-                field.setValues(newValues);
-            } else
-                throw new SAXException("Could not convert: " + input +
-                                       " to " + type.getTypeClass());
-        } else
+        } else {
             throw new SAXException("Unknown widget type: " + this.currentWidget);
+        }
     }
 
 
@@ -299,7 +322,16 @@
         start(id, attr);
         // Placing the handling DataWidget before ContainerWidget
         // means that an AggregateField is handled like a DataWidget
-        if (widget instanceof DataWidget) {
+        if (widget instanceof MultiValueField) {
+            Datatype datatype = ((MultiValueField)widget).getDatatype();
+            Object[] values = (Object[])widget.getValue();
+            if (values != null)
+                for (int i = 0; i < values.length; i++) {
+                    start(ITEM, attr);
+                    data(datatype.convertToString(values[i], this.locale));
+                    end(ITEM);
+                }
+        } else if (widget instanceof DataWidget) {
             Datatype datatype = ((DataWidget)widget).getDatatype();
             if (widget.getValue() != null)
                 data(datatype.convertToString(widget.getValue(), this.locale));
@@ -309,15 +341,6 @@
             if (widget.getValue() != null) {
                 data(widget.getValue().toString());
             }
-        } else if (widget instanceof MultiValueField) {
-            Datatype datatype = ((MultiValueField)widget).getDatatype();
-            Object[] values = (Object[])widget.getValue();
-            if (values != null)
-                for (int i = 0; i < values.length; i++) {
-                    start(ITEM, attr);
-                    data(datatype.convertToString(values[i], this.locale));
-                    end(ITEM);
-                }
         } else if (widget instanceof ContainerWidget) {
             Iterator children = ((ContainerWidget)widget).getChildren();
             while (children.hasNext())

Modified: cocoon/blocks/core/forms/trunk/samples/flow/binding_example.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/flow/binding_example.js?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/flow/binding_example.js (original)
+++ cocoon/blocks/core/forms/trunk/samples/flow/binding_example.js Thu Apr 14 05:32:26 2005
@@ -76,11 +76,12 @@
     bean.setPhoneNumber("123456");
     bean.setBirthday(new java.util.Date());
     bean.setSex(Packages.org.apache.cocoon.forms.samples.Sex.FEMALE);
+    //bean.setEnable(java.lang.Boolean.FALSE);
     var contact = new Packages.org.apache.cocoon.forms.samples.Contact();
     contact.setId("1");
     contact.setFirstName("Hermann");
     bean.addContact(contact);
-    bean.addDrink("Maer");
+    bean.addDrink("Maes");
     bean.addDrink("Leffe");
     
     form.load(bean);

Modified: cocoon/blocks/core/forms/trunk/samples/flow/bindings.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/flow/bindings.js?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/flow/bindings.js (original)
+++ cocoon/blocks/core/forms/trunk/samples/flow/bindings.js Thu Apr 14 05:32:26 2005
@@ -69,6 +69,16 @@
     // the back-end model itself however depends on sample and type. 
     var bean = createBeanForSample(backendType, sampleCode);
 	
+	if (bean == null) {
+        cocoon.sendPage("textresult-display-pipeline.jx", {
+            title: "Contact list",
+            text: "Sorry the sample '" + sampleCode +
+                "' is not yet available for the backend '" +
+                backendType + "'"
+            }
+        );
+        return;
+    }
     // loads the backend-bean into the form
     form.load(bean);
 
@@ -116,7 +126,11 @@
     } else {
         var factoryFunction = "create" + backendType + "BeanFor" + sampleCode;
         print("Using the bean returned by function " + factoryFunction + "()");
-        return this[factoryFunction].apply();
+        if (this[factoryFunction]) {
+            return this[factoryFunction].apply();
+        } else {
+            return null; // not available
+        }
     }
 }
 
@@ -266,7 +280,8 @@
 function createJSBeanFor05custom() {
     var bean = {"jswrap-value": "--wrapped value--",
                 "custom-value": "**custom value**",
-                "config-value": "[[config value]]"};
+                "config-value": "[[config value]]",
+		"test-value" : ""};
     return bean;
 }
 
@@ -278,5 +293,6 @@
     bean.put("jswrap-value", "--wrapped value--");
     bean.put("custom-value", "**custom value**");
     bean.put("config-value", "[[config value]]");
+    bean.put("test-value", "");
     return bean;
 }

Modified: cocoon/blocks/core/forms/trunk/samples/flow/forms_flow_example.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/flow/forms_flow_example.js?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/flow/forms_flow_example.js (original)
+++ cocoon/blocks/core/forms/trunk/samples/flow/forms_flow_example.js Thu Apr 14 05:32:26 2005
@@ -47,7 +47,7 @@
 function selectCar() {
     var form = new Form("forms/carselector_form.xml");
     form.lookupWidget("make").setValue(cocoon.parameters.defaultMake);
-    form.showForm("carselector-display-pipeline");
+    form.showForm("carselector-display-pipeline.jx");
     cocoon.request.setAttribute("carselectorform", form.getWidget());
     cocoon.sendPage("carselector-success-pipeline.xsp");
 }

Modified: cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-bind.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-bind.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-bind.xml (original)
+++ cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-bind.xml Thu Apr 14 05:32:26 2005
@@ -45,5 +45,15 @@
       factorymethod="createBinding" >
       <fb:config prefixchar="[" suffixchar="]" />      
   </fb:custom>
-
+  
+  <!-- This works??? -->
+  <fb:value id="test" path="test-value" direction="load"/>
+  <fb:javascript id="test" path="test-value" direction="save">
+    <fb:save-form>
+      var formValue = widget.getValue();
+      // you can call a function from anywhere in the flowscript
+      var appValue = doSaveConversion(formValue, "-");
+      jxpathPointer.setValue("MyconstantValue");
+    </fb:save-form>
+  </fb:javascript>
 </fb:context>

Modified: cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-data.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-data.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-data.xml (original)
+++ cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-data.xml Thu Apr 14 05:32:26 2005
@@ -19,4 +19,5 @@
   <jswrap-value>--something--</jswrap-value>
   <custom-value>**something more**</custom-value>
   <config-value>[[something else]]</config-value>
+  <test-value></test-value>
 </root>

Modified: cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-def.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-def.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-def.xml (original)
+++ cocoon/blocks/core/forms/trunk/samples/forms/binding/05custom-def.xml Thu Apr 14 05:32:26 2005
@@ -18,22 +18,25 @@
 <fd:form xmlns:fd="http://apache.org/cocoon/forms/1.0#definition">
 
   <fd:widgets>
-  
-    <fd:field id="jswrap" >
+    <fd:field id="jswrap">
       <fd:label>1st Value</fd:label>
-      <fd:datatype base="string" />
+      <fd:datatype base="string"/>
     </fd:field>
 
-    <fd:field id="custom" >
+    <fd:field id="custom">
       <fd:label>2nd Value</fd:label>
-      <fd:datatype base="string" />
+      <fd:datatype base="string"/>
     </fd:field>
     
-    <fd:field id="config" >
+    <fd:field id="config">
       <fd:label>3rd Value</fd:label>
-      <fd:datatype base="string" />
+      <fd:datatype base="string"/>
     </fd:field>
     
+    <fd:field id="test">
+      <fd:label>4th Value</fd:label>
+      <fd:datatype base="string"/>
+    </fd:field>
   </fd:widgets>
   
 </fd:form>

Modified: cocoon/blocks/core/forms/trunk/samples/forms/carselector_template.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/forms/carselector_template.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/forms/carselector_template.xml (original)
+++ cocoon/blocks/core/forms/trunk/samples/forms/carselector_template.xml Thu Apr 14 05:32:26 2005
@@ -14,7 +14,9 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<page xmlns:ft="http://apache.org/cocoon/forms/1.0#template" xmlns:fi="http://apache.org/cocoon/forms/1.0#instance">
+<page xmlns:ft="http://apache.org/cocoon/forms/1.0#template" xmlns:fi="http://apache.org/cocoon/forms/1.0#instance"  xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
+   <!-- Import the macros that define CForms template elements -->
+   <jx:import uri="resource://org/apache/cocoon/forms/generation/jx-macros.xml"/>
   <title>Car selector</title>
   <para>This example illustrates how you can programmatically update the
     content of a selection list.</para>
@@ -32,7 +34,7 @@
     See "carselector_form.xml" and "carselector_template.xml" to see how this is done.
   </para>
   <content>
-    <ft:form-template action="carselector" method="POST">
+    <ft:form-template action="carselector" method="POST" ajax="true">
       <ft:continuation-id/>
       <fi:group>
         <fi:styling layout="columns"/>

Modified: cocoon/blocks/core/forms/trunk/samples/forms/datasource_chooser_template.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/forms/datasource_chooser_template.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/forms/datasource_chooser_template.xml (original)
+++ cocoon/blocks/core/forms/trunk/samples/forms/datasource_chooser_template.xml Thu Apr 14 05:32:26 2005
@@ -23,7 +23,7 @@
   
   <title>Datasource chooser</title>
   <content>
-    <ft:form-template action="#{$cocoon/continuation/id}.continue" method="POST">
+    <ft:form-template action="#{$cocoon/continuation/id}.continue" method="POST" ajax="true">
       
       <p>Datasource name: <ft:widget id="name"/></p>
       <fieldset>

Modified: cocoon/blocks/core/forms/trunk/samples/forms/dynamicrepeater_template.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/forms/dynamicrepeater_template.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/forms/dynamicrepeater_template.xml (original)
+++ cocoon/blocks/core/forms/trunk/samples/forms/dynamicrepeater_template.xml Thu Apr 14 05:32:26 2005
@@ -38,7 +38,7 @@
   </style>
   
   <content>
-    <ft:form-template action="#{$cocoon/continuation/id}.continue" method="POST">
+    <ft:form-template action="#{$cocoon/continuation/id}.continue" method="POST" ajax="true">
     
     <p>This is an example of how dynamic templates (using the JX macros) allow to change the
        page structure depending on the form contents. By adding or removing rows in the repeater
@@ -47,8 +47,9 @@
       <li>when there are no contacts, only the "Add contact" button is displayed and the table isn't shown,</li>
       <li>when there are some contacts, the "move up" and "move down" buttons are not shown on the first and last lines, respectively.</li>
     </ul>
+      <ft:repeater id="contacts">
         <jx:choose>
-          <jx:when test="${form.getChild('contacts').getSize() == 0}">
+          <jx:when test="${repeater.getSize() == 0}">
             <p><strong><em>There are no contacts to display</em></strong></p>
           </jx:when>
           <jx:otherwise>
@@ -62,7 +63,7 @@
               </tr>
                   <!-- The contents of the repeater-widget element is a template that will
                        be applied to each row in the repeater. -->
-                  <ft:repeater-widget id="contacts">
+                  <ft:repeater-rows>
                     <tr class="forms-row-${repeaterLoop.index % 2}">
                       <td><ft:widget id="firstname"/></td>
                       <td><ft:widget id="lastname"/></td>
@@ -93,16 +94,17 @@
                       <ft:widget id="select"/>
                       </td>
                     </tr>
-                  </ft:repeater-widget>
+                  </ft:repeater-rows>
             </table>
                 </jx:otherwise>
               </jx:choose>
-              <p>
+             <p>
                   <ft:widget id="addcontact"/>
                   <jx:if test="${widget.getChild('contacts').getSize() > 0}">
                     <ft:widget id="removecontacts"/>
                   </jx:if>
               </p>
+            </ft:repeater>
             <ft:widget id="submit"/><br/>
             <a href="./do-dynaRepeater.flow">Restart this sample</a> - <a href="./">Back to samples</a>
 

Modified: cocoon/blocks/core/forms/trunk/samples/forms/form2_bind_bean.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/forms/form2_bind_bean.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/forms/form2_bind_bean.xml (original)
+++ cocoon/blocks/core/forms/trunk/samples/forms/form2_bind_bean.xml Thu Apr 14 05:32:26 2005
@@ -36,6 +36,15 @@
   <fb:value id="number" path="aNumber"/>
   <fb:value id="choose" path="choose"/>
   <fb:value id="sex" path="sex"/>
+  <!--<fb:value id="enable" path="enable" direction="load"/>-->
+  <fb:javascript id="enable" path="enable" direction="save">
+    <!--<fb:load-form>
+    	widget.setValue(java.lang.Boolean.FALSE);
+    </fb:load-form>-->
+    <fb:save-form>
+      jxpathPointer.setValue(java.lang.Boolean.TRUE);
+    </fb:save-form>
+  </fb:javascript>
 
   <!--
     - An aggregatefield is a special kind of widget that lets

Modified: cocoon/blocks/core/forms/trunk/samples/forms/form2_jx.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/forms/form2_jx.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/forms/form2_jx.xml (original)
+++ cocoon/blocks/core/forms/trunk/samples/forms/form2_jx.xml Thu Apr 14 05:32:26 2005
@@ -28,6 +28,8 @@
     <br/>
     I choose: ${form2bean.choose}
     <br/>
+    Enable: ${form2bean.enable}
+    <br/>
     Sex: <i18n:text xmlns:i18n="http://apache.org/cocoon/i18n/2.1">${form2bean.sex}</i18n:text>
 
     <table border="1">
@@ -51,7 +53,7 @@
 
     <table border="1">
       <tr>
-        <th>drink</th>
+        <th>drinks</th>
       </tr>
       <jx:forEach var="item" items="${form2bean.drinks}">
         <tr>

Modified: cocoon/blocks/core/forms/trunk/samples/forms/form2_model.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/forms/form2_model.xml?view=diff&r1=161263&r2=161264
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/forms/form2_model.xml (original)
+++ cocoon/blocks/core/forms/trunk/samples/forms/form2_model.xml Thu Apr 14 05:32:26 2005
@@ -103,6 +103,10 @@
       </fd:datatype>
       <fd:selection-list type="enum" class="org.apache.cocoon.forms.samples.Sex"/>
     </fd:field>
+    
+    <fd:booleanfield id="enable">
+      <fd:label>Register Enable</fd:label>
+    </fd:booleanfield>
   
     <fd:multivaluefield id="drinks">
       <fd:label>Indicate which 2 of the following drinks you'd like to receive:</fd:label>