All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.odell.glazedlists.impl.beans.BeanConnector Maven / Gradle / Ivy

/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.impl.beans;

import ca.odell.glazedlists.ObservableElementList;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.matchers.Matchers;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.EventListener;

/**
 * An {@link ObservableElementList.Connector} for the Java beans'
 * {@link PropertyChangeListener}.
 *
 * @author Jesse Wilson
 * @author James Lemieux
 */
public class BeanConnector implements ObservableElementList.Connector {

    /** The method to use when installing a PropertyChangeListener on an object. */
    private Method addListenerMethod;

    /** The method to use when uninstalling a PropertyChangeListener on an object. */
    private Method removeListenerMethod;

    /** The list which contains the elements being observed via this {@link ObservableElementList.Connector}. */
    private ObservableElementList list;

    /** The PropertyChangeListener to install on each list element. */
    protected PropertyChangeListener propertyChangeListener = this.createPropertyChangeListener();

    /** Matches PropertyChangeEvents to deliver to the ObservableElementList. */
    private Matcher eventMatcher = Matchers.trueMatcher();    
    
    /**
     * Reflection is used to install/uninstall the {@link #propertyChangeListener}
     * on list elements, so we cache the Object[] used in the reflection call
     * for a speed increase.
     */
    private final Object[] reflectionParameters = {propertyChangeListener};

    /**
     * The types taken by the methods which add and remove PropertyChangeListeners.
     */
    private static final Class[] REFLECTION_TYPES = {PropertyChangeListener.class};

    /**
     * Constructs a new Connector which uses reflection to add and remove a
     * PropertyChangeListener from instances of the beanClass. The
     * methods for adding and removing PropertyChangeListener from instances of
     * the given beanClass are assumed to follow the naming
     * convention:
     *
     * 
    *
  • add*(PropertyChangeListener) to add PropertyChangeListeners *
  • remove*(PropertyChangeListener) to remove PropertyChangeListeners *
* * where the * may be replaced with any string of valid java identifier * characters. * * @param beanClass the Class of all list elements within the {@link ObservableElementList} * @throws IllegalArgumentException if beanClass does not contain methods * matching the format described */ public BeanConnector(Class beanClass) { final Method[] methods = beanClass.getMethods(); for (int m = 0; m < methods.length; m++) { if(methods[m].getParameterTypes().length != 1) continue; if(methods[m].getParameterTypes()[0] != PropertyChangeListener.class) continue; if(methods[m].getName().startsWith("add")) this.addListenerMethod = methods[m]; if(methods[m].getName().startsWith("remove")) this.removeListenerMethod = methods[m]; } if (this.addListenerMethod == null || this.removeListenerMethod == null) throw new IllegalArgumentException("Couldn't find listener methods for " + beanClass.getName()); } /** * Constructs a new Connector which uses reflection to add and remove a * PropertyChangeListener from instances of the beanClass. The * methods for adding and removing PropertyChangeListener from instances of * the given beanClass are assumed to follow the naming * convention: * *
    *
  • add*(PropertyChangeListener) to add PropertyChangeListeners *
  • remove*(PropertyChangeListener) to remove PropertyChangeListeners *
* * where the * may be replaced with any string of valid java identifier * characters. * * @param beanClass the Class of all list elements within the {@link ObservableElementList} * @param eventMatcher the matcher for matching those PropertyChangeEvents, which should be * delivered to the ObservableElementList * @throws IllegalArgumentException if beanClass does not contain methods * matching the format described */ public BeanConnector(Class beanClass, Matcher eventMatcher) { this(beanClass); setEventMatcher(eventMatcher); } /** * Constructs a new Connector which uses reflection to add and remove a * PropertyChangeListener from instances of the beanClass * using the named methods. * * @param beanClass the Class of all list elements within the {@link ObservableElementList} * @param addListenerMethodName the name of the method which adds PropertyChangeListeners * to the elements within the {@link ObservableElementList} * @param removeListenerMethodName the name of the method which removes PropertyChangeListeners * from the elements within the {@link ObservableElementList} * @throws IllegalArgumentException if beanClass does not contain the named * methods or if the methods do no take a PropertyChangeListener as the single parameter */ public BeanConnector(Class beanClass, String addListenerMethodName, String removeListenerMethodName) { try { this.addListenerMethod = beanClass.getMethod(addListenerMethodName, REFLECTION_TYPES); this.removeListenerMethod = beanClass.getMethod(removeListenerMethodName, REFLECTION_TYPES); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("Failed to find method " + e.getMessage() + " in " + beanClass); } } /** * Constructs a new Connector which uses reflection to add and remove a PropertyChangeListener * from instances of the beanClass using the named methods. * * @param beanClass the Class of all list elements within the {@link ObservableElementList} * @param addListenerMethodName the name of the method which adds PropertyChangeListeners to the * elements within the {@link ObservableElementList} * @param removeListenerMethodName the name of the method which removes PropertyChangeListeners * from the elements within the {@link ObservableElementList} * @param eventMatcher the matcher for matching those PropertyChangeEvents, which should be * delivered to the ObservableElementList * @throws IllegalArgumentException if beanClass does not contain the named * methods or if the methods do no take a PropertyChangeListener as the single parameter */ public BeanConnector(Class beanClass, String addListenerMethodName, String removeListenerMethodName, Matcher eventMatcher) { this(beanClass, addListenerMethodName, removeListenerMethodName); setEventMatcher(eventMatcher); } /** * Start listening for PropertyChangeEvents from the specified * element. The PropertyChangeListener is installed using * reflection. * * @param element the element to be observed * @return the listener that was installed on the element * to be used as a parameter to {@link #uninstallListener(Object, EventListener)} * @throws RuntimeException if the reflection call fails to successfully * install the PropertyChangeListener */ public EventListener installListener(E element) { try { this.addListenerMethod.invoke(element, this.reflectionParameters); return this.propertyChangeListener; } catch (IllegalAccessException iae) { throw new RuntimeException(iae); } catch (InvocationTargetException ite) { throw new RuntimeException(ite.getCause()); } } /** * Stop listening for PropertyChangeEvents from the specified * element. The PropertyChangeListener is uninstalled using * reflection. * * @param element the observed element * @param listener the listener that was installed on the element * in {@link #installListener(Object)} * @throws RuntimeException if the reflection call fails to successfully * uninstall the PropertyChangeListener */ public void uninstallListener(E element, EventListener listener) { try { this.removeListenerMethod.invoke(element, this.reflectionParameters); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } } /** {@inheritDoc} */ public void setObservableElementList(ObservableElementList list) { this.list = list; } /** * Returns the event matcher. It matches those PropertyChangeEvents, which should be delivered * to the ObservableElementList. In other words, it serves as a filter for PropertyChangeEvents. */ public final Matcher getEventMatcher() { return eventMatcher; } /** * Sets the event matcher, may not be null. It matches those * PropertyChangeEvents, which should be delivered to the ObservableElementList. In other words, * it serves as a filter for PropertyChangeEvents. */ private void setEventMatcher(Matcher eventMatcher) { if (eventMatcher == null) throw new IllegalArgumentException("Event matcher may not be null."); this.eventMatcher = eventMatcher; } /** * A local factory method to produce the PropertyChangeListener which will * be installed on list elements. */ protected PropertyChangeListener createPropertyChangeListener() { return new PropertyChangeHandler(); } /** * The PropertyChangeListener which notifies the {@link ObservableElementList} within this * Connector of changes to list elements. */ public class PropertyChangeHandler implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent event) { if (getEventMatcher().matches(event)) { list.elementChanged(event.getSource()); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy