Hi,

For some reason this feature doesn't seem that usable to me... Maybe other
devs and users will like it...

See some comments from me inline:

Martin Grigorov
Wicket Training and Consulting
https://twitter.com/mtgrigorov


On Sun, Jul 13, 2014 at 3:21 AM, Garret Wilson <gar...@globalmentor.com>
wrote:

> On 7/12/2014 3:14 PM, Garret Wilson wrote:
>
>> ... The approach I am proposing allows one to encapsulate a whole series
>> of behaviors and component associations within one "bundle".
>>
>
> Another benefit of "behavior bundles" is that in Wicket 7.x, legacy
> behavior (such as wrapping disabled links in <em><span>) could easily be
> included in a Wicket6BehaviorBundle. This would obviate the need for
> one-off backwards-compatibility classes <https://issues.apache.org/
> jira/browse/WICKET-4904?focusedCommentId=14059918&page=com.atlassian.jira.
> plugin.system.issuetabpanels:comment-tabpanel#comment-14059918> such as
> DisabledLinkBehavior.LinkInstantiationListener to be included in the main
> framework.
>
> But without further ado, here is the class itself. Please tell me what you
> think about it. I'd like to contribute it. (I couldn't help but use a few
> Google Guava classes---they are so useful, and I've grown accustomed to
> having them handy, but if it's not possible to use them I can rip them out
> before submitting a patch.) Note the documentation around multiple
> registration of behaviors---I didn't know what Wicket allows in the way of
> a behavior added multiple times, so I took a simple implementation and
> documented it.
>
> As a reminder, the bundle is used like this (but preferably someone would
> create bundle subclasses to distribute separately):
>
>     final BehaviorBundle pureCSSBehaviorBundle = new BehaviorBundle();
>     pureCSSBehaviorBundle.registerBehavior(AbstractLink.class, new
> DisabledClassAttributeAppender("pure-button-disabled"));
>     getComponentInstantiationListeners().add(pureCSSBehaviorBundle);
>
> After some discussion on the list, would it be appropriate for me to go
> ahead and create a JIRA and then submit a patch? (I guess I'll be forced to
> touch Git, now. ;) )
>
> Code below. Cheers!
>
> Garret
>
> /**
>  * A collection of behaviors associated with component classes. When added
> to the application's collection of component
>  * instantiation listeners, the designated behaviors will be registered
> with the associated components and their subclasses. This
>  * facilities grouping behaviors based upon functionality or library.
>  * <p>
>  * For example, the <a href="http://purecss.io/";>Yahoo! Pure CSS</a>
> library requires that <a
>  * href="http://purecss.io/buttons/";>disabled buttons</a> have the
> <code>pure-button-disabled</code> class. A behavior bundle may
>  * be created supporting this feature maybe be added as follows:
>  * </p>
>  *
>  * <pre>
>  * <code>BehaviorBundle pureCSSBehaviorBundle = new BehaviorBundle();
>  * pureCSSBehaviorBundle.registerBehavior(AbstractLink.class, new
> DisabledClassAttributeAppender("pure-button-disabled"));
>  * getComponentInstantiationListeners().add(pureCSSBehaviorBundle);</code>
>  * </pre>
>  * <p>
>  * Note that a production behavior bundle would likely create a {@link
> BehaviorBundle} subclass containing the appropriate
>  * behaviors to be distributed as a unit.
>  * </p>
>  * @author Garret Wilson
>  * @see Application#getComponentInstantiationListeners()
>  */
> public class BehaviorBundle implements IComponentInstantiationListener {
>
>   /** The lock governing multithreaded access to the registered component
> behaviors. */
>   final ReadWriteLock lock = new ReentrantReadWriteLock();
>

Usually Wicket apps add the IXyzListeners in Application#init(). So there
is no really need to use locking.
It is possible to add/remove listeners (and in your case register
behaviors) in the "business logic" (e.g. after clicking some link or
similar) but usually people don't do it. Or at least I haven't seen such
code.


>
>   /** The map of behaviors, accessed under {@link #lock}. */
>   private final ListMultimap<Class<? extends Component>, Behavior>
> behaviorMap = ArrayListMultimap.create();
>
>   /**
>    * Registers a behavior with a component class for adding to each
> component when it is instantiated.
>    * <p>
>    * Behaviors registered with a given class will be added to instances of
> any child classes as well. For example, registering a
>    * behavior for {@link AbstractLink} will result in the behavior being
> added to instances of e.g. {@link BookmarkablePageLink}
>    * as well.
>    * </p>
>    * <p>
>    * Behaviors registered twice for one class, or at multiple places in
> the hierarchy of a class, will consequently be added
>    * multiple times to the same component.
>    * </p>
>    * @param componentClass The class and subclasses of which the behavior
> should be registered.
>    * @param behaviors The behavior to register for addition upon component
> instantiation..
>    * @return The bundle itself, to allow method call chaining.
>    */
>   public BehaviorBundle registerBehavior(final Class<? extends Component>
> componentClass, final Behavior behavior) {
>

since you use Guava anyway I think it would be better to use Precondition
that accepts the Class instead of the Class itself. It will be much more
flexible in the future.


>     lock.writeLock().lock();
>     try {
>       behaviorMap.put(checkNotNull(componentClass),
> checkNotNull(behavior));
>     } finally {
>       lock.writeLock().unlock();
>     }
>     return this;
>   }
>
>   /**
>    * {@inheritDoc} This implementation adds all behaviors registered with
> the component's class and all parent classes up to and
>    * including {@link Component}. Behaviors will be added in order from
> the most general (close to {@link Component} to the most
>    * specific (the component's actual class).
>    */
>   @Override
>   public void onInstantiation(final Component component) {
>     lock.readLock().lock();
>     try {
>       addBehaviors(component, component.getClass());
>     } finally {
>       lock.readLock().unlock();
>     }
>   }
>
>   /**
>    * Adds all registered behaviors to the given component based upon the
> given component class and the parent classes, stopping at
>    * {@link Component}.
>    * <p>
>    * Parent class behaviors are added first in order to allow general
> behaviors to take effect earlier.
>    * </p>
>    * <p>
>    * This method should only be called under a read lock.
>    * </p>
>    * @param component The component to which the behaviors should be added.
>    * @param componentClass
>    */
>   //we ensure manually that the parent classes are subclasses of Component
>   @SuppressWarnings("unchecked")
>   protected void addBehaviors(final Component component, final Class<?
> extends Component> componentClass) {
>
>     //do a depth-first traversal to add general (super-class) behaviors
> first
>     //no need to look higher up the hierarchy than Component
>     if (!componentClass.equals(Component.class)) {
>       final Class<?> parentComponentClass = componentClass.getSuperclass()
> ;
>       //the class is a subclass of Component; because we stop when we find
> Component,
>       //we expect never to run out of parents
>       assert parentComponentClass != null;
>

In Wicket we don't use JDK's assert anywhere so far.
In my experience these should be used only in the application code. A
library/framework should not change its behavior depending on JVM argument.


>       addBehaviors(component, (Class<? extends Component>)
> parentComponentClass);
>     }
>
>     //add behaviors (if any) registered for this class
>     for (final Behavior behavior : behaviorMap.get(componentClass)) {
>       component.add(behavior);
>     }
>
>   }
>
> }
>
>

Reply via email to