cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From simo...@apache.org
Subject svn commit: r449452 - in /cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms: binding/ formmodel/
Date Sun, 24 Sep 2006 18:26:04 GMT
Author: simoneg
Date: Sun Sep 24 11:26:03 2006
New Revision: 449452

URL: http://svn.apache.org/viewvc?view=rev&rev=449452
Log:
Enhanced repeater with pagination, sorting and filtering

Added:
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/EnhancedRepeaterJXPathBinding.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterAdapter.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterFilter.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterItem.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathAdapter.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathCollection.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterSorter.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/EnhancedRepeater.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterField.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterFieldDefinition.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterFieldDefinitionBuilder.java
Modified:
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBinding.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBindingBuilder.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/Repeater.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterActionDefinition.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterActionDefinitionBuilder.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterDefinition.java
    cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterDefinitionBuilder.java

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/EnhancedRepeaterJXPathBinding.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/EnhancedRepeaterJXPathBinding.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/EnhancedRepeaterJXPathBinding.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/EnhancedRepeaterJXPathBinding.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,96 @@
+package org.apache.cocoon.forms.binding;
+
+import java.util.Iterator;
+
+import org.apache.avalon.framework.CascadingRuntimeException;
+import org.apache.cocoon.forms.binding.JXPathBindingBuilderBase.CommonAttributes;
+import org.apache.cocoon.forms.formmodel.EnhancedRepeater;
+import org.apache.cocoon.forms.formmodel.Repeater;
+import org.apache.cocoon.forms.formmodel.Widget;
+import org.apache.cocoon.forms.formmodel.Repeater.RepeaterRow;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.Pointer;
+
+public class EnhancedRepeaterJXPathBinding extends RepeaterJXPathBinding {
+
+	private String adapterClass;
+
+	public EnhancedRepeaterJXPathBinding(CommonAttributes commonAtts, String repeaterId, String repeaterPath, 
+			String rowPath, String rowPathForInsert, JXPathBindingBase[] childBindings, 
+			JXPathBindingBase insertBinding, JXPathBindingBase[] deleteBindings, 
+			JXPathBindingBase[] identityBindings, String adapterClass) {
+		super(commonAtts, repeaterId, repeaterPath, rowPath, rowPathForInsert,
+				childBindings, insertBinding, deleteBindings, identityBindings);
+		this.adapterClass = adapterClass;
+	}
+
+	public void doLoad(Widget frmModel, JXPathContext jxpc) throws BindingException {
+		Repeater repeater = (Repeater) selectWidget(frmModel, super.getId());
+		if (!(repeater instanceof EnhancedRepeater)) {
+			super.doLoad(frmModel, jxpc);
+			return;
+		}
+		EnhancedRepeater rep = (EnhancedRepeater) repeater;
+		RepeaterAdapter adapter = null;
+		if (this.adapterClass != null) {
+			try {
+				adapter = (RepeaterAdapter) Class.forName(this.adapterClass).newInstance();
+			} catch (Exception e) {
+				throw new CascadingRuntimeException("Cannot instantiate adapter class for advanced repeater binding", e);
+			}
+		} else {
+			adapter = new RepeaterJXPathAdapter();
+		}
+		RepeaterJXPathCollection collection = new RepeaterJXPathCollection();
+		//Pointer ptr = jxpc.getPointer(super.getRepeaterPath());
+		//JXPathContext repeaterContext = jxpc.getRelativeContext(ptr);
+		collection.init(jxpc, super.getRowPath(), adapter);
+		adapter.setBinding(this);
+		adapter.setJXCollection(collection);
+		rep.setCollection(collection);
+		rep.doPageLoad();
+	}
+
+	public void doSave(Widget frmModel, JXPathContext jxpc) throws BindingException {
+		Repeater repeater = (Repeater) selectWidget(frmModel, super.getId());
+		if (!(repeater instanceof EnhancedRepeater)) {
+			super.doLoad(frmModel, jxpc);
+			return;
+		}
+		EnhancedRepeater rep = (EnhancedRepeater) repeater;
+		Pointer ptr = jxpc.getPointer(super.getRepeaterPath());
+		JXPathContext repeaterContext = jxpc.getRelativeContext(ptr);
+		RepeaterJXPathCollection collection = rep.getCollection(); 
+		
+        // iterate updated rows. note: we don't iterate over the whole context
+        for (Iterator iter = collection.getUpdatedRows().iterator(); iter.hasNext();) {
+            RepeaterItem item = (RepeaterItem) iter.next();
+            getRowBinding().saveFormToModel((RepeaterRow)item.getRow(), item.getContext());
+        }
+        
+        for (Iterator iter = collection.getDeletedRows().iterator(); iter.hasNext();) {
+            RepeaterItem item = (RepeaterItem) iter.next();
+            jxpc.removePath(item.getContext().createPath(".").asPath());
+        }
+        
+        // insert rows
+        int indexCount = collection.getOriginalCollectionSize() - collection.getDeletedRows().size();
+        for (Iterator iter = collection.getInsertedRows().iterator(); iter.hasNext();) {
+            indexCount++;
+            RepeaterItem item= (RepeaterItem) iter.next();
+            
+            // Perform the insert row binding.
+            if (getInsertRowBinding() != null) {
+            	getInsertRowBinding().saveFormToModel(item.getRow(), repeaterContext);
+            }
+            // -->  create the path to let the context be created
+            Pointer newRowContextPointer = repeaterContext.createPath(
+                    super.getInsertRowPath() + "[" + indexCount + "]");
+            JXPathContext newRowContext =
+                repeaterContext.getRelativeContext(newRowContextPointer);
+            //    + rebind to children for update
+            super.getRowBinding().saveFormToModel(item.getRow(), newRowContext);
+        }		
+	}
+
+}

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterAdapter.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterAdapter.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterAdapter.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterAdapter.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,22 @@
+package org.apache.cocoon.forms.binding;
+
+import java.util.Collection;
+
+import org.apache.cocoon.forms.formmodel.Repeater.RepeaterRow;
+
+public interface RepeaterAdapter {
+
+	public void setBinding(EnhancedRepeaterJXPathBinding binding);
+	public void setJXCollection(RepeaterJXPathCollection collection);
+	
+	public void setCollection(Collection c);
+	
+	// TODO expand with widget path
+	public RepeaterSorter sortBy(String path);
+	public RepeaterFilter getFilter();
+	
+	public RepeaterItem getItem(int i);
+	public RepeaterItem generateItem(RepeaterRow row);
+	public void populateRow(RepeaterItem item) throws BindingException;
+	
+}

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterFilter.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterFilter.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterFilter.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterFilter.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,9 @@
+package org.apache.cocoon.forms.binding;
+
+public interface RepeaterFilter {
+
+	public void setFilter(String field, Object value);
+	
+	public boolean shouldDisplay(RepeaterItem item);
+	
+}

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterItem.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterItem.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterItem.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterItem.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,45 @@
+package org.apache.cocoon.forms.binding;
+
+import org.apache.cocoon.forms.formmodel.Repeater.RepeaterRow;
+import org.apache.commons.jxpath.JXPathContext;
+
+public class RepeaterItem {
+
+	private Object handle;
+	
+	private JXPathContext context;
+	private RepeaterRow row;
+	
+	public RepeaterItem(Object handle) {
+		super();
+		this.handle = handle;
+	}
+	public JXPathContext getContext() {
+		return context;
+	}
+	public void setContext(JXPathContext context) {
+		this.context = context;
+	}
+	public Object getHandle() {
+		return handle;
+	}
+	public void setHandle(Object handle) {
+		this.handle = handle;
+	}
+	public RepeaterRow getRow() {
+		return row;
+	}
+	public void setRow(RepeaterRow attribute) {
+		this.row = attribute;
+	}
+	
+	public boolean equals(Object other) {
+		if (!(other instanceof RepeaterItem)) return false;
+		return this.handle.equals(((RepeaterItem)other).handle);
+	}
+	
+	public int hashCode() {
+		return this.handle.hashCode();
+	}
+	
+}

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathAdapter.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathAdapter.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathAdapter.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathAdapter.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,194 @@
+package org.apache.cocoon.forms.binding;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cocoon.forms.formmodel.Repeater.RepeaterRow;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.Pointer;
+
+public class RepeaterJXPathAdapter implements RepeaterAdapter {
+
+	private int progressive = 100000;
+	
+	private EnhancedRepeaterJXPathBinding binding;
+	private RepeaterJXPathCollection jxCollection;
+	private List sortedItems = null;
+	
+
+	public RepeaterFilter getFilter() {
+		return new RepeaterJXPathFilter();
+	}
+	
+	private String findPathFor(String field) {
+		JXPathBindingBase[] childBindings = binding.getRowBinding().getChildBindings();
+		String path = null;
+		for (int i = 0; i < childBindings.length; i++) {
+			if (childBindings[i] instanceof ValueJXPathBinding) {
+				ValueJXPathBinding bnd = (ValueJXPathBinding) childBindings[i];
+				if (bnd.getFieldId().equals(field)) {
+					path = bnd.getXPath();
+					break;
+				}
+			}
+		}
+		return path;
+	}
+
+	public RepeaterSorter sortBy(String field) {
+		if (field == null) {
+			sortedItems = null;
+			return new NormalOrderJXPathSorter();			
+		}
+		String path = findPathFor(field);
+		if (path == null) throw new IllegalStateException("Cannot find a path for sorting on widget " + field);
+		RepeaterSorter sort = new RepeaterJXPathSorter(path, field);
+		if (sortedItems == null) {
+			List tsortedItems = new ArrayList();
+			int i = 0;
+			RepeaterItem item = getItem(i);
+			while (item != null) {
+				tsortedItems.add(item);
+				i++;
+				item = getItem(i);
+			}
+			this.sortedItems = tsortedItems;			
+		}
+		Collections.sort(sortedItems, sort);
+		return sort;
+	}
+
+	public void setBinding(EnhancedRepeaterJXPathBinding binding) {
+		this.binding = binding;
+	}
+
+	public void setCollection(Collection c) {
+	}
+
+	public void setJXCollection(RepeaterJXPathCollection collection) {
+		this.jxCollection = collection;
+	}
+	
+    public RepeaterItem getItem(int i) {
+    	if (i < 0) return null;
+		if (i >= jxCollection.getOriginalCollectionSize()) return null;
+    	if (this.sortedItems == null) {
+	    	JXPathContext storageContext = this.jxCollection.getStorageContext();
+	        Pointer pointer = storageContext.getPointer(binding.getRowPath() + "[" + (i+1) + "]");
+	        JXPathContext rowContext = storageContext.getRelativeContext(pointer);
+	        RepeaterItem item = new RepeaterItem(new Integer(i + 1));
+	        item.setContext(rowContext);
+	        return item;
+    	} else {
+    		return (RepeaterItem) sortedItems.get(i);
+    	}
+    }
+	
+
+	class RepeaterJXPathFilter implements RepeaterFilter {
+
+		private Map fieldsPaths = new HashMap();
+		private Map fieldsValues = new HashMap();
+		
+		public boolean shouldDisplay(RepeaterItem item) {
+			for (Iterator iter = fieldsValues.keySet().iterator(); iter.hasNext();) {
+				String field = (String) iter.next();
+				Object value = fieldsValues.get(field);
+				Object acvalue = null;				
+				if (item.getRow() == null) {
+					String path = (String) fieldsPaths.get(field);
+					acvalue = item.getContext().getValue(path);					
+				} else {
+					acvalue = item.getRow().getChild(field).getValue();
+				}
+				if (acvalue == null) return false;
+				if (acvalue instanceof String && value instanceof String) {
+					return ((String)acvalue).startsWith((String)value);
+				} else {
+					return acvalue.equals(value);
+				}				
+			}
+			return true;
+		}
+
+		public void setFilter(String field, Object value) {
+			if (value == null || ((value instanceof String) && ((String)value).length() == 0)) {
+				fieldsPaths.remove(field);
+				fieldsValues.remove(field);
+			} else {
+				String path = findPathFor(field);
+				if (path == null) throw new IllegalStateException("Cannot find a path for filtering on widget " + field);
+				fieldsPaths.put(field, path);
+				fieldsValues.put(field, value);
+			}
+		}
+		
+	}
+	
+	class RepeaterJXPathSorter implements RepeaterSorter {
+
+		private String path;
+		private String field;
+
+		public RepeaterJXPathSorter(String path, String field) {
+			this.path = path;
+			this.field = field;
+		}
+		
+		public void setCollection(Collection c) {
+		}
+
+		public int compare(Object o1, Object o2) {
+			RepeaterItem i1 = (RepeaterItem) o1;
+			RepeaterItem i2 = (RepeaterItem) o2;
+			Object val1 = null;
+			if (i1.getRow() != null) {
+				val1 = i1.getRow().getChild(field).getValue();
+			} else {
+				val1 = i1.getContext().getValue(path);				
+			}
+			Object val2 = null;
+			if (i2.getRow() != null) {
+				val2 = i2.getRow().getChild(field).getValue();
+			} else {
+				val2 = i2.getContext().getValue(path);				
+			}
+			if (val1 instanceof Comparable) {
+				return ((Comparable)val1).compareTo(val2);
+			}
+			return val1.toString().compareTo(val2.toString());
+		}
+		
+	}
+
+	class NormalOrderJXPathSorter implements RepeaterSorter {
+		
+		public void setCollection(Collection c) {
+		}
+
+		public int compare(Object o1, Object o2) {
+			RepeaterItem i1 = (RepeaterItem) o1;
+			RepeaterItem i2 = (RepeaterItem) o2;
+			return ((Integer)i1.getHandle()).compareTo((Integer)i2.getHandle());
+		}
+		
+	}
+
+	public RepeaterItem generateItem(RepeaterRow row) {
+		RepeaterItem item = new RepeaterItem(new Integer(progressive++));
+		item.setRow(row);
+		return item;
+	}
+
+	public void populateRow(RepeaterItem item) throws BindingException {
+        binding.getRowBinding().loadFormFromModel(item.getRow(), item.getContext());            			
+	}
+	
+}
+
+

