package cern.gp.explorer.properties;
import java.beans.FeatureDescriptor;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.openide.explorer.propertysheet.ExPropertyModel;
import org.openide.explorer.propertysheet.PropertyModel;
import org.openide.explorer.propertysheet.PropertyPanel;
import org.openide.nodes.Node;
import org.openide.util.WeakListener;
import cern.gp.beans.BeanSupport;
import cern.gp.nodes.GPNode;
import cern.gp.util.Assertion;
/**
* A GUI field useful to edit one property which can be embedded in another GUI
* This is the same as used in the GP Explorers and in the Property Sheet opened by the
* {@link cern.gp.actions.PropertiesAction}, and therefore has the same behavior and
* representation as those property panels.
*
* It is based on {@link org.openide.explorer.propertysheet.PropertyPanel}
*
* @author Vito Baggiolini
*
* @version $Revision: 1.2 $, $Date: 2006/09/25 08:52:36 $, $Author: acaproni $
*/
public class PropertyField extends PropertyPanel {
/**
* Constant defining preferences in displaying of value.
* Value should be displayed in read-only mode.
*/
public static final int PREF_READ_ONLY = 0x0001;
/**
* Constant defining preferences in displaying of value.
* Value should be displayed in custom editor.
*/
public static final int PREF_CUSTOM_EDITOR = 0x0002;
/**
* Constant defining preferences in displaying of value.
* Value should be displayed in editor only.
*/
public static final int PREF_INPUT_STATE = 0x0004;
/**
* Constructor that creates a PropertyField from a node and a property Name,
* @param node the GPNode to which the property belongs
* @param propName the property name of this field
*/
public PropertyField(GPNode node, String propName) {
this(node, propName, PREF_INPUT_STATE);
}
/**
* Constructor that creates a PropertyField from a node and a property Name, with preferences
* @param node the GPNode to which the property belongs
* @param propName the property name of this field
* @param preferences one or more of {@link #PREF_CUSTOM_EDITOR}, {@link #PREF_INPUT_STATE},
* or {@link #PREF_READ_ONLY}
*/
public PropertyField(GPNode node, final String propName, int preferences) {
this(
(PropertyModel) new SimpleModel(getNodeProperty(node.getPeerNode().getPropertySets(), propName),
new Object[] { node.getBean()}),
node.getBean(),
propName,
preferences);
}
/**
* @param model
* @param preferences
*/
protected PropertyField(PropertyModel model, Object bean, String propName, int preferences) {
super(model, preferences);
BeanPropertyChangeListener bpcl;
if (bean instanceof BeanSupport) {
BeanSupport beanPropertySource = (BeanSupport) bean;
bpcl = new BeanPropertyChangeListener(beanPropertySource, propName);
} else {
bpcl = new BeanPropertyChangeListener(bean, propName);
}
bpcl.registerListener();
}
/**
* helper method, finds the Node.Property for the indicated propName
* @param psArr the Node.Property[] normally used to create a Property Sheet for a node
* @param propName the name of the property requested
* @return the Node.Property for the property propName
* @throws IllegalArgumentException if the property is not found
*/
protected static Node.Property getNodeProperty(Node.PropertySet[] psArr, String propName) {
Node.Property[] npArr;
for (int jx = 0; jx < psArr.length; jx++) {
npArr = psArr[jx].getProperties();
for (int ix = 0; ix < npArr.length; ix++) {
if (npArr[ix].getName().equals(propName)) {
return npArr[ix];
}
}
}
throw new IllegalArgumentException("unknown property: " + propName);
}
/**
* helper class that updates the PropertyField when the value gets updated in the
* Bean while the PropertyField is open
* It can handle both Beans that do and those that don't inherit from BeanSupport.
*/
protected final class BeanPropertyChangeListener implements PropertyChangeListener {
private final Method addPclMethod;
private final Method remPclMethod;
private final String propName;
private BeanSupport source;
/**
* Constructor
* @param source the bean to which this property belongs
* @param propName the name of the property
*/
protected BeanPropertyChangeListener(BeanSupport source, String propName) {
super();
this.propName = propName;
this.source = source;
this.addPclMethod = null;
this.remPclMethod = null;
}
protected BeanPropertyChangeListener(Object source, String propName) {
this.propName = propName;
// these temporary variables are needed to make *PclMethod final
Method add = null;
Method rem = null;
try {
add =
source.getClass().getMethod(
"addPropertyChangeListener",
new Class[] { String.class, java.beans.PropertyChangeListener.class });
rem =
source.getClass().getMethod(
"removePropertyChangeListener",
new Class[] { String.class, java.beans.PropertyChangeListener.class });
} catch (Exception e) {
e.printStackTrace();
}
addPclMethod = add;
remPclMethod = rem;
}
/**
* add a property change listener to the Bean
*/
public void registerListener() {
Assertion.assertTrue(source != null, "source != null. Maybe you called this twice?");
PropertyChangeListener weak = WeakListener.propertyChange(this, source);
if (addPclMethod != null) {
try {
addPclMethod.invoke(source, new Object[] { propName, weak });
} catch (Exception e) {
e.printStackTrace();
}
} else {
source.addPropertyChangeListener(propName, this);
}
source = null;
}
/*
* Handle a properyt change coming from the Bean
* (non-Javadoc)
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent evt) {
String pn = evt.getPropertyName();
if (!propName.equals(pn)) {
System.err.println("unexpected propName: " + pn);
return;
}
Object newValue = evt.getNewValue();
if (newValue == null) {
System.err.println("Null newValue not yet treated for prop " + pn);
}
try {
PropertyField.super.getModel().setValue(newValue);
PropertyField.super.repaint();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
/** this is copied from NetBeans {@link org.openide.explorer.propertysheet.PropertyPanel} */
/** Implementation of the <code>PropertyModel</code> interface keeping
* a <code>Node.Property</code>. */
static class SimpleModel implements ExPropertyModel {
/** Property to work with. */
private Node.Property prop;
/** Array of beans(nodes) to which belong the property. */
private Object[] beans;
/** Property change support. */
private PropertyChangeSupport sup = new PropertyChangeSupport(this);
/** Construct simple model instance.
* @param property proeprty to work with
* @param beans array of beans(nodes) to which belong the property */
public SimpleModel(Node.Property property, Object[] beans) {
this.prop = property;
this.beans = beans;
}
/** Implements <code>PropertyModel</code> interface. */
public Object getValue() throws InvocationTargetException {
try {
return prop.getValue();
} catch (IllegalAccessException iae) {
throw annotateException(iae);
} catch (InvocationTargetException ite) {
throw annotateException(ite);
}
}
/** Implements <code>PropertyModel</code> interface. */
public void setValue(Object v) throws InvocationTargetException {
try {
prop.setValue(v);
sup.firePropertyChange(PropertyModel.PROP_VALUE, null, null);
} catch (IllegalAccessException iae) {
throw annotateException(iae);
} catch (IllegalArgumentException iaae) {
throw annotateException(iaae);
} catch (InvocationTargetException ite) {
throw annotateException(ite);
}
}
/** Annotates specified exception. Helper method.
* @param exception original exception to annotate
* @return <code>IvocationTargetException</code> which annotates the
* original exception */
private InvocationTargetException annotateException(Exception exception) {
if (exception instanceof InvocationTargetException) {
return (InvocationTargetException) exception;
} else {
return new InvocationTargetException(exception);
}
}
/** Implements <code>PropertyModel</code> interface. */
public Class getPropertyType() {
return prop.getValueType();
}
/** Implements <code>PropertyModel</code> interface. */
public Class getPropertyEditorClass() {
Object ed = prop.getPropertyEditor();
if (ed != null) {
return ed.getClass();
}
return null;
}
/** Implements <code>PropertyModel</code> interface. */
public void addPropertyChangeListener(PropertyChangeListener l) {
Thread.dumpStack();
sup.addPropertyChangeListener(l);
}
/** Implements <code>PropertyModel</code> interface. */
public void removePropertyChangeListener(PropertyChangeListener l) {
sup.removePropertyChangeListener(l);
}
/** Implements <code>ExPropertyModel</code> interface. */
public Object[] getBeans() {
return beans;
}
/** Implements <code>ExPropertyModel</code> interface. */
public FeatureDescriptor getFeatureDescriptor() {
return prop;
}
void fireValueChanged() {
sup.firePropertyChange(PropertyModel.PROP_VALUE, null, null);
}
} // End of class SimpleModel.
}