/**
* Copyright (C) 2015 Valkyrie RCP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.valkyriercp.binding.support;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.valkyriercp.core.PropertyChangePublisher;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Consists exclusively of static convenience methods for adding
* and removing <code>PropertyChangeListener</code>s for bound
* JavaBean properties.<p>
*
* TODO: Move this code into {@link org.springframework.beans.BeanUtils}
*
* @author Karsten Lentzsch
* @author Oliver Hutchison
*/
public abstract class PropertyChangeSupportUtils {
/**
* Holds the class parameter list that is used to lookup
* the adder and remover methods for PropertyChangeListeners.
*/
private static final Class[] NAMED_PCL_PARAMS = new Class[] {String.class, PropertyChangeListener.class};
/**
* Checks and answers whether the given class supports bound properties,
* i.e. it provides a pair of bound property event listener registration methods:
* <pre>
* public void addPropertyChangeListener(String, PropertyChangeListener);
* public void removePropertyChangeListener(String, PropertyChangeListener);
* </pre>
*
* @param beanClass the class to test
* @return true if the class supports bound properties, false otherwise
*/
public static boolean supportsBoundProperties(Class beanClass) {
return PropertyChangePublisher.class.isAssignableFrom(beanClass)
|| ((getNamedPCLAdder(beanClass) != null) && (getNamedPCLRemover(beanClass) != null));
}
/**
* Adds a named property change listener to the given JavaBean. The bean
* must provide the optional support for listening on named properties
* as described in section 7.4.5 of the
* <a href="http://java.sun.com/products/javabeans/docs/spec.html">Java Bean
* Specification</a>. The bean class must provide the method:
* <pre>
* public void addPropertyChangeListener(String, PropertyChangeListener);
* </pre>
*
* @param bean the JavaBean to add a property change handler
* @param propertyName the name of the property to be observed
* @param listener the listener to add
* @throws PropertyNotBindableException
* if the property change handler cannot be added successfully
*/
public static void addPropertyChangeListener(Object bean, String propertyName, PropertyChangeListener listener) {
Assert.notNull(propertyName, "The property name must not be null.");
Assert.notNull(listener, "The listener must not be null.");
if (bean instanceof PropertyChangePublisher) {
((PropertyChangePublisher)bean).addPropertyChangeListener(propertyName, listener);
}
else {
Class beanClass = bean.getClass();
Method namedPCLAdder = getNamedPCLAdder(beanClass);
if (namedPCLAdder == null)
throw new FatalBeanException("Could not find the bean method"
+ "/npublic void addPropertyChangeListener(String, PropertyChangeListener);/nin bean '" + bean
+ "'");
try {
namedPCLAdder.invoke(bean, new Object[] {propertyName, listener});
}
catch (InvocationTargetException e) {
throw new FatalBeanException("Due to an InvocationTargetException we failed to add "
+ "a named PropertyChangeListener to bean '" + bean + "'", e);
}
catch (IllegalAccessException e) {
throw new FatalBeanException("Due to an IllegalAccessException we failed to add "
+ "a named PropertyChangeListener to bean '" + bean + "'", e);
}
}
}
/**
* Removes a named property change listener to the given JavaBean. The bean
* must provide the optional support for listening on named properties
* as described in section 7.4.5 of the
* <a href="http://java.sun.com/products/javabeans/docs/spec.html">Java Bean
* Specification</a>. The bean class must provide the method:
* <pre>
* public void removePropertyChangeHandler(String, PropertyChangeListener);
* </pre>
*
* @param bean the bean to remove the property change listener from
* @param propertyName the name of the observed property
* @param listener the listener to remove
* @throws FatalBeanException
* if the property change handler cannot be removed successfully
*/
public static void removePropertyChangeListener(Object bean, String propertyName, PropertyChangeListener listener) {
Assert.notNull(propertyName, "The property name must not be null.");
Assert.notNull(listener, "The listener must not be null.");
if (bean instanceof PropertyChangePublisher) {
((PropertyChangePublisher)bean).removePropertyChangeListener(propertyName, listener);
}
else {
Class beanClass = bean.getClass();
Method namedPCLRemover = getNamedPCLRemover(beanClass);
if (namedPCLRemover == null)
throw new FatalBeanException("Could not find the bean method"
+ "/npublic void removePropertyChangeListener(String, PropertyChangeListener);/nin bean '"
+ bean + "'");
try {
namedPCLRemover.invoke(bean, new Object[] {propertyName, listener});
}
catch (InvocationTargetException e) {
throw new FatalBeanException("Due to an InvocationTargetException we failed to remove "
+ "a named PropertyChangeListener from bean '" + bean + "'", e);
}
catch (IllegalAccessException e) {
throw new FatalBeanException("Due to an IllegalAccessException we failed to remove "
+ "a named PropertyChangeListener from bean '" + bean + "'", e);
}
}
}
/**
* Looks up and returns the method that adds a PropertyChangeListener
* for a specified property name to instances of the given class.
*
* @param beanClass the class that provides the adder method
* @return the method that adds the PropertyChangeListeners
*/
private static Method getNamedPCLAdder(Class beanClass) {
try {
return beanClass.getMethod("addPropertyChangeListener", NAMED_PCL_PARAMS);
}
catch (NoSuchMethodException e) {
return null;
}
}
/**
* Looks up and returns the method that removes a PropertyChangeListener
* for a specified property name from instances of the given class.
*
* @param beanClass the class that provides the remover method
* @return the method that removes the PropertyChangeListeners
*/
private static Method getNamedPCLRemover(Class beanClass) {
try {
return beanClass.getMethod("removePropertyChangeListener", NAMED_PCL_PARAMS);
}
catch (NoSuchMethodException e) {
return null;
}
}
}