Modified: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBinding.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBinding.java?view=diff&rev=449452&r1=449451&r2=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBinding.java (original)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBinding.java Sun Sep 24 11:26:03 2006
@@ -123,12 +123,6 @@
                     + "] referenced in the binding does not exist in the form definition.");
         }
 
-        if (repeater.isPageable()) {
-            PageStorage pageStorage = new PageStorage(repeater,jxpc);
-            pageStorage.doPageLoad();
-            return;
-        }
-
         repeater.clear();
 
         Pointer ptr = jxpc.getPointer(this.repeaterPath);
@@ -173,11 +167,6 @@
         // Find the repeater
         Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId);
 
-        if (repeater.isPageable()) {
-            repeater.getStorage().doSave();
-            return;
-        }
-
         // and his context, creating the path if needed
         JXPathContext repeaterContext =
             jxpc.getRelativeContext(jxpc.createPath(this.repeaterPath));
@@ -406,158 +395,6 @@
         if (this.identityBinding != null) {
             this.identityBinding.enableLogging(logger);
         }
-    }
-
-    public class PageStorage {
-
-        private Repeater repeater;
-        private JXPathContext storageContext;
-        private Map updatedRows;
-        private int maxPage;
-
-        public PageStorage(Repeater repeater, JXPathContext storageContext) {
-            this.repeater = repeater;
-            this.storageContext = storageContext;
-
-            this.repeater.setStorage(this);
-            this.updatedRows = new HashMap();
-
-            /*
-             * workaround for jxpath doesn't simply calls size() on count(..) using collections
-             */
-
-            int collectionSize = 0;
-            Object value = storageContext.getValue(rowPath);
-            if (value != null) {
-                if (value instanceof Collection) {
-                    collectionSize = ((Collection)value).size();
-                } else {
-                    collectionSize = ((Double) storageContext.getValue("count("+rowPath+")")).intValue();
-                }
-            }
-            this.maxPage = collectionSize / this.repeater.getPageSize() - 1;
-        }
-
-        public void doPageLoad() throws BindingException {
-            repeater.clear();
-
-            Pointer ptr = storageContext.getPointer(".");
-            if (ptr.getNode() != null) {
-                int initialSize = repeater.getSize();
-                JXPathContext repeaterContext = storageContext;
-
-                Iterator rowPointers = repeaterContext.iteratePointers(rowPath + getPaginationClause());
-                while (rowPointers.hasNext()) {
-                    Repeater.RepeaterRow thisRow;
-                    if (initialSize > 0) {
-                        thisRow = repeater.getRow(--initialSize);
-                    } else {
-                        thisRow = repeater.addRow();
-                    }
-                    Pointer jxp = (Pointer)rowPointers.next();
-                    JXPathContext rowContext = repeaterContext.getRelativeContext(jxp);
-                    List contextIdentity = getIdentity(rowContext);
-                    if (isIdentityInUpdatedRows(contextIdentity)) {
-                        RowStore rowStore = (RowStore)this.updatedRows.get(contextIdentity);
-                        rowStore.recallRow(thisRow);
-                        continue;
-                    }
-                    if (identityBinding != null) {
-                        identityBinding.loadFormFromModel(thisRow, rowContext);
-                    }
-                    rowBinding.loadFormFromModel(thisRow, rowContext);
-                }
-            }
-            if (getLogger().isDebugEnabled())
-                getLogger().debug("done loading page rows " + this.toString());
-        }
-
-        public void doPageSave() throws BindingException {
-            JXPathContext repeaterContext = this.storageContext;
-
-            // 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(rowPath + getPaginationClause());
-                    while (rowPointers.hasNext()) {
-                        Pointer jxp = (Pointer) rowPointers.next();
-                        JXPathContext rowContext = repeaterContext.getRelativeContext(jxp);
-                        List contextIdentity = getIdentity(rowContext);
-                        if (ListUtils.isEqualList(identity, contextIdentity)) {
-                            updatedRows.put(identity,new RowStore(thisRow));
-                        }
-                    }
-                }
-            }
-        }
-
-        public JXPathContext doSave() throws BindingException {
-
-            // iterate context and saveToModel
-            Iterator rowPointers = this.storageContext.iteratePointers(rowPath);
-            while (rowPointers.hasNext()) {
-                Pointer jxp = (Pointer) rowPointers.next();
-                JXPathContext rowContext = this.storageContext.getRelativeContext(jxp);
-                List contextIdentity = getIdentity(rowContext);
-                if (isIdentityInUpdatedRows(contextIdentity)) {
-                    RepeaterRow repeaterRow = this.repeater.new RepeaterRow((RepeaterDefinition) this.repeater.getDefinition());
-                    RowStore rowStore = (RowStore)this.updatedRows.get(contextIdentity);
-                    rowStore.recallRow(repeaterRow);
-                    rowBinding.saveFormToModel(repeaterRow, rowContext);
-                }
-            }
-            return this.storageContext;
-        }
-
-        private String getPaginationClause() {
-            String paginationClause;
-            int start = repeater.getCurrentPage() * repeater.getPageSize();
-            int end = start + repeater.getPageSize() + 1;
-            paginationClause = "[position() > " + start + " and position() < " + end + "]";
-            return paginationClause;
-        }
-
-        private boolean isIdentityInUpdatedRows(List identity) {
-            return this.updatedRows.containsKey(identity);
-        }
-
-        public int getMaxPage() {
-            return this.maxPage;
-        }
-
-        private class RowStore {
-            private HashMap values;
-
-            public RowStore(RepeaterRow repeaterRow) {
-                this.values = new HashMap();
-                this.storeRow(repeaterRow);
-            }
-
-            private void recallRow(RepeaterRow repeaterRow) {
-                Iterator iterator = values.keySet().iterator();
-                while (iterator.hasNext()) {
-                    String key = (String) iterator.next();
-                    repeaterRow.lookupWidget(key).setValue(values.get(key));
-                }
-            }
-
-            private void storeRow(RepeaterRow repeaterRow) {
-               Iterator iterator = repeaterRow.getChildren();
-               while (iterator.hasNext()) {
-                   Widget widget = (Widget) iterator.next();
-                   values.put(widget.getName(),widget.getValue());
-               }
-            }
-
-        }
-
     }
 
 }

