package org.obo.app.model;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
/**
* Default implementation for the PropertyChangeObject interface. Subclasses should call some firePropertyChange
* method for changes to each observable property, and also override getClass(String) to return the appropriate
* class for each property.
* @author Jim Balhoff
*/
public abstract class AbstractPropertyChangeObject implements PropertyChangeObject {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/* (non-Javadoc)
* @see org.phenoscape.app.PropertyChangeObject#addPropertyChangeListener(java.beans.PropertyChangeListener)
*/
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
this.pcs.addPropertyChangeListener(l);
}
/* (non-Javadoc)
* @see org.phenoscape.app.PropertyChangeObject#addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
*/
@Override
public void addPropertyChangeListener(String propName, PropertyChangeListener l) {
this.pcs.addPropertyChangeListener(propName, l);
}
/* (non-Javadoc)
* @see org.phenoscape.app.PropertyChangeObject#removePropertyChangeListener(java.beans.PropertyChangeListener)
*/
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
this.pcs.removePropertyChangeListener(l);
}
/* (non-Javadoc)
* @see org.phenoscape.app.PropertyChangeObject#removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
*/
@Override
public void removePropertyChangeListener(String propName, PropertyChangeListener l) {
this.pcs.removePropertyChangeListener(propName, l);
}
/**
* This implementation searches for and calls a method of the form "setProperty" for a given "property",
* taking a single argument of the class returned by getClass(String) for that property. If no
* such method is found, UndefinedKeyException is thrown.
*/
@Override
public void putValue(String propertyKey, Object value) {
final String setter = this.setter(propertyKey);
if (this.hasMethod(setter, this.getClass(propertyKey))) {
this.callMethod(this.getMethod(setter, this.getClass(propertyKey)), value);
} else {
throw new UndefinedKeyException(propertyKey);
}
}
/**
* This implementation searches for and returns the result of a method of the form "getProperty"
* for a given "property", taking no arguments. If no such method is found, UndefinedKeyException is thrown.
*/
@Override
public Object getValue(String propertyKey) throws UndefinedKeyException {
final String getter = this.getter(propertyKey);
final Object item;
if (this.hasMethod(getter)) {
item = this.callMethod(this.getMethod(getter));
} else {
throw new UndefinedKeyException(propertyKey);
}
return item;
}
/**
* Subclasses should override to return the appropriate class for each observable property. If propertyKey
* does not refer to an observable property, subclasses should return the result of calling super, which
* throws UndefinedKeyException.
*/
@Override
public Class<?> getClass(String propertyKey) throws UndefinedKeyException {
throw new UndefinedKeyException(propertyKey);
}
/**
* Notify all property change listeners that some property has changed. Subclasses should
* generally fire changes for specific properties instead.
*/
protected void firePropertyChange(PropertyChangeEvent evt) {
this.pcs.firePropertyChange(evt);
}
/**
* Notify listeners that the specified property has changed.
*/
protected void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
this.pcs.firePropertyChange(propertyName, oldValue, newValue);
}
/**
* Notify listeners that the specified property has changed.
*/
protected void firePropertyChange(String propertyName, int oldValue, int newValue) {
this.pcs.firePropertyChange(propertyName, oldValue, newValue);
}
/**
* Notify listeners that the specified property has changed.
*/
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
this.pcs.firePropertyChange(propertyName, oldValue, newValue);
}
private String getter(String key) {
return "get" + StringUtils.capitalize(key);
}
private String setter(String key) {
return "set" + StringUtils.capitalize(key);
}
private boolean hasMethod(String methodName, Class<?>... parameterTypes) {
return this.getMethod(methodName, parameterTypes) != null;
}
private Method getMethod(String methodName, Class<?>... parameterTypes) {
try {
final Method method = this.getClass().getMethod(methodName, parameterTypes);
return method;
} catch (SecurityException e) {
return null;
} catch (NoSuchMethodException e) {
return null;
}
}
private Object callMethod(Method method, Object... args) {
try {
return method.invoke(this, args);
} catch (IllegalArgumentException e) {
log().error("Unable to invoke method", e);
} catch (IllegalAccessException e) {
log().error("Unable to invoke method", e);
} catch (InvocationTargetException e) {
log().error("Unable to invoke method", e);
}
return null;
}
private Logger log() {
return Logger.getLogger(this.getClass());
}
}