model wrapper for ReuseIfModelsEqualStrategy or alternative reuse strategy?

2007-08-27 Thread Eelco Hillenius
Hi,

For Wicket In Action in converted an example from a ListView to a
RepeatingView. This example has to function in a form, so I set the
'ReuseIfModelsEqualStrategy'. Overriding hashCode (and equals) on the
model however results in quite a bit of code bloat, while my hunch is
that comparing the model values is actually a pretty common use case.

So, in my quest to cut down a few lines, I would basically have two
choices: implement a reuse strategy that does this, or implement a
model wrapper that implements hashCode and equals in a generic
fashion. I choose the latter, and would like to hear your opinions
whether this is something we could add to the project (I think the use
case for it should be common enough, especially since I would mention
it in the book), or whether you would prefer to have a reuse strategy
that does this.

I'd really like to see one of these in the project, as otherwise I
would have to either include the equals/ hashCode implementation
inline, which bloats the example, or I would have to give the code in
the book as people will want to play with it. Both would suck.

Cheers,

Eelco

My implementation (no javadocs):

package wicket.in.action.common;

import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.wicket.model.IModel;
import org.apache.wicket.util.lang.Objects;

public final class EqualsDecorator {

  private EqualsDecorator() {
  }

  public static IModel decorate(final IModel model) {
return (IModel) Proxy.newProxyInstance(model.getClass()
.getClassLoader(), model.getClass().getInterfaces(),
new Decorator(model));
  }

  private static class Decorator implements InvocationHandler,
  Serializable {

private final IModel model;

Decorator(IModel model) {
  this.model = model;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
  String methodName = method.getName();
  if (methodName.equals(equals)) {
if (args[0] instanceof IModel) {
  return Objects.equal(model.getObject(), ((IModel) args[0])
  .getObject());
}
  } else if (methodName.equals(hashCode)) {
Object val = model.getObject();
return 37 + (val != null ? val.hashCode() : 0);
  } else if (methodName.equals(writeReplace)) {
return new SerializableReplacement(model);
  }
  return method.invoke(model, args);
}
  }

  private static class SerializableReplacement implements
  Serializable {
private final IModel model;

SerializableReplacement(IModel model) {
  this.model = model;
}

private Object readResolve() throws ObjectStreamException {
  return decorate(model);
}
  }
}


Re: model wrapper for ReuseIfModelsEqualStrategy or alternative reuse strategy?

2007-08-27 Thread Igor Vaynberg
the reason i did not provide this when i wrote the repeaters package is that
it can lead down some pretty awful roads. an easy example where this goes
totally wrong is when you use detachable models for the items. if you use
this decorator all models from the previous page will be loaded because they
will all be compared - but in case of the detachable model it is not
necesary to load the object, it is enough to only compare the id.

so while this makes it easy it also makes it easy to make something
inefficient because you dont have to think about it.

+0

-igor


On 8/26/07, Eelco Hillenius [EMAIL PROTECTED] wrote:

 Hi,

 For Wicket In Action in converted an example from a ListView to a
 RepeatingView. This example has to function in a form, so I set the
 'ReuseIfModelsEqualStrategy'. Overriding hashCode (and equals) on the
 model however results in quite a bit of code bloat, while my hunch is
 that comparing the model values is actually a pretty common use case.

 So, in my quest to cut down a few lines, I would basically have two
 choices: implement a reuse strategy that does this, or implement a
 model wrapper that implements hashCode and equals in a generic
 fashion. I choose the latter, and would like to hear your opinions
 whether this is something we could add to the project (I think the use
 case for it should be common enough, especially since I would mention
 it in the book), or whether you would prefer to have a reuse strategy
 that does this.

 I'd really like to see one of these in the project, as otherwise I
 would have to either include the equals/ hashCode implementation
 inline, which bloats the example, or I would have to give the code in
 the book as people will want to play with it. Both would suck.

 Cheers,

 Eelco

 My implementation (no javadocs):

 package wicket.in.action.common;

 import java.io.ObjectStreamException;
 import java.io.Serializable;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;

 import org.apache.wicket.model.IModel;
 import org.apache.wicket.util.lang.Objects;

 public final class EqualsDecorator {

   private EqualsDecorator() {
   }

   public static IModel decorate(final IModel model) {
 return (IModel) Proxy.newProxyInstance(model.getClass()
 .getClassLoader(), model.getClass().getInterfaces(),
 new Decorator(model));
   }

   private static class Decorator implements InvocationHandler,
   Serializable {

 private final IModel model;

 Decorator(IModel model) {
   this.model = model;
 }

 public Object invoke(Object proxy, Method method, Object[] args)
 throws Throwable {
   String methodName = method.getName();
   if (methodName.equals(equals)) {
 if (args[0] instanceof IModel) {
   return Objects.equal(model.getObject(), ((IModel) args[0])
   .getObject());
 }
   } else if (methodName.equals(hashCode)) {
 Object val = model.getObject();
 return 37 + (val != null ? val.hashCode() : 0);
   } else if (methodName.equals(writeReplace)) {
 return new SerializableReplacement(model);
   }
   return method.invoke(model, args);
 }
   }

   private static class SerializableReplacement implements
   Serializable {
 private final IModel model;

 SerializableReplacement(IModel model) {
   this.model = model;
 }

 private Object readResolve() throws ObjectStreamException {
   return decorate(model);
 }
   }
 }