Modified: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBindingBuilder.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBindingBuilder.java?view=diff&rev=449452&r1=449451&r2=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBindingBuilder.java (original)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathBindingBuilder.java Sun Sep 24 11:26:03 2006
@@ -64,9 +64,7 @@
      * @return JXPathBindingBase
      */
     public JXPathBindingBase buildBinding(Element bindingElm,
-                                          JXPathBindingManager.Assistant assistant)
-    throws BindingException {
-
+            JXPathBindingManager.Assistant assistant) throws BindingException {
         if (bindingElm.hasAttribute("unique-row-id")) {
             throw new BindingException("Attribute 'unique-row-id' is no more supported, use <fb:identity> instead",
                                        LocationAttributes.getLocation(bindingElm));
@@ -88,15 +86,17 @@
             String rowPath =
                     DomHelper.getAttribute(bindingElm, "row-path", null);
             String rowPathForInsert =
-                    DomHelper.getAttribute(bindingElm, "row-path-insert", rowPath);
-
-            // retrieve inherited bindings
+                DomHelper.getAttribute(bindingElm, "row-path-insert", rowPath);
+            String adapterClass = 
+            	DomHelper.getAttribute(bindingElm, "adapter-class", null);
+            
+//          do inheritance
+            RepeaterJXPathBinding otherBinding = (RepeaterJXPathBinding)assistant.getContext().getSuperBinding();
             JXPathBindingBase[] existingOnBind = null;
             JXPathBindingBase[] existingOnDelete = null;
             JXPathBindingBase[] existingOnInsert = null;
             JXPathBindingBase[] existingIdentity = null;
 
-            RepeaterJXPathBinding otherBinding = (RepeaterJXPathBinding)assistant.getContext().getSuperBinding();
             if (otherBinding != null) {
             	commonAtts = JXPathBindingBuilderBase.mergeCommonAttributes(otherBinding.getCommonAtts(), commonAtts);
 
@@ -175,11 +175,11 @@
                 // Use the children of the current element
                 childBindings = assistant.makeChildBindings(bindingElm, existingOnBind);
             }
-
+            
             RepeaterJXPathBinding repeaterBinding =
-                new RepeaterJXPathBinding(commonAtts, repeaterId, parentPath,
+                new EnhancedRepeaterJXPathBinding(commonAtts, repeaterId, parentPath,
                         rowPath, rowPathForInsert,
-                        childBindings, insertBinding, deleteBindings, identityBinding);
+                        childBindings, insertBinding, deleteBindings, identityBinding, adapterClass);
             return repeaterBinding;
         } catch (BindingException e) {
             throw e;

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathCollection.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathCollection.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathCollection.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterJXPathCollection.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,252 @@
+package org.apache.cocoon.forms.binding;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cocoon.forms.formmodel.Repeater.RepeaterRow;
+import org.apache.commons.jxpath.JXPathContext;
+
+/**
+ * Implements a collection that takes care about removed, updated and inserted
+ * elements, obtaining from a {@link RepeaterAdapter} all the needed objects.
+ */
+public class RepeaterJXPathCollection {
+
+    private JXPathContext storageContext;
+    
+    private Map updatedRows;
+    private Set deletedRows;
+    private List insertedRows;
+    
+	private int collectionSize;
+	
+	private RepeaterSorter sorter = null;
+	private RepeaterFilter filter = null;
+	private RepeaterAdapter adapter = null;
+	
+	private List itemsCache = new ArrayList();
+
+    public void init(JXPathContext storageContext, String rowpath, RepeaterAdapter adapter) {
+        this.storageContext = storageContext;
+        collectionSize = 0;
+        Object value = storageContext.getValue(rowpath);
+        if (value != null) {
+            if (value instanceof Collection) {
+                collectionSize = ((Collection)value).size();
+            } else {
+                collectionSize = ((Double) storageContext.getValue("count("+rowpath+")")).intValue();
+            }
+        }
+        
+        this.updatedRows = new HashMap();
+        this.deletedRows = new HashSet();
+        this.insertedRows = new ArrayList();
+        this.adapter = adapter;
+        this.sorter = adapter.sortBy(null);
+    }
+    
+    private int getStartIndex(int start) {
+    	int i = start;
+    	RepeaterItem item = adapter.getItem(i);
+    	// In case start is after the end of the collection try to go back 
+    	// until a valid item is found
+    	while (item == null && i > 0) {
+    		i--;
+    		item = adapter.getItem(i);
+    	}
+    	if (item == null) return 0;
+    	// Now move the index ahead of one for each deleted item "before"
+    	// the desired one
+        for (Iterator iter = deletedRows.iterator(); iter.hasNext();) {
+			RepeaterItem delitem = (RepeaterItem) iter.next();
+			if (sorter.compare(delitem, item) < 0) {
+				i++;
+			}
+		}
+        // And move it backward for each inserted row before the actual index
+        for (Iterator iter = insertedRows.iterator(); iter.hasNext();) {
+			RepeaterItem insitem = (RepeaterItem) iter.next();
+			if (sorter.compare(insitem, item) < 0) {
+				i--;
+			}
+		}
+        if (i < 0) return 0;
+        // Now we should have the correct start
+        return i;
+    }
+
+    public List getItems(int start, int length) {
+    	List ret = new ArrayList();
+    	int rlength = length;
+    	int rstart = getStartIndex(start);;
+    	RepeaterItem startItem = null;
+    	if (rstart > 0) {
+    		// Try to fetch one element before, so that we can distinguish 
+    		// where we started after inferring added elements.
+    		startItem = getItem(rstart - 1);
+    	}
+    	if (startItem != null) {
+    		ret.add(startItem);
+    	}
+    	int i = rstart;
+    	RepeaterItem item;
+        while (length > 0) {
+        	item = getItem(i);
+        	if (item == null) break;
+        	// skip deleted items
+            while (isDeleted(item)) {
+            	i++;
+            	item = getItem(i);
+                if (item == null) break;
+            }
+            if (filter != null) {
+                while (!filter.shouldDisplay(item)) {
+                	i++;
+                	item = getItem(i);
+                    if (item == null) break;
+                }            	
+            }
+            if (item == null) break;
+        	ret.add(item);
+        	i++;
+        	length--;
+        }
+        // Infer the inserted rows.
+        if (this.insertedRows.size() > 0) {
+            if (filter != null) {
+            	for (Iterator iter = this.insertedRows.iterator(); iter.hasNext();) {
+					RepeaterItem acitem = (RepeaterItem) iter.next();
+					if (filter.shouldDisplay(acitem)) {
+						ret.add(acitem);
+					}
+				}
+            } else {
+            	ret.addAll(this.insertedRows);
+            }
+	    	Collections.sort(ret, this.sorter);
+        }
+    	if (startItem != null) {
+	    	// Now get from the element after our start element.
+	    	int pos = ret.indexOf(startItem);
+	    	for (int j = 0; j <= pos; j++) {
+	    		ret.remove(0);
+	    	}
+    	}
+    	while (ret.size() > rlength) ret.remove(ret.size() - 1);
+    	
+        this.itemsCache.clear();
+        this.itemsCache.addAll(ret);
+        return ret;
+    }
+    
+    public List getCachedItems() {
+    	return this.itemsCache;
+    }
+    
+    public void flushCachedItems() {
+    	this.itemsCache.clear();
+    }
+    
+    private RepeaterItem getItem(int i) {
+        // Take the element from the original collection and check if it was updated
+        RepeaterItem item = this.adapter.getItem(i);
+        if (item == null) return null;
+        if (isUpdated(item)) {
+        	item = (RepeaterItem) this.updatedRows.get(item.getHandle());
+        }
+        return item;
+    }
+    
+    public void updateRow(RepeaterItem item) {
+    	if (!isInserted(item) && !isDeleted(item)) {
+    		this.updatedRows.put(item.getHandle(), item);
+    	}
+    }
+    
+    public void deleteRow(RepeaterItem item) {
+    	if (isInserted(item)) {
+    		this.insertedRows.remove(item);
+    		return;
+    	} else if (isUpdated(item)) {
+    		this.updatedRows.remove(item);
+    	}
+    	this.deletedRows.add(item);  
+    }
+    
+    public void addRow(RepeaterItem item) {
+    	this.insertedRows.add(item);
+    }
+    
+    public int getOriginalCollectionSize() {
+        return collectionSize;
+    }
+    
+    public int getActualCollectionSize() {
+    	return getOriginalCollectionSize() - this.deletedRows.size() + this.insertedRows.size();    	
+    }
+        
+    /*
+     * convenience methods to search the cache
+     */ 
+    
+    private boolean isUpdated(RepeaterItem item) {
+        return this.updatedRows.containsKey(item.getHandle());
+    }
+    
+    private boolean isDeleted(RepeaterItem item) {
+        return this.deletedRows.contains(item);
+    }
+    
+    private boolean isInserted(RepeaterItem item) {
+        return this.insertedRows.contains(item);
+    }
+
+	public JXPathContext getStorageContext() {
+		return storageContext;
+	}
+
+	public List getDeletedRows() {
+		// FIXME we should sort by natural order
+		List ret = new ArrayList(this.deletedRows);
+    	Collections.sort(ret, this.sorter);
+    	Collections.reverse(ret);
+		return ret;
+	}
+
+	public List getInsertedRows() {
+		return insertedRows;
+	}
+
+	public Collection getUpdatedRows() {
+		return updatedRows.values();
+	}
+
+	public RepeaterAdapter getAdapter() {
+		return this.adapter;
+	}
+
+	public void addRow(RepeaterRow row) {
+		RepeaterItem item = this.adapter.generateItem(row);
+		this.addRow(item);
+	}
+
+	public void sortBy(String field) {
+		this.sorter = this.adapter.sortBy(field);
+	}
+
+	public void filter(String field, Object value) {
+		if (filter == null) {
+			filter = this.adapter.getFilter();
+		}
+		filter.setFilter(field, value);
+	}
+	
+            
+}

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterSorter.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterSorter.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterSorter.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/binding/RepeaterSorter.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,10 @@
+package org.apache.cocoon.forms.binding;
+
+import java.util.Collection;
+import java.util.Comparator;
+
+public interface RepeaterSorter extends Comparator {
+
+	public void setCollection(Collection c);
+	
+}

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/EnhancedRepeater.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/EnhancedRepeater.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/EnhancedRepeater.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/EnhancedRepeater.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,233 @@
+package org.apache.cocoon.forms.formmodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.cocoon.forms.binding.BindingException;
+import org.apache.cocoon.forms.binding.RepeaterItem;
+import org.apache.cocoon.forms.binding.RepeaterJXPathCollection;
+import org.apache.cocoon.forms.datatype.StaticSelectionList;
+import org.apache.cocoon.xml.AttributesImpl;
+
+public class EnhancedRepeater extends Repeater {
+    private RepeaterJXPathCollection collection;
+    private String customPageFieldId;
+    private Field customPageField;
+	
+    // pagination
+    private int currentPage;
+    private int pageSize;
+
+	
+    public EnhancedRepeater(RepeaterDefinition repeaterDefinition) {
+		super(repeaterDefinition);
+    	this.currentPage = this.definition.getInitialPage();
+    	this.pageSize = this.definition.getPageSize();
+        this.customPageFieldId = this.definition.getCustomPageId();
+	}
+
+	public void doPageLoad() throws BindingException {
+        clearAllRows();
+        collection.flushCachedItems();
+        int start = getStartIndex();
+        List items = collection.getItems(start, this.pageSize);
+        for (Iterator iter = items.iterator(); iter.hasNext();) {
+			RepeaterItem item = (RepeaterItem) iter.next();
+	        if (item == null) break;
+            if (item.getRow() != null) {
+            	addRow((RepeaterRow) item.getRow());
+            } else {
+                RepeaterRow thisRow = addRow();
+                item.setRow(thisRow);
+                collection.getAdapter().populateRow(item);
+            }
+        }
+        
+        // set customPageField
+        if (this.customPageField != null) {
+            StaticSelectionList selectionList = new StaticSelectionList(this.customPageField.getDatatype());
+            int j;
+            for (j = 0; j <= this.getMaxPage();j++) {
+                selectionList.addItem(new Integer(j),(j+1)+"");
+            }
+            this.customPageField.setSelectionList(selectionList);
+            this.customPageField.setValue(new Integer(this.currentPage));
+        }
+    }
+
+    /**
+     * save current page to cache-collections (updatedRows, deleted rows, insertedRows)
+     * @throws BindingException
+     */
+    public void doPageSave() throws BindingException {
+        List tempUpdatedRows = new ArrayList();
+        List tempInsertedRows = new ArrayList();
+        
+        List cache = collection.getCachedItems();
+        // iterate rows in the form model...
+        int formRowCount = getSize();
+        for (int i = 0; i < formRowCount; i++) {
+            Repeater.RepeaterRow thisRow = getRow(i);
+            boolean found = false;
+            for (int j = 0; j < cache.size(); j++) {
+            	RepeaterItem item = (RepeaterItem) cache.get(j);
+            	if (item == null) break;
+            	if (item.getRow() == thisRow) {
+                	// Found the matching row
+                	// TODO we need a way to know if the row was really modified or not, maybe a FormHandler?
+                	tempUpdatedRows.add(item);
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+            	tempInsertedRows.add(thisRow);
+            }
+        }
+        
+        List toDelete = new ArrayList();
+        for (int j = 0; j < cache.size(); j++) {
+        	RepeaterItem item = (RepeaterItem) cache.get(j);
+        	if (item == null) break;
+        	boolean found = false;
+            for (int i = 0; i < formRowCount; i++) {
+                Repeater.RepeaterRow thisRow = getRow(i);
+                if (thisRow == item.getRow()) {
+                	found = true;
+                	break;
+                }
+            }
+            if (!found) {
+            	toDelete.add(item);
+            }
+        }
+        for (Iterator iter = tempUpdatedRows.iterator(); iter.hasNext();) {
+			RepeaterItem ele = (RepeaterItem) iter.next();
+			collection.updateRow(ele);
+		}
+        for (Iterator iter = tempInsertedRows.iterator(); iter.hasNext();) {
+        	RepeaterRow row = (RepeaterRow) iter.next();
+			collection.addRow(row);
+		}
+        for (Iterator iter = toDelete.iterator(); iter.hasNext();) {
+        	RepeaterItem ele = (RepeaterItem) iter.next();
+			collection.deleteRow(ele);
+		}
+        collection.flushCachedItems();
+    }
+	
+    private int getStartIndex() {
+        return this.currentPage * this.pageSize;
+    }
+            
+    public int getMaxPage() {
+        return ((int)(Math.ceil((double)collection.getActualCollectionSize() / (double)pageSize))) - 1;
+    }
+    
+    public int getCustomPageWidgetValue() {
+        return ((Integer)this.customPageField.getValue()).intValue();
+    }
+    
+    public int getCurrentPage() {
+        return currentPage;
+    }
+
+    
+    
+    /*
+     * convenience methods for presentation
+     */
+    
+    public int getDisplayableCurrentPage() {
+        return this.getCurrentPage() + 1;
+    }
+    
+    public int getDisplayableLastPage() {
+        // increment if we created a new page for insertion
+        if (this.getCurrentPage() > this.getMaxPage()) {
+            return this.getMaxPage() + 2;
+        }
+        return this.getMaxPage() + 1;
+    }
+    
+    public boolean isFirstPage() {
+        return this.getCurrentPage() == 0;
+    }
+    
+    public boolean isLastPage() {
+    	return this.getCurrentPage() >= this.getMaxPage();
+    }
+
+	public int getPageSize() {
+		return pageSize;
+	}
+
+	public void setPageSize(int pageSize) {
+		this.pageSize = pageSize;
+	}
+
+	public boolean isEnhanced() {
+		return true;
+	}
+
+	public AttributesImpl getXMLElementAttributes() {
+		AttributesImpl elementAttributes = super.getXMLElementAttributes();
+	    if (this.pageSize < Integer.MAX_VALUE) {
+	    	elementAttributes.addCDATAAttribute("page", String.valueOf(currentPage));
+	    }
+		return elementAttributes;
+	}
+	
+    private void addRow(RepeaterRow row) {
+    	rows.add(row);
+        getForm().addWidgetUpdate(this);
+    }
+    
+    private void clearAllRows() {
+        rows.clear();    	
+        getForm().addWidgetUpdate(this);
+    }
+
+	public void setCollection(RepeaterJXPathCollection collection) {
+		this.collection = collection;
+	}
+
+	public void initialize() {
+		super.initialize();
+        Widget widget = getForm().lookupWidget(this.customPageFieldId);
+        if (widget instanceof Field) {
+            this.customPageField = (Field)widget;
+        }
+	}
+
+	public RepeaterJXPathCollection getCollection() {
+		return collection;
+	}
+
+	public void refreshPage() throws BindingException {
+		doPageSave();
+		doPageLoad();
+	}
+
+	public void goToPage(int page) throws BindingException {
+		doPageSave();
+		this.currentPage = page;
+		doPageLoad();
+	}
+
+	public void sortBy(String field) throws BindingException {
+		doPageSave();
+		this.collection.sortBy(field);
+		this.currentPage = 0;
+		doPageLoad();
+	}
+
+	public void setFilter(String field, Object value) throws BindingException {
+		doPageSave();
+		this.collection.filter(field, value);
+		this.currentPage = 0;
+		doPageLoad();		
+	}
+    
+}

Modified: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/Repeater.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/Repeater.java?view=diff&rev=449452&r1=449451&r2=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/Repeater.java (original)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/Repeater.java Sun Sep 24 11:26:03 2006
@@ -25,8 +25,6 @@
 import org.apache.cocoon.forms.FormContext;
 import org.apache.cocoon.forms.FormsConstants;
 import org.apache.cocoon.forms.FormsRuntimeException;
-import org.apache.cocoon.forms.binding.Binding;
-import org.apache.cocoon.forms.binding.RepeaterJXPathBinding.PageStorage;
 import org.apache.cocoon.forms.event.RepeaterEvent;
 import org.apache.cocoon.forms.event.RepeaterEventAction;
 import org.apache.cocoon.forms.event.RepeaterListener;
@@ -62,18 +60,12 @@
     private static final String LABEL_EL = "label";
     private static final String REPEATER_SIZE_EL = "repeater-size";
 
-    private final RepeaterDefinition definition;
+    protected final RepeaterDefinition definition;
     protected final List rows = new ArrayList();
     protected ValidationError validationError;
     private boolean orderable = false;
     private RepeaterListener listener;
 
-    // pagination
-    private PageStorage storage;
-    private boolean pageable = false;
-    private int currentPage;
-    private int pageSize;
-
     public Repeater(RepeaterDefinition repeaterDefinition) {
         super(repeaterDefinition);
         this.definition = repeaterDefinition;
@@ -85,13 +77,6 @@
 
         this.orderable = this.definition.getOrderable();
         this.listener = this.definition.getRepeaterListener();
-
-        this.pageable = this.definition.isPageable();
-        if (pageable) {
-            this.currentPage = this.definition.getInitialPage();
-            this.pageSize = this.definition.getPageSize();
-        }
-
     }
 
     public WidgetDefinition getDefinition() {
@@ -127,7 +112,7 @@
         broadcastEvent(new RepeaterEvent(this, RepeaterEventAction.ROW_ADDED, rows.size() - 1));
         return repeaterRow;
     }
-
+    
     public RepeaterRow addRow(int index) {
         RepeaterRow repeaterRow = new RepeaterRow(definition);
         if (index >= this.rows.size()) {
@@ -484,9 +469,6 @@
         if (size != Integer.MAX_VALUE) {
             attrs.addCDATAAttribute("max-size", String.valueOf(size));
         }
-        if (isPageable()) {
-            attrs.addCDATAAttribute("page", String.valueOf(currentPage));
-        }
         return attrs;
     }
 
@@ -634,38 +616,6 @@
         public void broadcastEvent(WidgetEvent event) {
             throw new UnsupportedOperationException("Widget " + this + " doesn't handle events.");
         }
-    }
-
-    public int getCurrentPage() {
-        return currentPage;
-    }
-
-    public void setCurrentPage(int currentPage) {
-        this.currentPage = currentPage;
-    }
-
-    public boolean isPageable() {
-        return pageable;
-    }
-
-    public void setPageable(boolean pageable) {
-        this.pageable = pageable;
-    }
-
-    public int getPageSize() {
-        return pageSize;
-    }
-
-    public void setPageSize(int pageSize) {
-        this.pageSize = pageSize;
-    }
-
-
-    public PageStorage getStorage() {
-        return storage;
-    }
-
-    public void setStorage(PageStorage storage) {
-        this.storage = storage;
-    }
+    }	
+    
 }

Modified: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterActionDefinition.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterActionDefinition.java?view=diff&rev=449452&r1=449451&r2=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterActionDefinition.java (original)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterActionDefinition.java Sun Sep 24 11:26:03 2006
@@ -16,7 +16,9 @@
  */
 package org.apache.cocoon.forms.formmodel;
 
+import org.apache.avalon.framework.CascadingRuntimeException;
 import org.apache.cocoon.forms.FormsException;
+import org.apache.cocoon.forms.binding.BindingException;
 import org.apache.cocoon.forms.event.ActionEvent;
 import org.apache.cocoon.forms.event.ActionListener;
 
@@ -118,14 +120,24 @@
             // Call action listeners, if any
             super.fireActionEvent(event);
 
-            // and actually delete the rows
             Repeater repeater = ((RepeaterAction)event.getSource()).getRepeater();
+            
+            // and actually delete the rows
             for (int i = repeater.getSize() - 1; i >= 0; i--) {
                 Repeater.RepeaterRow row = repeater.getRow(i);
                 if (Boolean.TRUE.equals(row.getChild(this.selectName).getValue())) {
                     repeater.removeRow(i);
                 }
             }
+            
+            if (repeater instanceof EnhancedRepeater) {
+            	try {
+					((EnhancedRepeater)repeater).refreshPage();
+				} catch (BindingException e) {
+					throw new CascadingRuntimeException("Error refreshing repeater page", e);
+				}
+            }
+            
         }
     }
 
