Attached is the patch. Changed DataView added OIRModel() helper and a test
DataViewOIRModelTest.
With this implementation there is no exception thrown if two objects share
an equal IModel. For those ori is silently turned off. Please look at the
comments in the code.
I also replace getDataProvider() to return an empty model instead of null.
I think in the current code (from the cvs and my patch) the dataitems are
not always rendered in the order as they come from the iterator. I guess
for existing DataItems setting the index has no effect. Is this right? If
so I think this should be fixed.
Christian
On Fri, 12 Aug 2005 16:30:37 -0700, Igor Vaynberg <[EMAIL PROTECTED]>
wrote:
Why don't you implement it and email us the patch. That way we can all
take
a look at it and see if it works.
-Igor
-----Original Message-----
From: [EMAIL PROTECTED]
[mailto:[EMAIL PROTECTED] On Behalf Of
Christian Essl
Sent: Friday, August 12, 2005 1:48 PM
To: [email protected]
Subject: Re: [Wicket-user] [Wicket] DataView and optimized
item removal
On Fri, 12 Aug 2005 12:57:37 -0700, Igor Vaynberg
<[EMAIL PROTECTED]>
wrote:
> Explain the benefits of your approach as opposed to the current
> implementation - I cant think of any off the top of my head.
Because you can use any object not just a string. This means
you can provide optimized item removal (lets call it oir)
also to things like wicket.examples.guestbook.Comment objects
( return new EqualsModel(ob,ob) ). I do not know an easy way
to create a string id for this not db held objects. Or in
case a particular object is contained double in the iterator
you can turn of oir for just this particular item new Model(ob).
new Model(ob) gives you always an indeed unique id.
>
> As far as collections go, performance wise I think you
would be better
> off transforming it to a list first or to an array and writing an
> ArrayAdapter.
> Assuming your collection interface isnt backed by a dynamic source
> this should work ok.
>
Performance wise you are right. The difference is with new
List(colleciton) the Model reflects a snapshot not the actual maybe
changing collection.
Christian
___________________________________________________________
Gesendet von Yahoo! Mail - Jetzt mit 1GB Speicher kostenlos -
Hier anmelden: http://mail.yahoo.de
-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO
September 19-22, 2005 * San Francisco, CA * Development
Lifecycle Practices
Agile & Plan-Driven Development * Managing Projects & Teams *
Testing & QA
Security * Process Improvement & Measurement *
http://www.sqe.com/bsce5sf
_______________________________________________
Wicket-user mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/wicket-user
-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO
September 19-22, 2005 * San Francisco, CA * Development Lifecycle
Practices
Agile & Plan-Driven Development * Managing Projects & Teams * Testing &
QA
Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf
_______________________________________________
Wicket-user mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/wicket-user
--
Christian Essl
Index: DataView.java
===================================================================
RCS file:
/cvsroot/wicket-stuff/wicket-contrib-dataview/src/wicket/contrib/dataview/DataView.java,v
retrieving revision 1.9
diff -u -r1.9 DataView.java
--- DataView.java 11 Aug 2005 20:54:48 -0000 1.9
+++ DataView.java 13 Aug 2005 10:23:49 -0000
@@ -17,11 +17,16 @@
*/
package wicket.contrib.dataview;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
import wicket.Component;
+import wicket.WicketRuntimeException;
import wicket.model.IModel;
import wicket.model.Model;
@@ -90,8 +95,12 @@
*/
protected final IDataProvider getDataProvider()
{
- return (IDataProvider)getModelObject();
+ IDataProvider ret = (IDataProvider)getModelObject();
+ if(ret == null)
+ ret = EmptyDataProvider.SINGLETON;
+ return ret;
}
+
/**
* Returns the total number of items available through the DataProvider
@@ -199,32 +208,110 @@
return this;
}
- /**
- * @see wicket.MarkupContainer#internalOnBeginRequest()
- */
protected void internalOnBeginRequest()
{
- final IDataProvider dataProvider = getDataProvider();
-
- // Get number of items to be displayed
final int size = getViewSize();
-
- if (size > 0)
- {
-
- IUniqueIdProvider idProvider=null;
+ if(size > 0){
+ final IDataProvider dataProvider = getDataProvider();
if (dataProvider instanceof IUniqueIdProvider) {
- idProvider=(IUniqueIdProvider)dataProvider;
- } else {
- removeAll();
- idProvider=new IUniqueIdProvider() {
- public String uniqueId(Object object)
- {
- return
String.valueOf(DataView.this.generateChildId());
+ IUniqueIdProvider udp =
(IUniqueIdProvider)dataProvider;
+ internalOnBeginRequestUniqueId(dataProvider,
udp,size);
+ }else{
+ internalOnBeginRequestModel(dataProvider,size);
+ }
+ }else{
+ removeAll();
+ }
+ }
+
+ private static final Object EXISTING_BEFORE_MARKER = new Object();
+ private void internalOnBeginRequestModel(final IDataProvider
dataProvider, final int size)
+ {
+ Iterator it = null;
+
+
+ final Map existingModels = new HashMap(size());
+ List removeItems = null;
+ it = iterator();
+ while (it.hasNext())
+ {
+ final DataItem child = (DataItem)it.next();
+ final IModel oldModel = child.getModel();
+ //?: There are different possibilities what to
do when IDataProvider.model() returns equal
+ //Models
+ //1.) throw an Excpetion immidiatley: do the
check when the model is gotten from the dataProvider
+ // this is the same as with
IUniqueIdProviders
+ //2.) throw an Exception in the second turn:
this would be at this place and would be
+ // I guess more efficient
+ //3.) silently turn off optimized item removal:
for all DataItems sharing the same model
+ //4.) silently keep oir for (abitraraly) one
DataItem and turn off for others
+ //I for now choose 3 I guess that comes closer
to what you would expect.
+
+ final Object edi = existingModels.get(oldModel);
+ if(edi == null){
+ existingModels.put(oldModel, child);
+ }else{
+ it.remove();
+ if(edi != EXISTING_BEFORE_MARKER){
+ if(removeItems == null)
+ removeItems = new
LinkedList();
+ DataItem di = (DataItem) edi;
+ removeItems.add(di); //remember
for later removal
+ existingModels.put(oldModel,
EXISTING_BEFORE_MARKER);
}
- };
+ }
}
+ //remove the items which have identical models
+ if(removeItems != null){
+ for (Iterator iter = removeItems.iterator();
iter.hasNext(); ) {
+ DataItem di = (DataItem) iter.next();
+ remove(di.getId());
+ }
+ }
+
+
+ it = dataProvider.iterator(firstIndex, size);
+
+ // render all items not already present
+ int index = 0;
+ while (it.hasNext()&&index<size)
+ {
+ final Object object = it.next();
+ final String id = super.generateChildId();
+ final IModel model = dataProvider.model(object);
+
+ final Object ob = existingModels.get(model);
+ if(ob == null || ob == EXISTING_BEFORE_MARKER){
+ DataItem newItem = newItem(id, index,
model);
+ add(newItem);
+ populateItem(newItem);
+ }else{
+ DataItem oldItem = (DataItem) ob;
+ oldItem.setIndex(index);
+ existingModels.remove(model);
+ }
+ index++;
+ }
+
+ // remove dataitems no longer present in the view
+ it = existingModels.values().iterator();
+ while (it.hasNext())
+ {
+ Object ob = it.next();
+ if(ob != EXISTING_BEFORE_MARKER){
+ DataItem di = (DataItem)ob;
+ remove(di.getId());
+ }
+ }
+ }
+
+ /**
+ * @see wicket.MarkupContainer#internalOnBeginRequest()
+ */
+ private void internalOnBeginRequestUniqueId(final IDataProvider
dataProvider,final IUniqueIdProvider idProvider,final int size)
+ {
+
Iterator it = null;
Set existingIds = new HashSet(size());
@@ -266,11 +353,6 @@
{
remove((String)it.next());
}
- }
- else
- {
- removeAll();
- }
}
/**
@@ -320,4 +402,46 @@
* The item to populate
*/
protected abstract void populateItem(final DataItem item);
+
+ /**
+ * used internally for when there is no IDataProvider set as model.
+ * @author Igor Vaynberg ( ivaynberg )
+ *
+ */
+ private static final class EmptyDataProvider implements IDataProvider{
+
+ final static EmptyDataProvider SINGLETON = new
EmptyDataProvider();
+ public Iterator iterator(int first, int count)
+ {
+ return new Iterator(){
+
+ public boolean hasNext()
+ {
+ return false;
+ }
+
+ public Object next()
+ {
+ throw new IndexOutOfBoundsException();
+ }
+
+ public void remove()
+ {
+ throw new
UnsupportedOperationException();
+ }
+
+ };
+ }
+
+ public int size()
+ {
+ return 0;
+ }
+
+ public IModel model(Object object)
+ {
+ throw new WicketRuntimeException("object is not
supplied by this IDataProvider");
+ }
+ }
+
}
Index: DataViewOIRModelTest.java
===================================================================
RCS file: DataViewOIRModelTest.java
diff -N DataViewOIRModelTest.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ DataViewOIRModelTest.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,316 @@
+package wicket.contrib.dataview;
+
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import wicket.MarkupContainer;
+import wicket.model.IModel;
+import wicket.model.Model;
+
+import junit.framework.TestCase;
+
+public class DataViewOIRModelTest extends TestCase
+{
+
+ private List _data;
+ private static int DATASIZE = 5;
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _data = new ArrayList();
+ for(int i=0;i< DATASIZE;i++){
+ _data.add(new DataBean((long)i,"Some text "+i));
+ }
+
+ }
+
+ public void testOIRModel(){
+ OIRModel model = new OIRModel("some","some");
+ OIRModel model2 = new OIRModel("other","some");
+ OIRModel model3 = new OIRModel("some", new Long(3));
+ OIRModel model4 = new OIRModel("other", new Long(3));
+ OIRModel model5 = new OIRModel("some",null);
+ OIRModel model6 = new OIRModel("other",null);
+
+ assertEquals(model,model2);
+ assertEquals(model, model);
+ assertEquals(model3,model4);
+ assertEquals(model5, model5);
+
+ assertFalse(model5.equals(model6));
+ assertFalse(model.equals(model6));
+ assertFalse(model.equals(model3));
+ assertFalse(model3.equals(model6));
+ assertFalse(model3.equals(model));
+
+ }
+
+ public void testNoOIR(){
+ TDataView dv = new TDataView("dv",new TDataProvider(){
+ public IModel model(Object object)
+ {
+ return new Model((Serializable)object);
+ }
+ });
+
+ assertEquals(0,dv.size());
+ dv.doBeginRequest();
+ assertEquals(DATASIZE,dv.size());
+
+ List oldDataItems = new ArrayList(new ContainerCollection(dv));
+ assertEquals(DATASIZE,oldDataItems.size());
+
+ //make sure all dataItems are new
+ //(i can use collections for identity check as long as DataItem
does not implement equals itself)
+ dv.doBeginRequest();
+ assertEquals(DATASIZE,dv.getItemCount());
+ Collection col = new ContainerCollection(dv);
+ assertEquals(DATASIZE, col.size());
+ oldDataItems.retainAll(col);
+ assertEquals(0,oldDataItems.size());
+ }
+
+ public void testAllOIR(){
+ TDataView dv = new TDataView("dv",new TDataProvider(){
+ public IModel model(Object object)
+ {
+ return new OIRModel((Serializable)object,new
Long(((DataBean)object)._id));
+ }});
+
+ assertEquals(0,dv.size());
+ dv.doBeginRequest();
+ assertEquals(DATASIZE,dv.size());
+
+ List oldDataItems = new ArrayList(new ContainerCollection(dv));
+ assertEquals(DATASIZE, oldDataItems.size());
+
+ //now all DataItems must be retained
+ dv.doBeginRequest();
+ Collection col = new ContainerCollection(dv);
+ assertEquals(DATASIZE,col.size());
+ oldDataItems.retainAll(col);
+ assertEquals(DATASIZE,oldDataItems.size());
+
+ }
+
+ public void testRemove(){
+ TDataView dv = new TDataView("dv",new TDataProvider(){
+ public IModel model(Object object)
+ {
+ return new OIRModel((Serializable)object,new
Long(((DataBean)object)._id));
+ }});
+
+ assertEquals(0,dv.size());
+ dv.doBeginRequest();
+ assertEquals(DATASIZE,dv.size());
+
+ List oldDataItems = new ArrayList(new ContainerCollection(dv));
+ assertEquals(DATASIZE, oldDataItems.size());
+
+ //add one and remove one
+ _data.remove(2);
+ dv.doBeginRequest();
+ assertEquals(DATASIZE-1,dv.size());
+
+ //now one item must be more not identity in the oldDataItems
+ Collection col = new ContainerCollection(dv);
+ assertEquals(DATASIZE-1,col.size());
+ oldDataItems.removeAll(col);
+ assertEquals(1,oldDataItems.size());
+ DataBean dI =
(DataBean)((DataItem)oldDataItems.get(0)).getModelObject();
+ assertEquals(2,dI._id);
+ }
+
+ public void testAdd(){
+ TDataView dv = new TDataView("dv",new TDataProvider(){
+ public IModel model(Object object)
+ {
+ return new OIRModel((Serializable)object,new
Long(((DataBean)object)._id));
+ }});
+
+ assertEquals(0,dv.size());
+ dv.doBeginRequest();
+ assertEquals(DATASIZE,dv.size());
+
+ List oldDataItems = new ArrayList(new ContainerCollection(dv));
+ assertEquals(DATASIZE, oldDataItems.size());
+
+ //add one and remove one
+ _data.add(new DataBean(DATASIZE+1,""));
+ dv.doBeginRequest();
+ assertEquals(DATASIZE+1,dv.size());
+
+ //now one item must be more not identity in the oldDataItems
+ List col = new ArrayList(new ContainerCollection(dv));
+ assertEquals(DATASIZE+1,col.size());
+ col.removeAll(oldDataItems);
+ assertEquals(1,col.size());
+ DataBean dI = (DataBean)((DataItem)col.get(0)).getModelObject();
+ assertEquals(DATASIZE+1,dI._id);
+
+ }
+
+ public void testRemoveAdd(){
+ TDataView dv = new TDataView("dv",new TDataProvider(){
+ public IModel model(Object object)
+ {
+ return new OIRModel((Serializable)object,new
Long(((DataBean)object)._id));
+ }});
+
+ assertEquals(0,dv.size());
+ dv.doBeginRequest();
+ assertEquals(DATASIZE,dv.getItemCount());
+
+ List oldDataItems = new ArrayList(new ContainerCollection(dv));
+ assertEquals(DATASIZE, oldDataItems.size());
+
+ //add one and remove one
+ _data.remove(2);
+ _data.add(new DataBean(DATASIZE+1,""));
+ dv.doBeginRequest();
+ assertEquals(DATASIZE,dv.size());
+
+ //now DATASIZE -2 dataItems must be equal
+ Collection col = new ContainerCollection(dv);
+ assertEquals(DATASIZE,col.size());
+ col.removeAll(oldDataItems);
+ assertEquals(1,col.size());
+ }
+
+ public void testDoubleIModel(){
+ TDataView dv = new TDataView("dv",new TDataProvider(){
+ public IModel model(Object object)
+ {
+ return new OIRModel((Serializable)object,new
Long(((DataBean)object)._id));
+ }});
+
+ assertEquals(0,dv.size());
+
+ //add some the bean 0 again
+ _data.add(_data.get(0));
+ _data.add(_data.get(0));
+
+ //build
+ dv.doBeginRequest();
+ assertEquals(DATASIZE+2,dv.getItemCount());
+
+ List oldDataItems = new ArrayList(new ContainerCollection(dv));
+ assertEquals(DATASIZE+2, oldDataItems.size());
+
+ //when rerendering all dataitems for bean0 must be recreated.
This is index 0, DATASIZE-1 and DATASIZE
+ dv.doBeginRequest();
+ List col = new ArrayList(new ContainerCollection(dv));
+
+ assertEquals(DATASIZE+2,col.size());
+// assertNotSame(col.get(0), oldDataItems.get(0));
+// assertNotSame(col.get(DATASIZE -1),
oldDataItems.get(DATASIZE-1));
+// assertNotSame(col.get(DATASIZE), oldDataItems.get(DATASIZE));
+
+ col.removeAll(oldDataItems);
+ assertEquals(3,col.size());
+
assertSame(_data.get(0),((DataItem)col.get(0)).getModelObject());
+
assertSame(_data.get(0),((DataItem)col.get(1)).getModelObject());
+
assertSame(_data.get(0),((DataItem)col.get(2)).getModelObject());
+
+ }
+
+
+
+ private static class TDataView extends DataView{
+
+ public TDataView(String id, IDataProvider dataProvider)
+ {
+ super(id, dataProvider);
+ }
+
+ public void doBeginRequest()
+ {
+ super.internalOnBeginRequest();
+ //make sure the cached item count is cleared
+ super.internalOnEndRequest();
+ }
+
+ protected void internalOnBeginRequest()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ protected DataItem newItem(String id, int index, IModel model)
+ {
+ return new TDataItem(id, index, model);
+ }
+
+ protected void populateItem(DataItem item)
+ {
+ assertTrue(item.getModelObject() instanceof DataBean);
+ }
+
+ //need this to always make sure to have always an identity
check in the collections
+ private static class TDataItem extends DataItem{
+
+ public TDataItem(String id, int index, IModel model)
+ {
+ super(id, index, model);
+ }
+
+ public int hashCode()
+ {
+ return System.identityHashCode(this);
+ }
+
+ public boolean equals(Object obj)
+ {
+ return this == obj;
+ }
+
+ }
+
+ }
+
+ private static class DataBean implements Serializable{
+ final public String _text;
+ final public long _id;
+ DataBean(long id, String text){
+ _text = text;
+ _id = id;
+ }
+ }
+
+ abstract private class TDataProvider implements IDataProvider{
+
+ public Iterator iterator(int first, int count)
+ {
+ return _data.listIterator(first);
+ }
+
+ public int size()
+ {
+ return _data.size();
+ }
+ }
+
+ private static class ContainerCollection extends AbstractCollection{
+
+ private final MarkupContainer _container;
+
+ public ContainerCollection(MarkupContainer cont){
+ _container = cont;
+ }
+ public Iterator iterator()
+ {
+ return _container.iterator();
+ }
+
+ public int size()
+ {
+ return _container.size();
+ }
+
+ }
+}
Index: OIRModel.java
===================================================================
RCS file: OIRModel.java
diff -N OIRModel.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ OIRModel.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,36 @@
+package wicket.contrib.dataview;
+
+import java.io.Serializable;
+
+import wicket.model.Model;
+
+public class OIRModel extends Model
+{
+
+ private Serializable _differentiator;
+
+ public OIRModel(Serializable object, Serializable differentiator)
+ {
+ super(object);
+ _differentiator = differentiator;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if(_differentiator == null)
+ return super.equals(obj);
+ if (obj instanceof OIRModel) {
+ OIRModel mod = (OIRModel)obj;
+ return _differentiator.equals(mod._differentiator);
+ }
+ return false;
+ }
+
+ public int hashCode()
+ {
+ if(_differentiator == null)
+ return super.hashCode();
+ return _differentiator.hashCode();
+ }
+
+}