You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by si...@apache.org on 2006/07/28 13:53:02 UTC

svn commit: r426485 - in /cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl/src/main/java/org/apache/cocoon/forms: binding/ formmodel/

Author: simoneg
Date: Fri Jul 28 04:53:02 2006
New Revision: 426485

URL: http://svn.apache.org/viewvc?rev=426485&view=rev
Log:
COCOON-1877 : Repeater pagination from Matthias Epheser's Google Summer of Code

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/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

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?rev=426485&r1=426484&r2=426485&view=diff
==============================================================================
--- 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 Fri Jul 28 04:53:02 2006
@@ -16,15 +16,20 @@
 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.avalon.framework.logger.Logger;
 import org.apache.cocoon.forms.formmodel.Repeater;
+import org.apache.cocoon.forms.formmodel.RepeaterDefinition;
 import org.apache.cocoon.forms.formmodel.Widget;
+import org.apache.cocoon.forms.formmodel.Repeater.RepeaterRow;
 import org.apache.cocoon.forms.datatype.convertor.ConversionResult;
 import org.apache.commons.collections.ListUtils;
 import org.apache.commons.jxpath.JXPathContext;
@@ -116,8 +121,15 @@
             throw new BindingException("The repeater with the ID [" + this.repeaterId
                     + "] 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);
         if (ptr.getNode() != null) {
             // There are some nodes to load from
@@ -159,6 +171,12 @@
     throws BindingException {
         // Find the repeater
         Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId);
+        
+        if (repeater.isPageable()) {
+            jxpc = repeater.getStorage().doSave();
+            return;
+        }
+        
         // and his context, creating the path if needed
         JXPathContext repeaterContext =
             jxpc.getRelativeContext(jxpc.createPath(this.repeaterPath));
@@ -388,4 +406,161 @@
             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 " + 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;
+            private boolean deleted;
+            
+            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/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?rev=426485&r1=426484&r2=426485&view=diff
==============================================================================
--- 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 Fri Jul 28 04:53:02 2006
@@ -24,6 +24,8 @@
 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;
@@ -64,6 +66,12 @@
     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);
@@ -76,6 +84,13 @@
         
         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() {
@@ -337,7 +352,7 @@
     public void readFromRequest(FormContext formContext) {
         if (!getCombinedState().isAcceptingInputs())
             return;
-
+        
         // read number of rows from request, and make an according number of rows
         Request req = formContext.getRequest();
         String paramName = getRequestParameterName();
@@ -465,6 +480,9 @@
         if (size != Integer.MAX_VALUE) {
             attrs.addCDATAAttribute("max-size", String.valueOf(size));
         }
+        if (this.isPageable()) {
+        	attrs.addCDATAAttribute("page", String.valueOf(currentPage));
+        }
 		return attrs;
 	}
 
@@ -611,4 +629,41 @@
             throw new UnsupportedOperationException("Widget " + this.getRequestParameterName() + " 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?rev=426485&r1=426484&r2=426485&view=diff
==============================================================================
--- 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 Fri Jul 28 04:53:02 2006
@@ -15,6 +15,8 @@
  */
 package org.apache.cocoon.forms.formmodel;
 
+import org.apache.cocoon.forms.binding.BindingException;
+import org.apache.cocoon.forms.binding.RepeaterJXPathBinding;
 import org.apache.cocoon.forms.event.ActionEvent;
 import org.apache.cocoon.forms.event.ActionListener;
 
@@ -202,4 +204,72 @@
             });
         }
     }
+    
+
+  
+    public static class ChangePageActionDefinition extends RepeaterActionDefinition {
+
+       protected int method;
+       
+       public static final int FIRST = 0; 
+       public static final int PREV = 1;
+       public static final int NEXT = 2;
+       public static final int LAST = 3;
+       public static final int CUSTOM = 4;
+
+        /**
+         * initialize this definition with the other, sort of like a copy constructor
+         */
+        public void initializeFrom(WidgetDefinition definition) throws Exception {
+            super.initializeFrom(definition);
+            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()+")");
+            }
+        }
+
+        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()) {
+                        try {
+                            if (repeater.validate()) {
+                                repeater.getStorage().doPageSave();
+                                repeater.setCurrentPage(page);
+                                repeater.getStorage().doPageLoad();
+                            }
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        }
+                    } 
+                }
+            });
+        }
+    }
+    
+    
 }

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?rev=426485&r1=426484&r2=426485&view=diff
==============================================================================
--- 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 Fri Jul 28 04:53:02 2006
@@ -107,6 +107,21 @@
             String select = DomHelper.getAttribute(element, "select");
             return new RepeaterActionDefinition.InsertRowsActionDefinition(repeater, select);
 
+        } 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);
+
         } else {
             throw new Exception("Unknown repeater action '" + actionCommand + "' at " + DomHelper.getLineLocation(element));
         }

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?rev=426485&r1=426484&r2=426485&view=diff
==============================================================================
--- 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 Fri Jul 28 04:53:02 2006
@@ -30,6 +30,10 @@
     private int maxSize;
     private boolean orderable;
     private RepeaterListener listener;
+    
+    private boolean pageable=false;
+    private int initialPage=0;
+    private int pageSize;
 
     public RepeaterDefinition(int initialSize, int minSize, int maxSize, boolean selectable, boolean orderable) {
         super();
@@ -39,7 +43,22 @@
         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;
+	}
+
+
+
+	/**
      * initialize this definition with the other, sort of like a copy constructor
      */
     public void initializeFrom(WidgetDefinition definition) throws Exception {
@@ -93,5 +112,27 @@
     public RepeaterListener getRepeaterListener() {
         return this.listener;
     }
+
+
+
+	public int getInitialPage() {
+		return initialPage;
+	}
+
+
+
+	public boolean isPageable() {
+		return pageable;
+	}
+
+
+
+	public int getPageSize() {
+		return pageSize;
+	}
+    
+    
+    
+    
     
 }

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?rev=426485&r1=426484&r2=426485&view=diff
==============================================================================
--- 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 Fri Jul 28 04:53:02 2006
@@ -18,6 +18,7 @@
 import java.util.Iterator;
 
 import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.cocoon.forms.FormsConstants;
 import org.apache.cocoon.forms.event.RepeaterListener;
 import org.apache.cocoon.forms.util.DomHelper;
 import org.w3c.dom.Element;
@@ -54,7 +55,18 @@
         boolean orderable = DomHelper.getAttributeAsBoolean(repeaterElement, "orderable", false);
         boolean selectable = DomHelper.getAttributeAsBoolean(repeaterElement, "selectable", false);
 
-        RepeaterDefinition repeaterDefinition = new RepeaterDefinition(initialSize, minSize, maxSize, selectable, orderable);
+        boolean pageable = false;
+        int initialPage = 0;
+        int pageSize = 0;
+        
+        Element pagesElement = DomHelper.getChildElement(repeaterElement,FormsConstants.DEFINITION_NS,"pages");
+        if (pagesElement!=null) {
+            pageable = true;
+            initialPage = DomHelper.getAttributeAsInteger(pagesElement, "initial", 0);
+            pageSize = DomHelper.getAttributeAsInteger(pagesElement, "size", 0);
+        }
+        
+        RepeaterDefinition repeaterDefinition = new RepeaterDefinition(initialSize, minSize, maxSize, selectable, orderable, pageable,initialPage,pageSize);
         super.setupDefinition(repeaterElement, repeaterDefinition);
         setDisplayData(repeaterElement, repeaterDefinition);