@@ -135,16 +147,22 @@
      * The definition of a repeater action that adds a row to a sibling repeater.
      */
     public static class AddRowActionDefinition extends RepeaterActionDefinition {
-        private int insertRows;
+        private int insertRows = 1;
 
-        public AddRowActionDefinition(String repeaterName, int insertRows) {
+        public AddRowActionDefinition(String repeaterName) {
             super(repeaterName);
-            this.insertRows = insertRows;
-
+            
             this.addActionListener(new ActionListener() {
                 public void actionPerformed(ActionEvent event) {
                     Repeater repeater = ((RepeaterAction)event.getSource()).getRepeater();
-                    for (int i=0; i<AddRowActionDefinition.this.insertRows; i++) {
+                    if (repeater instanceof EnhancedRepeater) {
+                    	try {
+							((EnhancedRepeater)repeater).goToPage(((EnhancedRepeater)repeater).getMaxPage());
+						} catch (BindingException e) {
+							throw new CascadingRuntimeException("Error switching page", e);
+						}
+                    }
+                    for (int i=0; i < AddRowActionDefinition.this.insertRows; i++) {
                         repeater.addRow();
                     }
                 }
@@ -204,14 +222,39 @@
             });
         }
     }
+    
+    public static class SortActionDefinition extends RepeaterActionDefinition {
+    	protected String field = null;
+    	
+        public SortActionDefinition(String repeaterName, String field) {
+            super(repeaterName);
+            this.field = field;
+            
+            this.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent event) {
+                    Repeater repeater = ((RepeaterAction)event.getSource()).getRepeater();
+                    if (repeater instanceof EnhancedRepeater) {
+                    	EnhancedRepeater erep = (EnhancedRepeater) repeater;
+                        try {
+                            if (repeater.validate()) {
+                            	erep.sortBy(SortActionDefinition.this.field);
+                            }
+                        } catch (Exception e) {
+                            throw new CascadingRuntimeException("Error switching page", e);
+                        }
+                    }
+                }
+            });
+            
+        }
+    }
 
