package com.horstmann.violet.web.property;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import com.horstmann.violet.framework.injection.resources.ResourceBundleConstant;
import com.horstmann.violet.framework.util.SerializableEnumeration;
import eu.webtoolkit.jwt.WCompositeWidget;
import eu.webtoolkit.jwt.WWidget;
public abstract class AbstractPropertyEditorWidget<T> extends WCompositeWidget {
private List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>();
private T oldValue;
private T newValue;
private Object bean;
private PropertyDescriptor propertyDescriptor;
public AbstractPropertyEditorWidget(Object bean, PropertyDescriptor propertyDescriptor) {
this.bean = bean;
this.propertyDescriptor = propertyDescriptor;
setImplementation(getCustomEditor());
}
protected Object getBean() {
return this.bean;
}
protected PropertyDescriptor getPropertyDescriptor() {
return this.propertyDescriptor;
}
protected String getPropertyTitle() {
ResourceBundle rs = ResourceBundle.getBundle(ResourceBundleConstant.NODE_AND_EDGE_STRINGS, Locale.getDefault());
// Try to extract title from resource bundle
String title = this.propertyDescriptor.getName();
try {
String translatedTitle = rs.getString(title.toLowerCase());
if (translatedTitle != null)
title = translatedTitle;
} catch (MissingResourceException e) {
// Nothing to do
}
// Upper case the first character
title = title.substring(0, Math.min(1, title.length())).toUpperCase() + title.substring(Math.min(1, title.length()), title.length());
return title;
}
/**
* A PropertyEditor may choose to make available a full custom Component
* that edits its property value. It is the responsibility of the
* PropertyEditor to hook itself up to its editor Component itself and
* to report property value changes by firing a PropertyChange event.
* <P>
* The higher-level code that calls getCustomEditor may either embed
* the Component in some larger property sheet, or it may put it in
* its own individual dialog, or ...
*
* @return A widget Component that will allow a human to directly
* edit the current property value. May be null if this is
* not supported.
*/
protected abstract WWidget getCustomEditor();
/**
* Called when setValue() is invoked to refreshUp editor content
*/
protected abstract void updateCustomEditor();
/**
* Set (or change) the object that is to be edited. Primitive types such
* as "int" must be wrapped as the corresponding object type such as
* "java.lang.Integer".
*
* @param value The new target object to be edited. Note that this
* object should not be modified by the PropertyEditor, rather
* the PropertyEditor should create a new object to hold any
* modified value.
*/
public void setValue(T value) {
this.newValue = value;
firePropertyChanged(this.oldValue, this.newValue);
if (!isKnownImmutable(this.propertyDescriptor.getPropertyType()))
{
try
{
value = (T) value.getClass().getMethod("clone").invoke(value);
}
catch (Throwable t)
{
// we tried
}
}
this.oldValue = value;
updateCustomEditor();
}
private boolean isKnownImmutable(Class<?> type)
{
if (type.isPrimitive()) return true;
if (SerializableEnumeration.class.isAssignableFrom(type)) return true;
if (Cloneable.class.isAssignableFrom(type)) return true;
return false;
}
/**
* Gets the property value.
*
* @return The value of the property. Primitive types such as "int" will
* be wrapped as the corresponding object type such as "java.lang.Integer".
*/
public T getValue() {
return this.newValue;
}
/**
* Adds a listener for the value change.
* When the property editor changes its value
* it should fire a {@link PropertyChangeEvent}
* on all registered {@link PropertyChangeListener}s,
* specifying the {@code null} value for the property name
* and itself as the source.
*
* @param listener the {@link PropertyChangeListener} to add
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
this.listeners.add(listener);
}
/**
* Removes a listener for the value change.
*
* @param listener the {@link PropertyChangeListener} to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.listeners.remove(listener);
}
/**
* Fires property change event
*
* @param oldValue
* @param newValue
*/
private void firePropertyChanged(T oldValue, T newValue) {
PropertyChangeEvent event = new PropertyChangeEvent(this.bean, this.propertyDescriptor.getName(), oldValue, newValue);
for (PropertyChangeListener aListener : this.listeners) {
aListener.propertyChange(event);
}
}
}