Re: model wrapper for ReuseIfModelsEqualStrategy or alternative reuse strategy?

2007-08-27 Thread Eelco Hillenius
On 8/27/07, Igor Vaynberg [EMAIL PROTECTED] wrote:
 the reason i did not provide this when i wrote the repeaters package is that
 it can lead down some pretty awful roads. an easy example where this goes
 totally wrong is when you use detachable models for the items. if you use
 this decorator all models from the previous page will be loaded because they
 will all be compared - but in case of the detachable model it is not
 necesary to load the object, it is enough to only compare the id.
 so while this makes it easy it also makes it easy to make something
 inefficient because you dont have to think about it.

Ok, I can follow that. If you are not using such models however, this
is a very convenient class. I think that if we give a clear enough
warning in the docs (and book) it should be ok.

How about facilitating the case you described a little bit better as
well? Every project probably has it's own variants of entity models by
now. Wouldn't it be an idea to introduce an interface like
ICompressedModel { Object getCompressed() } or something along those
lines that people can implement and which would return e.g. the id
which would then be used by the wrapper?

Eelco


Re: model wrapper for ReuseIfModelsEqualStrategy or alternative reuse strategy?

2007-08-27 Thread Matej Knopp
Perhaps IEntityModel Object getEntityId/PK() would be better name?
ICompressedModel doesn't really say much, sounds too abstract to me. Also I
could live with EqualsDecorator as long as it clearly states with CAPITAL
LETTERS that it can't be used on detachable models :)

-Matej

On 8/27/07, Eelco Hillenius [EMAIL PROTECTED] wrote:

 On 8/27/07, Igor Vaynberg [EMAIL PROTECTED] wrote:
  the reason i did not provide this when i wrote the repeaters package is
 that
  it can lead down some pretty awful roads. an easy example where this
 goes
  totally wrong is when you use detachable models for the items. if you
 use
  this decorator all models from the previous page will be loaded because
 they
  will all be compared - but in case of the detachable model it is not
  necesary to load the object, it is enough to only compare the id.
  so while this makes it easy it also makes it easy to make something
  inefficient because you dont have to think about it.

 Ok, I can follow that. If you are not using such models however, this
 is a very convenient class. I think that if we give a clear enough
 warning in the docs (and book) it should be ok.

 How about facilitating the case you described a little bit better as
 well? Every project probably has it's own variants of entity models by
 now. Wouldn't it be an idea to introduce an interface like
 ICompressedModel { Object getCompressed() } or something along those
 lines that people can implement and which would return e.g. the id
 which would then be used by the wrapper?

 Eelco



Re: model wrapper for ReuseIfModelsEqualStrategy or alternative reuse strategy?

2007-08-27 Thread Igor Vaynberg
i dont know if we want to go down this road. its a mixin that is not very
obvious, how many people do you think know about IStyledColumn? besides it
might be two objects that represent the pk - like two longs for composite
pks. i really dont think it is that big a deal to implement equals/hashcode
on a model, and if people have their own variants of entitymodels they only
have one place to implement it. imho this is just bloat.

-igor


On 8/27/07, Matej Knopp [EMAIL PROTECTED] wrote:

 Perhaps IEntityModel Object getEntityId/PK() would be better name?
 ICompressedModel doesn't really say much, sounds too abstract to me. Also
 I
 could live with EqualsDecorator as long as it clearly states with CAPITAL
 LETTERS that it can't be used on detachable models :)

 -Matej

 On 8/27/07, Eelco Hillenius [EMAIL PROTECTED] wrote:
 
  On 8/27/07, Igor Vaynberg [EMAIL PROTECTED] wrote:
   the reason i did not provide this when i wrote the repeaters package
 is
  that
   it can lead down some pretty awful roads. an easy example where this
  goes
   totally wrong is when you use detachable models for the items. if you
  use
   this decorator all models from the previous page will be loaded
 because
  they
   will all be compared - but in case of the detachable model it is not
   necesary to load the object, it is enough to only compare the id.
   so while this makes it easy it also makes it easy to make something
   inefficient because you dont have to think about it.
 
  Ok, I can follow that. If you are not using such models however, this
  is a very convenient class. I think that if we give a clear enough
  warning in the docs (and book) it should be ok.
 
  How about facilitating the case you described a little bit better as
  well? Every project probably has it's own variants of entity models by
  now. Wouldn't it be an idea to introduce an interface like
  ICompressedModel { Object getCompressed() } or something along those
  lines that people can implement and which would return e.g. the id
  which would then be used by the wrapper?
 
  Eelco