-
-
+  
     public static class ChangePageActionDefinition extends RepeaterActionDefinition {
 
        protected int method;
-
-       public static final int FIRST = 0;
+       
+       public static final int FIRST = 0; 
        public static final int PREV = 1;
        public static final int NEXT = 2;
        public static final int LAST = 3;
@@ -222,57 +265,49 @@
          */
         public void initializeFrom(WidgetDefinition definition) throws Exception {
             super.initializeFrom(definition);
-
-            if (!(definition instanceof ChangePageActionDefinition)) {
-                throw new FormsException("Ancestor definition " + definition.getClass().getName() + " is not a ChangePageActionDefinition.",
-                                         getLocation());
+            if(definition instanceof ChangePageActionDefinition) {
+                ChangePageActionDefinition other = (ChangePageActionDefinition)definition;
+                this.method = other.method;
+            } else {
+                throw new Exception("Definition to inherit from is not of the right type! (at "+getLocation()+")");
             }
-
-            ChangePageActionDefinition other = (ChangePageActionDefinition) definition;
-
-            this.method = other.method;
         }
 
         public ChangePageActionDefinition(String repeaterName, int m) {
             super(repeaterName);
-
+            
             this.method = m;
-
+            
             this.addActionListener(new ActionListener() {
                 public void actionPerformed(ActionEvent event) {
                     Repeater repeater = ((RepeaterAction)event.getSource()).getRepeater();
-
-                    int page = repeater.getCurrentPage();
-
-                    if (method == FIRST) {
-                        page = 0;
-                    } else if (method == PREV && page > 0) {
-                        page = repeater.getCurrentPage() - 1;
-                    } else if (method == NEXT && page < repeater.getStorage().getMaxPage()) {
-                        page = repeater.getCurrentPage() + 1;
-                    } else if (method == LAST) {
-                        page = repeater.getStorage().getMaxPage();
-                    } else if (method == CUSTOM) {
-                        ((Integer)repeater.getForm().lookupWidget("page-custom").getValue()).intValue();
-                    } else {
-                        return;
-                    }
-
-                    if (repeater.isPageable()) {
+                    if (repeater instanceof EnhancedRepeater) {
+                    	EnhancedRepeater erep = (EnhancedRepeater) repeater;
+                        int page = erep.getCurrentPage();
+                        if (method == FIRST) {
+                            page = 0;
+                        } else if (method == PREV && page > 0) {
+                            page = erep.getCurrentPage() - 1;
+                        } else if (method == NEXT && page < erep.getMaxPage()) {
+                            page = erep.getCurrentPage() + 1;
+                        } else if (method == LAST) {
+                            page = erep.getMaxPage();
+                        } else if (method == CUSTOM) {
+                            page = erep.getCustomPageWidgetValue();
+                        } else {
+                            return;
+                        }
                         try {
                             if (repeater.validate()) {
-                                repeater.getStorage().doPageSave();
-                                repeater.setCurrentPage(page);
-                                repeater.getStorage().doPageLoad();
+                            	erep.goToPage(page);
                             }
                         } catch (Exception e) {
-                            e.printStackTrace();
+                            throw new CascadingRuntimeException("Error switching page", e);
                         }
-                    }
+                    } 
                 }
             });
         }
     }
-
-
+    
 }

