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);