cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jheym...@apache.org
Subject svn commit: r330548 [86/132] - in /cocoon/whiteboard/maven2/cocoon-flat-layout: ./ cocoon-ajax-block/ cocoon-ajax-block/api/ cocoon-ajax-block/api/src/ cocoon-ajax-block/api/src/main/ cocoon-ajax-block/api/src/main/java/ cocoon-ajax-block/api/src/main/...
Date Thu, 03 Nov 2005 14:00:48 GMT
Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JXPathBindingBuilderBase.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JXPathBindingBuilderBase.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JXPathBindingBuilderBase.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JXPathBindingBuilderBase.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,242 @@
+/*
+ * 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.
+ */
+package org.apache.cocoon.forms.binding;
+
+import java.util.Map;
+
+import org.apache.avalon.framework.logger.LogEnabled;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.cocoon.forms.util.DomHelper;
+import org.apache.commons.lang.BooleanUtils;
+import org.w3c.dom.Element;
+
+/**
+ * Abstract base class enabling logging and supporting the intrepretation of
+ * common configuration settings on all specific implementations of
+ * {@link org.apache.cocoon.forms.binding.JXPathBindingBase}.
+ *
+ * Common supported configurations: {@link #getCommonAttributes(Element)}
+ * <ul>
+ * <li>Attribute direction="load|save|both": defaults to 'both'</li>
+ * <li>Attribute lenient="true|false|[undefined]": defaults to [undefined]
+ *     which means: "continue in same leniency-mode as parent" </li>
+ * </ul>
+ *
+ * @version $Id: JXPathBindingBuilderBase.java 289538 2005-09-16 13:46:22Z sylvain $
+ */
+public abstract class JXPathBindingBuilderBase implements LogEnabled {
+
+    private Logger logger;
+
+    /**
+     * Receives the Avalon logger to use.
+     */
+    public void enableLogging(Logger logger) {
+        this.logger = logger;
+        if (logger.isDebugEnabled()) {
+            logger.debug("JXPathBindingBuilderBase got logger...");
+        }
+    }
+    
+
+    /**
+     * Makes the logger available to the subclasses.
+     * @return Logger
+     */
+    protected Logger getLogger() {
+        return this.logger;
+    }
+
+    /**
+     * Builds a configured binding object based on the configuration as
+     * described in the bindingElement.  The BuilderMap can be used to
+     * find appropriate builders for possible subBinders.
+     *
+     * @param bindingElm
+     * @param assistant
+     * @return JXPathBindingBase
+     */
+    public abstract JXPathBindingBase buildBinding(
+        Element bindingElm,
+        JXPathBindingManager.Assistant assistant) throws BindingException;
+
+    /**
+     * Helper method for interpreting the common attributes which are supported
+     * on each of the Bindings.  These are
+     * <br>
+     * <code>@direction</code> can hold one of the following values:
+     * <ol><li><code>'load'</code>: This binding will only load.</li>
+     * <li><code>'save'</code>: This binding will only save.</li>
+     * <li><code>'both'</code>: This binding will perform both operations.</li>
+     * </ol>
+     * <br>
+     * <code>@lenient</code> can either be:
+     * <ol><li><code>'true'</code>: This binding will set the jxpath context to
+     * be lenient towards the usage of inexisting paths on the back-end model.</li>
+     * <li><code>'false'</code>: This binding will set the jxpath context to be
+     * strict and throwing exceptions for the usage of inexisting paths on the
+     * back-end model.</li>
+     * <li><code>(unset)</code>: This binding will not change the leniency behaviour
+     * on the jxpath this binding receives from his parent binding.</li>
+     * </ol>
+     * @param bindingElm
+     * @return an instance of CommonAttributes
+     * @throws BindingException
+     */
+    protected static CommonAttributes getCommonAttributes(Element bindingElm) throws BindingException {
+        try {
+            String location = DomHelper.getLocation(bindingElm);
+            //TODO: should we eventually remove this?
+            //throw an error if people are still using the old-style @read-only or @readonly
+            if (DomHelper.getAttributeAsBoolean(bindingElm, "readonly", false)) {
+                throw new BindingException("Error in binding file " + location
+                        + "\nThe usage of the attribute @readonly has been deprecated in favour of @direction.");
+            }
+            if (DomHelper.getAttributeAsBoolean(bindingElm, "read-only", false)) {
+                throw new BindingException("Error in binding file " + location
+                        + "\nThe usage of the attribute @read-only has been deprecated in favour of @direction.");
+            }
+
+            String direction = DomHelper.getAttribute(bindingElm, "direction", "both");
+
+            String leniency = DomHelper.getAttribute(bindingElm, "lenient", null);
+
+            //TODO: current jxpath is not inheriting registered namespaces over to 
+            // child-relative jxpath contexts --> because of that we can't just 
+            // remember the getLocalNSDeclarations but need the full set from 
+            // getInheritedNSDeclarations
+            // IMPORTANT NOTE: if jxpath would change this behaviour we would however
+            // still need to be able to unregister namespace-declarations 
+            // (in a smart way: unhide what is possably available from your parent.   
+            // So both changes to jxpath need to be available before changing the below.
+            Map nsDeclarationMap = DomHelper.getInheritedNSDeclarations(bindingElm);
+            // we (actually jxpath) doesn't support un-prefixed namespace-declarations:
+            // so we decide to break on those above silently ignoring them
+            if (nsDeclarationMap != null && nsDeclarationMap.values().contains(null))
+                throw new BindingException("Error in binding file " + location
+                                + "\nBinding doesn't support having namespace-declarations without explicit prefixes.");
+            
+            return new CommonAttributes(location, direction, leniency, nsDeclarationMap);
+        } catch (BindingException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new BindingException("Error building binding defined at " + DomHelper.getLocation(bindingElm), e);
+        }
+     }
+    
+    public static CommonAttributes mergeCommonAttributes(CommonAttributes existing, CommonAttributes extra) {
+    	
+    	if (extra == null)
+    		return existing;
+    	
+    	Boolean leniency = null;
+    	if(existing.leniency==null)
+    		leniency = extra.leniency;
+    	else
+    		leniency = existing.leniency;
+    	
+    	String strLeniency = null;
+    	if(leniency != null)
+    		strLeniency = leniency.toString();
+    	
+    	String direction = existing.direction;
+    	if(extra.direction!=null) // was defined
+    		direction = extra.direction;
+    	
+    	
+    	return new CommonAttributes(extra.location,direction,strLeniency,extra.nsDeclarations);
+    }
+
+     /**
+      * CommonAttributes is a simple helper class for holding the distinct data
+      * member fields indicating the activity of the sepearate load and save
+      * actions of a given binding.
+      */
+     protected static class CommonAttributes{
+
+    	/**
+    	 * store direction (load/save enabledness) too for easier merging
+    	 */
+    	String direction;
+        /**
+         * Source location of this binding.
+         */
+        final String location;
+        /**
+         * Flag which controls whether a binding is active during loading.
+         */
+        final boolean loadEnabled;
+        /**
+         * Flag which controls whether a binding is active during saving.
+         */
+        final boolean saveEnabled;
+        /**
+         * Flag which controls whether the jxpath context used by this binding 
+         * should be operating in lenient mode or not 
+         */
+        final Boolean leniency;
+        /**
+         * Array of namespace-declarations (prefix-uri pairs) that need to be set on the jxpath 
+         */
+        final Map nsDeclarations;
+
+        final static CommonAttributes DEFAULT = new CommonAttributes("location unknown", true, true, null, null);
+
+        CommonAttributes(String location, String direction, String leniency, Map nsDeclarations){
+            this(location, isLoadEnabled(direction), isSaveEnabled(direction), decideLeniency(leniency), nsDeclarations);
+            this.direction = direction;
+        }
+
+        CommonAttributes(String location, boolean loadEnabled, boolean saveEnabled, Boolean leniency, Map nsDeclarations){
+        	this.direction = null;
+            this.location = location;
+            this.loadEnabled = loadEnabled;
+            this.saveEnabled = saveEnabled;
+            this.leniency = leniency;
+            this.nsDeclarations = nsDeclarations;
+        }
+
+        /**
+         * Interpretes the value of the direction attribute into activity of the load action.
+         * @param direction
+         * @return true if direction is either set to "both" or "load"
+         */
+        private static boolean isLoadEnabled(String direction) {
+            return "both".equals(direction) || "load".equals(direction);
+        }
+
+        /**
+         * Interpretes the value of the direction attribute into activity of the save action.
+         * @param direction value of the @direction attribute
+         * @return true if direction is either set to "both" or "save"
+         */
+        private static boolean isSaveEnabled(String direction) {
+            return "both".equals(direction) || "save".equals(direction);
+        }
+
+
+        /**
+         * Interpretes the value of the lenient attribute into a Boolean object
+         * allowing three-state logic (true/false/unset)
+         * @param leniency value of the @lenient attribute
+         * @return null if the leniency parameter is null or a String otherwise the allowed values
+         */
+        private static Boolean decideLeniency(String leniency) {
+            return BooleanUtils.toBooleanObject(leniency);
+        }
+        
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JXPathBindingBuilderBase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JXPathBindingManager.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JXPathBindingManager.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JXPathBindingManager.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JXPathBindingManager.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,391 @@
+/*
+ * 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.
+ */
+package org.apache.cocoon.forms.binding;
+
+import java.util.ArrayList;
+import java.util.Stack;
+
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.activity.Initializable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.components.LifecycleHelper;
+import org.apache.cocoon.forms.CacheManager;
+import org.apache.cocoon.forms.binding.library.Library;
+import org.apache.cocoon.forms.binding.library.LibraryException;
+import org.apache.cocoon.forms.binding.library.LibraryManager;
+import org.apache.cocoon.forms.binding.library.LibraryManagerImpl;
+import org.apache.cocoon.forms.datatype.DatatypeManager;
+import org.apache.cocoon.forms.util.DomHelper;
+import org.apache.cocoon.forms.util.SimpleServiceSelector;
+import org.apache.cocoon.util.location.LocationAttributes;
+import org.apache.commons.lang.exception.NestableRuntimeException;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceResolver;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+
+/**
+ * JXPathBindingManager provides an implementation of {@link BindingManager}by
+ * usage of the <a href="http://jakarta.apache.org/commons/jxpath/index.html">
+ * JXPath package </a>.
+ *
+ * @version $Id: JXPathBindingManager.java 292615 2005-09-30 03:40:16Z antonio $
+ */
+public class JXPathBindingManager extends AbstractLogEnabled implements
+BindingManager, Contextualizable, Serviceable, Disposable, Initializable, Configurable,
+ThreadSafe {
+    
+    private static final String PREFIX = "CocoonFormBinding:";
+    
+    private ServiceManager manager;
+    
+    private DatatypeManager datatypeManager;
+    
+    private Configuration configuration;
+    
+    private SimpleServiceSelector bindingBuilderSelector;
+    
+    private CacheManager cacheManager;
+    
+    private Context avalonContext;
+    
+    private LibraryManagerImpl libraryManager;
+    
+    public void contextualize(Context context) throws ContextException {
+        this.avalonContext = context;
+    }
+    
+    public void service(ServiceManager manager) throws ServiceException {
+        this.manager = manager;
+        this.datatypeManager = (DatatypeManager) manager.lookup(DatatypeManager.ROLE);
+        this.cacheManager = (CacheManager) manager.lookup(CacheManager.ROLE);
+    }
+    
+    public void configure(Configuration configuration)
+    throws ConfigurationException {
+        this.configuration = configuration;
+    }
+    
+    public void initialize() throws Exception {
+        bindingBuilderSelector = new SimpleServiceSelector("binding",
+                JXPathBindingBuilderBase.class);
+        LifecycleHelper.setupComponent(bindingBuilderSelector,
+                getLogger(),
+                this.avalonContext,
+                this.manager,
+                configuration.getChild("bindings"));
+        
+        libraryManager = new LibraryManagerImpl();
+        libraryManager.setBindingManager(this);
+        LifecycleHelper.setupComponent(libraryManager,
+                getLogger(),
+                this.avalonContext,
+                this.manager,
+                configuration.getChild("library"));
+    }
+    
+    public Binding createBinding(Source source) throws BindingException {
+        Binding binding = (Binding) this.cacheManager.get(source, PREFIX);
+        
+        if (binding != null && !binding.isValid())
+            binding = null; //invalidate
+        
+        if (binding == null) {
+            try {
+                InputSource is = new InputSource(source.getInputStream());
+                is.setSystemId(source.getURI());
+                
+                Document doc = DomHelper.parse(is, this.manager);
+                Element rootElm = doc.getDocumentElement();
+                if (BindingManager.NAMESPACE.equals(rootElm.getNamespaceURI())) {
+                    binding = getBuilderAssistant()
+                    .getBindingForConfigurationElement(rootElm);
+                    ((JXPathBindingBase) binding).enableLogging(getLogger());
+                    if (getLogger().isDebugEnabled()) {
+                        getLogger().debug("Creation of new binding finished. " + binding);
+                    }
+                } else {
+                    if (getLogger().isDebugEnabled()) {
+                        getLogger().debug("Root Element of said binding file is in wrong namespace.");
+                    }
+                }
+                
+                this.cacheManager.set(binding, source, PREFIX);
+            } catch (BindingException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new BindingException("Error creating binding from " +
+                        source.getURI(), e);
+            }
+        }
+        
+        return binding;
+    }
+    
+    public Binding createBinding(String bindingURI) throws BindingException {
+        SourceResolver sourceResolver = null;
+        Source source = null;
+        
+        try {
+            try {
+                sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
+                source = sourceResolver.resolveURI(bindingURI);
+            } catch (Exception e) {
+                throw new BindingException("Error resolving binding source: " +
+                        bindingURI);
+            }
+            return createBinding(source);
+        } finally {
+            if (source != null) {
+                sourceResolver.release(source);
+            }
+            if (sourceResolver != null) {
+                manager.release(sourceResolver);
+            }
+        }
+    }
+    
+    public Assistant getBuilderAssistant() {
+        return new Assistant();
+    }
+    
+    public void dispose() {
+        if (this.bindingBuilderSelector != null) {
+            this.bindingBuilderSelector.dispose();
+            this.bindingBuilderSelector = null;
+        }
+        this.manager.release(this.datatypeManager);
+        this.datatypeManager = null;
+        this.manager.release(this.cacheManager);
+        this.cacheManager = null;
+        this.manager = null;
+    }
+    
+    /**
+     * Assistant Inner class discloses enough features to the created
+     * childBindings to recursively
+     *
+     * This patterns was chosen to prevent Inversion Of Control between this
+     * factory and its builder classes (that could be provided by third
+     * parties.)
+     */
+    /*
+     * NOTE: To get access to the logger in this inner class you must not call
+     * getLogger() as with JDK 1.3 this gives a NoSuchMethod error. You need to
+     * implement an explicit access method for the logger in the outer class.
+     */
+    public class Assistant {
+        
+        private BindingBuilderContext context = new BindingBuilderContext();
+        private Stack contextStack = new Stack();
+        
+        private JXPathBindingBuilderBase getBindingBuilder(String bindingType)
+        throws BindingException {
+            try {
+                return (JXPathBindingBuilderBase) bindingBuilderSelector
+                .select(bindingType);
+            } catch (ServiceException e) {
+                throw new BindingException(
+                        "Cannot handle binding element with " + "name \""
+                        + bindingType + "\".", e);
+            }
+        }
+        
+        /**
+         * Creates a {@link Binding} following the specification in the
+         * provided config element.
+         */
+        public JXPathBindingBase getBindingForConfigurationElement(
+                Element configElm) throws BindingException {
+            String bindingType = configElm.getLocalName();
+            JXPathBindingBuilderBase bindingBuilder = getBindingBuilder(bindingType);
+            
+            boolean flag = false;
+            if(context.getLocalLibrary() == null) {
+                Library lib = new Library(libraryManager);
+                context.setLocalLibrary(lib);
+                lib.setAssistant(getBuilderAssistant());
+                lib.setSourceURI(LocationAttributes.getURI(configElm));
+                flag = true;
+            }
+            
+            if(context.getLocalLibrary()!=null 
+                    && configElm.hasAttribute("extends")) {
+                try {
+                    context.setSuperBinding(context.getLocalLibrary().getBinding(configElm.getAttribute("extends")));
+                    
+                } catch(LibraryException e) {
+                    //throw new RuntimeException("Error extending binding! (at "+DomHelper.getLocation(configElm)+")",e);
+                    throw new NestableRuntimeException("Error extending binding! (at "+DomHelper.getLocation(configElm)+")",e);
+                }
+            } else {
+                context.setSuperBinding(null);
+            }
+            
+            JXPathBindingBase childBinding = bindingBuilder.buildBinding(configElm, this);
+            
+            if(flag && childBinding != null) {
+                childBinding.setLocalLibary(context.getLocalLibrary());
+            }
+            
+            // this might get called unnecessarily, but solves issues with the libraries
+            if(childBinding != null)
+                childBinding.enableLogging(getLogger());
+            
+            
+            return childBinding;
+        }
+        
+        private JXPathBindingBase[] mergeBindings(JXPathBindingBase[] existing, JXPathBindingBase[] extra) {
+            
+            if(existing == null || existing.length == 0)
+                return extra;
+            
+            if(extra == null || extra.length == 0)
+                return existing;
+            
+            // have to do it the stupid painter way..
+            ArrayList list = new ArrayList(existing.length);
+            for(int i=0; i<existing.length; i++)
+                list.add(existing[i]);
+            
+            for(int i=0; i<extra.length; i++) {
+                if(extra[i].getId()==null)
+                    list.add(extra[i]);
+                else {
+                    // try to replace existing one
+                    boolean match = false;
+                    for(int j=0; j<list.size(); j++) {
+                        if(extra[i].getId().equals(((JXPathBindingBase)list.get(j)).getId())) {
+                            list.set(j,extra[i]);
+                            match = true;
+                            break; // stop searching
+                        }
+                    }
+                    // if no match, just add
+                    if(!match)
+                        list.add(extra[i]);
+                }
+            }	
+            
+            return (JXPathBindingBase[])list.toArray(new JXPathBindingBase[0]);
+        }
+        
+        /**
+         * proxy for compatibility
+         * 
+         */
+        public JXPathBindingBase[] makeChildBindings(Element parentElement) throws BindingException {
+            return makeChildBindings(parentElement,new JXPathBindingBase[0]);
+        }
+        
+        /**
+         * Makes an array of childBindings for the child-elements of the
+         * provided configuration element.
+         */
+        public JXPathBindingBase[] makeChildBindings(Element parentElement, JXPathBindingBase[] existingBindings)
+        throws BindingException {
+            if (existingBindings == null)
+                existingBindings = new JXPathBindingBase[0];
+            
+            if (parentElement != null) {
+                Element[] childElements = DomHelper.getChildElements(
+                        parentElement, BindingManager.NAMESPACE);
+                if (childElements.length > 0) {
+                    JXPathBindingBase[] childBindings = new JXPathBindingBase[childElements.length];
+                    for (int i = 0; i < childElements.length; i++) {
+                        
+                        pushContext();
+                        context.setSuperBinding(null);
+                        
+                        String id = DomHelper.getAttribute(childElements[i], "id", null);
+                        String path = DomHelper.getAttribute(childElements[i], "path", null);
+                        if(context.getLocalLibrary()!=null && childElements[i].getAttribute("extends")!=null) {
+                            try {
+                                context.setSuperBinding(context.getLocalLibrary().getBinding(childElements[i].getAttribute("extends")));
+                                
+                                if(context.getSuperBinding() == null) // not found in library
+                                    context.setSuperBinding(getBindingByIdOrPath(id,path,existingBindings));
+                                
+                            } catch(LibraryException e) {
+                                throw new BindingException("Error extending binding! (at "+DomHelper.getLocation(childElements[i])+")",e);
+                            }
+                        }
+                        
+                        childBindings[i] = getBindingForConfigurationElement(childElements[i]);
+                        
+                        popContext();
+                    }
+                    return mergeBindings(existingBindings,childBindings);
+                }
+            }
+            return existingBindings;
+        }
+        
+        private JXPathBindingBase getBindingByIdOrPath(String id, String path, JXPathBindingBase[] bindings) {
+            String name = id;
+            if(name == null) {
+                name = "Context:"+path;
+            }
+            
+            for(int i=0; i<bindings.length; i++) {
+                if(name.equals(bindings[i].getId()))
+                    return bindings[i];
+            }
+            return null;
+        }
+        
+        public DatatypeManager getDatatypeManager() {
+            return datatypeManager;
+        }
+        
+        public ServiceManager getServiceManager() {
+            return manager;
+        }
+        
+        public LibraryManager getLibraryManager() {
+            return libraryManager;
+        }
+        
+        public BindingBuilderContext getContext() {
+            return this.context;
+        }
+        private void pushContext() {
+            BindingBuilderContext c = new BindingBuilderContext(context);
+            contextStack.push(context);
+            context = c;
+        }
+        private void popContext() {
+            if(!contextStack.empty()) {
+                context = (BindingBuilderContext)contextStack.pop();
+            } else {
+                context = new BindingBuilderContext();
+            }
+        }
+        
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JXPathBindingManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JavaScriptJXPathBinding.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JavaScriptJXPathBinding.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JavaScriptJXPathBinding.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JavaScriptJXPathBinding.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+package org.apache.cocoon.forms.binding;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.avalon.framework.CascadingRuntimeException;
+import org.apache.avalon.framework.context.Context;
+import org.apache.cocoon.components.ContextHelper;
+import org.apache.cocoon.components.flow.javascript.ScriptableMap;
+import org.apache.cocoon.forms.formmodel.Widget;
+import org.apache.cocoon.forms.util.JavaScriptHelper;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.Pointer;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.Scriptable;
+
+/**
+ *
+ * @author <a href="http://www.apache.org/~sylvain/">Sylvain Wallez</a>
+ * @version $Id: JavaScriptJXPathBinding.java 327146 2005-10-21 10:38:37Z sylvain $
+ */
+public class JavaScriptJXPathBinding extends JXPathBindingBase {
+
+    private final Context avalonContext;
+    private final String id;
+    private final String path;
+    private final Function loadScript;
+    private final Function saveScript;
+    private final Scriptable childBindings;
+    private final Map childBindingsMap;
+    
+    final static String[] LOAD_PARAMS = { "widget", "jxpathPointer", "jxpathContext", "childBindings" };
+    final static String[] SAVE_PARAMS = { "widget", "jxpathPointer", "jxpathContext", "childBindings" };
+
+    public JavaScriptJXPathBinding(
+    	    Context context, JXPathBindingBuilderBase.CommonAttributes commonAtts, String id,
+            String path, Function loadScript, Function saveScript, Map childBindings) {
+        super(commonAtts);
+        this.id = id;
+        this.path = path;
+        this.loadScript = loadScript;
+        this.saveScript = saveScript;
+        this.avalonContext = context;
+        
+        // Set parent on child bindings
+        for(Iterator iter = childBindings.values().iterator(); iter.hasNext(); ) {
+        	    ((Binding)iter.next()).setParent(this);
+        }
+        
+        this.childBindingsMap = childBindings;
+        this.childBindings = new ScriptableMap(childBindings);
+    }
+    
+    public String getPath() { return path; }
+    public String getId() { return id; }
+    public Context getContext() { return avalonContext; }
+    public Function getLoadScript() { return loadScript; }
+    public Function getSaveScript() { return saveScript; }
+    public Map getChildBindingsMap() { return childBindingsMap; }
+
+    public void doLoad(Widget frmModel, JXPathContext jctx) {
+        if (this.loadScript != null) {
+            Widget widget = selectWidget(frmModel,this.id);
+    
+            // Move to widget context
+            Pointer pointer = jctx.getPointer(this.path);
+    
+            Map objectModel = ContextHelper.getObjectModel(this.avalonContext);
+
+            try {
+                JXPathContext newCtx = pointer.getNode() == null ? null :
+                	    jctx.getRelativeContext(pointer);
+
+                JavaScriptHelper.callFunction(this.loadScript, widget,
+                		new Object[] {widget, pointer, newCtx, this.childBindings}, objectModel);
+    
+            } catch(RuntimeException re) {
+                // rethrow
+                throw re;
+            } catch(Exception e) {
+                throw new CascadingRuntimeException("Error invoking JavaScript event handler", e);
+            }
+        } else {
+            if (this.getLogger().isInfoEnabled()) {
+                this.getLogger().info("[Javascript Binding] - loadForm: No javascript code avaliable. Widget id=" + this.getId());
+            }
+        }
+    }
+
+    public void doSave(Widget frmModel, JXPathContext jctx) throws BindingException {
+        if (this.saveScript != null) {
+            Widget widget = selectWidget(frmModel,this.id);
+
+            // Move to widget context and create the path if needed
+            Pointer pointer = jctx.createPath(this.path);
+            JXPathContext widgetCtx = jctx.getRelativeContext(pointer);
+            try {
+                Map objectModel = ContextHelper.getObjectModel(this.avalonContext);
+
+                JavaScriptHelper.callFunction(this.saveScript, widget,
+                		new Object[] {widget, pointer, widgetCtx, this.childBindings}, objectModel);
+
+            } catch(RuntimeException re) {
+                // rethrow
+                throw re;
+            } catch(Exception e) {
+                throw new CascadingRuntimeException("Error invoking JavaScript event handler", e);
+            }
+        } else {
+            if (this.getLogger().isInfoEnabled()) {
+                this.getLogger().info("[Javascript Binding] - saveForm: No code avaliable on the javascript binding with id \"" + this.getId() + "\"");
+            }
+        }
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JavaScriptJXPathBinding.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JavaScriptJXPathBindingBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JavaScriptJXPathBindingBuilder.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JavaScriptJXPathBindingBuilder.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JavaScriptJXPathBindingBuilder.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+package org.apache.cocoon.forms.binding;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.cocoon.forms.binding.JXPathBindingManager.Assistant;
+import org.apache.cocoon.forms.util.DomHelper;
+import org.apache.cocoon.forms.util.JavaScriptHelper;
+import org.mozilla.javascript.Function;
+import org.w3c.dom.Element;
+
+/**
+ * Builds a {@link Binding} based on two JavaScript snippets, respectively for loading and saving the form.
+ * This binding also optionally accepts named child bindings, which are useful when the bound widget is a container.
+ * <p>
+ * The syntax for this binding is as follows:
+ * <pre>
+ *   &lt;fb:javascript id="foo" path="@foo"&gt;
+ *     &lt;fb:load-form&gt;
+ *       var appValue = jxpathPointer.getValue();
+ *       var formValue = doLoadConversion(appValue);
+ *       widget.setValue(formValue);
+ *       childBindings["foo"].loadFormFromModel(widget, jxpathContext);
+ *     &lt;/fb:load-form&gt;
+ *     &lt;fb:save-form&gt;
+ *       var formValue = widget.getValue();
+ *       var appValue = doSaveConversion(formValue);
+ *       jxpathPointer.setValue(appValue);
+ *       childBindings["foo"].saveFormToModel(widget, jxpathContext);
+ *     &lt;/fb:save-form&gt;
+ *     &lt;fb:child-binding name="foo"&gt;
+ *       &lt;fb:value id="bar" path="baz"/&gt;
+ *     &lt;/fb:child-binding&gt;
+ *   &lt;/fb:javascript&gt;
+ * </pre>
+ * This example is rather trivial and could be replaced by a simple &lt;fb:value&gt;, but
+ * it shows the available variables in the script:
+ * <ul>
+ * <li><code>widget</code>: the widget identified by the "id" attribute,
+ * <li><code>jxpathPointer</code>: the JXPath pointer corresponding to the "path" attribute,
+ * <li><code>jxpathContext</code> (not shown): the JXPath context corresponding to the "path" attribute
+ * </ul>
+ * <b>Notes:</b><ul>
+ * <li>The &lt;fb:save-form&gt; snippet should be ommitted if the "direction" attribute is set to "load".</li>
+ * <li>The &lt;fb:load-form&gt; snippet should be ommitted if the "direction" attribute is set to "save".</li>
+ * </ul>
+ *
+ * @author <a href="http://www.apache.org/~sylvain/">Sylvain Wallez</a>
+ * @version $Id: JavaScriptJXPathBindingBuilder.java 289538 2005-09-16 13:46:22Z sylvain $
+ */
+public class JavaScriptJXPathBindingBuilder extends JXPathBindingBuilderBase implements Contextualizable {
+
+    private Context avalonContext;
+
+    public void contextualize(Context context) throws ContextException {
+        this.avalonContext = context;
+    }
+
+    public JXPathBindingBase buildBinding(Element element, Assistant assistant) throws BindingException {
+        try {
+            CommonAttributes commonAtts = JXPathBindingBuilderBase.getCommonAttributes(element);
+
+            String id = DomHelper.getAttribute(element, "id", null);
+            String path = DomHelper.getAttribute(element, "path", null);
+
+            JavaScriptJXPathBinding otherBinding = (JavaScriptJXPathBinding)assistant.getContext().getSuperBinding();
+            
+            if(otherBinding!=null) {
+            	commonAtts = JXPathBindingBuilderBase.mergeCommonAttributes(otherBinding.getCommonAtts(),commonAtts);
+            	
+            	if(id==null)
+            		id=otherBinding.getId();
+            	if(path==null)
+            		path=otherBinding.getPath();
+            }
+            
+            // Build load script
+            Function loadScript = null;
+            if (commonAtts.loadEnabled) {
+                if (otherBinding != null)
+                	loadScript = otherBinding.getLoadScript();
+            	
+                Element loadElem = DomHelper.getChildElement(element, BindingManager.NAMESPACE, "load-form");
+                if (loadElem != null) {
+                	loadScript = JavaScriptHelper.buildFunction(loadElem, "loadForm", JavaScriptJXPathBinding.LOAD_PARAMS);
+                }
+            }
+
+            // Build save script
+            Function saveScript = null;
+            if (commonAtts.saveEnabled) {
+            	if (otherBinding != null)
+            		saveScript = otherBinding.getSaveScript();
+            	
+                Element saveElem = DomHelper.getChildElement(element, BindingManager.NAMESPACE, "save-form");
+                if (saveElem != null) {
+                	saveScript = JavaScriptHelper.buildFunction(saveElem, "saveForm", JavaScriptJXPathBinding.SAVE_PARAMS);
+                }
+            }
+
+            // Build child bindings
+            Map childBindings = new HashMap();
+            
+            if (otherBinding != null) {
+            	Map otherChildren = otherBinding.getChildBindingsMap();
+            	Iterator it = otherChildren.entrySet().iterator();
+            	while(it.hasNext()) {
+            		Map.Entry entry = (Map.Entry)it.next();
+            		childBindings.put(entry.getKey(),entry.getValue());
+            	}
+            }
+            
+            Element[] children = DomHelper.getChildElements(element, BindingManager.NAMESPACE, "child-binding");
+            if (children.length != 0) {
+                for (int i = 0; i < children.length; i++) {
+                    Element child = children[i];
+
+                    // Get the binding name and check its uniqueness
+                    String name = DomHelper.getAttribute(child, "name");
+                    
+                    JXPathBindingBase[] otherBindings = null;
+                    if (childBindings.containsKey(name)) {
+                        //throw new BindingException("Duplicate name '" + name + "' at " + DomHelper.getLocation(child));
+                    	otherBindings = ((ComposedJXPathBindingBase)childBindings.get(name)).getChildBindings();
+                    }
+                    
+                    // Build the child binding
+                    JXPathBindingBase[] bindings = assistant.makeChildBindings(child,otherBindings);
+                    if (bindings == null) {
+                        bindings = new JXPathBindingBase[0];
+                    }
+
+                    ComposedJXPathBindingBase composedBinding = new ComposedJXPathBindingBase(commonAtts, bindings);
+                    composedBinding.enableLogging(getLogger());
+                    childBindings.put(name, composedBinding);
+                }
+            }
+
+            JXPathBindingBase result = new JavaScriptJXPathBinding(this.avalonContext, commonAtts, id, path, loadScript, saveScript,
+                Collections.unmodifiableMap(childBindings));
+            result.enableLogging(getLogger());
+            return result;
+
+        } catch(BindingException be) {
+            throw be;
+        } catch(Exception e) {
+            throw new BindingException("Cannot build binding at " + DomHelper.getLocation(element), e);
+        }
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/JavaScriptJXPathBindingBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/MultiValueJXPathBinding.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/MultiValueJXPathBinding.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/MultiValueJXPathBinding.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/MultiValueJXPathBinding.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+package org.apache.cocoon.forms.binding;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.cocoon.forms.datatype.convertor.Convertor;
+import org.apache.cocoon.forms.datatype.convertor.ConversionResult;
+import org.apache.cocoon.forms.formmodel.Widget;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.Pointer;
+
+/**
+ * Simple binding for multi fields: on save, first deletes the target data
+ * before recreating it from scratch.
+ *
+ * @version $Id: MultiValueJXPathBinding.java 289538 2005-09-16 13:46:22Z sylvain $
+ */
+public class MultiValueJXPathBinding extends JXPathBindingBase {
+
+    private final String multiValueId;
+    private final String multiValuePath;
+    private final String rowPath;
+    private final JXPathBindingBase updateBinding;
+    private final Convertor convertor;
+    private final Locale convertorLocale;
+
+    public MultiValueJXPathBinding(
+        JXPathBindingBuilderBase.CommonAttributes commonAtts, String multiValueId,
+        String multiValuePath, String rowPath,
+        JXPathBindingBase[] updateBindings, Convertor convertor, Locale convertorLocale) {
+        super(commonAtts);
+        this.multiValueId = multiValueId;
+        this.multiValuePath = multiValuePath;
+        this.rowPath = rowPath;
+        this.updateBinding = new ComposedJXPathBindingBase(JXPathBindingBuilderBase.CommonAttributes.DEFAULT, updateBindings);
+        this.convertor = convertor;
+        this.convertorLocale = convertorLocale;
+    }
+    
+    public String getId() { return multiValueId; }
+    public String getMultiValuePath() { return multiValuePath; }
+    public String getRowPath() { return rowPath; }
+    public ComposedJXPathBindingBase getUpdateBinding() { return (ComposedJXPathBindingBase)updateBinding; }
+    public Convertor getConvertor() { return convertor; }
+    public Locale getLocale() { return convertorLocale; }
+
+    public void doLoad(Widget frmModel, JXPathContext jctx) throws BindingException {
+        Widget widget = selectWidget(frmModel,this.multiValueId);
+        if (widget == null) {
+            throw new BindingException("The widget with the ID [" + this.multiValueId
+                    + "] referenced in the binding does not exist in the form definition.");
+        }
+
+        // Move to multi value context
+        Pointer ptr = jctx.getPointer(this.multiValuePath);
+        if (ptr.getNode() != null) {
+            // There are some nodes to load from
+
+            JXPathContext multiValueContext = jctx.getRelativeContext(ptr);
+            // build a jxpath iterator for pointers
+            Iterator rowPointers = multiValueContext.iterate(this.rowPath);
+
+            LinkedList list = new LinkedList();
+
+            while (rowPointers.hasNext()) {
+                Object value = rowPointers.next();
+
+                if (value != null && convertor != null) {
+                    if (value instanceof String) {
+                        ConversionResult conversionResult = convertor.convertFromString((String)value, convertorLocale, null);
+                        if (conversionResult.isSuccessful())
+                            value = conversionResult.getResult();
+                        else
+                            value = null;
+                    } else {
+                        getLogger().warn("Convertor ignored on backend-value which isn't of type String.");
+                    }
+                }
+
+                list.add(value);
+            }
+
+            widget.setValue(list.toArray());
+        }
+
+        if (getLogger().isDebugEnabled())
+            getLogger().debug("done loading values " + toString());
+    }
+
+    public void doSave(Widget frmModel, JXPathContext jctx) throws BindingException {
+        Widget widget = selectWidget(frmModel,this.multiValueId);
+        Object[] values = (Object[])widget.getValue();
+
+        JXPathContext multiValueContext = jctx.getRelativeContext(jctx.createPath(this.multiValuePath));
+
+        // Delete all that is already present
+        
+        // Unfortunately the following statement doesn't work (it doesn't removes all elements from the 
+        // list because of a bug in JXPath) so I had to work out another immediate solution
+        //multiValueContext.removeAll(this.rowPath);
+        
+        Iterator rowPointers = multiValueContext.iteratePointers(this.rowPath);
+        List l = new ArrayList();
+        while( rowPointers.hasNext() )
+        {
+            Pointer p = (Pointer)rowPointers.next();
+            l.add(p.asPath());
+        }
+        Collections.sort(l);
+        for( int i = l.size()-1; i >= 0; i-- )
+        {
+            multiValueContext.removePath((String)l.get(i));
+        }
+
+        boolean update = false;
+
+        if (values != null) {
+            // first update the values
+            for (int i = 0; i < values.length; i++) {
+                String path = this.rowPath + '[' + (i+1) + ']';
+                Pointer rowPtr = multiValueContext.createPath(path);
+
+                Object value = values[i];
+                if (value != null && convertor != null) {
+                    value = convertor.convertToString(value, convertorLocale, null);
+                }
+
+                rowPtr.setValue(value);
+            }
+
+            // now perform any other bindings that need to be performed when the value is updated
+            this.updateBinding.saveFormToModel(frmModel, multiValueContext);
+
+            update = true;
+        }
+
+
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("done saving " + toString() + " -- on-update == " + update);
+        }
+
+
+
+    }
+
+    public String toString() {
+        return "MultiValueJXPathBinding [widget=" + this.multiValueId + ", xpath=" + this.multiValuePath + "]";
+    }
+
+    public void enableLogging(Logger logger) {
+        super.enableLogging(logger);
+        this.updateBinding.enableLogging(logger);
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/MultiValueJXPathBinding.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/MultiValueJXPathBindingBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/MultiValueJXPathBindingBuilder.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/MultiValueJXPathBindingBuilder.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/MultiValueJXPathBindingBuilder.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+package org.apache.cocoon.forms.binding;
+
+import java.util.Locale;
+
+import org.apache.cocoon.forms.FormsConstants;
+import org.apache.cocoon.forms.datatype.convertor.Convertor;
+import org.apache.cocoon.forms.util.DomHelper;
+import org.apache.cocoon.i18n.I18nUtils;
+import org.w3c.dom.Element;
+
+/**
+ * A simple multi field binding that will replace (i.e. delete then re-add all) its
+ * content.
+ * <pre><code>
+ * &lt;fb:multi-value id="<i>widget-id</i>"
+ *   parent-path="<i>xpath-expression</i>"
+ *   row-path="<i>xpath-expression</i>"&gt;
+ *   &lt;!-- optional child binding to be executed upon 'save' of changed value --&gt;
+ *   &lt;fb:on-update&gt;
+ *     &lt;!-- any childbinding --&gt;
+ *   &lt;/fb:on-update&gt;
+ * &lt;/fb:multi-value&gt;
+ * </code></pre>
+ *
+ * @version $Id: MultiValueJXPathBindingBuilder.java 326838 2005-10-20 06:26:53Z sylvain $
+ */
+public class MultiValueJXPathBindingBuilder
+    extends JXPathBindingBuilderBase {
+
+    public JXPathBindingBase buildBinding(
+        Element bindingElem,
+        JXPathBindingManager.Assistant assistant) throws BindingException {
+
+        try {
+            CommonAttributes commonAtts = JXPathBindingBuilderBase.getCommonAttributes(bindingElem);
+
+            String multiValueId = DomHelper.getAttribute(bindingElem, "id", null);
+            String parentPath = DomHelper.getAttribute(bindingElem, "parent-path", null);
+            String rowPath = DomHelper.getAttribute(bindingElem, "row-path", null);
+
+            
+            Convertor convertor = null;
+            Locale convertorLocale = Locale.US;
+            Element convertorEl = DomHelper.getChildElement(bindingElem, FormsConstants.DEFINITION_NS, "convertor");
+            if (convertorEl != null) {
+                String datatype = DomHelper.getAttribute(convertorEl, "datatype", null);
+                String localeStr = DomHelper.getAttribute(convertorEl, "locale", null);
+                if (localeStr != null) {
+                    convertorLocale = I18nUtils.parseLocale(localeStr);
+                }
+
+                convertor = assistant.getDatatypeManager().createConvertor(datatype, convertorEl);
+            }
+            
+            MultiValueJXPathBinding existingBinding = (MultiValueJXPathBinding)assistant.getContext().getSuperBinding();
+            JXPathBindingBase[] existingBindings = new JXPathBindingBase[0]; 
+            if(existingBinding != null) {
+            	commonAtts = JXPathBindingBuilderBase.mergeCommonAttributes(existingBinding.getCommonAtts(),commonAtts);
+            	existingBindings = existingBinding.getUpdateBinding().getChildBindings();
+            	
+            	if(multiValueId == null)
+            		multiValueId = existingBinding.getId();
+            	if(parentPath == null)
+            		parentPath = existingBinding.getMultiValuePath();
+            	if(rowPath == null)
+            		rowPath = existingBinding.getRowPath();
+            	
+            	if(convertor == null) {
+            		convertor = existingBinding.getConvertor();
+            		convertorLocale = existingBinding.getLocale();
+            	}	
+            }
+            
+            Element updateWrapElement =
+                DomHelper.getChildElement(bindingElem, BindingManager.NAMESPACE, "on-update");
+            JXPathBindingBase[] updateBindings = assistant.makeChildBindings(updateWrapElement,existingBindings);
+
+
+            return new MultiValueJXPathBinding( commonAtts, multiValueId, parentPath, rowPath,
+                                                updateBindings, convertor, convertorLocale);
+        } catch (BindingException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new BindingException("Error building multi value binding defined at " + DomHelper.getLocation(bindingElem), e);
+        }
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/MultiValueJXPathBindingBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/NewJXPathBinding.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/NewJXPathBinding.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/NewJXPathBinding.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/NewJXPathBinding.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+package org.apache.cocoon.forms.binding;
+
+import org.apache.cocoon.forms.formmodel.Widget;
+import org.apache.commons.jxpath.JXPathContext;
+
+/**
+ * NewJXPathBinding provides an implementation of a {@link Binding}
+ * that references a class of bindings.
+ * <p>
+ * NOTES: <ol>
+ * <li>This Binding assumes that the provided widget-id points to a
+ * class that contains other widgets.</li>
+ * </ol>
+ *
+ * @version $Id: NewJXPathBinding.java 289538 2005-09-16 13:46:22Z sylvain $
+ */
+public class NewJXPathBinding extends ComposedJXPathBindingBase {
+
+    private final String widgetId;
+
+    private Binding classBinding;
+
+    /**
+     * Constructs NewJXPathBinding
+     * @param commonAtts
+     * @param widgetId
+     * @param childBindings
+     */
+    public NewJXPathBinding(JXPathBindingBuilderBase.CommonAttributes commonAtts,
+            String widgetId, JXPathBindingBase[] childBindings) {
+        super(commonAtts, childBindings);
+        this.widgetId = widgetId;
+        this.classBinding = null;
+    }
+    
+    public String getId() { return widgetId; }
+
+    /**
+     * Recursively resolves references.
+     */
+    private void resolve() throws BindingException {
+        classBinding = getClass(widgetId);
+        if (classBinding == null) {
+            throw new BindingException("Class \"" + widgetId + "\" does not exist");
+        }
+    }
+
+    /**
+     * Narrows the scope on the form-model to the member widget-field, and
+     * narrows the scope on the object-model to the member xpath-context
+     * before continuing the binding over the child-bindings.
+     */
+    public void doLoad(Widget frmModel, JXPathContext jxpc) throws BindingException {
+        if (classBinding == null)
+            resolve();
+        Binding[] subBindings = ((ComposedJXPathBindingBase)classBinding).getChildBindings();
+        if (subBindings != null) {
+            int size = subBindings.length;
+            for (int i = 0; i < size; i++) {
+                subBindings[i].loadFormFromModel(frmModel, jxpc);
+            }
+        }
+    }
+
+    /**
+     * Narrows the scope on the form-model to the member widget-field, and
+     * narrows the scope on the object-model to the member xpath-context
+     * before continuing the binding over the child-bindings.
+     */
+    public void doSave(Widget frmModel, JXPathContext jxpc) throws BindingException {
+        if (classBinding == null)
+            resolve();
+        Binding[] subBindings = ((ComposedJXPathBindingBase)classBinding).getChildBindings();
+        if (subBindings != null) {
+            int size = subBindings.length;
+            for (int i = 0; i < size; i++) {
+                subBindings[i].saveFormToModel(frmModel, jxpc);
+            }
+        }
+    }
+
+    public String toString() {
+        return "NewJXPathBinding [widget=" + this.widgetId + "]";
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/NewJXPathBinding.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/NewJXPathBindingBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/NewJXPathBindingBuilder.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/NewJXPathBindingBuilder.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/NewJXPathBindingBuilder.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+package org.apache.cocoon.forms.binding;
+
+import org.apache.cocoon.forms.util.DomHelper;
+import org.w3c.dom.Element;
+
+/**
+ * NewJXPathBindingBuilder provides a helper class for the Factory
+ * implemented in {@link JXPathBindingManager} that helps construct the
+ * actual {@link NewJXPathBinding} out of the configuration in the
+ * provided configElement which looks like:
+ * <pre><code>
+ * &lt;fb:new id="<i>widget-id</i>"&gt;
+ *   &lt;fb:field id="<i>sub-widget-id</i>" path="<i>relative-xpath</i>"
+ *       direction="<i>load|save</i>" lenient="<i>true/false</i>"/&gt;
+ * &lt;/fb:new&gt;
+ * </code></pre>
+ *
+ * @version $Id: NewJXPathBindingBuilder.java 289538 2005-09-16 13:46:22Z sylvain $
+ */
+public class NewJXPathBindingBuilder
+    extends JXPathBindingBuilderBase {
+
+    public JXPathBindingBase buildBinding(Element bindingElm, JXPathBindingManager.Assistant assistant)
+            throws BindingException {
+        try {
+            String widgetId = DomHelper.getAttribute(bindingElm, "id", null);
+            CommonAttributes commonAtts = JXPathBindingBuilderBase.getCommonAttributes(bindingElm);
+
+            JXPathBindingBase[] childBindings = new JXPathBindingBase[0];
+
+//          do inheritance
+            NewJXPathBinding otherBinding = (NewJXPathBinding)assistant.getContext().getSuperBinding();
+            if(otherBinding!=null) {
+            	childBindings = otherBinding.getChildBindings();
+            	commonAtts = JXPathBindingBuilderBase.mergeCommonAttributes(otherBinding.getCommonAtts(),commonAtts);
+            	
+            	if(widgetId==null)
+            		widgetId = otherBinding.getId();
+            }
+            
+            childBindings = assistant.makeChildBindings(bindingElm,childBindings);
+            
+            NewJXPathBinding newBinding = new NewJXPathBinding(commonAtts, widgetId, childBindings);
+            return newBinding;
+        } catch (BindingException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new BindingException("Error building new binding defined at " + DomHelper.getLocation(bindingElm), e);
+        }
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/NewJXPathBindingBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBinding.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBinding.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBinding.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBinding.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,390 @@
+/*
+ * 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.
+ */
+package org.apache.cocoon.forms.binding;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.cocoon.forms.formmodel.Repeater;
+import org.apache.cocoon.forms.formmodel.Widget;
+import org.apache.cocoon.forms.datatype.convertor.ConversionResult;
+import org.apache.commons.collections.ListUtils;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.Pointer;
+
+/**
+ * RepeaterJXPathBinding provides an implementation of a {@link Binding}
+ * that allows for bidirectional binding of a repeater-widget to/from
+ * repeating structures in the back-end object model.
+ *
+ * @version $Id: RepeaterJXPathBinding.java 290473 2005-09-20 15:30:48Z sylvain $
+ */
+public class RepeaterJXPathBinding extends JXPathBindingBase {
+    
+    private final String repeaterId;
+    private final String repeaterPath;
+    private final String rowPath;
+    private final String rowPathForInsert;
+    private final JXPathBindingBase rowBinding;
+    private final JXPathBindingBase insertRowBinding;
+    private final JXPathBindingBase deleteRowBinding;
+    private final ComposedJXPathBindingBase identityBinding;
+    
+    /**
+     * Constructs RepeaterJXPathBinding
+     */
+    public RepeaterJXPathBinding(
+            JXPathBindingBuilderBase.CommonAttributes commonAtts,
+            String repeaterId, String repeaterPath, String rowPath,
+            String rowPathForInsert,
+            JXPathBindingBase[] childBindings, JXPathBindingBase insertBinding,
+            JXPathBindingBase[] deleteBindings, JXPathBindingBase[] identityBindings) {
+        super(commonAtts);
+        this.repeaterId = repeaterId;
+        this.repeaterPath = repeaterPath;
+        this.rowPath = rowPath;
+        this.rowPathForInsert = rowPathForInsert;
+        
+        this.rowBinding = new ComposedJXPathBindingBase(
+                JXPathBindingBuilderBase.CommonAttributes.DEFAULT,
+                childBindings);
+        this.rowBinding.setParent(this);
+        
+        this.insertRowBinding = insertBinding;
+        if (this.insertRowBinding != null) {
+            this.insertRowBinding.setParent(this);
+        }
+        
+        if (deleteBindings != null) {
+            this.deleteRowBinding = new ComposedJXPathBindingBase(
+                    JXPathBindingBuilderBase.CommonAttributes.DEFAULT,
+                    deleteBindings);
+            this.deleteRowBinding.setParent(this);
+        } else {
+            this.deleteRowBinding = null;
+        }
+        
+        
+        if (identityBindings != null) {
+            
+            this.identityBinding = new ComposedJXPathBindingBase(
+                    JXPathBindingBuilderBase.CommonAttributes.DEFAULT,
+                    identityBindings);
+            this.identityBinding.setParent(this);
+        }
+        else
+            this.identityBinding = null;
+    }
+    
+    public String getId() { return repeaterId; }
+    public String getRepeaterPath() { return repeaterPath; }
+    public String getRowPath() { return rowPath; }
+    public String getInsertRowPath() { return rowPathForInsert; }
+    public ComposedJXPathBindingBase getRowBinding() { return (ComposedJXPathBindingBase)rowBinding; }
+    public ComposedJXPathBindingBase getDeleteRowBinding() { return (ComposedJXPathBindingBase)deleteRowBinding; }
+    public ComposedJXPathBindingBase getIdentityBinding() { return (ComposedJXPathBindingBase)identityBinding; }
+    public JXPathBindingBase getInsertRowBinding() { return insertRowBinding; }
+    
+    /**
+     * Binds the unique-id of the repeated rows, and narrows the context on
+     * objectModelContext and Repeater to the repeated rows before handing
+     * over to the actual binding-children.
+     */
+    public void doLoad(Widget frmModel, JXPathContext jxpc)
+    throws BindingException {
+        // Find the repeater
+        Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId);
+        if (repeater == null) {
+            throw new BindingException("The repeater with the ID [" + this.repeaterId
+                    + "] referenced in the binding does not exist in the form definition.");
+        }
+        repeater.clear();
+        int initialSize = repeater.getSize();
+        
+        // build a jxpath iterator for pointers
+        JXPathContext repeaterContext =
+            jxpc.getRelativeContext(jxpc.getPointer(this.repeaterPath));
+        Iterator rowPointers = repeaterContext.iteratePointers(this.rowPath);
+        //iterate through it
+        while (rowPointers.hasNext()) {
+            // create a new row, take that as the frmModelSubContext
+            Repeater.RepeaterRow thisRow;
+            if (initialSize > 0) {
+                thisRow = repeater.getRow(--initialSize);
+            } else {
+                thisRow = repeater.addRow();
+            }
+            // make a jxpath ObjectModelSubcontext on the iterated element
+            Pointer jxp = (Pointer)rowPointers.next();
+            JXPathContext rowContext = repeaterContext.getRelativeContext(jxp);
+            // hand it over to children
+            if (this.identityBinding != null) {
+                this.identityBinding.loadFormFromModel(thisRow, rowContext);
+            }
+            this.rowBinding.loadFormFromModel(thisRow, rowContext);
+        }
+        if (getLogger().isDebugEnabled())
+            getLogger().debug("done loading rows " + toString());
+    }
+    
+    /**
+     * Uses the mapped identity of each row to detect if rows have been
+     * updated, inserted or removed.  Depending on what happened the appropriate
+     * child-bindings are allowed to visit the narrowed contexts.
+     */
+    public void doSave(Widget frmModel, JXPathContext jxpc)
+    throws BindingException {
+        // Find the repeater
+        Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId);
+        // and his context
+        JXPathContext repeaterContext =
+            jxpc.getRelativeContext(jxpc.getPointer(this.repeaterPath));
+        
+        // create set of updatedRowIds
+        Set updatedRows = new HashSet();
+        //create list of rows to insert at end
+        List rowsToInsert = new ArrayList();
+        
+        // iterate rows in the form model...
+        int formRowCount = repeater.getSize();
+        for (int i = 0; i < formRowCount; i++) {
+            Repeater.RepeaterRow thisRow = repeater.getRow(i);
+            
+            // Get the identity
+            List identity = getIdentity(thisRow);
+            
+            if (hasNonNullElements(identity)) {
+                // iterate nodes to find match
+                Iterator rowPointers = repeaterContext.iteratePointers(this.rowPath);
+                boolean found = false;
+                while (rowPointers.hasNext()) {
+                    Pointer jxp = (Pointer) rowPointers.next();
+                    JXPathContext rowContext = repeaterContext.getRelativeContext(jxp);
+                    List contextIdentity = getIdentity(rowContext);
+                    if (ListUtils.isEqualList(identity, contextIdentity)) {
+                        // match! --> bind to children
+                        this.rowBinding.saveFormToModel(thisRow, rowContext);
+                        //        --> store rowIdValue in list of updatedRowIds
+                        updatedRows.add(identity);
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    // this is a new row
+                    rowsToInsert.add(thisRow);
+                    // also add it to the updated row id's so that this row doesn't get deleted
+                    updatedRows.add(identity);
+                }
+            } else {
+                // if there is no value to determine the identity --> this is a new row
+                rowsToInsert.add(thisRow);
+            }
+        }
+        // Iterate again nodes for deletion
+        Iterator rowPointers = repeaterContext.iteratePointers(this.rowPath);
+        List rowsToDelete = new ArrayList();
+        while (rowPointers.hasNext()) {
+            Pointer jxp = (Pointer)rowPointers.next();
+            JXPathContext rowContext = repeaterContext.getRelativeContext((Pointer)jxp.clone());
+            List contextIdentity = getIdentity(rowContext);
+            // check if the identity of the rowContext is in the updated rows
+            //     if not --> bind for delete
+            if (!isIdentityInUpdatedRows(updatedRows, contextIdentity)) {
+                rowsToDelete.add(rowContext);
+            }
+        }
+        if (rowsToDelete.size() > 0) {
+            if (this.deleteRowBinding != null) {
+                // run backwards through the list, so that we don't get into
+                // trouble by shifting indexes
+                for (int i = rowsToDelete.size() - 1; i >= 0; i--) {
+                    this.deleteRowBinding.saveFormToModel(frmModel,
+                            rowsToDelete.get(i));
+                }
+            } else {
+                if (getLogger().isWarnEnabled()) {
+                    getLogger().warn(
+                            "RepeaterBinding has detected rows to delete, " +
+                            "but misses the <on-delete-row> binding to do it."
+                    );
+                }
+            }
+        }
+        // count how many we have now
+        int indexCount = 1;
+        rowPointers = repeaterContext.iteratePointers(this.rowPathForInsert);
+        while (rowPointers.hasNext()) {
+            rowPointers.next();
+            indexCount++;
+        }
+        // end with rows to insert (to make sure they don't get deleted!)
+        if (rowsToInsert.size() > 0) {
+            if (this.insertRowBinding != null) {
+                Iterator rowIterator = rowsToInsert.iterator();
+                //register the factory!
+                while (rowIterator.hasNext()) {
+                    Repeater.RepeaterRow thisRow = (Repeater.RepeaterRow)rowIterator.next();
+                    // Perform the insert row binding.
+                    this.insertRowBinding.saveFormToModel(repeater, repeaterContext);
+                    // -->  create the path to let the context be created
+                    Pointer newRowContextPointer = repeaterContext.createPath(
+                            this.rowPathForInsert + "[" + indexCount + "]");
+                    JXPathContext newRowContext =
+                        repeaterContext.getRelativeContext(newRowContextPointer);
+                    if (getLogger().isDebugEnabled()) {
+                        getLogger().debug("inserted row at " + newRowContextPointer.asPath());
+                    }
+                    //    + rebind to children for update
+                    this.rowBinding.saveFormToModel(thisRow, newRowContext);
+                    getLogger().debug("bound new row");
+                    indexCount++;
+                }
+            } else {
+                if (getLogger().isWarnEnabled()) {
+                    getLogger().warn("RepeaterBinding has detected rows to insert, but misses "
+                            + "the <on-insert-row> binding to do it.");
+                }
+            }
+        }
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("done saving rows " + toString());
+        }
+    }
+    
+    /**
+     * Tests if an identity is already contained in a Set of identities.
+     * @param identitySet the Set of identities.
+     * @param identity the identity that is tested if it is already in the Set.
+     * @return true if the Set contains the identity, false otherwise.
+     */
+    private boolean isIdentityInUpdatedRows(Set identitySet, List identity) {
+        Iterator iter = identitySet.iterator();
+        while (iter.hasNext()) {
+            List identityFromSet = (List)iter.next();
+            if (ListUtils.isEqualList(identityFromSet, identity)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Tests if any of the elements in a List is not null.
+     * @param list
+     * @return
+     */
+    private boolean hasNonNullElements(List list) {
+        Iterator iter = list.iterator();
+        while (iter.hasNext()) {
+            if (iter.next() != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Get the identity of the given row context. That's infact a list of all
+     * the values of the fields in the bean or XML that constitute the identity. 
+     * @param rowContext
+     * @return List the identity of the row context
+     */
+    private List getIdentity(JXPathContext rowContext) {
+        if (this.identityBinding == null) {
+            return Collections.EMPTY_LIST;
+        }
+
+        List identity = new ArrayList();
+        
+        JXPathBindingBase[] childBindings = this.identityBinding.getChildBindings();
+        if (childBindings != null) {
+            int size = childBindings.length;
+            for (int i = 0; i < size; i++) {
+                ValueJXPathBinding vBinding = (ValueJXPathBinding)childBindings[i];
+                Object value = rowContext.getValue(vBinding.getXPath());
+                if (value != null && vBinding.getConvertor() != null) {
+                    if (value instanceof String) {
+                        ConversionResult conversionResult = vBinding.getConvertor().convertFromString(
+                                (String)value, vBinding.getConvertorLocale(), null);
+                        if (conversionResult.isSuccessful())
+                            value = conversionResult.getResult();
+                        else
+                            value = null;
+                    } else {
+                        if (getLogger().isWarnEnabled()) {
+                            getLogger().warn("Convertor ignored on backend-value " +
+                            "which isn't of type String.");
+                        }
+                    }
+                }
+                identity.add(value);
+            }   
+        }
+        return identity;
+    }
+    
+    /**
+     * Get the identity of the given row. That's infact a list of all the values
+     * of the fields in the form model that constitute the identity. 
+     * @param row
+     * @return List the identity of the row
+     */
+    private List getIdentity(Repeater.RepeaterRow row) {
+        // quit if we don't have an identity binding
+        if (this.identityBinding == null) {
+            return Collections.EMPTY_LIST;
+        }
+        
+        List identity = new ArrayList();
+        
+        JXPathBindingBase[] childBindings = this.identityBinding.getChildBindings();
+        if (childBindings != null) {
+            int size = childBindings.length;
+            for (int i = 0; i < size; i++) {
+                String fieldId = ((ValueJXPathBinding)childBindings[i]).getFieldId();
+                Widget widget = row.getChild(fieldId);
+                Object value = widget.getValue();
+                identity.add(value);
+            }
+        }
+        return identity;
+    }
+    
+    public String toString() {
+        return "RepeaterJXPathBinding [widget=" + this.repeaterId +
+        ", xpath=" + this.repeaterPath + "]";
+    }
+    
+    public void enableLogging(Logger logger) {
+        super.enableLogging(logger);
+        if (this.deleteRowBinding != null) {
+            this.deleteRowBinding.enableLogging(logger);
+        }
+        if (this.insertRowBinding != null) {
+            this.insertRowBinding.enableLogging(logger);
+        }
+        this.rowBinding.enableLogging(logger);
+        if (this.identityBinding != null) {
+            this.identityBinding.enableLogging(logger);
+        }
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBinding.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message