Modified: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterActionDefinitionBuilder.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterActionDefinitionBuilder.java?view=diff&rev=449452&r1=449451&r2=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterActionDefinitionBuilder.java (original)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterActionDefinitionBuilder.java Sun Sep 24 11:26:03 2006
@@ -105,25 +105,28 @@
             return new RepeaterActionDefinition.DeleteRowsActionDefinition(repeater, select);
 
         } else if ("add-row".equals(actionCommand)) {
-            int insertRows = DomHelper.getAttributeAsInteger(element,"number-of-rows",1);
-            return new RepeaterActionDefinition.AddRowActionDefinition(repeater,insertRows);
+            return new RepeaterActionDefinition.AddRowActionDefinition(repeater);
 
         } else if ("insert-rows".equals(actionCommand)) {
             String select = DomHelper.getAttribute(element, "select");
             return new RepeaterActionDefinition.InsertRowsActionDefinition(repeater, select);
 
+        } else if ("sort-by".equals(actionCommand)) {
+            String field = DomHelper.getAttribute(element, "field", null);
+            return new RepeaterActionDefinition.SortActionDefinition(repeater, field);
+            
         } else if ("page-first".equals(actionCommand)) {
             return new RepeaterActionDefinition.ChangePageActionDefinition(repeater, RepeaterActionDefinition.ChangePageActionDefinition.FIRST);
-
+        
         } else if ("page-prev".equals(actionCommand)) {
             return new RepeaterActionDefinition.ChangePageActionDefinition(repeater, RepeaterActionDefinition.ChangePageActionDefinition.PREV);
-
+        
         } else if ("page-next".equals(actionCommand)) {
             return new RepeaterActionDefinition.ChangePageActionDefinition(repeater, RepeaterActionDefinition.ChangePageActionDefinition.NEXT);
-
+        
         } else if ("page-last".equals(actionCommand)) {
             return new RepeaterActionDefinition.ChangePageActionDefinition(repeater, RepeaterActionDefinition.ChangePageActionDefinition.LAST);
-
+        
         } else if ("page-custom".equals(actionCommand)) {
             return new RepeaterActionDefinition.ChangePageActionDefinition(repeater, RepeaterActionDefinition.ChangePageActionDefinition.CUSTOM);
 

Modified: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterDefinition.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterDefinition.java?view=diff&rev=449452&r1=449451&r2=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterDefinition.java (original)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterDefinition.java Sun Sep 24 11:26:03 2006
@@ -34,9 +34,10 @@
     private boolean orderable;
     private RepeaterListener listener;
 
-    private boolean pageable=false;
+    private boolean enhanced=false;
     private int initialPage=0;
     private int pageSize;
+	private String customPageId;
 
 
     public RepeaterDefinition(int initialSize, int minSize, int maxSize,
@@ -47,19 +48,18 @@
         this.maxSize = maxSize;
         this.orderable = orderable;
     }
-
-    public RepeaterDefinition(int initialSize, int minSize, int maxSize,
-                              boolean selectable, boolean orderable, boolean pageable,
-                              int initialPage, int pageSize) {
-        super();
-        this.initialSize = initialSize;
-        this.minSize = minSize;
-        this.maxSize = maxSize;
-        this.orderable = orderable;
-        this.pageable = pageable;
-        this.initialPage = initialPage;
-        this.pageSize = pageSize;
-    }
+    
+    public RepeaterDefinition(int initialSize, int minSize, int maxSize, boolean selectable,boolean orderable,boolean enhanced, int initialPage, int pageSize, String customPageId) {
+		super();
+		this.initialSize = initialSize;
+		this.minSize = minSize;
+		this.maxSize = maxSize;
+		this.orderable = orderable;
+		this.enhanced = enhanced;
+		this.initialPage = initialPage;
+		this.pageSize = pageSize;
+		this.customPageId = customPageId;
+	}
 
     /**
      * initialize this definition with the other, sort of like a copy constructor
@@ -73,14 +73,21 @@
         }
 
         RepeaterDefinition other = (RepeaterDefinition) definition;
-
         this.initialSize = other.initialSize;
         this.maxSize = other.maxSize;
         this.minSize = other.minSize;
+        this.enhanced = other.enhanced;
+        this.orderable = other.orderable;
+        this.initialPage = other.initialPage;
+        this.pageSize = other.pageSize;
     }
 
     public Widget createInstance() {
-        return new Repeater(this);
+    	if (enhanced) {
+    		return new EnhancedRepeater(this);
+    	} else {
+    		return new Repeater(this);
+    	}
     }
 
     public int getInitialSize() {
@@ -118,15 +125,20 @@
         return this.listener;
     }
 
-    public int getInitialPage() {
-        return initialPage;
-    }
-
-    public boolean isPageable() {
-        return pageable;
-    }
-
-    public int getPageSize() {
-        return pageSize;
-    }
+	public int getInitialPage() {
+		return initialPage;
+	}
+
+	public int getPageSize() {
+		return pageSize;
+	}
+
+	public boolean isEnhanced() {
+		return enhanced;
+	}
+
+	public String getCustomPageId() {
+		return customPageId;
+	}
+    
 }

Modified: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterDefinitionBuilder.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterDefinitionBuilder.java?view=diff&rev=449452&r1=449451&r2=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterDefinitionBuilder.java (original)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterDefinitionBuilder.java Sun Sep 24 11:26:03 2006
@@ -59,19 +59,21 @@
 
         boolean orderable = DomHelper.getAttributeAsBoolean(repeaterElement, "orderable", false);
         boolean selectable = DomHelper.getAttributeAsBoolean(repeaterElement, "selectable", false);
+        boolean enhanced = DomHelper.getAttributeAsBoolean(repeaterElement, "enhanced", false);
 
-        boolean pageable = false;
         int initialPage = 0;
-        int pageSize = 0;
+        int pageSize = Integer.MAX_VALUE;
+        String customPageId = null;
 
         Element pagesElement = DomHelper.getChildElement(repeaterElement, FormsConstants.DEFINITION_NS, "pages");
         if (pagesElement!=null) {
-            pageable = true;
-            initialPage = DomHelper.getAttributeAsInteger(pagesElement, "initial", 0);
+            enhanced = true;
+            initialPage = DomHelper.getAttributeAsInteger(pagesElement, "initial", 1) - 1;
             pageSize = DomHelper.getAttributeAsInteger(pagesElement, "size", 0);
+            customPageId = DomHelper.getAttribute(pagesElement, "page-field", null);
         }
 
-        RepeaterDefinition repeaterDefinition = new RepeaterDefinition(initialSize, minSize, maxSize, selectable, orderable, pageable,initialPage,pageSize);
+        RepeaterDefinition repeaterDefinition = new RepeaterDefinition(initialSize, minSize, maxSize, selectable, orderable, enhanced, initialPage, pageSize, customPageId);
         super.setupDefinition(repeaterElement, repeaterDefinition);
         setDisplayData(repeaterElement, repeaterDefinition);
 

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterField.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterField.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterField.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterField.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,41 @@
+package org.apache.cocoon.forms.formmodel;
+
+import org.apache.avalon.framework.CascadingRuntimeException;
+import org.apache.cocoon.forms.binding.BindingException;
+import org.apache.cocoon.forms.event.ValueChangedEvent;
+import org.apache.cocoon.forms.event.ValueChangedListener;
+
+public class RepeaterFilterField extends Field {
+
+	private EnhancedRepeater repeater;
+	private String field;
+
+	public RepeaterFilterField(RepeaterFilterFieldDefinition fieldDefinition) {
+		super(fieldDefinition);
+		this.field = fieldDefinition.getField();
+	}
+
+	public void initialize() {
+		super.initialize();
+		String name = ((RepeaterFilterFieldDefinition)getDefinition()).getRepeaterName();
+		Widget w = getParent().lookupWidget(name);
+		if (w == null) throw new IllegalArgumentException("Cannot find repeater named " + name);
+		if (!(w instanceof EnhancedRepeater)) throw new IllegalArgumentException("The repeater named " + name + " is not an enhanced repeater");
+		this.repeater = (EnhancedRepeater) w;
+		
+		this.addValueChangedListener(new ValueChangedListener() {
+			public void valueChanged(ValueChangedEvent event) {
+				if (repeater.validate()) {
+					try {
+						repeater.setFilter(field, event.getNewValue());
+					} catch (BindingException e) {
+						throw new CascadingRuntimeException("Error setting filter",e);
+					}
+				}
+			}
+		});
+	}
+	
+	
+
+}

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterFieldDefinition.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterFieldDefinition.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterFieldDefinition.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterFieldDefinition.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,39 @@
+package org.apache.cocoon.forms.formmodel;
+
+public class RepeaterFilterFieldDefinition extends FieldDefinition {
+	
+	private String repeaterName;
+	private String field;
+
+	public String getRepeaterName() {
+		return repeaterName;
+	}
+
+	public void setRepeaterName(String repeaterName) {
+		this.repeaterName = repeaterName;
+	}
+
+	public Widget createInstance() {
+        RepeaterFilterField field = new RepeaterFilterField(this);
+        return field;
+	}
+
+	public void initializeFrom(WidgetDefinition definition) throws Exception {
+		if (!(definition instanceof RepeaterFilterFieldDefinition)) 
+			throw new IllegalArgumentException("Wrong definition type to initialize from : " + definition.getClass().getName());
+		super.initializeFrom(definition);
+		this.repeaterName = ((RepeaterFilterFieldDefinition)definition).getRepeaterName();
+		this.field = ((RepeaterFilterFieldDefinition)definition).getField();
+	}
+
+	public String getField() {
+		return field;
+	}
+
+	public void setField(String field) {
+		this.field = field;
+	}
+
+	
+	
+}

Added: cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterFieldDefinitionBuilder.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterFieldDefinitionBuilder.java?view=auto&rev=449452
==============================================================================
--- cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterFieldDefinitionBuilder.java (added)
+++ cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms/formmodel/RepeaterFilterFieldDefinitionBuilder.java Sun Sep 24 11:26:03 2006
@@ -0,0 +1,22 @@
+package org.apache.cocoon.forms.formmodel;
+
+import org.apache.cocoon.forms.util.DomHelper;
+import org.w3c.dom.Element;
+
+public class RepeaterFilterFieldDefinitionBuilder extends
+		FieldDefinitionBuilder {
+
+	public WidgetDefinition buildWidgetDefinition(Element widgetElement) throws Exception {
+        RepeaterFilterFieldDefinition definition = new RepeaterFilterFieldDefinition();
+        setupDefinition(widgetElement, definition);
+        definition.makeImmutable();
+        return definition;
+	}
+
+	protected void setupDefinition(Element widgetElement,RepeaterFilterFieldDefinition definition) throws Exception {
+		super.setupDefinition(widgetElement, definition);
+		definition.setRepeaterName(DomHelper.getAttribute(widgetElement, "repeater"));
+		definition.setField(DomHelper.getAttribute(widgetElement, "field"));
+	}
+
+}



Mime
View raw message