* This implementation calls {@code sourceEdited} if the provided event returns * {@code true} from {@code getValueChanged}. */ public void sourceChanged(Binding binding, PropertyStateEvent event) { if (event.getValueChanged()) { sourceEdited(binding); } } /** * {@inheritDoc} *
* This implementation calls {@code targetEdited} if the provided event returns * {@code true} from {@code getValueChanged}. */ public void targetChanged(Binding binding, PropertyStateEvent event) { if (event.getValueChanged()) { targetEdited(binding); } } /** * Notification that the source property of a {@code Binding} has fired * a {@code PropertyStateEvent} indicating that its value or readability * has changed for the {@code Binding's} source object. Called by the default * {@code AbstractBindingListener's} implementation of {@code sourceChanged}. * * @param binding the {@code Binding} * @deprecated This method has been replaced by {@link #sourceChanged} and it * will go away soon. It is being kept for a short period only, * to assist in migration. */ @Deprecated public void sourceEdited(Binding binding) {} /** * Notification that the target property of a {@code Binding} has fired * a {@code PropertyStateEvent} indicating that its value or readability * has changed for the {@code Binding's} target object. Called by the default * {@code AbstractBindingListener's} implementation of {@code targetChanged}. * * @param binding the {@code Binding} * @deprecated This method has been replaced by {@link #targetChanged} and it * will go away soon. It is being kept for a short period only, * to assist in migration. */ @Deprecated public void targetEdited(Binding binding) {} } beansbinding-1.2.1/src/org/jdesktop/beansbinding/AutoBinding.java 0000664 0000000 0000000 00000023263 11516446575 0025054 0 ustar 00root root 0000000 0000000 /* * Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is * subject to license terms. */ package org.jdesktop.beansbinding; /** * An implementation of {@code Binding} that automatically syncs the source * and target by refreshing and saving according to one of three update * strategies. The update strategy is specified for an {@code AutoBinding} * on creation, and is one of: *
*
* The behavior of {@code AutoBinding} for each * of the update strategies is defined as follows: *
*
| {@code READ_ONCE} | ** |
* Summary: * Tries to sync the target from the source only once, at bind time. *
* Details: |
*
| {@code READ} | ** |
* Summary: * Tries to keep the target in sync with the source. *
* Details: |
*
| {@code READ_WRITE} | ** |
* Summary: * Tries to keep both the source and target in sync with each other. *
* Details: * Automatically responds to changes in the state of the source as follows: * If the change represents a value change, use the try-refresh-then-save * procedure mentioned above. Otherwise, if the change represents the * source becoming writeable, tries to update the source from the target * by calling {@code saveAndNotify}. * * Automatically responds to changes in the state of the target as follows: * If the change represents the target simply becoming writeable, try to * sync the target from the source by calling {@code refreshAndNotify}. If * the change represents the target becoming writeable and the value changing * together, use the try-refresh-then-save procedure mentioned above. Finally * if the change represents the target's value changing alone, first try to * sync the source from the target by calling {@code save}. * If that succeeds, notify the listeners of a successful sync. If it * returns failure due to conversion or validation, notify the listeners of a sync * failure, providing the conversion or validation failure. If it fails for * any other reason, then instead try to sync the target from the source by * calling {@code refresh}. If this succeeds, notify the listeners of successful * sync. Otherwise notify them of failure with the reasons for the original * save failure. * |
*
*
* BeanProperty.create("firstName");
*
* * Or to create a property representing the {@code firstName} property of * an object's {@code mother} property: *
*
* BeanProperty.create("mother.firstName");
*
* * An instance of {@code BeanProperty} is immutable and can be used with * different source objects. When a {@code PropertyStateListener} is added to * a {@code BeanProperty} for a given source object, the {@code BeanProperty} * starts listening to all objects along the path (based on that source object) * for change notification, and reflects any changes by notifying the * listener associated with the property for that source object. So, in the second * example above, if a {@code PropertyStateListener} is added to the property * for an object {@code Duke}, the {@code PropertyStateListener} is notified * when either {@code Duke's} mother changes (if the new mother's name is * different), or {@code Duke's mother's firstName} changes. *
* It is very important that any bean properties addressed via a {@code BeanProperty} * follow the Java Beans specification, including firing property change notification; * otherwise, {@code BeanProperty} cannot respond to change. As some beans outside * of your control may not follow the Java Beans specification, {@code BeanProperty} * always checks the {@link org.jdesktop.beansbinding.ext.BeanAdapterFactory} to * see if a delegate provider has been registered to provide a delegate bean to take * the place of an object for a given property. See the * ext package level documentation for more * details. *
* When there are no {@code PropertyStateListeners} installed on a {@code BeanProperty} * for a given source, all {@code Property} methods act by traversing the entire * path from the source to the end point, thereby always providing "live" information. * On the contrary, when there are {@code PropertyStateListeners} installed, the beans * along the path (including the final value) are cached, and only updated upon * notification of change from a bean. Again, this makes it very important that any * bean property that could change along the path fires property change notification. *
* Readability of a {@code BeanProperty} for a given source is defined as follows: * A {@code BeanProperty} is readable for a given source if and only if * a) each bean in the path, starting with the source, defines a Java Beans getter * method for the the property to be read on it AND b) each bean in the path, * starting with the source and ending with the bean on which we read the final * property, is {@code non-null}. The final value being {@code null} does not * affect the readability. *
* So, in the second example given earlier, the {@code BeanProperty} is readable for (@code Duke} when all * of the following are true: {@code Duke} defines a Java Beans getter for * {@code mother}, {@code Duke's mother} defines a Java Beans getter for * {@code firstName}, {@code Duke} is {@code non-null}, {@code Duke's mother} * is {@code non-null}. The {@code BeanProperty} is therefore unreadable when * any of the following is true: {@code Duke} does not define a Java Beans * getter for {@code mother}, {@code Duke's mother} does not define a Java * Beans getter for {@code firstName}, {@code Duke} is {@code null}, * {@code Duke's mother} is {@code null}. *
* Writeability of a {@code BeanProperty} for a given source is defined as follows: * A {@code BeanProperty} is writeable for a given source if and only if * a) each bean in the path, starting with the source and ending with the bean on * which we set the final property, defines a Java Beans getter method for the * property to be read on it AND b) the bean on which we set the final property * defines a Java Beans setter for the property to be set on it AND c) each bean * in the path, starting with the source and ending with the bean on which we * set the final property, is {@code non-null}. The final value being {@code null} * does not affect the writeability. *
* So, in the second example given earlier, the {@code BeanProperty} is writeable for {@code Duke} when all * of the following are true: {@code Duke} defines a Java Beans getter for * {@code mother}, {@code Duke's mother} defines a Java Beans setter for * {@code firstName}, {@code Duke} is {@code non-null}, {@code Duke's mother} * is {@code non-null}. The {@code BeanProperty} is therefore unreadable when * any of the following is true: {@code Duke} does not define a Java Beans * getter for {@code mother}, {@code Duke's mother} does not define a Java * Beans setter for {@code firstName}, {@code Duke} is {@code null}, * {@code Duke's mother} is {@code null}. *
* In addition to working on Java Beans properties, any object in the path * can be an instance of {@code Map}. In this case, the {@code Map's get} * method is used with the property name as the getter, and the * {@code Map's put} method is used with the property name as the setter. * {@code BeanProperty} can only respond to changes in {@code Maps} * if they are instances of {@link org.jdesktop.observablecollections.ObservableMap}. *
* Some methods in this class document that they can throw
* {@code PropertyResolutionException} if an exception occurs while trying
* to resolve the path. The throwing of this exception represents an abnormal
* condition and if listeners are installed for the given source object,
* leaves the {@code BeanProperty} in an inconsistent state for that source object.
* A {@code BeanProperty} should not be used again for that same source object
* after such an exception without first removing all listeners associated with
* the {@code BeanProperty} for that source object.
*
* @param
* See the class level documentation for the definition of writeability.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws PropertyResolutionException if an exception occurs while resolving the path
* @see #setValue
* @see #isWriteable
*/
public Class extends V> getWriteType(S source) {
SourceEntry entry = map.get(source);
if (entry != null) {
entry.validateCache(-1);
if (entry.cachedWriter == null) {
throw new UnsupportedOperationException("Unwriteable");
}
return (Class extends V>)getType(entry.cache[path.length() - 1], path.getLast());
}
return (Class extends V>)getType(getLastSource(source), path.getLast());
}
/**
* {@inheritDoc}
*
* See the class level documentation for the definition of readability.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws PropertyResolutionException if an exception occurs while resolving the path
* @see #isReadable
*/
public V getValue(S source) {
SourceEntry entry = map.get(source);
if (entry != null) {
entry.validateCache(-1);
if (entry.cachedValue == NOREAD) {
throw new UnsupportedOperationException("Unreadable");
}
return (V)entry.cachedValue;
}
Object src = getLastSource(source);
if (src == null || src == NOREAD) {
throw new UnsupportedOperationException("Unreadable");
}
src = getProperty(src, path.getLast());
if (src == NOREAD) {
log("getValue()", "missing read method");
throw new UnsupportedOperationException("Unreadable");
}
return (V)src;
}
/**
* {@inheritDoc}
*
* See the class level documentation for the definition of writeability.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws PropertyResolutionException if an exception occurs while resolving the path
* @see #isWriteable
* @see #getWriteType
*/
public void setValue(S source, V value) {
SourceEntry entry = map.get(source);
if (entry != null) {
entry.validateCache(-1);
if (entry.cachedWriter == null) {
throw new UnsupportedOperationException("Unwritable");
}
try {
entry.ignoreChange = true;
write(entry.cachedWriter, entry.cache[path.length() - 1], path.getLast(), value);
} finally {
entry.ignoreChange = false;
}
Object oldValue = entry.cachedValue;
entry.updateCachedValue();
notifyListeners(entry.cachedIsWriteable(), oldValue, entry);
} else {
setProperty(getLastSource(source), path.getLast(), value);
}
}
/**
* {@inheritDoc}
*
* See the class level documentation for the definition of readability.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws PropertyResolutionException if an exception occurs while resolving the path
* @see #isWriteable
*/
public boolean isReadable(S source) {
SourceEntry entry = map.get(source);
if (entry != null) {
entry.validateCache(-1);
return entry.cachedIsReadable();
}
Object src = getLastSource(source);
if (src == null || src == NOREAD) {
return false;
}
Object reader = getReader(src, path.getLast());
if (reader == null) {
log("isReadable()", "missing read method");
return false;
}
return true;
}
/**
* {@inheritDoc}
*
* See the class level documentation for the definition of writeability.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws PropertyResolutionException if an exception occurs while resolving the path
* @see #isReadable
*/
public boolean isWriteable(S source) {
SourceEntry entry = map.get(source);
if (entry != null) {
entry.validateCache(-1);
return entry.cachedIsWriteable();
}
Object src = getLastSource(source);
if (src == null || src == NOREAD) {
return false;
}
Object writer = getWriter(src, path.getLast());
if (writer == null) {
log("isWritable()", "missing write method");
return false;
}
return true;
}
private Object getBeanFromSource(S source) {
if (baseProperty == null) {
if (source == null) {
log("getBeanFromSource()", "source is null");
}
return source;
}
if (!baseProperty.isReadable(source)) {
log("getBeanFromSource()", "unreadable source property");
return NOREAD;
}
Object bean = baseProperty.getValue(source);
if (bean == null) {
log("getBeanFromSource()", "source property returned null");
return null;
}
return bean;
}
protected final void listeningStarted(S source) {
SourceEntry entry = map.get(source);
if (entry == null) {
entry = new SourceEntry(source);
map.put(source, entry);
}
}
protected final void listeningStopped(S source) {
SourceEntry entry = map.remove(source);
if (entry != null) {
entry.cleanup();
}
}
private static boolean didValueChange(Object oldValue, Object newValue) {
return oldValue == null || newValue == null || !oldValue.equals(newValue);
}
private void notifyListeners(boolean wasWriteable, Object oldValue, SourceEntry entry) {
PropertyStateListener[] listeners = getPropertyStateListeners(entry.source);
if (listeners == null || listeners.length == 0) {
return;
}
oldValue = toUNREADABLE(oldValue);
Object newValue = toUNREADABLE(entry.cachedValue);
boolean valueChanged = didValueChange(oldValue, newValue);
boolean writeableChanged = (wasWriteable != entry.cachedIsWriteable());
if (!valueChanged && !writeableChanged) {
return;
}
PropertyStateEvent pse = new PropertyStateEvent(this,
entry.source,
valueChanged,
oldValue,
newValue,
writeableChanged,
entry.cachedIsWriteable());
this.firePropertyStateChange(pse);
}
/**
* Returns a string representation of the {@code BeanProperty}. This
* method is intended to be used for debugging purposes only, and
* the content and format of the returned string may vary between
* implementations. The returned string may be empty but may not
* be {@code null}.
*
* @return a string representation of this {@code BeanProperty}
*/
public String toString() {
return getClass().getName() + "[" + path + "]";
}
/**
* @throws PropertyResolutionException
*/
private static BeanInfo getBeanInfo(Object object) {
assert object != null;
try {
// PENDING(shannonh) - not sure about the last flag
return Introspector.getBeanInfo(object.getClass(), Introspector.IGNORE_ALL_BEANINFO);
} catch (IntrospectionException ie) {
throw new PropertyResolutionException("Exception while introspecting " + object.getClass().getName(), ie);
}
}
/**
* @throws PropertyResolutionException
*/
private static PropertyDescriptor getPropertyDescriptor(Object object, String string) {
assert object != null;
PropertyDescriptor[] pds = getBeanInfo(object).getPropertyDescriptors();
if (pds == null) {
return null;
}
for (PropertyDescriptor pd : pds) {
if (!(pd instanceof IndexedPropertyDescriptor) && pd.getName().equals(string)) {
return pd;
}
}
return null;
}
private static EventSetDescriptor getEventSetDescriptor(Object object) {
assert object != null;
EventSetDescriptor[] eds = getBeanInfo(object).getEventSetDescriptors();
for (EventSetDescriptor ed : eds) {
if (ed.getListenerType() == PropertyChangeListener.class) {
return ed;
}
}
return null;
}
/**
* @throws PropertyResolutionException
*/
private static Object invokeMethod(Method method, Object object, Object... args) {
Exception reason = null;
try {
return method.invoke(object, args);
} catch (IllegalArgumentException ex) {
reason = ex;
} catch (IllegalAccessException ex) {
reason = ex;
} catch (InvocationTargetException ex) {
reason = ex;
}
throw new PropertyResolutionException("Exception invoking method " + method + " on " + object, reason);
}
private Object getReader(Object object, String string) {
assert object != null;
if (object instanceof Map) {
return object;
}
object = getAdapter(object, string);
PropertyDescriptor pd = getPropertyDescriptor(object, string);
Method readMethod = null;
return pd == null ? null : pd.getReadMethod();
}
/**
* @throws PropertyResolutionException
*/
private Object read(Object reader, Object object, String string) {
assert reader != null;
if (reader instanceof Map) {
assert reader == object;
return ((Map)reader).get(string);
}
object = getAdapter(object, string);
return invokeMethod((Method)reader, object);
}
/**
* @throws PropertyResolutionException
*/
private Object getProperty(Object object, String string) {
if (object == null || object == NOREAD) {
return NOREAD;
}
Object reader = getReader(object, string);
if (reader == null) {
return NOREAD;
}
return read(reader, object, string);
}
/**
* @throws PropertyResolutionException
*/
private Class> getType(Object object, String string) {
if (object == null || object == NOREAD) {
throw new UnsupportedOperationException("Unwritable");
}
if (object instanceof Map) {
return Object.class;
}
object = getAdapter(object, string);
PropertyDescriptor pd = getPropertyDescriptor(object, string);
if (pd == null || pd.getWriteMethod() == null) {
log("getType()", "missing write method");
throw new UnsupportedOperationException("Unwritable");
}
return pd.getPropertyType();
}
private Object getWriter(Object object, String string) {
assert object != null;
if (object instanceof Map) {
return object;
}
object = getAdapter(object, string);
PropertyDescriptor pd = getPropertyDescriptor(object, string);
Method writeMethod = null;
return pd == null ? null : pd.getWriteMethod();
}
/**
* @throws PropertyResolutionException
*/
private void write(Object writer, Object object, String string, Object value) {
assert writer != null;
if (writer instanceof Map) {
assert writer == object;
((Map)writer).put(string, value);
return;
}
object = getAdapter(object, string);
invokeMethod((Method)writer, object, value);
}
/**
* @throws PropertyResolutionException
* @throws IllegalStateException
*/
private void setProperty(Object object, String string, Object value) {
if (object == null || object == NOREAD) {
throw new UnsupportedOperationException("Unwritable");
}
Object writer = getWriter(object, string);
if (writer == null) {
log("setProperty()", "missing write method");
throw new UnsupportedOperationException("Unwritable");
}
write(writer, object, string, value);
}
private static Object toUNREADABLE(Object src) {
return src == NOREAD ? UNREADABLE : src;
}
private void registerListener(Object object, String property, SourceEntry entry) {
assert object != null;
if (object != NOREAD) {
if (object instanceof ObservableMap) {
((ObservableMap)object).addObservableMapListener(entry);
} else if (!(object instanceof Map)) {
object = getAdapter(object, property);
addPropertyChangeListener(object, entry);
}
}
}
/**
* @throws PropertyResolutionException
*/
private void unregisterListener(Object object, String property, SourceEntry entry) {
if (object != null && object != NOREAD) {
if (object instanceof ObservableMap) {
((ObservableMap)object).removeObservableMapListener(entry);
} else if (!(object instanceof Map)) {
object = getAdapter(object, property);
removePropertyChangeListener(object, entry);
}
}
}
/**
* @throws PropertyResolutionException
*/
private static void addPropertyChangeListener(Object object, PropertyChangeListener listener) {
EventSetDescriptor ed = getEventSetDescriptor(object);
Method addPCMethod = null;
if (ed == null || (addPCMethod = ed.getAddListenerMethod()) == null) {
log("addPropertyChangeListener()", "can't add listener");
return;
}
invokeMethod(addPCMethod, object, listener);
}
/**
* @throws PropertyResolutionException
*/
private static void removePropertyChangeListener(Object object, PropertyChangeListener listener) {
EventSetDescriptor ed = getEventSetDescriptor(object);
Method removePCMethod = null;
if (ed == null || (removePCMethod = ed.getRemoveListenerMethod()) == null) {
log("removePropertyChangeListener()", "can't remove listener from source");
return;
}
invokeMethod(removePCMethod, object, listener);
}
private static boolean wrapsLiteral(Object o) {
assert o != null;
return o instanceof String ||
o instanceof Byte ||
o instanceof Character ||
o instanceof Boolean ||
o instanceof Short ||
o instanceof Integer ||
o instanceof Long ||
o instanceof Float ||
o instanceof Double;
}
// need special match method because when using reflection
// to get a primitive value, the value is always wrapped in
// a new object
private static boolean match(Object a, Object b) {
if (a == b) {
return true;
}
if (a == null) {
return false;
}
if (wrapsLiteral(a)) {
return a.equals(b);
}
return false;
}
private Object getAdapter(Object o, String property) {
Object adapter = null;
adapter = BeanAdapterFactory.getAdapter(o, property);
return adapter == null ? o : adapter;
}
private static final boolean LOG = false;
private static void log(String method, String message) {
if (LOG) {
System.err.println("LOG: " + method + ": " + message);
}
}
}
beansbinding-1.2.1/src/org/jdesktop/beansbinding/Binding.java 0000664 0000000 0000000 00000172456 11516446575 0024234 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.beansbinding;
import java.util.List;
import java.util.ArrayList;
import java.beans.*;
/**
* {@code Binding} is an abstract class that represents the concept of a
* binding between two properties, typically of two objects, and contains
* methods for explicitly syncing the values of the two properties. {@code Binding}
* itself does no automatic syncing between property values. Subclasses
* will typically keep the values in sync according to some strategy.
*
* Some {@code Bindings} are managed, often by another {@code Binding}.
* A managed {@code Binding} does not allow certain methods to be called by
* the user. These methods are identified in their documentation.
* Subclasses should call {@code setManaged(true)} to make themselves managed.
* {@code Binding} provides protected versions of the managed methods with the
* suffix {@code "Unmanaged"} for subclasses to use internally without
* checking whether or not they are managed.
*
* Any {@code PropertyResolutionExceptions} thrown by {@code Property}
* objects used by this binding are allowed to flow through to the caller
* of the {@code Binding} methods.
*
* @param
* {@code Binding} fires a property change notification with
* property name {@code "sourceProperty"} when the value of
* this property changes.
*
* This method may not be called on a bound binding.
*
* @param sourceProperty the source property
* @throws IllegalArgumentException if the source property is {@code null}
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isBound()
*/
protected final void setSourceProperty(Property
* {@code Binding} fires a property change notification with
* property name {@code "targetProperty"} when the value of
* this property changes.
*
* This method may not be called on a bound binding.
*
* @param targetProperty the target property
* @throws IllegalArgumentException if the target property is {@code null}
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isBound()
*/
protected final void setTargetProperty(Property
* {@code Binding} fires a property change notification with
* property name {@code "sourceObject"} when the value of
* this property changes.
*
* This method may not be called on a managed or bound binding.
*
* @param sourceObject the source object, or {@code null}
* @throws UnsupportedOperationException if the {@code Binding} is managed
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isManaged()
* @see #isBound()
*/
public final void setSourceObject(SS sourceObject) {
throwIfManaged();
setSourceObjectUnmanaged(sourceObject);
}
/**
* A protected version of {@link #setSourceObject} that allows managed
* subclasses to set the source object without throwing an exception
* for being managed.
*
* @param sourceObject the source object, or {@code null}
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isManaged()
* @see #isBound()
*/
protected final void setSourceObjectUnmanaged(SS sourceObject) {
throwIfBound();
SS old = this.sourceObject;
this.sourceObject = sourceObject;
firePropertyChange("sourceObject", old, sourceObject);
}
/**
* Sets the {@code Binding's} target object, which may be {@code null}.
*
* {@code Binding} fires a property change notification with
* property name {@code "targetObject"} when the value of
* this property changes.
*
* This method may not be called on a managed or bound binding.
*
* @param targetObject the target object, or {@code null}
* @throws UnsupportedOperationException if the {@code Binding} is managed
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isManaged()
* @see #isBound()
*/
public final void setTargetObject(TS targetObject) {
throwIfManaged();
setTargetObjectUnmanaged(targetObject);
}
/**
* A protected version of {@link #setTargetObject} that allows managed
* subclasses to set the target object without throwing an exception
* for being managed.
*
* @param targetObject the target object, or {@code null}
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isManaged()
* @see #isBound()
*/
protected final void setTargetObjectUnmanaged(TS targetObject) {
throwIfBound();
TS old = this.targetObject;
this.targetObject = targetObject;
firePropertyChange("targetObject", old, targetObject);
}
/**
* Sets the {@code Validator} for the {@code Binding}, which may be {@code null}.
*
* {@code Binding} fires a property change notification with
* property name {@code "validator"} when the value of
* this property changes.
*
* This method may not be called on a bound binding.
*
* See the documentation on {@link #getTargetValueForSource} for details on how
* a {@code Binding's Validator} is used.
*
* @param validator the {@code Validator}, or {@code null}
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isBound()
*/
public final void setValidator(Validator super SV> validator) {
throwIfBound();
Validator super SV> old = this.validator;
this.validator = validator;
firePropertyChange("validator", old, validator);
}
/**
* Returns the {@code Binding's Validator}, which may be {@code null}.
*
* @return the {@code Binding's Validator}, or {@code null}
* @see #setValidator
*/
public final Validator super SV> getValidator() {
return validator;
}
/**
* Sets the {@code Converter} for the {@code Binding}, which may be {@code null}.
*
* {@code Binding} fires a property change notification with
* property name {@code "converter"} when the value of
* this property changes.
*
* This method may not be called on a bound binding.
*
* See the documentation on {@link #getTargetValueForSource} and
* {@link #getSourceValueForTarget} for details on how
* a {@code Binding's Converter} is used.
*
* @param converter the {@code Converter}, or {@code null}
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isBound()
*/
public final void setConverter(Converter
* {@code Binding} fires a property change notification with
* property name {@code "sourceNullValue"} when the value of
* this property changes.
*
* This method may not be called on a bound binding.
*
* @param sourceNullValue the value, or {@code null}
* @throws IllegalStateException if the {@code Binding} is bound
*/
public final void setSourceNullValue(TV sourceNullValue) {
throwIfBound();
TV old = this.sourceNullValue;
this.sourceNullValue = sourceNullValue;
firePropertyChange("sourceNullValue", old, sourceNullValue);
}
/**
* Returns the value to be returned by {@link #getSourceValueForTarget}
* when the source property returns {@code null} for the source object.
* The default for this property is {@code null}.
*
* @return the value that replaces a source value of {@code null}, or {@code null}
* if there is no replacement
* @see #setSourceNullValue
*/
public final TV getSourceNullValue() {
return sourceNullValue;
}
/**
* Sets the value to be returned by {@link #getTargetValueForSource}
* when the target property returns {@code null} for the target object.
* The default for this property is {@code null}.
*
* {@code Binding} fires a property change notification with
* property name {@code "targetNullValue"} when the value of
* this property changes.
*
* This method may not be called on a bound binding.
*
* @param targetNullValue the value, or {@code null}
* @throws IllegalStateException if the {@code Binding} is bound
*/
public final void setTargetNullValue(SV targetNullValue) {
throwIfBound();
SV old = this.targetNullValue;
this.targetNullValue = targetNullValue;
firePropertyChange("targetNullValue", old, targetNullValue);
}
/**
* Returns the value to be returned by {@link #getTargetValueForSource}
* when the target property returns {@code null} for the target object.
* The default for this property is {@code null}.
*
* @return the value that replaces a target value of {@code null}, or {@code null}
* if there is no replacement
* @see #setTargetNullValue
*/
public final SV getTargetNullValue() {
return targetNullValue;
}
/**
* Sets the value to be returned by {@link #getSourceValueForTarget}
* when the source property is unreadable for the source object.
* Calling this method stores the given value and indicates that
* {@code getSourceValueForTarget} should use it, by setting the
* {@code sourceUnreadableValueSet} property to {@code true}.
*
* By default, the {@code sourceUnreadableValue} property is unset,
* indicated by the {@code sourceUnreadableValueSet} property being
* {@code false}.
*
* Setting this property to {@code null} acts the same as setting it to
* any other value. To return the property to the unset state (clearing
* the value and setting {@code sourceUnreadableValueSet} back to
* {@code false}) call {@link #unsetSourceUnreadableValue}.
*
* If this property was previously unset, this method fires a property
* change notification with property name {@code "sourceUnreadableValueSet"}.
* For all invocations, it also fires a property change notification with
* property name {@code "sourceUnreadableValue"}, if necessary, to indicate
* a change in the property value. If previously unset, the event will
* indicate an old value of {@code null}.
*
* This method may not be called on a bound binding.
*
* @param sourceUnreadableValue the value, which may be {@code null}
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isSourceUnreadableValueSet
* @see #getSourceUnreadableValue
*/
public final void setSourceUnreadableValue(TV sourceUnreadableValue) {
throwIfBound();
TV old = this.sourceUnreadableValue;
boolean oldSet = this.sourceUnreadableValueSet;
this.sourceUnreadableValue = sourceUnreadableValue;
this.sourceUnreadableValueSet = true;
firePropertyChange("sourceUnreadableValueSet", oldSet, true);
firePropertyChange("sourceUnreadableValue", old, sourceUnreadableValue);
}
/**
* Unsets the value of the {@code sourceUnreadableValue} property by clearing
* the value and setting the value of the {@code sourceUnreadableValueSet}
* property to {@code false}.
*
* If the property was previously set, fires a property change notification
* with property name {@code "sourceUnreadableValueSet"}, and a property
* change notification with property name {@code "sourceUnreadableValue"}.
* The event for the latter notification will have a new value of {@code null}.
*
* See the documentation for {@link #setSourceUnreadableValue} for more
* information on the {@code sourceUnreadableValue} property.
*
* This method may not be called on a bound binding.
*
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isSourceUnreadableValueSet
* @see #getSourceUnreadableValue
*/
public final void unsetSourceUnreadableValue() {
throwIfBound();
if (isSourceUnreadableValueSet()) {
TV old = this.sourceUnreadableValue;
this.sourceUnreadableValue = null;
this.sourceUnreadableValueSet = false;
firePropertyChange("sourceUnreadableValueSet", true, false);
firePropertyChange("sourceUnreadableValue", old, null);
}
}
/**
* Returns the value of the {@code sourceUnreadableValueSet} property,
* which indicates whether or not the {@code sourceUnreadableValue} property
* is set on the {@code Binding}.
*
* See the documentation for {@link #setSourceUnreadableValue} for more
* information on the {@code sourceUnreadableValue} property.
*
* @return whether or not the {@code sourceUnreadableValue} property
* is set on the {@code Binding}
* @see #unsetSourceUnreadableValue
* @see #getSourceUnreadableValue
*/
public final boolean isSourceUnreadableValueSet() {
return sourceUnreadableValueSet;
}
/**
* If set, returns the value to be returned by {@link #getSourceValueForTarget}
* when the source property is unreadable for the source object. Throws
* {@code UnsupportedOperationException} if the property is not set,
* as indicated by {@link #isSourceUnreadableValueSet}.
*
* See the documentation for {@link #setSourceUnreadableValue} for more
* information on this property.
*
* @return the value that replaces an unreadable source value, which may
* be {@code null}
* @see #unsetSourceUnreadableValue
* @throws UnsupportedOperationException if the property is not set,
* as indicated by {@code isSourceUnreadableValueSet}
*/
public final TV getSourceUnreadableValue() {
if (!isSourceUnreadableValueSet()) {
throw new UnsupportedOperationException("not set");
}
return sourceUnreadableValue;
}
/**
* Adds a {@code BindingListener} to be notified of changes to this {@code Binding}.
* Does nothing if the listener is {@code null}. If a listener is added more than once,
* notifications are sent to that listener once for every time that it has
* been added. The ordering of listener notification is unspecified.
*
* @param listener the listener to add
*/
public final void addBindingListener(BindingListener listener) {
if (listener == null) {
return;
}
if (listeners == null) {
listeners = new ArrayList
* First, if the target property is not writeable for the target object,
* a {@code ValueResult} is returned representing a failure
* with failure type {@code SyncFailureType.TARGET_UNWRITEABLE}.
* Then, if the source property is unreadable for the source object,
* the value of {@link #isSourceUnreadableValueSet} is checked. If {@code true}
* then a {@code ValueResult} is returned containing the value of the
* {@code Binding's} {@link #getSourceUnreadableValue}. Otherwise a
* {@code ValueResult} is returned representing a failure with failure
* type {@code SyncFailureType.SOURCE_UNREADABLE}.
*
* Next, the value of the source property is fetched for the source
* object. If the value is {@code null}, a {@code ValueResult} is
* returned containing the value of the {@code Binding's}
* {@link #getSourceNullValue}. If the value is {@code non-null},
* the {@code Binding's Converter}, if any, is run to convert
* the value from source type to the target property's
* {@code getWriteType}, by calling its {@code convertForward}
* method with the value. If no {@code Converter} is registered,
* a set of default converters is checked to see if one of them
* can convert the value to the target type. Finally, the value
* (converted or not) is cast to the target write type.
*
* This final value is returned in a {@code ValueResult}.
*
* Any {@code RuntimeException} or {@code ClassCastException} thrown by a
* converter or the final cast is propogated up to the caller of this method.
*
* @return a {@code ValueResult} as described above
* @throws RuntimeException if thrown by any of the converters
* @throws ClassCastException if thrown by a converter or the final cast
*/
public final ValueResult
* First, if the source property is not writeable for the source object,
* a {@code ValueResult} is returned representing a failure
* with failure type {@code SyncFailureType.SOURCE_UNWRITEABLE}.
* Then, if the target property is not readable for the target object,
* a {@code ValueResult} is returned representing a failure
* with failure type {@code SyncFailureType.TARGET_UNREADABLE}.
*
* Next, the value of the target property is fetched for the target
* object. If the value is {@code null}, a {@code ValueResult} is
* returned containing the value of the {@code Binding's}
* {@link #getTargetNullValue}. If the value is {@code non-null},
* the {@code Binding's Converter}, if any, is run to convert
* the value from target type to the source property's
* {@code getWriteType}, by calling its {@code convertReverse}
* method with the value. If no {@code Converter} is registered,
* a set of default converters is checked to see if one of them
* can convert the value to the source type. Finally, the value
* (converted or not) is cast to the source write type.
*
* If a converter throws a {@code RuntimeException} other than
* {@code ClassCastException}, this method returns a
* {@code ValueResult} containing the failure, with failure type
* {@code SyncFailureType.CONVERSION_FAILURE}.
*
* As the last step, the {@code Binding's Validator}, if any, is called
* upon to validate the final value. If the {@code Validator}
* returns {@code non-null} from its {@code validate} method,
* a {@code ValueResult} is returned containing the validation
* result, with failure type {@code SyncFailureType.VALIDATION_FAILURE}.
* Otherwise a {@code ValueResult} is returned containing the
* final validated value.
*
* Any {@code ClassCastException} thrown by a converter or the final
* cast is propogated up to the caller of this method.
*
* @return a {@code ValueResult} as described above
* @throws ClassCastException if thrown by a converter or the final cast
*/
public final ValueResult
* {@code Binding} fires a property change notification with
* property name {@code "bound"} when the value of
* this property changes.
*
* @return whether or not the {@code Binding} is bound
* @see #bind
* @see #unbind
*/
public final boolean isBound() {
return isBound;
}
/**
* Sets whether or not this {@code Binding} is managed. Some
* {@code Bindings} are managed, often by another {@code Binding}.
* A managed {@code Binding} does not allow certain methods to be called by
* the user. These methods are identified in their documentation.
* Subclasses should call {@code setManaged(true)} to make themselves managed.
* {@code Binding} provides protected versions of the managed methods, with the
* suffix {@code "Unmanaged"}, for subclasses to use internally without
* checking whether or not they are managed.
*/
protected final void setManaged(boolean isManaged) {
this.isManaged = isManaged;
}
/**
* Returns whether or not this {@code Binding} is managed. Some
* {@code Bindings} are managed, often by another {@code Binding}.
* A managed {@code Binding} does not allow certain methods to be called by
* the user. These methods are identified in their documentation.
* Subclasses should call {@code setManaged(true)} to make themselves managed.
* {@code Binding} provides protected versions of the managed methods, with the
* suffix {@code "Unmanaged"}, for subclasses to use internally without
* checking whether or not they are managed.
*
* @return whether or not the {@code Binding} is managed
* @see #setManaged
*/
public final boolean isManaged() {
return isManaged;
}
/**
* Notifies all registered {@code BindingListeners} of a successful
* sync ({@code refresh} or {@code save}), by calling {@code synced}
* on each one.
*/
protected final void notifySynced() {
if (listeners == null) {
return;
}
for (BindingListener listener : listeners) {
listener.synced(this);
}
}
/**
* Notifies all registered {@code BindingListeners} of a failure to
* sync ({@code refresh} or {@code save}), by calling
* {@code syncFailed} on each one.
*
* @param failure the reason that the sync failed
*/
protected final void notifySyncFailed(SyncFailure failure) {
if (listeners == null) {
return;
}
for (BindingListener listener : listeners) {
listener.syncFailed(this, failure);
}
}
private final SyncFailure notifyAndReturn(SyncFailure failure) {
if (failure == null) {
notifySynced();
} else {
notifySyncFailed(failure);
}
return failure;
}
/**
* The same as {@link #refresh} with the additional
* behavior of notifying all registered {@code BindingListeners}
* with {@code synced} if {@code refresh} returns {@code null}
* or {@code syncFailed} if {@code refresh} returns a
* {@code SyncFailure}.
*
* @return the return value from the call to {@code refresh}
* @throws UnsupportedOperationException if the {@code Binding} is managed
* @throws RuntimeException as specified by {@link #refresh}
* @throws ClassCastException as specified by {@link #refresh}
* @see #isManaged()
*/
public final SyncFailure refreshAndNotify() {
return notifyAndReturn(refresh());
}
/**
* A protected version of {@link #refreshAndNotify} that allows managed
* subclasses to refresh and notify without throwing an exception
* for being managed.
*
* @return the return value from the call to {@code refresh}
* @throws RuntimeException as specified by {@link #refresh}
* @throws ClassCastException as specified by {@link #refresh}
* @see #isManaged()
*/
protected final SyncFailure refreshAndNotifyUnmanaged() {
return notifyAndReturn(refreshUnmanaged());
}
/**
* The same as {@link #save} with the additional
* behavior of notifying all registered {@code BindingListeners}
* with {@code synced} if {@code save} returns {@code null}
* or {@code syncFailed} if {@code save} returns a
* {@code SyncFailure}.
*
* @return the return value from the call to {@code save}
* @throws UnsupportedOperationException if the {@code Binding} is managed
* @throws ClassCastException as specified by {@link #refresh}
* @see #isManaged()
*/
public final SyncFailure saveAndNotify() {
return notifyAndReturn(save());
}
/**
* A protected version of {@link #saveAndNotify} that allows managed
* subclasses to save and notify without throwing an exception
* for being managed.
*
* @return the return value from the call to {@code save}
* @throws ClassCastException as specified by {@link #save}
* @see #isManaged()
*/
protected final SyncFailure saveAndNotifyUnmanaged() {
return notifyAndReturn(saveUnmanaged());
}
/**
* Fetches the value of the source property for the source object and sets
* it as the value of the target property for the target object.
* First calls {@link #getSourceValueForTarget}. If the return value
* from that method represents a failure, this method returns the failure.
* Otherwise, it calls {@code setValue} on the target property for the
* target object with the value obtained from the source.
*
* @return the reason for failure if the binding could not be refreshed,
* or {@code null} for success
* @throws UnsupportedOperationException if the {@code Binding} is managed
* @throws RuntimeException if thrown by {@link #getSourceValueForTarget}
* @throws ClassCastException if thrown by {@link #getSourceValueForTarget}
* @see #isManaged()
* @see #save
*/
public final SyncFailure refresh() {
throwIfManaged();
return refreshUnmanaged();
}
/**
* A protected version of {@link #refresh} that allows managed
* subclasses to refresh without throwing an exception
* for being managed.
*
* @return the reason for failure if the binding could not be refreshed,
* or {@code null} for success
* @throws RuntimeException if thrown by {@link #getSourceValueForTarget}
* @throws ClassCastException if thrown by {@link #getSourceValueForTarget}
* @see #isManaged()
*/
protected final SyncFailure refreshUnmanaged() {
ValueResult
* {@code Binding} fires property change notification for the following
* properties:
*
*
* For other types of {@code Binding} notifications register a
* {@code BindingListener}.
*
* @param listener the listener to add
* @see #addBindingListener
*/
public final void addPropertyChangeListener(PropertyChangeListener listener) {
if (changeSupport == null) {
changeSupport = new PropertyChangeSupport(this);
}
changeSupport.addPropertyChangeListener(listener);
}
/**
* Adds a {@code PropertyChangeListener} to be notified when the property identified
* by the {@code propertyName} argument changes on this {@code Binding}.
* Does nothing if the property name or listener is {@code null}.
* If a listener is added more than once, notifications are
* sent to that listener once for every time that it has been added.
* The ordering of listener notification is unspecified.
*
* {@code Binding} fires property change notification for the following
* properties:
*
*
* For other types of {@code Binding} notifications register a
* {@code BindingListener}.
*
* @param propertyName the name of the property to listen for changes on
* @param listener the listener to add
*/
public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
if (changeSupport == null) {
changeSupport = new PropertyChangeSupport(this);
}
changeSupport.addPropertyChangeListener(propertyName, listener);
}
/**
* Removes a {@code PropertyChangeListener} from the {@code Binding}. Does
* nothing if the listener is {@code null} or is not one of those registered.
* If the listener being removed was registered more than once, only one
* occurrence of the listener is removed from the list of listeners.
* The ordering of listener notification is unspecified.
*
* @param listener the listener to remove
* @see #addPropertyChangeListener
*/
public final void removePropertyChangeListener(PropertyChangeListener listener) {
if (changeSupport == null) {
return;
}
changeSupport.removePropertyChangeListener(listener);
}
/**
* Removes a {@code PropertyChangeListener} from the {@code Binding} for the given
* property name. Does nothing if the property name or listener is
* {@code null} or the listener is not one of those registered.
* If the listener being removed was registered more than once, only one
* occurrence of the listener is removed from the list of listeners.
* The ordering of listener notification is unspecified.
*
* @param propertyName the name of the property to remove the listener for
* @param listener the listener to remove
* @see #addPropertyChangeListener(String, PropertyChangeListener)
*/
public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
if (changeSupport == null) {
return;
}
changeSupport.removePropertyChangeListener(propertyName, listener);
}
/**
* Returns the list of {@code PropertyChangeListeners} registered on this
* {@code Binding}. Order is undefined. Returns an empty array if there are
* no listeners.
*
* @return the list of {@code PropertyChangeListeners} registered on this {@code Binding}
* @see #addPropertyChangeListener
*/
public final PropertyChangeListener[] getPropertyChangeListeners() {
if (changeSupport == null) {
return new PropertyChangeListener[0];
}
return changeSupport.getPropertyChangeListeners();
}
/**
* Returns the list of {@code PropertyChangeListeners} registered on this
* {@code Binding} for the given property name. Order is undefined. Returns an empty array
* if there are no listeners registered for the property name.
*
* @param propertyName the property name to retrieve the listeners for
* @return the list of {@code PropertyChangeListeners} registered on this {@code Binding}
* for the given property name
* @see #addPropertyChangeListener(String, PropertyChangeListener)
*/
public final PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
if (changeSupport == null) {
return new PropertyChangeListener[0];
}
return changeSupport.getPropertyChangeListeners(propertyName);
}
/**
* Sends a {@code PropertyChangeEvent} to the {@code PropertyChangeListeners}
* registered on the {@code Binding}.
*
* @param propertyName the name of the property that's changed
* @param oldValue the old value of the property
* @param newValue the new value of the property
*/
protected final void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
if (changeSupport != null) {
changeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
}
private class PSL implements PropertyStateListener {
public void propertyStateChanged(PropertyStateEvent pse) {
if (ignoreChange) {
return;
}
if (pse.getSourceProperty() == sourceProperty && pse.getSourceObject() == sourceObject) {
sourceChanged(pse);
} else {
targetChanged(pse);
}
}
}
}
beansbinding-1.2.1/src/org/jdesktop/beansbinding/BindingGroup.java 0000664 0000000 0000000 00000021746 11516446575 0025244 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.beansbinding;
import java.util.*;
/**
* {@code BindingGroup} allows you to create a group of {@code Bindings}
* and operate on and/or track state changes to the {@code Bindings} as
* a group.
*
* @author Shannon Hickey
*/
public class BindingGroup {
private final List
* The conversion methods can throw {@code RuntimeExceptions} in response
* to a problem in conversion. For example, a {@code String} to {@code Integer}
* converter might throw a {@code NumberFormatException} if the {@code String}
* can't be parsed properly into an {@code Integer}.
*
* @param
*
* To create a property representing the concatenation of a {@code Person} bean's
* {@code firstName} and {@code lastName} properties:
*
*
* To create a property that is {@code true} or {@code false} depending
* on whether or not the {@code Person's} mother is older than 65:
*
*
* Paths specified in the EL expressions are resolved against the source object
* with which the property is being used.
*
* An instance of {@code ELProperty} is immutable and can be used with
* different source objects. When a {@code PropertyStateListener} is added to
* an {@code ELProperty} for a given source object, the {@code ELProperty}
* starts listening to all objects along the paths in the expression (based on that source object)
* for change notification, and reflects any changes by notifying the
* listener associated with the property for that source object. So, for example,
* if a {@code PropertyStateListener} is added to the property from the second example above
* for an object {@code Duke}, the {@code PropertyStateListener} is
* notified when either {@code Duke's} first name changes, or his last name changes.
* If a listener is added to the property from the third example, the {@code PropertyStateListener}
* is notified when either a change in {@code Duke's} mother or {@code Duke's} mother's {@code age}
* results in a change to the result of the expression.
*
* It is very important that any bean properties addressed via a {@code ELProperty}
* follow the Java Beans specification, including firing property change notification;
* otherwise, {@code ELProperty} cannot respond to change. As some beans outside
* of your control may not follow the Java Beans specification, {@code ELProperty}
* always checks the {@link org.jdesktop.beansbinding.ext.BeanAdapterFactory} to
* see if a delegate provider has been registered to provide a delegate bean to take
* the place of an object for a given property. See the
* ext package level documentation for more
* details.
*
* When there are no {@code PropertyStateListeners} installed on an {@code ELProperty}
* for a given source, all {@code Property} methods act by evaluating the full expression,
* thereby always providing "live" information.
* On the contrary, when there are {@code PropertyStateListeners} installed, the beans
* along the paths, and the final value, are cached, and only updated upon
* notification of change from a bean. Again, this makes it very important that any
* bean property that could change along the path fires property change notification.
* Note: The {@code setValue} method is currently excluded from the previous
* assertion; with the exception of checking the cache to determine if the property is
* writeable, it always evaluates the entire expression. The result of this is that
* when working with paths containing beans that don't fire property change notification,
* you can end up with all methods (including {@code getValue}) working on cached
* information, but {@code setValue} working on the live expression. There are plans
* to resolve this inconsistency in a future release.
*
* Readability of an {@code ELProperty} for a given source is defined as follows:
* An {@code ELProperty} is readable for a given source if and only if the
* following is true for all paths used in the expression:
* a) each bean the path, starting with the source, defines a Java Beans getter
* method for the the property to be read on it AND b) each bean in the path,
* starting with the source and ending with the bean on which we read the final
* property, is {@code non-null}. The final value being {@code null} does not
* affect the readability.
*
* So, in the third example given earlier, the {@code ELProperty} is readable for {@code Duke} when all
* of the following are true: {@code Duke} defines a Java Beans getter for
* {@code mother}, {@code Duke's mother} defines a Java Beans getter for
* {@code age}, {@code Duke} is {@code non-null}, {@code Duke's mother}
* is {@code non-null}. The {@code ELProperty} is therefore unreadable when
* any of the following is true: {@code Duke} does not define a Java Beans
* getter for {@code mother}, {@code Duke's mother} does not define a Java
* Beans getter for {@code age}, {@code Duke} is {@code null},
* {@code Duke's mother} is {@code null}.
*
* Writeability of an {@code ELProperty} for a given source is defined as follows:
* An {@code ELProperty} is writeable for a given source if and only if
* a) the EL expression itself is not read-only
* (ie. it is a simple expression involving one path such as "${foo.bar.baz}" AND
* b) each bean in the path, starting with the source and ending with the bean on
* which we set the final property, defines a Java Beans getter method for the
* property to be read on it AND c) the bean on which we set the final property
* defines a Java Beans setter for the property to be set on it AND d) each bean
* in the path, starting with the source and ending with the bean on which we
* set the final property, is {@code non-null}. The final value being {@code null}
* does not affect the writeability.
*
* So in the first example given earlier (a simple path), the {@code ELProperty}
* is writeable for {@code Duke} when all of the following are true: {@code Duke} defines a Java Beans getter for
* {@code mother}, {@code Duke's mother} defines a Java Beans setter for
* {@code firstName}, {@code Duke} is {@code non-null}, {@code Duke's mother}
* is {@code non-null}. The {@code ELProperty} is therefore unreadable when
* any of the following is true: {@code Duke} does not define a Java Beans
* getter for {@code mother}, {@code Duke's mother} does not define a Java
* Beans setter for {@code firstName}, {@code Duke} is {@code null},
* {@code Duke's mother} is {@code null}. The second and third examples above
* both represent read-only ELExpressions and are therefore unwritable.
*
* In addition to working on Java Beans properties, any object in the paths
* can be an instance of {@code Map}. In this case, the {@code Map's get}
* method is used with the property name as the getter, and the
* {@code Map's put} method is used with the property name as the setter.
* {@code ELProperty} can only respond to changes in {@code Maps}
* if they are instances of {@link org.jdesktop.observablecollections.ObservableMap}.
*
* Some methods in this class document that they can throw
* {@code PropertyResolutionException} if an exception occurs while trying
* to evaluate the expression. The throwing of this exception represents an abnormal
* condition and if listeners are installed for the given source object,
* leaves the {@code ELProperty} in an inconsistent state for that source object.
* An {@code ELProperty} should not be used again for that same source object
* after such an exception without first removing all listeners associated with
* the {@code ELProperty} for that source object.
*
* @param
* See the class level documentation for the definition of writeability.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws PropertyResolutionException if an exception occurs while evaluating the expression
* @see #setValue
* @see #isWriteable
*/
public Class extends V> getWriteType(S source) {
SourceEntry entry = map.get(source);
if (entry != null) {
entry.validateCache(-1);
if (!entry.cachedIsWriteable) {
throw new UnsupportedOperationException("Unwriteable");
}
return (Class extends V>)entry.cachedWriteType;
}
try {
expression.setSource(getBeanFromSource(source, true));
Expression.Result result = expression.getResult(context, false);
if (result.getType() == Expression.Result.Type.UNRESOLVABLE) {
log("getWriteType()", "expression is unresolvable");
throw new UnsupportedOperationException("Unwriteable");
}
if (expression.isReadOnly(context)) {
log("getWriteType()", "property is unwriteable");
throw new UnsupportedOperationException("Unwriteable");
}
return (Class extends V>)expression.getType(context);
} catch (ELException ele) {
throw new PropertyResolutionException("Error evaluating EL expression " + expression + " on " + source, ele);
} finally {
expression.setSource(null);
}
}
/**
* {@inheritDoc}
*
* See the class level documentation for the definition of readability.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws PropertyResolutionException if an exception occurs while evaluating the expression
* @see #isReadable
*/
public V getValue(S source) {
SourceEntry entry = map.get(source);
if (entry != null) {
entry.validateCache(-1);
if (entry.cachedValue == NOREAD) {
throw new UnsupportedOperationException("Unreadable");
}
return (V)entry.cachedValue;
}
try {
expression.setSource(getBeanFromSource(source, true));
Expression.Result result = expression.getResult(context, false);
if (result.getType() == Expression.Result.Type.UNRESOLVABLE) {
log("getValue()", "expression is unresolvable");
throw new UnsupportedOperationException("Unreadable");
}
return (V)result.getResult();
} catch (ELException ele) {
throw new PropertyResolutionException("Error evaluating EL expression " + expression + " on " + source, ele);
} finally {
expression.setSource(null);
}
}
/**
* {@inheritDoc}
*
* See the class level documentation for the definition of writeability.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws PropertyResolutionException if an exception occurs while evaluating the expression
* @see #isWriteable
* @see #getWriteType
*/
public void setValue(S source, V value) {
SourceEntry entry = map.get(source);
if (entry != null) {
entry.validateCache(-1);
if (!entry.cachedIsWriteable) {
throw new UnsupportedOperationException("Unwritable");
}
try {
entry.ignoreChange = true;
expression.setSource(getBeanFromSource(source, false));
expression.setValue(context, value);
} catch (ELException ele) {
throw new PropertyResolutionException("Error evaluating EL expression " + expression + " on " + source, ele);
} finally {
entry.ignoreChange = false;
expression.setSource(null);
}
Object oldValue = entry.cachedValue;
// PENDING(shannonh) - too heavyweight; should just update cached value
entry.updateCache();
notifyListeners(entry.cachedIsWriteable, oldValue, entry);
return;
}
try {
expression.setSource(getBeanFromSource(source, true));
Expression.Result result = expression.getResult(context, false);
if (result.getType() == Expression.Result.Type.UNRESOLVABLE) {
log("setValue()", "expression is unresolvable");
throw new UnsupportedOperationException("Unwriteable");
}
if (expression.isReadOnly(context)) {
log("setValue()", "property is unwriteable");
throw new UnsupportedOperationException("Unwriteable");
}
expression.setValue(context, value);
} catch (ELException ele) {
throw new PropertyResolutionException("Error evaluating EL expression " + expression + " on " + source, ele);
} finally {
expression.setSource(null);
}
}
/**
* {@inheritDoc}
*
* See the class level documentation for the definition of readability.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws PropertyResolutionException if an exception occurs while evaluating the expression
* @see #isWriteable
*/
public boolean isReadable(S source) {
SourceEntry entry = map.get(source);
if (entry != null) {
entry.validateCache(-1);
return entry.cachedIsReadable();
}
try {
expression.setSource(getBeanFromSource(source, true));
Expression.Result result = expression.getResult(context, false);
if (result.getType() == Expression.Result.Type.UNRESOLVABLE) {
log("isReadable()", "expression is unresolvable");
return false;
}
return true;
} catch (ELException ele) {
throw new PropertyResolutionException("Error evaluating EL expression " + expression + " on " + source, ele);
} finally {
expression.setSource(null);
}
}
/**
* {@inheritDoc}
*
* See the class level documentation for the definition of writeability.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws PropertyResolutionException if an exception occurs while evaluating the expression
* @see #isReadable
*/
public boolean isWriteable(S source) {
SourceEntry entry = map.get(source);
if (entry != null) {
entry.validateCache(-1);
return entry.cachedIsWriteable;
}
try {
expression.setSource(getBeanFromSource(source, true));
Expression.Result result = expression.getResult(context, false);
if (result.getType() == Expression.Result.Type.UNRESOLVABLE) {
log("isWriteable()", "expression is unresolvable");
return false;
}
if (expression.isReadOnly(context)) {
log("isWriteable()", "property is unwriteable");
return false;
}
return true;
} catch (ELException ele) {
throw new PropertyResolutionException("Error evaluating EL expression " + expression + " on " + source, ele);
} finally {
expression.setSource(null);
}
}
private Object getBeanFromSource(S source, boolean logErrors) {
if (baseProperty == null) {
if (source == null) {
if (logErrors) {
log("getBeanFromSource()", "source is null");
}
}
return source;
}
if (!baseProperty.isReadable(source)) {
if (logErrors) {
log("getBeanFromSource()", "unreadable source property");
}
return NOREAD;
}
Object bean = baseProperty.getValue(source);
if (bean == null) {
if (logErrors) {
log("getBeanFromSource()", "source property returned null");
}
return null;
}
return bean;
}
protected final void listeningStarted(S source) {
SourceEntry entry = map.get(source);
if (entry == null) {
entry = new SourceEntry(source);
map.put(source, entry);
}
}
protected final void listeningStopped(S source) {
SourceEntry entry = map.remove(source);
if (entry != null) {
entry.cleanup();
}
}
private static boolean didValueChange(Object oldValue, Object newValue) {
return oldValue == null || newValue == null || !oldValue.equals(newValue);
}
private void notifyListeners(boolean wasWriteable, Object oldValue, SourceEntry entry) {
PropertyStateListener[] listeners = getPropertyStateListeners(entry.source);
if (listeners == null || listeners.length == 0) {
return;
}
oldValue = toUNREADABLE(oldValue);
Object newValue = toUNREADABLE(entry.cachedValue);
boolean valueChanged = didValueChange(oldValue, newValue);
boolean writeableChanged = (wasWriteable != entry.cachedIsWriteable);
if (!valueChanged && !writeableChanged) {
return;
}
PropertyStateEvent pse = new PropertyStateEvent(this,
entry.source,
valueChanged,
oldValue,
newValue,
writeableChanged,
entry.cachedIsWriteable);
this.firePropertyStateChange(pse);
}
/**
* Returns a string representation of the {@code ELProperty}. This
* method is intended to be used for debugging purposes only, and
* the content and format of the returned string may vary between
* implementations. The returned string may be empty but may not
* be {@code null}.
*
* @return a string representation of this {@code ELProperty}
*/
public String toString() {
return getClass().getName() + "[" + expression + "]";
}
/**
* @throws PropertyResolutionException
*/
private static BeanInfo getBeanInfo(Object object) {
assert object != null;
try {
return Introspector.getBeanInfo(object.getClass(), Introspector.IGNORE_ALL_BEANINFO);
} catch (IntrospectionException ie) {
throw new PropertyResolutionException("Exception while introspecting " + object.getClass().getName(), ie);
}
}
private static EventSetDescriptor getEventSetDescriptor(Object object) {
assert object != null;
EventSetDescriptor[] eds = getBeanInfo(object).getEventSetDescriptors();
for (EventSetDescriptor ed : eds) {
if (ed.getListenerType() == PropertyChangeListener.class) {
return ed;
}
}
return null;
}
/**
* @throws PropertyResolutionException
*/
private static Object invokeMethod(Method method, Object object, Object... args) {
Exception reason = null;
try {
return method.invoke(object, args);
} catch (IllegalArgumentException ex) {
reason = ex;
} catch (IllegalAccessException ex) {
reason = ex;
} catch (InvocationTargetException ex) {
reason = ex;
}
throw new PropertyResolutionException("Exception invoking method " + method + " on " + object, reason);
}
private static Object toUNREADABLE(Object src) {
return src == NOREAD ? UNREADABLE : src;
}
private void registerListener(ResolvedProperty resolved, SourceEntry entry) {
Object source = resolved.getSource();
Object property = resolved.getProperty();
if (source != null && property instanceof String) {
String sProp = (String)property;
if (source instanceof ObservableMap) {
RegisteredListener rl = new RegisteredListener(source, sProp);
if (!entry.registeredListeners.contains(rl)) {
if (!entry.lastRegisteredListeners.remove(rl)) {
((ObservableMap)source).addObservableMapListener(entry);
}
entry.registeredListeners.add(rl);
}
} else if (!(source instanceof Map)) {
source = getAdapter(source, sProp);
RegisteredListener rl = new RegisteredListener(source, sProp);
if (!entry.registeredListeners.contains(rl)) {
if (!entry.lastRegisteredListeners.remove(rl)) {
addPropertyChangeListener(source, entry);
}
entry.registeredListeners.add(rl);
}
}
}
}
private void unregisterListener(RegisteredListener rl, SourceEntry entry) {
Object source = rl.getSource();
if (source instanceof ObservableMap) {
((ObservableMap)source).removeObservableMapListener(entry);
} else if (!(source instanceof Map)) {
removePropertyChangeListener(source, entry);
}
}
/**
* @throws PropertyResolutionException
*/
private static void addPropertyChangeListener(Object object, PropertyChangeListener listener) {
EventSetDescriptor ed = getEventSetDescriptor(object);
Method addPCMethod = null;
if (ed == null || (addPCMethod = ed.getAddListenerMethod()) == null) {
log("addPropertyChangeListener()", "can't add listener");
return;
}
invokeMethod(addPCMethod, object, listener);
}
/**
* @throws PropertyResolutionException
*/
private static void removePropertyChangeListener(Object object, PropertyChangeListener listener) {
EventSetDescriptor ed = getEventSetDescriptor(object);
Method removePCMethod = null;
if (ed == null || (removePCMethod = ed.getRemoveListenerMethod()) == null) {
log("removePropertyChangeListener()", "can't remove listener from source");
return;
}
invokeMethod(removePCMethod, object, listener);
}
private static boolean wrapsLiteral(Object o) {
assert o != null;
return o instanceof String ||
o instanceof Byte ||
o instanceof Character ||
o instanceof Boolean ||
o instanceof Short ||
o instanceof Integer ||
o instanceof Long ||
o instanceof Float ||
o instanceof Double;
}
// need special match method because when using reflection
// to get a primitive value, the value is always wrapped in
// a new object
private static boolean match(Object a, Object b) {
if (a == b) {
return true;
}
if (a == null) {
return false;
}
if (wrapsLiteral(a)) {
return a.equals(b);
}
return false;
}
private Object getAdapter(Object o, String property) {
Object adapter = null;
adapter = BeanAdapterFactory.getAdapter(o, property);
return adapter == null ? o : adapter;
}
private static final boolean LOG = false;
private static void log(String method, String message) {
if (LOG) {
System.err.println("LOG: " + method + ": " + message);
}
}
private static final class RegisteredListener {
private final Object source;
private final String property;
RegisteredListener(Object source) {
this(source, null);
}
RegisteredListener(Object source, String property) {
this.source = source;
if (property != null) {
property = property.intern();
}
this.property = property;
}
public Object getSource() {
return source;
}
public String getProperty() {
return property;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof RegisteredListener) {
RegisteredListener orl = (RegisteredListener) obj;
return (orl.source == source && orl.property == property);
}
return false;
}
public int hashCode() {
int result = 17;
result = 37 * result + source.hashCode();
if (property != null) {
result = 37 * result + property.hashCode();
}
return result;
}
public String toString() {
return "RegisteredListener [" +
" source=" + source +
" property=" + property +
"]";
}
}
}
beansbinding-1.2.1/src/org/jdesktop/beansbinding/ObjectProperty.java 0000664 0000000 0000000 00000010555 11516446575 0025624 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.beansbinding;
import static org.jdesktop.beansbinding.PropertyStateEvent.UNREADABLE;
/**
* An immutable, read-only, {@code Property} implementation whose {@code getValue}
* method returns the source object that it is given. This class is useful when
* you want to configure a {@code Binding} to use its source object directly,
* rather than some property of the source object. For example:
*
*
* Explicitly using {@code ObjectProperty} isn't necessary when creating {@code Bindings}
* from this package or the {@code SwingBindings} package, as the set of static creation
* methods include versions that handle this for you.
*
* @param
* A {@code Property} implementation may, however, be designed such that the
* {@code Property} itself is a mutable thing that stores a property value.
* In such a case, the {@code Property} implementation may ignore the source
* object. {@code Property} implementations should clearly document their
* behavior in this regard.
*
* You can listen for changes in the state of a {@code Property} by
* registering {@code PropertyStateListeners} on the {@code Property}.
*
* @param
* {@code PropertyHelper} also provides, by way of the protected methods
* {@link #listeningStarted} and {@link #listeningStopped} a hook for subclasses
* to know when it's time to start tracking changes to a particular source object.
*
* @param
* Note: To indicate a change in readability, specify {@code valueChanged}
* as {@code true} and reflect the readability status in the {@code oldValue}
* and {@code newValue} arguments.
*
* @param sourceProperty the {@code Property} whose state has changed
* @param sourceObject the source object for which the {@code Property's} state has changed
* @param valueChanged whether or not the {@code Property's} value has changed for the source object
* @param oldValue the old value of the {@code Property} for the source object,
* or {@code UNREADABLE} if the {@code Property} was not previously readable for the source object
* @param newValue the new value of the {@code Property} for the source object,
* or {@code UNREADABLE} if the {@code Property} is not currently readable for the source object
* @param writeableChanged whether or not the {@code Property's} writeability has changed for the source object
* @param isWriteable whether or not the {@code Property} is now writeable for the source object
* @throws IllegalArgumentException if neither the value or the writeability has changed
* @throws IllegalArgumentException if {@code valueChanged} is {@code true} and both
* {@code oldValue} and {@code newValue} are {@code UNREADABLE}
*/
public PropertyStateEvent(Property sourceProperty,
Object sourceObject,
boolean valueChanged,
Object oldValue,
Object newValue,
boolean writeableChanged,
boolean isWriteable) {
super(sourceProperty);
if (!writeableChanged && !valueChanged) {
throw new IllegalArgumentException("Nothing has changed");
}
if (valueChanged && oldValue == UNREADABLE && newValue == UNREADABLE) {
throw new IllegalArgumentException("Value can't change from UNREADABLE to UNREADABLE");
}
this.sourceObject = sourceObject;
this.valueChanged = valueChanged;
this.oldValue = oldValue;
this.newValue = newValue;
this.writeableChanged = writeableChanged;
this.isWriteable = isWriteable;
}
/**
* Returns the {@code Property} whose state has changed.
* The preferred way to access this value is via the
* {@link #getSourceProperty} method.
*
* @return the {@code Property} whose state has changed.
*/
public final Object getSource() {
return super.getSource();
}
/**
* Returns the {@code Property} whose state has changed.
*
* @return the {@code Property} whose state has changed.
*/
public final Property getSourceProperty() {
return (Property)getSource();
}
/**
* Returns the source object for which the {@code Property's} state has changed.
*
* @return the source object for which the {@code Property's} state has changed
*/
public final Object getSourceObject() {
return sourceObject;
}
/**
* Returns whether or not the {@code Property's} value has changed for the source object.
*
* @return whether or not the {@code Property's} value has changed for the source object.
*/
public final boolean getValueChanged() {
return valueChanged;
}
/**
* Returns the old value of the {@code Property} for the source object,
* or {@code UNREADABLE} if the {@code Property} was not previously readable for the
* source object.
*
* Note: This method must only be called if {@code getValueChanged} returns
* {@code true}.
*
* @return the old value of the {@code Property} for the source object
* or {@code UNREADABLE}
* @throws UnsupportedOperationException if the value hasn't changed
*/
public final Object getOldValue() {
if (!valueChanged) {
throw new UnsupportedOperationException("value hasn't changed");
}
return oldValue;
}
/**
* Returns the new value of the {@code Property} for the source object,
* or {@code UNREADABLE} if the {@code Property} is not currently readable for the
* source object.
*
* Note: This method must only be called if {@code getValueChanged} returns
* {@code true}.
*
* @return the new value of the {@code Property} for the source object
* or {@code UNREADABLE}
* @throws UnsupportedOperationException if the value hasn't changed
*/
public final Object getNewValue() {
if (!valueChanged) {
throw new UnsupportedOperationException("value hasn't changed");
}
return newValue;
}
/**
* Returns whether or not the {@code Property's} readability has changed for
* the source object. In particuler, this returns {@code true} if the value
* has changed and either the old value or new value is {@code UNREADABLE},
* and {@code false} otherwise.
*
* @return whether or not the {@code Property's} readability has changed for
* the source object.
*/
public final boolean getReadableChanged() {
return valueChanged && oldValue != newValue && (oldValue == UNREADABLE || newValue == UNREADABLE);
}
/**
* Returns whether or not the {@code Property} is currently readable for
* the source object. In particular, this returns {@code true} if and only
* if the new value is not {@code UNREADABLE}.
*
* Note: This method must only be called if {@code getReadableChanged} returns
* {@code true}.
*
* @return whether or not the {@code Property} is currently readable for
* the source object.
* @throws UnsupportedOperationException if the readability hasn't changed
*/
public final boolean isReadable() {
if (!getReadableChanged()) {
throw new UnsupportedOperationException("readability hasn't changed");
}
return newValue != UNREADABLE;
}
/**
* Returns whether or not the {@code Property's} writeability has changed for
* the source object.
*
* @return whether or not the {@code Property's} writeability has changed for
* the source object.
*/
public final boolean getWriteableChanged() {
return writeableChanged;
}
/**
* Returns whether or not the {@code Property} is currently writeable for
* the source object.
*
* Note: This method must only be called if {@code getWriteableChanged} returns
* {@code true}.
*
* @return whether or not the {@code Property} is currently writeable for
* the source object.
* @throws UnsupportedOperationException if the writeability hasn't changed
*/
public final boolean isWriteable() {
if (!writeableChanged) {
throw new UnsupportedOperationException("writeability hasn't changed");
}
return isWriteable;
}
/**
* Returns a string representation of the {@code PropertyStateEvent}. This
* method is intended to be used for debugging purposes only, and
* the content and format of the returned string may vary between
* implementations. The returned string may be empty but may not
* be {@code null}.
*
* @return a string representation of this {@code PropertyStateEvent}
*/
public String toString() {
StringBuffer buffer = new StringBuffer(getClass().getName());
buffer.append(": Property ").append(getSourceProperty()).append(" changed on ").append(getSourceObject()).append(":\n");
if (getValueChanged()) {
buffer.append(" value changed from ").append(getOldValue()).append(" to ").append(getNewValue()).append('\n');
}
if (getReadableChanged()) {
buffer.append(" readable changed from ").append(!isReadable()).append(" to ").append(isReadable()).append('\n');
}
if (getWriteableChanged()) {
buffer.append(" writeable changed from ").append(!isWriteable()).append(" to ").append(isWriteable()).append('\n');
}
buffer.deleteCharAt(buffer.length() - 1);
return buffer.toString();
}
}
beansbinding-1.2.1/src/org/jdesktop/beansbinding/PropertyStateListener.java 0000664 0000000 0000000 00000001426 11516446575 0027201 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.beansbinding;
import java.util.EventListener;
/**
* {@code PropertyStateListeners} are registerd on {@link org.jdesktop.beansbinding.Property}
* instances, to be notified when the state of the property changes.
*
* @author Shannon Hickey
*/
public interface PropertyStateListener extends EventListener {
/**
* Called to notify the listener that a change of state has occurred to
* one of the {@code Property} instances upon which the listener is registered.
*
* @param pse an event describing the state change, {@code non-null}
*/
public void propertyStateChanged(PropertyStateEvent pse);
}
beansbinding-1.2.1/src/org/jdesktop/beansbinding/TempELContext.java 0000664 0000000 0000000 00000007555 11516446575 0025352 0 ustar 00root root 0000000 0000000 package org.jdesktop.beansbinding;
import java.util.*;
import java.beans.*;
import org.jdesktop.beansbinding.ext.BeanAdapterFactory;
import org.jdesktop.el.BeanELResolver;
import org.jdesktop.el.CompositeELResolver;
import org.jdesktop.el.ELContext;
import org.jdesktop.el.ELResolver;
import org.jdesktop.el.FunctionMapper;
import org.jdesktop.el.MapELResolver;
import org.jdesktop.el.VariableMapper;
import org.jdesktop.el.impl.lang.FunctionMapperImpl;
import org.jdesktop.el.impl.lang.VariableMapperImpl;
/**
* This class is temporary. Moving forward, we'll instead have a factory for
* configuring this.
*
* @author Shannon Hickey
*/
class TempELContext extends ELContext {
private final CompositeELResolver resolver;
private final VariableMapper variableMapper = new VariableMapperImpl();
private final FunctionMapper functionMapper = new FunctionMapperImpl();
public TempELContext() {
resolver = new CompositeELResolver();
// PENDING(shannonh) - EL also has an ArrayELResolver. Should that be added too?
resolver.add(new MapELResolver());
resolver.add(new BeanDelegateELResolver());
}
public ELResolver getELResolver() {
return resolver;
}
public FunctionMapper getFunctionMapper() {
return functionMapper;
}
public VariableMapper getVariableMapper() {
return variableMapper;
}
private class BeanDelegateELResolver extends BeanELResolver {
public Iterator
* A {@code Result} can contain an error code and/or description.
* These values are for your own reporting purposes and are not used
* internally.
*/
public class Result {
private final Object errorCode;
private final String description;
/**
* Creates a {@code Result} with the given error code and description.
*
* @param errorCode an error code for this {@code Result}, may be {@code null}
* @param description a textual description of the {@code Result}, may be {@code null}
*/
public Result(Object errorCode, String description) {
this.description = description;
this.errorCode = errorCode;
}
/**
* Returns the error code for the result, which may be {@code null}.
*
* @return the error code
*/
public Object getErrorCode() {
return errorCode;
}
/**
* Returns a description of the validation result, which may be {@code null}.
*
* @return the description
*/
public String getDescription() {
return description;
}
/**
* Returns a string representation of the {@code Result}. This
* method is intended to be used for debugging purposes only, and
* the content and format of the returned string may vary between
* implementations. The returned string may be empty but may not
* be {@code null}.
*
* @return a string representation of this {@code Result}
*/
public String toString() {
return getClass().getName() +
" [" +
"errorCode=" + errorCode +
", description=" + description +
"]";
}
}
/**
* Validates a value; returns {@code null} for a valid value, and a
* {@code Result} object describing the problem for an invalid value.
*
* @param value the value to validate, may be {@code null}
* @return {@code null} for a valid value or a {@code Result}
* describing the problem for an invalid value
*/
public abstract Result validate(T value);
}
beansbinding-1.2.1/src/org/jdesktop/beansbinding/ext/ 0000775 0000000 0000000 00000000000 11516446575 0022600 5 ustar 00root root 0000000 0000000 beansbinding-1.2.1/src/org/jdesktop/beansbinding/ext/BeanAdapterFactory.java 0000664 0000000 0000000 00000017222 11516446575 0027145 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2006-2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.beansbinding.ext;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.beans.*;
public final class BeanAdapterFactory {
private static final BeanAdapterFactory INSTANCE = new BeanAdapterFactory();
private final Map
Before a value from a source property is set on a target property, it passes
through an optional {@link org.jdesktop.beansbinding.Converter} to convert
it between the source type and target type. Before a value passed from a
target property back to a source property, it passes first through the
optional {@code Converter} and then through an optional
{@link org.jdesktop.beansbinding.Validator}, which can reject invalid values.
This resolver handles base objects that are Java language arrays.
* It accepts any object as a property and coerces that object into an
* integer index into the array. The resulting value is the value in the array
* at that index. This resolver can be constructed in read-only mode, which means that
* {@link #isReadOnly} will always return If the base is a Assuming the base is an If the base is a Java language array, the
* If the base is a Java language array, the
* If this resolver was constructed in read-only mode, this method will
* always throw If the base is a Java language array, the
* If this resolver was constructed in read-only mode, this method will
* always return The {@link #getCommonPropertyType} method returns sufficient
* information about what properties this resolver accepts. Assuming the base is an array, this method will always return
* This resolver handles base objects of any type, as long as the
* base is not This resolver can be constructed in read-only mode, which means that
* {@link #isReadOnly} will always return Because this resolver handles base objects of any type, it should
* be placed near the end of a composite resolver. Otherwise, it will
* claim to have resolved a property before any resolvers that come after
* it get a chance to test if they can do so as well. If the base is not The provided property will first be coerced to a If the base is not The provided property name will first be coerced to a
* If the base is not If this resolver was constructed in read-only mode, this method will
* always throw The provided property name will first be coerced to a
* If the base is not If this resolver was constructed in read-only mode, this method will
* always return The provided property name will first be coerced to a
* The Assuming the base is not Though only a single For the {@link #getValue}, {@link #getType}, {@link #setValue} and
* {@link #isReadOnly} methods, an The The Resolvers are consulted in the order in which they are added. If this resolver handles the given (base, property) pair,
* the First, Next, for each component resolver in this composite:
* If none of the component resolvers were able to perform this
* operation, the value Any exception thrown by component resolvers during the iteration
* is propagated to the caller of this method. If this resolver handles the given (base, property) pair,
* the First, Next, for each component resolver in this composite:
* If none of the component resolvers were able to perform this
* operation, the value Any exception thrown by component resolvers during the iteration
* is propagated to the caller of this method. If this resolver handles the given (base, property) pair,
* the First, Next, for each component resolver in this composite:
* If none of the component resolvers were able to perform this
* operation, the Any exception thrown by component resolvers during the iteration
* is propagated to the caller of this method. If this resolver handles the given (base, property) pair,
* the First, Next, for each component resolver in this composite:
* If none of the component resolvers were able to perform this
* operation, the value Any exception thrown by component resolvers during the iteration
* is propagated to the caller of this method. The The The To evaluate an {@link Expression}, an The collection of context objects is necessary because each
* Creation of Because it stores state during expression evaluation, an
* The {@link CompositeELResolver} checks this property to determine
* whether it should consider or skip other component resolvers. The {@link CompositeELResolver} checks this property to determine
* whether it should consider or skip other component resolvers. The By convention, the The By convention, the object returned will be of the type specified by
* the The Once an While evaluating an expression, the For example, in the EL expression Most methods in this class accept a In the case of property resolution, the Though only a single For the {@link #getValue}, {@link #getType}, {@link #setValue} and
* {@link #isReadOnly} methods, an The {@link #getFeatureDescriptors} and {@link #getCommonPropertyType}
* methods are primarily designed for design-time tool support, but must
* handle invocation at runtime as well. The
* {@link java.beans.Beans#isDesignTime} method can be used to determine
* if the resolver is being consulted at design-time or runtime. The attribute name of the named attribute in the
* The attribute name of the named attribute in the
* If this resolver handles the given (base, property) pair,
* the If this resolver handles the given (base, property) pair,
* the This is not always the same as If this resolver handles the given (base, property) pair,
* the If this resolver handles the given (base, property) pair,
* the If the The Each The caller should be aware that the This is a "best-effort" list. Not all The This assists tools in auto-completion and also provides a
* way to express that the resolver accepts a primitive value,
* such as an integer index into an array. For example, the
* {@link ArrayELResolver} will accept any Utility methods for this portion of the EL implementation Methods on this class use a Map instance stored in ThreadLocal storage
* to minimize the performance impact on operations that take place multiple
* times on a single Thread. The keys and values of the Map
* are implementation private. This class may not be constructed. The Replace the Map with the argument context. Convenience method, calls through to
* {@link #getExceptionMessageString(javax.el.ELContext,java.lang.String,Object []).
* Return a Localized message String suitable for use as an Exception message.
* Examine the argument All expressions must implement the All expressions must also be This is used for debugging purposes but also for the purposes
* of comparison (e.g. to ensure the expression in a configuration
* file has not changed). This method does not provide sufficient information to
* re-create an expression. Two different expressions can have exactly
* the same expression string but different function mappings.
* Serialization should be used to save and restore the state of an
* The result is Note that two expressions can be equal if their expression
* Strings are different. For example, See the note in the {@link #equals} method on how two expressions
* can be equal if their expression Strings are different. Recall that
* if two objects are equal according to the This method must return Classes that implement the EL expression language expose their
* functionality via this abstract class. There is no concrete implementation
* of this API available in this package. Technologies such as
* JavaServer Pages and JavaServer Faces provide access to an
* implementation via factory methods. The {@link #createValueExpression} method is used to parse expressions
* that evaluate to values (both l-values and r-values are supported).
* The {@link #createMethodExpression} method is used to parse expressions
* that evaluate to a reference to a method on an object. Unlike previous incarnations of this API, there is no way to parse
* and evaluate an expression in one single step. The expression needs to first
* be parsed, and then evaluated. Resolution of model objects is performed at evaluation time, via the
* {@link ELResolver} associated with the {@link ELContext} passed to
* the The ELContext object also provides access to the {@link FunctionMapper}
* and {@link VariableMapper} to be used when parsing the expression.
* EL function and variable mapping is performed at parse-time, and
* the results are
* bound to the expression. Therefore, the {@link ELContext},
* {@link FunctionMapper},
* and {@link VariableMapper}
* are not stored for future use and do not have to be
* The The The following types of input are illegal and must cause an
* {@link ELException} to be thrown:
* This method should perform syntactic validation of the expression.
* If in doing so it detects errors, it should raise an
*
* Note that within the EL, the ${} and #{} syntaxes are treated identically.
* This includes the use of VariableMapper and FunctionMapper at expression creation
* time. Each is invoked if not null, independent
* of whether the #{} or ${} syntax is used for the expression.
* If the expression is a String literal, a This method should perform syntactic validation of the expression.
* If in doing so it detects errors, it should raise an
*
* Note that within the EL, the ${} and #{} syntaxes are treated identically.
* This includes the use of VariableMapper and FunctionMapper at expression creation
* time. Each is invoked if not null, independent
* of whether the #{} or ${} syntax is used for the expression. An A Returns This resolver handles base objects of type This resolver can be constructed in read-only mode, which means that
* {@link #isReadOnly} will always return If the base is a Assuming the base is a If the base is a If the base is a If this resolver was constructed in read-only mode, this method will
* always throw If a If the base is a If this resolver was constructed in read-only mode, this method will
* always return If a The {@link #getCommonPropertyType} method returns sufficient
* information about what properties this resolver accepts. Assuming the base is a This resolver handles base objects of type This resolver can be constructed in read-only mode, which means that
* {@link #isReadOnly} will always return If the base is a Assuming the base is a If the base is a Just as in {@link java.util.Map#get}, just because If the base is a If this resolver was constructed in read-only mode, this method will
* always throw If a If the base is a If this resolver was constructed in read-only mode, this method will
* always return If a The Assuming the base is a The {@link ExpressionFactory#createMethodExpression} method
* can be used to parse an expression string and return a concrete instance
* of The {@link #getMethodInfo} and {@link #invoke} methods will evaluate the
* expression each time they are called. The {@link ELResolver} in the
* See the notes about comparison, serialization and immutability in
* the {@link Expression} javadocs.
*
* @see ELResolver
* @see Expression
* @see ExpressionFactory
* @since JSP 2.1
*/
public abstract class MethodExpression extends Expression
{
// Evaluation
/**
* Evaluates the expression relative to the provided context, and
* returns information about the actual referenced method.
*
* @param context The context of this evaluation
* @return an instance of For example, this could be triggered by an index out of bounds
* while setting an array value, or by an unreadable property while
* getting the value of a JavaBeans property. For example, this could be triggered by trying to set a map value
* on an unmodifiable map.
* This resolver handles base objects of type
*
* This resolver is read only and will throw a
* {@link PropertyNotWritableException} if
*
* If the base is
* The
* Assuming the base is a In previous incarnations of this API, expressions could only be
* read. The The {@link #getValue}, {@link #setValue}, {@link #isReadOnly} and
* {@link #getType} methods will evaluate the expression each time they are
* called. The {@link ELResolver} in the See the notes about comparison, serialization and immutability in
* the {@link Expression} javadocs.
*
* @see ELResolver
* @see Expression
* @see ExpressionFactory
* @since JSP 2.1
*/
public abstract class ValueExpression
extends Expression
{
private Object source;
/**
* Sets the source of the expression. For ValueExpressions that have a
* source, any identifiers are evaluated relative to the source. For
* example, if the expression {@code "${first.name}"} has a source,
* then {@code "first"} is evaluated relative to the source.
*
* @param source the initial source for identifiers; may be {@code null}
*/
public void setSource(Object source) {
this.source = source;
}
/**
* Returns the source of the expression.
*
* @return the source of the expression
*/
public Object getSource() {
return source;
}
/**
* Evaluates the expression relative to the provided context, and
* returns the resulting value.
*
* The resulting value is automatically coerced to the type
* returned by This is not always the same as
* The {@link #getMethodInfo} and {@link #invoke} methods will evaluate the
* expression each time they are called. The {@link ELResolver} in the
* See the notes about comparison, serialization and immutability in
* the {@link Expression} javadocs.
*
* @see javax.el.ELResolver
* @see javax.el.Expression
* @see javax.el.ExpressionFactory
* @see javax.el.MethodExpression
*
* @author Jacob Hookom [jacob@hookom.net]
* @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
*/
public final class MethodExpressionImpl extends MethodExpression implements
Externalizable {
private Class expectedType;
private String expr;
private FunctionMapper fnMapper;
private VariableMapper varMapper;
private transient Node node;
private Class[] paramTypes;
/**
*
*/
public MethodExpressionImpl() {
super();
}
/**
* @param expr
* @param node
* @param fnMapper
* @param expectedType
* @param paramTypes
*/
public MethodExpressionImpl(String expr, Node node,
FunctionMapper fnMapper, VariableMapper varMapper,
Class expectedType, Class[] paramTypes) {
super();
this.expr = expr;
this.node = node;
this.fnMapper = fnMapper;
this.varMapper = varMapper;
this.expectedType = expectedType;
this.paramTypes = paramTypes;
}
/**
* Determines whether the specified object is equal to this
*
* The result is
* Note that two expressions can be equal if their expression Strings are
* different. For example,
* This is used for debugging purposes but also for the purposes of
* comparison (e.g. to ensure the expression in a configuration file has not
* changed).
*
* This method does not provide sufficient information to re-create an
* expression. Two different expressions can have exactly the same
* expression string but different function mappings. Serialization should
* be used to save and restore the state of an
* See the note in the {@link #equals} method on how two expressions can be
* equal if their expression Strings are different. Recall that if two
* objects are equal according to the
* In previous incarnations of this API, expressions could only be read.
*
* The {@link #getValue}, {@link #setValue}, {@link #isReadOnly} and
* {@link #getType} methods will evaluate the expression each time they are
* called. The {@link ELResolver} in the See the notes about comparison, serialization and immutability in
* the {@link Expression} javadocs.
*
* @see javax.el.ELResolver
* @see javax.el.Expression
* @see javax.el.ExpressionFactory
* @see javax.el.ValueExpression
*
* @author Jacob Hookom [jacob@hookom.net]
* @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
*/
public final class ValueExpressionImpl extends ValueExpression implements
Externalizable {
private Class expectedType;
private String expr;
private FunctionMapper fnMapper;
private VariableMapper varMapper;
private transient Node node;
public ValueExpressionImpl() {
}
/**
*
*/
public ValueExpressionImpl(String expr, Node node, FunctionMapper fnMapper,
VariableMapper varMapper, Class expectedType) {
this.expr = expr;
this.node = node;
this.fnMapper = fnMapper;
this.varMapper = varMapper;
this.expectedType = expectedType;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
return (obj instanceof ValueExpressionImpl && obj.hashCode() == this
.hashCode());
}
/*
* (non-Javadoc)
*
* @see javax.el.ValueExpression#getExpectedType()
*/
public Class getExpectedType() {
return this.expectedType;
}
/**
* Returns the type the result of the expression will be coerced to after
* evaluation.
*
* @return the
Customized version of EL for Beans Binding; not for general use.
It is expected that we'll sync up with an official version of EL
in the future.
Customized version of EL for Beans Binding; not for general use.
It is expected that we'll sync up with an official version of EL
in the future.
Customized version of EL for Beans Binding; not for general use.
It is expected that we'll sync up with an official version of EL
in the future.
Customized version of EL for Beans Binding; not for general use.
It is expected that we'll sync up with an official version of EL
in the future.
Provides the API for the Unified Expression Language shared by
the JSP 2.1 and JSF 1.2 technologies.
The Expression Language (EL) is a simple language designed to satisfy
the specific needs of web application developers. It is currently defined
in its own specification document within the JavaServer Pages (tm) (JSP)
2.1 specification, but does not have any dependencies on any portion
of the JSP 2.1 specification. It is intended for general use outside of
the JSP and JSF specifications as well. This package contains the classes and interfaces that describe
and define the programmatic access to the Expression Language engine. The API
is logically partitioned as follows:
An important goal of the EL is to ensure it can be used in
a variety of environments. It must therefore provide enough flexibility
to adapt to the specific requirements of the environment where it is
being used. Class {@link javax.el.ELContext} is what links
the EL with the specific environment where it is being used.
It provides
the mechanism through which all relevant context for creating or
evaluating an expression is specified.
Creation of Some technologies provide the ability to add an {@link javax.el.ELContextListener}
so that applications and frameworks can ensure their own context objects
are attached to any newly created At the core of the Expression Language is the notion of an expression
that gets parsed according to the grammar defined by the Expression Language. There are two types of expressions defined by the EL: value expressions
and method expressions. A {@link javax.el.ValueExpression} such as
A {@link javax.el.MethodExpression} such as
All expression classes extend the base class {@link javax.el.Expression}, making them
serializable and forcing them to implement An expression is created through the {@link javax.el.ExpressionFactory} class.
The factory provides two creation methods; one for each type of expression
supported by the EL. To create an expression, one must provide an {@link javax.el.ELContext},
a string representing
the expression, and the expected type ( Through the {@link javax.el.ELResolver} base class, the EL
features a pluggable mechanism
to resolve model object references as well as properties of these objects. The EL API provides implementations of Tools can easily obtain more information about resolvable model objects and their
resolvable properties by calling
method If an EL expression uses a function
(for example Just like {@link javax.el.FunctionMapper} provides
a flexible mechanism to add functions to the EL, {@link javax.el.VariableMapper}
provides a flexible mechanism to support the notion of
EL variables.
An EL variable does not directly refer to a model object that can then
be resolved by an
For example, in the following code snippet
However, in this other example:
the type of source object that this {@code BeanProperty} operates on
* @param extends PropertyHelper {
private Property baseProperty;
private final PropertyPath path;
private IdentityHashMap map = new IdentityHashMap();
private static final Object NOREAD = new Object();
private final class SourceEntry implements PropertyChangeListener,
ObservableMapListener,
PropertyStateListener {
private S source;
private Object cachedBean;
private Object[] cache;
private Object cachedValue;
private Object cachedWriter;
private boolean ignoreChange;
private SourceEntry(S source) {
this.source = source;
cache = new Object[path.length()];
cache[0] = NOREAD;
if (baseProperty != null) {
baseProperty.addPropertyStateListener(source, this);
}
updateCachedBean();
updateCachedSources(0);
updateCachedValue();
updateCachedWriter();
}
private void cleanup() {
for (int i = 0; i < path.length(); i++) {
unregisterListener(cache[i], path.get(i), this);
}
if (baseProperty != null) {
baseProperty.removePropertyStateListener(source, this);
}
cachedBean = null;
cache = null;
cachedValue = null;
cachedWriter = null;
}
private boolean cachedIsReadable() {
return cachedValue != NOREAD;
}
private boolean cachedIsWriteable() {
return cachedWriter != null;
}
private int getSourceIndex(Object object) {
for (int i = 0; i < cache.length; i++) {
if (cache[i] == object) {
return i;
}
}
if (object instanceof Map) {
return -1;
}
for (int i = 0; i < cache.length; i++) {
if (cache[i] != null) {
Object adapter = getAdapter(cache[i], path.get(i));
if (adapter == object) {
return i;
}
}
}
return -1;
}
private void updateCachedBean() {
cachedBean = getBeanFromSource(source);
}
private void updateCachedSources(int index) {
boolean loggedYet = false;
Object src;
if (index == 0) {
src = cachedBean;
if (cache[0] != src) {
unregisterListener(cache[0], path.get(0), this);
cache[0] = src;
if (src == null) {
loggedYet = true;
log("updateCachedSources()", "source is null");
} else {
registerListener(src, path.get(0), this);
}
}
index++;
}
for (int i = index; i < path.length(); i++) {
Object old = cache[i];
src = getProperty(cache[i - 1], path.get(i - 1));
if (src != old) {
unregisterListener(old, path.get(i), this);
cache[i] = src;
if (src == null) {
if (!loggedYet) {
loggedYet = true;
log("updateCachedSources()", "missing source");
}
} else if (src == NOREAD) {
if (!loggedYet) {
loggedYet = true;
log("updateCachedSources()", "missing read method");
}
} else {
registerListener(src, path.get(i), this);
}
}
}
}
// -1 already used to mean validate all
// 0... means something in the path changed
private void validateCache(int ignore) {
/* In the future, this debugging code can be enabled via a flag */
/*
for (int i = 0; i < path.length() - 1; i++) {
if (i == ignore - 1) {
continue;
}
Object src = cache[i];
if (src == NOREAD) {
return;
}
Object next = getProperty(src, path.get(i));
if (!match(next, cache[i + 1])) {
log("validateCache()", "concurrent modification");
}
}
if (path.length() != ignore) {
Object next = getProperty(cache[path.length() - 1], path.getLast());
if (!match(cachedValue, next)) {
log("validateCache()", "concurrent modification");
}
Object src = cache[path.length() - 1];
Object writer;
if (src == null || src == NOREAD) {
writer = null;
} else {
writer = getWriter(cache[path.length() - 1], path.getLast());
}
if (cachedWriter != writer && (cachedWriter == null || !cachedWriter.equals(writer))) {
log("validateCache()", "concurrent modification");
}
}
*/
}
private void updateCachedWriter() {
Object src = cache[path.length() - 1];
if (src == null || src == NOREAD) {
cachedWriter = null;
} else {
cachedWriter = getWriter(src, path.getLast());
if (cachedWriter == null) {
log("updateCachedWriter()", "missing write method");
}
}
}
private void updateCachedValue() {
Object src = cache[path.length() - 1];
if (src == null || src == NOREAD) {
cachedValue = NOREAD;
} else {
cachedValue = getProperty(cache[path.length() - 1], path.getLast());
if (cachedValue == NOREAD) {
log("updateCachedValue()", "missing read method");
}
}
}
private void bindingPropertyChanged(PropertyStateEvent pse) {
validateCache(0);
Object oldValue = cachedValue;
boolean wasWriteable = cachedIsWriteable();
updateCachedBean();
updateCachedSources(0);
updateCachedValue();
updateCachedWriter();
notifyListeners(wasWriteable, oldValue, this);
}
private void cachedValueChanged(int index) {
validateCache(index);
boolean wasWriteable = cachedIsWriteable();
Object oldValue = cachedValue;
updateCachedSources(index);
updateCachedValue();
if (index != path.length()) {
updateCachedWriter();
}
notifyListeners(wasWriteable, oldValue, this);
}
private void mapValueChanged(ObservableMap map, Object key) {
if (ignoreChange) {
return;
}
int index = getSourceIndex(map);
if (index == -1) {
throw new AssertionError();
}
if (key.equals(path.get(index))) {
cachedValueChanged(index + 1);
}
}
public void propertyStateChanged(PropertyStateEvent pe) {
if (!pe.getValueChanged()) {
return;
}
bindingPropertyChanged(pe);
}
private void propertyValueChanged(PropertyChangeEvent pce) {
if (ignoreChange) {
return;
}
int index = getSourceIndex(pce.getSource());
if (index == -1) {
throw new AssertionError();
}
String propertyName = pce.getPropertyName();
if (propertyName == null || path.get(index).equals(propertyName)) {
cachedValueChanged(index + 1);
}
}
public void propertyChange(PropertyChangeEvent e) {
propertyValueChanged(e);
}
public void mapKeyValueChanged(ObservableMap map, Object key, Object lastValue) {
mapValueChanged(map, key);
}
public void mapKeyAdded(ObservableMap map, Object key) {
mapValueChanged(map, key);
}
public void mapKeyRemoved(ObservableMap map, Object key, Object value) {
mapValueChanged(map, key);
}
}
/**
* Creates an instance of {@code BeanProperty} for the given path.
*
* @param path the path
* @return an instance of {@code BeanProperty} for the given path
* @throws IllegalArgumentException if the path is null, or contains
* no property names
*/
public static final BeanProperty create(String path) {
return new BeanProperty(null, path);
}
/**
* Creates an instance of {@code BeanProperty} for the given base property
* and path. The path is relative to the value of the base property.
*
* @param baseProperty the base property
* @param path the path
* @return an instance of {@code BeanProperty} for the given base property and path
* @throws IllegalArgumentException if the path is null, or contains
* no property names
*/
public static final BeanProperty create(Property baseProperty, String path) {
return new BeanProperty(baseProperty, path);
}
/**
* @throws IllegalArgumentException for empty or {@code null} path.
*/
private BeanProperty(Property baseProperty, String path) {
this.path = PropertyPath.createPropertyPath(path);
this.baseProperty = baseProperty;
}
private Object getLastSource(S source) {
Object src = getBeanFromSource(source);
if (src == null || src == NOREAD) {
return src;
}
for (int i = 0; i < path.length() - 1; i++) {
src = getProperty(src, path.get(i));
if (src == null) {
log("getLastSource()", "missing source");
return null;
}
if (src == NOREAD) {
log("getLastSource()", "missing read method");
return NOREAD;
}
}
return src;
}
/**
* {@inheritDoc}
*
*
*
*
* the {@code Converter's} source type
* @param {
/**
* Converts a value from the source type to the target type.
* Can throw a {@code RuntimeException} to indicate a problem
* with the conversion.
*
* @param value the source value to convert
* @return the value, converted to the target type
*/
public abstract T convertForward(S value);
/**
* Converts a value from the target type to the source type.
* Can throw a {@code RuntimeException} to indicate a problem
* with the conversion.
*
* @param value the target value to convert
* @return the value, converted to the source type
*/
public abstract S convertReverse(T value);
static final Converter BYTE_TO_STRING_CONVERTER = new Converter() {
public Object convertForward(Object value) {
return Byte.toString((Byte)value);
}
public Object convertReverse(Object value) {
return Byte.parseByte((String)value);
}
};
static final Converter SHORT_TO_STRING_CONVERTER = new Converter() {
public Object convertForward(Object value) {
return Short.toString((Short)value);
}
public Object convertReverse(Object value) {
return Short.parseShort((String)value);
}
};
static final Converter INT_TO_STRING_CONVERTER = new Converter() {
public Object convertForward(Object value) {
return Integer.toString((Integer)value);
}
public Object convertReverse(Object value) {
return Integer.parseInt((String)value);
}
};
static final Converter LONG_TO_STRING_CONVERTER = new Converter() {
public Object convertForward(Object value) {
return Long.toString((Long)value);
}
public Object convertReverse(Object value) {
return Long.parseLong((String)value);
}
};
static final Converter FLOAT_TO_STRING_CONVERTER = new Converter() {
public Object convertForward(Object value) {
return Float.toString((Float)value);
}
public Object convertReverse(Object value) {
return Float.parseFloat((String)value);
}
};
static final Converter DOUBLE_TO_STRING_CONVERTER = new Converter() {
public Object convertForward(Object value) {
return Double.toString((Double)value);
}
public Object convertReverse(Object value) {
return Double.parseDouble((String)value);
}
};
static final Converter CHAR_TO_STRING_CONVERTER = new Converter() {
public Object convertForward(Object value) {
return ((Character)value).toString();
}
public Object convertReverse(Object value) {
String strVal = (String)value;
if (strVal.length() != 1) {
throw new IllegalArgumentException("String doesn't represent a char");
}
return strVal.charAt(0);
}
};
static final Converter BOOLEAN_TO_STRING_CONVERTER = new Converter() {
public Object convertForward(Object value) {
return ((Boolean)value).toString();
}
public Object convertReverse(Object value) {
return new Boolean((String)value);
}
};
static final Converter INT_TO_BOOLEAN_CONVERTER = new Converter() {
public Object convertForward(Object value) {
if (((Integer)value).intValue() == 0) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
public Object convertReverse(Object value) {
if (((Boolean)value).booleanValue()) {
return 1;
}
return 0;
}
};
static final Converter BIGINTEGER_TO_STRING_CONVERTER = new Converter() {
public Object convertForward(Object value) {
return ((BigInteger)value).toString();
}
public Object convertReverse(Object value) {
return new BigInteger((String)value);
}
};
static final Converter BIGDECIMAL_TO_STRING_CONVERTER = new Converter() {
public Object convertForward(Object value) {
return ((BigDecimal)value).toString();
}
public Object convertReverse(Object value) {
return new BigDecimal((String)value);
}
};
static final Object defaultConvert(Object source, Class> targetType) {
Class sourceType = source.getClass();
if (sourceType == targetType) {
return source;
}
if (targetType == String.class) {
if (sourceType == Byte.class) {
return BYTE_TO_STRING_CONVERTER.convertForward(source);
} else if (sourceType == Short.class) {
return SHORT_TO_STRING_CONVERTER.convertForward(source);
} else if (sourceType == Integer.class) {
return INT_TO_STRING_CONVERTER.convertForward(source);
} else if (sourceType == Long.class) {
return LONG_TO_STRING_CONVERTER.convertForward(source);
} else if (sourceType == Float.class) {
return FLOAT_TO_STRING_CONVERTER.convertForward(source);
} else if (sourceType == Double.class) {
return DOUBLE_TO_STRING_CONVERTER.convertForward(source);
} else if (sourceType == Boolean.class) {
return BOOLEAN_TO_STRING_CONVERTER.convertForward(source);
} else if (sourceType == Character.class) {
return CHAR_TO_STRING_CONVERTER.convertForward(source);
} else if (sourceType == BigInteger.class) {
return BIGINTEGER_TO_STRING_CONVERTER.convertForward(source);
} else if (sourceType == BigDecimal.class) {
return BIGDECIMAL_TO_STRING_CONVERTER.convertForward(source);
}
} else if (sourceType == String.class) {
if (targetType == Byte.class) {
return BYTE_TO_STRING_CONVERTER.convertReverse(source);
} else if (targetType == Short.class) {
return SHORT_TO_STRING_CONVERTER.convertReverse(source);
} else if (targetType == Integer.class) {
return INT_TO_STRING_CONVERTER.convertReverse(source);
} else if (targetType == Long.class) {
return LONG_TO_STRING_CONVERTER.convertReverse(source);
} else if (targetType == Float.class) {
return FLOAT_TO_STRING_CONVERTER.convertReverse(source);
} else if (targetType == Double.class) {
return DOUBLE_TO_STRING_CONVERTER.convertReverse(source);
} else if (targetType == Boolean.class) {
return BOOLEAN_TO_STRING_CONVERTER.convertReverse(source);
} else if (targetType == Character.class) {
return CHAR_TO_STRING_CONVERTER.convertReverse(source);
} else if (targetType == BigInteger.class) {
return BIGINTEGER_TO_STRING_CONVERTER.convertReverse(source);
} else if (targetType == BigDecimal.class) {
return BIGDECIMAL_TO_STRING_CONVERTER.convertReverse(source);
}
} else if (sourceType == Integer.class && targetType == Boolean.class) {
return INT_TO_BOOLEAN_CONVERTER.convertForward(source);
} else if (sourceType == Boolean.class && targetType == Integer.class) {
return INT_TO_BOOLEAN_CONVERTER.convertReverse(source);
}
return source;
}
}
beansbinding-1.2.1/src/org/jdesktop/beansbinding/ELProperty.java 0000664 0000000 0000000 00000107312 11516446575 0024714 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2006-2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.beansbinding;
import org.jdesktop.el.impl.ExpressionFactoryImpl;
import org.jdesktop.el.ELContext;
import org.jdesktop.el.ELException;
import org.jdesktop.el.Expression;
import org.jdesktop.el.Expression.ResolvedProperty;
import org.jdesktop.el.ValueExpression;
import java.beans.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import org.jdesktop.observablecollections.ObservableMap;
import org.jdesktop.observablecollections.ObservableMapListener;
import static org.jdesktop.beansbinding.PropertyStateEvent.UNREADABLE;
import org.jdesktop.beansbinding.ext.BeanAdapterFactory;
/**
* An implementation of {@code Property} that allows Java Beans properties of
* source objects to be addressed using a simple dot-separated path syntax
* within an EL expression. For example, to create a simple property representing
* a {@code Person} bean's mother's {@code firstName}:
*
*
* ELProperty.create("${mother.firstName}")
*
*
* ELProperty.create("${firstName} ${lastName}");
*
*
* BeanProperty.create("${mother.age > 65}");
* the type of source object that this {@code ELProperty} operates on
* @param extends PropertyHelper {
private Property baseProperty;
private final ValueExpression expression;
private final ELContext context = new TempELContext();
private IdentityHashMap map = new IdentityHashMap();
private static final Object NOREAD = new Object();
private final class SourceEntry implements PropertyChangeListener,
ObservableMapListener,
PropertyStateListener {
private S source;
private Object cachedBean;
private Object cachedValue;
private boolean cachedIsWriteable;
private Class> cachedWriteType;
private boolean ignoreChange;
private Set ELProperty create(String expression) {
return new ELProperty(null, expression);
}
/**
* Creates an instance of {@code ELProperty} for the given base property
* and expression. The expression is relative to the value of the base property.
*
* @param baseProperty the base property
* @param expression the expression
* @return an instance of {@code ELProperty} for the given base property and expression
* @throws IllegalArgumentException if the path is null or empty
* @throws PropertyResolutionException if there's a problem with the expression
*/
public static final ELProperty create(Property baseProperty, String expression) {
return new ELProperty(baseProperty, expression);
}
/**
* @throws IllegalArgumentException for empty or {@code null} expression.
*/
private ELProperty(Property baseProperty, String expression) {
if (expression == null || expression.length() == 0) {
throw new IllegalArgumentException("expression must be non-null and non-empty");
}
try {
this.expression = new ExpressionFactoryImpl().createValueExpression(context, expression, Object.class);
} catch (ELException ele) {
throw new PropertyResolutionException("Error creating EL expression " + expression, ele);
}
this.baseProperty = baseProperty;
}
/**
* {@inheritDoc}
*
*
* new SomeBindingClass(sourceObject, ObjectProperty.create(), targetObject, targetProperty);
* the type of source object that this {@code Property} operates on
* and therefore the type of value that it represents
*
* @author Shannon Hickey
*/
public final class ObjectProperty extends Property {
/**
* Creates an instance of {@code ObjectProperty}.
*/
public static ObjectProperty create() {
return new ObjectProperty();
}
private ObjectProperty() {}
/**
* Throws {@code UnsupportedOperationException}; {@code ObjectProperty} is never writeable.
*
* @param source {@inheritDoc}
* @return never returns; always throws {@code UnsupportedOperationException}; {@code ObjectProperty} is never writeable
* @throws UnsupportedOperationException always; {@code ObjectProperty} is never writeable
* @see #isWriteable
*/
public Class extends S> getWriteType(S source) {
throw new UnsupportedOperationException("Unwriteable");
}
/**
* Returns the source object passed to the method.
*
* @return the value of the {@code source} argument
* @see #isReadable
*/
public S getValue(S source) {
return source;
}
/**
* Throws {@code UnsupportedOperationException}; {@code ObjectProperty} is never writeable.
*
* @param source {@inheritDoc}
* @throws UnsupportedOperationException always; {@code ObjectProperty} is never writeable
* @see #isWriteable
* @see #getWriteType
*/
public void setValue(S source, S value) {
throw new UnsupportedOperationException("Unwriteable");
}
/**
* Returns {@code true}; {@code ObjectProperty} is always readable.
*
* @return {@code true}; {@code ObjectPropert} is always readable
* @see #isWriteable
*/
public boolean isReadable(Object source) {
return true;
}
/**
* Returns {@code false}; {@code ObjectProperty} is never writeable.
*
* @return {@code false}; {@code ObjectPropert} is never writeable
* @see #isReadable
*/
public boolean isWriteable(Object source) {
return false;
}
/**
* Returns a string representation of the {@code ObjectProperty}. This
* method is intended to be used for debugging purposes only, and
* the content and format of the returned string may vary between
* implementations. The returned string may be empty but may not
* be {@code null}.
*
* @return a string representation of this {@code ObjectProperty}
*/
public String toString() {
return getClass().getName();
}
/**
* Does nothing; the state of an {@code ObjectProperty} never changes so
* listeners aren't useful.
*/
public void addPropertyStateListener(S source, PropertyStateListener listener) {}
/**
* Does nothing; the state of an {@code ObjectProperty} never changes so
* listeners aren't useful.
*
* @see #addPropertyStateListener
*/
public void removePropertyStateListener(S source, PropertyStateListener listener) {}
/**
* Returns an empty array; the state of an {@code ObjectProperty} never changes
* so listeners aren't useful.
*
* @return an empty array
* @see #addPropertyStateListener
*/
public PropertyStateListener[] getPropertyStateListeners(S source) {
return new PropertyStateListener[0];
}
}
beansbinding-1.2.1/src/org/jdesktop/beansbinding/Property.java 0000664 0000000 0000000 00000011630 11516446575 0024470 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.beansbinding;
/**
* {@code Property} defines a uniform way to access the value of a property.
* A typical {@code Property} implemention allows you to create an immutable
* representation of a way to derive some property from a source object.
* As such, all methods of this class take a source object as an argument.
* the type of source object that this {@code Property} operates on
* @param {
/**
* Returns the type of object that is suitable for setting as the value
* of this {@code Property} by calls to {@code setValue}.
*
* @param source the source object on which to operate
* @return the type of object suitable for setting as the value
* @throws UnsupportedOperationException if the {@code Property} is not
* writeable for the given source
* @see #setValue
* @see #isWriteable
*/
public abstract Class extends V> getWriteType(S source);
/**
* Returns the value of this {@code Property} for the given source.
*
* @param source the source object on which to operate
* @return the value of this {@code Property} for the given source
* @throws UnsupportedOperationException if the {@code Property} is not
* readable for the given source
* @see #isReadable
*/
public abstract V getValue(S source);
/**
* Sets the value of this {@code Property} for the given source.
*
* @param source the source object on which to operate
* @param value the new value for the {@code Property}
* @throws UnsupportedOperationException if the {@code Property} is not
* writeable for the given source
* @see #isWriteable
* @see #getWriteType
*/
public abstract void setValue(S source, V value);
/**
* Returns whether or not the {@code Property} is readable for the given source.
*
* @param source the source object on which to operate
* @return whether or not the {@code Property} is readable for the given source.
* @see #isWriteable
*/
public abstract boolean isReadable(S source);
/**
* Returns whether or not the {@code Property} is writeable for the given source.
*
* @param source the source object on which to operate
* @return whether or not the {@code Property} is writeable for the given source.
* @see #isReadable
*/
public abstract boolean isWriteable(S source);
/**
* Adds a {@code PropertyStateListener} to be notified when the state of the
* {@code Property} changes with respect to the given source. Does nothing if
* the listener is {@code null}. If a listener is added more than once,
* notifications are sent to that listener once for every time that it has
* been added. The ordering of listener notification is unspecified.
*
* @param source the source object on which to operate
* @param listener the listener to be notified
*/
public abstract void addPropertyStateListener(S source, PropertyStateListener listener);
/**
* Removes a {@code PropertyStateListener} for the given source. Does
* nothing if the listener is {@code null} or is not one of those registered
* for this source object. If the listener being removed was registered more
* than once, only one occurrence of the listener is removed from the list of
* listeners. The ordering of listener notification is unspecified.
*
* @param source the source object on which to operate
* @param listener the listener to be removed
* @see #addPropertyStateListener
*/
public abstract void removePropertyStateListener(S source, PropertyStateListener listener);
/**
* Returns an arry containing the listeners registered for the given source.
* Order is undefined. Returns an empty array if there are no listeners.
*
* @param source the source object on which to operate
* @return the set of listeners registered for the given source
* @see #addPropertyStateListener
*/
public abstract PropertyStateListener[] getPropertyStateListeners(S source);
}
beansbinding-1.2.1/src/org/jdesktop/beansbinding/PropertyHelper.java 0000664 0000000 0000000 00000016545 11516446575 0025642 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.beansbinding;
import java.util.*;
/**
* An abstract subclass of {@code Property} that helps with the management of
* {@code PropertyStateListeners} by implementing the methods for adding, removing,
* and getting listeners. {@code PropertyHelper} can be constructed
* to manage listeners for multiple source objects, or to ignore the source
* object argument when dealing with listeners and associate them directly with
* the {@code PropertyHelper} instance itself. This makes {@code PropertyHelper}
* useful as a base for both property types described in the documentation for
* {@code Property}.
* the type of source object that this {@code Property} operates on
* @param extends Property {
private final boolean ignoresSource;
private Object listeners;
/**
* Create a {@code PropertyHelper} that manages listeners for multiple
* source objects.
*/
public PropertyHelper() {
this(false);
}
/**
* Create a {@code PropertyHelper}, specifying whether it manages
* listeners for multiple source objects, or ignores the source object
* argument when dealing with listeners
*
* @param ignoresSource whether or not the source argument is ignored
* when dealing with listeners
*/
public PropertyHelper(boolean ignoresSource) {
this.ignoresSource = ignoresSource;
}
private List> map = (IdentityHashMap>)listeners;
if (map == null) {
if (create) {
map = new IdentityHashMap>();
listeners = map;
} else {
return null;
}
}
List
BeanProperty firstNameP = BeanProperty.create("firstName");
BeanProperty textP = BeanProperty.create("text");
Binding binding = Bindings.createAutoBinding(READ_WRITE, person, firstNameP, jTextField, textP);
binding.bind();
true and
* {@link #setValue} will always throw
* PropertyNotWritableException.ELResolvers are combined together using
* {@link CompositeELResolver}s, to define rich semantics for evaluating
* an expression. See the javadocs for {@link ELResolver} for details.ArrayELResolver.
*/
public ArrayELResolver() {
this.isReadOnly = false;
}
/**
* Creates a new ArrayELResolver whose read-only status is
* determined by the given parameter.
*
* @param isReadOnly true if this resolver cannot modify
* arrays; false otherwise.
*/
public ArrayELResolver(boolean isReadOnly) {
this.isReadOnly = isReadOnly;
}
/**
* If the base object is an array, returns the most general acceptable type
* for a value in this array.
*
* array, the
* propertyResolved property of the ELContext
* object must be set to true by this resolver, before
* returning. If this property is not true after this method
* is called, the caller should ignore the return value.array, this method will always
* return base.getClass().getComponentType(), which is
* the most general type of component that can be stored at any given
* index in the array.propertyResolved property of
* ELContext was set to true, then
* the most general acceptable type; otherwise undefined.
* @throws PropertyNotFoundException if the given index is out of
* bounds for this array.
* @throws NullPointerException if context is null
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public Class> getType(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base.getClass().isArray()) {
context.setPropertyResolved(true);
int index = toInteger (property);
if (index < 0 || index >= Array.getLength(base)) {
throw new PropertyNotFoundException();
}
return base.getClass().getComponentType();
}
return null;
}
/**
* If the base object is a Java language array, returns the value at the
* given index. The index is specified by the property
* argument, and coerced into an integer. If the coercion could not be
* performed, an IllegalArgumentException is thrown. If the
* index is out of bounds, null is returned.
*
* propertyResolved property of the ELContext
* object must be set to true by this resolver, before
* returning. If this property is not true after this
* method is called, the caller should ignore the return value.propertyResolved property of
* ELContext was set to true, then
* the value at the given index or null
* if the index was out of bounds. Otherwise, undefined.
* @throws IllegalArgumentException if the property could not be coerced
* into an integer.
* @throws NullPointerException if context is null.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public Object getValue(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base.getClass().isArray()) {
context.setPropertyResolved(true);
int index = toInteger (property);
if (index >= 0 && index < Array.getLength(base)) {
return Array.get(base, index);
}
}
return null;
}
/**
* If the base object is a Java language array, attempts to set the
* value at the given index with the given value. The index is specified
* by the property argument, and coerced into an integer.
* If the coercion could not be performed, an
* IllegalArgumentException is thrown. If the index is
* out of bounds, a PropertyNotFoundException is thrown.
*
* propertyResolved property of the ELContext
* object must be set to true by this resolver, before
* returning. If this property is not true after this method
* is called, the caller can safely assume no value was set.PropertyNotWritableException.null.
* @throws IllegalArgumentException if the property could not be coerced
* into an integer, or if some aspect of the specified element
* prevents it from being added to this array.
* @throws PropertyNotWritableException if this resolver was constructed
* in read-only mode.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public void setValue(ELContext context,
Object base,
Object property,
Object val) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base.getClass().isArray()) {
context.setPropertyResolved(true);
if (isReadOnly) {
throw new PropertyNotWritableException();
}
Class> type = base.getClass().getComponentType();
if (val != null && ! type.isAssignableFrom(val.getClass())) {
throw new ClassCastException();
}
int index = toInteger (property);
if (index < 0 || index >= Array.getLength(base)) {
throw new PropertyNotFoundException();
}
Array.set(base, index, val);
}
}
/**
* If the base object is a Java language array, returns whether a call to
* {@link #setValue} will always fail.
*
* propertyResolved property of the ELContext
* object must be set to true by this resolver, before
* returning. If this property is not true after this method
* is called, the caller should ignore the return value.true. Otherwise, it returns
* false.propertyResolved property of
* ELContext was set to true, then
* true if calling the setValue method
* will always fail or false if it is possible that
* such a call may succeed; otherwise undefined.
* @throws PropertyNotFoundException if the given index is out of
* bounds for this array.
* @throws NullPointerException if context is null
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public boolean isReadOnly(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base.getClass().isArray()) {
context.setPropertyResolved(true);
int index = toInteger (property);
if (index < 0 || index >= Array.getLength(base)) {
throw new PropertyNotFoundException();
}
}
return isReadOnly;
}
/**
* Always returns null, since there is no reason to
* iterate through set set of all integers.
*
* null.
*/
public Iteratorproperty argument.
* Otherwise, returns null.
*
* Integer.class. This is because arrays accept integers
* for their index.null if base is not a Java language array;
* otherwise Integer.class.
*/
public Class> getCommonPropertyType(ELContext context,
Object base) {
if (base != null && base.getClass().isArray()) {
return Integer.class;
}
return null;
}
private int toInteger(Object p) {
if (p instanceof Integer) {
return ((Integer) p).intValue();
}
if (p instanceof Character) {
return ((Character) p).charValue();
}
if (p instanceof Boolean) {
return ((Boolean) p).booleanValue()? 1: 0;
}
if (p instanceof Number) {
return ((Number) p).intValue();
}
if (p instanceof String) {
return Integer.parseInt((String) p);
}
throw new IllegalArgumentException();
}
private boolean isReadOnly;
}
beansbinding-1.2.1/src/org/jdesktop/el/BeanELResolver.java 0000664 0000000 0000000 00000054534 11516446575 0023443 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.beans.FeatureDescriptor;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.beans.IntrospectionException;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
* Defines property resolution behavior on objects using the JavaBeans
* component architecture.
*
* null. It accepts any object as a property, and
* coerces it to a string. That string is then used to find a JavaBeans
* compliant property on the base object. The value is accessed using
* JavaBeans getters and setters.true and
* {@link #setValue} will always throw
* PropertyNotWritableException.ELResolvers are combined together using
* {@link CompositeELResolver}s, to define rich semantics for evaluating
* an expression. See the javadocs for {@link ELResolver} for details.BeanELResolver.
*/
public BeanELResolver() {
this.isReadOnly = false;
}
/**
* Creates a new BeanELResolver whose read-only status is
* determined by the given parameter.
*
* @param isReadOnly true if this resolver cannot modify
* beans; false otherwise.
*/
public BeanELResolver(boolean isReadOnly) {
this.isReadOnly = isReadOnly;
}
/**
* If the base object is not null, returns the most
* general acceptable type that can be set on this bean property.
*
* null, the
* propertyResolved property of the ELContext
* object must be set to true by this resolver, before
* returning. If this property is not true after this
* method is called, the caller should ignore the return value.String.
* If there is a BeanInfoProperty for this property and
* there were no errors retrieving it, the propertyType of
* the propertyDescriptor is returned. Otherwise, a
* PropertyNotFoundException is thrown.String.
* @return If the propertyResolved property of
* ELContext was set to true, then
* the most general acceptable type; otherwise undefined.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if base is not
* null and the specified property does not exist
* or is not readable.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public Class> getType(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base == null || property == null){
return null;
}
BeanProperty bp = getBeanProperty(context, base, property);
context.setPropertyResolved(true);
return bp.getPropertyType();
}
/**
* If the base object is not null, returns the current
* value of the given property on this bean.
*
* null, the
* propertyResolved property of the ELContext
* object must be set to true by this resolver, before
* returning. If this property is not true after this
* method is called, the caller should ignore the return value.String. If the property is a readable property of the
* base object, as per the JavaBeans specification, then return the
* result of the getter call. If the getter throws an exception,
* it is propagated to the caller. If the property is not found or is
* not readable, a PropertyNotFoundException is thrown.String.
* @return If the propertyResolved property of
* ELContext was set to true, then
* the value of the given property. Otherwise, undefined.
* @throws NullPointerException if context is null.
* @throws PropertyNotFoundException if base is not
* null and the specified property does not exist
* or is not readable.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public Object getValue(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base == null || property == null) {
return null;
}
Method method;
BeanProperty bp = getBeanProperty(context, base, property);
if (bp == null || (method = bp.getReadMethod()) == null) {
return null;
}
Object value;
try {
value = method.invoke(base, new Object[0]);
context.setPropertyResolved(true);
} catch (ELException ex) {
throw ex;
} catch (InvocationTargetException ite) {
throw new ELException(ite.getCause());
} catch (Exception ex) {
throw new ELException(ex);
}
return value;
}
/**
* If the base object is not null, attempts to set the
* value of the given property on this bean.
*
* null, the
* propertyResolved property of the ELContext
* object must be set to true by this resolver, before
* returning. If this property is not true after this
* method is called, the caller can safely assume no value was set.PropertyNotWritableException.String. If property is a writable property of
* base (as per the JavaBeans Specification), the setter
* method is called (passing value). If the property exists
* but does not have a setter, then a
* PropertyNotFoundException is thrown. If the property
* does not exist, a PropertyNotFoundException is thrown.String.
* @param val The value to be associated with the specified key.
* @throws NullPointerException if context is null.
* @throws PropertyNotFoundException if base is not
* null and the specified property does not exist.
* @throws PropertyNotWritableException if this resolver was constructed
* in read-only mode, or if there is no setter for the property.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public void setValue(ELContext context,
Object base,
Object property,
Object val) {
if (context == null) {
throw new NullPointerException();
}
if (base == null || property == null){
return;
}
if (isReadOnly) {
throw new PropertyNotWritableException(
ELUtil.getExceptionMessageString(context,
"resolverNotwritable",
new Object[] { base.getClass().getName() }));
}
BeanProperty bp = getBeanProperty(context, base, property);
Method method = bp.getWriteMethod();
if (method == null) {
throw new PropertyNotWritableException(
ELUtil.getExceptionMessageString(context,
"propertyNotWritable",
new Object[] { base.getClass().getName(),
property.toString()}));
}
try {
method.invoke(base, new Object[] {val});
context.setPropertyResolved(true);
} catch (ELException ex) {
throw ex;
} catch (InvocationTargetException ite) {
throw new ELException(ite.getCause());
} catch (Exception ex) {
if (null == val) {
val = "null";
}
String message = ELUtil.getExceptionMessageString(context,
"setPropertyFailed",
new Object[] { property.toString(),
base.getClass().getName(), val });
throw new ELException(message, ex);
}
}
/**
* If the base object is not null, returns whether a call
* to {@link #setValue} will always fail.
*
* null, the
* propertyResolved property of the ELContext
* object must be set to true by this resolver, before
* returning. If this property is not true after this
* method is called, the caller can safely assume no value was set.true.String. If property is a writable property of
* base, false is returned. If the property is
* found but is not writable, true is returned. If the
* property is not found, a PropertyNotFoundException
* is thrown.String.
* @return If the propertyResolved property of
* ELContext was set to true, then
* true if calling the setValue method
* will always fail or false if it is possible that
* such a call may succeed; otherwise undefined.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if base is not
* null and the specified property does not exist.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public boolean isReadOnly(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base == null || property == null){
return false;
}
context.setPropertyResolved(true);
if (isReadOnly) {
return true;
}
BeanProperty bp = getBeanProperty(context, base, property);
return bp.isReadOnly();
}
/**
* If the base object is not null, returns an
* Iterator containing the set of JavaBeans properties
* available on the given object. Otherwise, returns null.
*
* Iterator returned must contain zero or more
* instances of {@link java.beans.FeatureDescriptor}. Each info object
* contains information about a property in the bean, as obtained by
* calling the BeanInfo.getPropertyDescriptors method.
* The FeatureDescriptor is initialized using the same
* fields as are present in the PropertyDescriptor,
* with the additional required named attributes "type" and
* "resolvableAtDesignTime" set as follows:
*
*
PropertyDescriptor.getPropertyType().true.Iterator containing zero or more
* FeatureDescriptor objects, each representing a property
* on this bean, or null if the base
* object is null.
*/
public Iteratornull, returns the most
* general type that this resolver accepts for the
* property argument. Otherwise, returns null.
*
* null, this method will always
* return Object.class. This is because any object is
* accepted as a key and is coerced into a string.null if base is null; otherwise
* Object.class.
*/
public Class> getCommonPropertyType(ELContext context,
Object base) {
if (base == null){
return null;
}
return Object.class;
}
/*
* Get a public method form a public class or interface of a given method.
* Note that if a PropertyDescriptor is obtained for a non-public class that
* implements a public interface, the read/write methods will be for the
* class, and therefore inaccessible. To correct this, a version of the
* same method must be found in a superclass or interface.
**/
static private Method getMethod(Class cl, Method method) {
if (Modifier.isPublic (cl.getModifiers ())) {
return method;
}
Class [] interfaces = cl.getInterfaces ();
for (int i = 0; i < interfaces.length; i++) {
Class c = interfaces[i];
Method m = null;
try {
m = c.getMethod(method.getName(), method.getParameterTypes());
c = m.getDeclaringClass();
if ((m = getMethod(c, m)) != null)
return m;
} catch (NoSuchMethodException ex) {
}
}
Class c = cl.getSuperclass();
if (c != null) {
Method m = null;
try {
m = c.getMethod(method.getName(), method.getParameterTypes());
c = m.getDeclaringClass();
if ((m = getMethod(c, m)) != null)
return m;
} catch (NoSuchMethodException ex) {
}
}
return null;
}
private BeanProperty getBeanProperty(ELContext context,
Object base,
Object prop) {
String property = prop.toString();
Class baseClass = base.getClass();
BeanProperties bps = properties.get(baseClass);
if (bps == null && (bps = properties2.get(baseClass)) == null) {
if (properties.size() > SIZE) {
// Discard the old map to limit the cache size.
properties2.clear();
properties2.putAll(properties);
properties.clear();
}
bps = new BeanProperties(baseClass);
properties.put(baseClass, bps);
}
return bps.getBeanProperty(property);
}
}
beansbinding-1.2.1/src/org/jdesktop/el/CompositeELResolver.java 0000664 0000000 0000000 00000052753 11516446575 0024541 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
import java.util.ArrayList;
import java.util.Iterator;
import java.beans.FeatureDescriptor;
/**
* Maintains an ordered composite list of child ELResolvers.
*
* ELResolver is associated with an
* ELContext, there are usually multiple resolvers considered
* for any given variable or property resolution. ELResolvers
* are combined together using a CompositeELResolver, to define
* rich semantics for evaluating an expression.ELResolver is not
* responsible for resolving all possible (base, property) pairs. In fact,
* most resolvers will only handle a base of a single type.
* To indicate that a resolver has successfully resolved a particular
* (base, property) pair, it must set the propertyResolved
* property of the ELContext to true. If it could
* not handle the given pair, it must leave this property alone. The caller
* must ignore the return value of the method if propertyResolved
* is false.CompositeELResolver initializes the
* ELContext.propertyResolved flag to false, and uses
* it as a stop condition for iterating through its component resolvers.ELContext.propertyResolved flag is not used for the
* design-time methods {@link #getFeatureDescriptors} and
* {@link #getCommonPropertyType}. Instead, results are collected and
* combined from all child ELResolvers for these methods.null.
*/
public void add(ELResolver elResolver) {
if (elResolver == null) {
throw new NullPointerException();
}
elResolvers.add(elResolver);
}
/**
* Attempts to resolve the given property object on the given
* base object by querying all component resolvers.
*
* propertyResolved property of the
* ELContext object must be set to true
* by the resolver, before returning. If this property is not
* true after this method is called, the caller should ignore
* the return value.propertyResolved is set to false on
* the provided ELContext.
*
getValue() method is called, passing in
* the provided context, base and
* property.ELContext's propertyResolved
* flag is false then iteration continues.getValue() is
* returned by this method.null is returned and the
* propertyResolved flag remains set to
* falsenull to resolve a top-level variable.
* @param property The property or variable to be resolved.
* @return If the propertyResolved property of
* ELContext was set to true, then
* the result of the variable or property resolution; otherwise
* undefined.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if the given (base, property) pair
* is handled by this ELResolver but the specified
* variable or property does not exist or is not readable.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public Object getValue(ELContext context,
Object base,
Object property) {
context.setPropertyResolved(false);
int i = 0, len = this.elResolvers.size();
ELResolver elResolver;
Object value;
while (i < len) {
elResolver = this.elResolvers.get(i);
value = elResolver.getValue(context, base, property);
if (context.isPropertyResolved()) {
return value;
}
i++;
}
return ELContext.UNRESOLVABLE_RESULT;
}
/**
* For a given base and property, attempts to
* identify the most general type that is acceptable for an object to be
* passed as the value parameter in a future call
* to the {@link #setValue} method. The result is obtained by
* querying all component resolvers.
*
* propertyResolved property of the
* ELContext object must be set to true
* by the resolver, before returning. If this property is not
* true after this method is called, the caller should ignore
* the return value.propertyResolved is set to false on
* the provided ELContext.
*
getType() method is called, passing in
* the provided context, base and
* property.ELContext's propertyResolved
* flag is false then iteration continues.getType() is
* returned by this method.null is returned and the
* propertyResolved flag remains set to
* falsenull to analyze a top-level variable.
* @param property The property or variable to return the acceptable
* type for.
* @return If the propertyResolved property of
* ELContext was set to true, then
* the most general acceptable type; otherwise undefined.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if the given (base, property) pair
* is handled by this ELResolver but the specified
* variable or property does not exist or is not readable.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public Class> getType(ELContext context,
Object base,
Object property) {
context.setPropertyResolved(false);
int i = 0, len = this.elResolvers.size();
ELResolver elResolver;
Class> type;
while (i < len) {
elResolver = this.elResolvers.get(i);
type = elResolver.getType(context, base, property);
if (context.isPropertyResolved()) {
return type;
}
i++;
}
return null;
}
/**
* Attempts to set the value of the given property
* object on the given base object. All component
* resolvers are asked to attempt to set the value.
*
* propertyResolved property of the
* ELContext object must be set to true
* by the resolver, before returning. If this property is not
* true after this method is called, the caller can
* safely assume no value has been set.propertyResolved is set to false on
* the provided ELContext.
*
setValue() method is called, passing in
* the provided context, base,
* property and value.ELContext's propertyResolved
* flag is false then iteration continues.propertyResolved flag remains set to
* falsenull to set a top-level variable.
* @param property The property or variable to be set.
* @param val The value to set the property or variable to.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if the given (base, property) pair
* is handled by this ELResolver but the specified
* variable or property does not exist.
* @throws PropertyNotWritableException if the given (base, property)
* pair is handled by this ELResolver but the specified
* variable or property is not writable.
* @throws ELException if an exception was thrown while attempting to
* set the property or variable. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public void setValue(ELContext context,
Object base,
Object property,
Object val) {
context.setPropertyResolved(false);
int i = 0, len = this.elResolvers.size();
ELResolver elResolver;
while (i < len) {
elResolver = this.elResolvers.get(i);
elResolver.setValue(context, base, property, val);
if (context.isPropertyResolved()) {
return;
}
i++;
}
}
/**
* For a given base and property, attempts to
* determine whether a call to {@link #setValue} will always fail. The
* result is obtained by querying all component resolvers.
*
* propertyResolved property of the
* ELContext object must be set to true
* by the resolver, before returning. If this property is not
* true after this method is called, the caller should ignore
* the return value.propertyResolved is set to false on
* the provided ELContext.
*
isReadOnly() method is called, passing in
* the provided context, base and
* property.ELContext's propertyResolved
* flag is false then iteration continues.isReadOnly() is
* returned by this method.false is returned and the
* propertyResolved flag remains set to
* falsenull to analyze a top-level variable.
* @param property The property or variable to return the read-only status
* for.
* @return If the propertyResolved property of
* ELContext was set to true, then
* true if the property is read-only or
* false if not; otherwise undefined.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if the given (base, property) pair
* is handled by this ELResolver but the specified
* variable or property does not exist.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public boolean isReadOnly(ELContext context,
Object base,
Object property) {
context.setPropertyResolved(false);
int i = 0, len = this.elResolvers.size();
ELResolver elResolver;
boolean readOnly;
while (i < len) {
elResolver = this.elResolvers.get(i);
readOnly = elResolver.isReadOnly(context, base, property);
if (context.isPropertyResolved()) {
return readOnly;
}
i++;
}
return false; // Does not matter
}
/**
* Returns information about the set of variables or properties that
* can be resolved for the given base object. One use for
* this method is to assist tools in auto-completion. The results are
* collected from all component resolvers.
*
* propertyResolved property of the
* ELContext is not relevant to this method.
* The results of all ELResolvers are concatenated.Iterator returned is an iterator over the
* collection of FeatureDescriptor objects returned by
* the iterators returned by each component resolver's
* getFeatureDescriptors method. If null is
* returned by a resolver, it is skipped.null to enumerate the set of
* top-level variables that this resolver can evaluate.
* @return An Iterator containing zero or more (possibly
* infinitely more) FeatureDescriptor objects, or
* null if this resolver does not handle the given
* base object or that the results are too complex to
* represent with this method
*/
public Iteratorproperty argument, given a base object.
* One use for this method is to assist tools in auto-completion. The
* result is obtained by querying all component resolvers.
*
* Class returned is the most specific class that is
* a common superclass of all the classes returned by each component
* resolver's getCommonPropertyType method. If
* null is returned by a resolver, it is skipped.null to enumerate the set of
* top-level variables that this resolver can evaluate.
* @return null if this ELResolver does not
* know how to handle the given base object; otherwise
* Object.class if any type of property
* is accepted; otherwise the most general property
* type accepted for the given base.
*/
public Class> getCommonPropertyType(ELContext context,
Object base) {
Class> commonPropertyType = null;
IteratorELContext must be
* provided. The ELContext holds:
*
*
ELResolversELResolver may need access to a different context object.
* For example, JSP and Faces resolvers need access to a
* {@link javax.servlet.jsp.JspContext} and a
* {@link javax.faces.context.FacesContext}, respectively.ELContext objects is controlled through
* the underlying technology. For example, in JSP the
* JspContext.getELContext() factory method is used.
* Some technologies provide the ability to add an {@link ELContextListener}
* so that applications and frameworks can ensure their own context objects
* are attached to any newly created ELContext.ELContext object is not thread-safe. Care should be taken
* to never share an ELContext instance between two or more
* threads.ELResolver has successfully
* resolved a given (base, property) pair.
*
* ELContext.
*
* ELContext maintains a collection of context objects
* relevant to the evaluation of an expression. These context objects
* are used by ELResolvers. This method is used to
* add a context object to that collection.contextObject will be of the
* type specified by the key. However, this is not
* required and the key is used strictly as a unique identifier.ELContext maintains a collection of context objects
* relevant to the evaluation of an expression. These context objects
* are used by ELResolvers. This method is used to
* retrieve the context with the given key from the collection.key. However, this is not required and the key is
* used strictly as a unique identifier.ELContext.
* @return The context object associated with the given key, or null
* if no such context was found.
* @throws NullPointerException if key is null.
*/
public Object getContext(Class key) {
if(key == null) {
throw new NullPointerException();
}
return map.get(key);
}
/**
* Retrieves the ELResolver associated with this context.
*
* ELContext maintains a reference to the
* ELResolver that will be consulted to resolve variables
* and properties during an expression evaluation. This method
* retrieves the reference to the resolver.ELContext is constructed, the reference to the
* ELResolver associated with the context cannot be changed.FunctionMapper associated with this
* ELContext.
*
* @return The function mapper to be consulted for the resolution of
* EL functions.
*/
public abstract FunctionMapper getFunctionMapper();
/**
* Holds value of property locale.
*/
private Locale locale;
/**
* Get the Locale stored by a previous invocation to
* {@link #setLocale}. If this method returns non null,
* this Locale must be used for all localization needs
* in the implementation. The Locale must not be cached
* to allow for applications that change Locale dynamically.
*
* @return The Locale in which this instance is operating.
* Used primarily for message localization.
*/
public Locale getLocale() {
return this.locale;
}
/**
* Set the Locale for this instance. This method may be
* called by the party creating the instance, such as JavaServer
* Faces or JSP, to enable the EL implementation to provide localized
* messages to the user. If no Locale is set, the implementation
* must use the locale returned by Locale.getDefault( ).
*/
public void setLocale(Locale locale) {
this.locale = locale;
}
/**
* Retrieves the VariableMapper associated with this
* ELContext.
*
* @return The variable mapper to be consulted for the resolution of
* EL variables.
*/
public abstract VariableMapper getVariableMapper();
private boolean resolved;
private HashMap map = new HashMap();
}
beansbinding-1.2.1/src/org/jdesktop/el/ELContextEvent.java 0000664 0000000 0000000 00000001750 11516446575 0023472 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
/**
* An event which indicates that an {@link ELContext} has been created.
* The source object is the ELContext that was created.
*
* @see ELContext
* @see ELContextListener
* @since JSP 2.1
*/
public class ELContextEvent extends java.util.EventObject {
/**
* Constructs an ELContextEvent object to indicate that an
* ELContext has been created.
*
* @param source the ELContext that was created.
*/
public ELContextEvent(ELContext source) {
super(source);
}
/**
* Returns the ELContext that was created.
* This is a type-safe equivalent of the {@link #getSource} method.
*
* @return the ELContext that was created.
*/
public ELContext getELContext() {
return (ELContext) getSource();
}
}
beansbinding-1.2.1/src/org/jdesktop/el/ELContextListener.java 0000664 0000000 0000000 00000001106 11516446575 0024171 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
/**
* The listener interface for receiving notification when an
* {@link ELContext} is created.
*
* @see ELContext
* @see ELContextEvent
* @since JSP 2.1
*/
public interface ELContextListener extends java.util.EventListener {
/**
* Invoked when a new ELContext has been created.
*
* @param ece the notification event.
*/
public void contextCreated(ELContextEvent ece);
}
beansbinding-1.2.1/src/org/jdesktop/el/ELException.java 0000664 0000000 0000000 00000002705 11516446575 0023003 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
/**
* Represents any of the exception conditions that can arise during
* expression evaluation.
*
* @since JSP 2.1
*/
public class ELException extends RuntimeException {
//-------------------------------------
/**
* Creates an ELException with no detail message.
*/
public ELException () {
super ();
}
//-------------------------------------
/**
* Creates an ELException with the provided detail message.
*
* @param pMessage the detail message
*/
public ELException (String pMessage) {
super (pMessage);
}
//-------------------------------------
/**
* Creates an ELException with the given cause.
*
* @param pRootCause the originating cause of this exception
*/
public ELException (Throwable pRootCause) {
super( pRootCause );
}
//-------------------------------------
/**
* Creates an ELException with the given detail message and root cause.
*
* @param pMessage the detail message
* @param pRootCause the originating cause of this exception
*/
public ELException (String pMessage,
Throwable pRootCause) {
super (pMessage, pRootCause);
}
}
beansbinding-1.2.1/src/org/jdesktop/el/ELResolver.java 0000664 0000000 0000000 00000041275 11516446575 0022653 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
import java.util.Iterator;
import java.beans.FeatureDescriptor;
/**
* Enables customization of variable and property resolution behavior for EL
* expression evaluation.
*
* ELResolver associated
* with the {@link ELContext} is consulted to do the initial resolution of
* the first variable of an expression. It is also consulted when a
* . or [] operator is encountered, except for the
* last such operator in a method expression, in which case the resultion
* rules are hard coded.${employee.lastName},
* the ELResolver determines what object employee
* refers to, and what it means to get the lastName property on
* that object.base
* and property parameter. In the case of variable resolution
* (e.g. determining what employee refers to in
* ${employee.lastName}), the base parameter will
* be null and the property parameter will always
* be of type String. In this case, if the property
* is not a String, the behavior of the ELResolver
* is undefined.base parameter
* identifies the base object and the property object identifies
* the property on that base. For example, in the expression
* ${employee.lastName}, base is the result of the
* variable resolution for employee and property
* is the string "lastName". In the expression
* ${y[x]}, base is the result of the variable
* resolution for y and property is the result of
* the variable resolution for x.ELResolver is associated with an
* ELContext, there are usually multiple resolvers considered
* for any given variable or property resolution. ELResolvers
* are combined together using {@link CompositeELResolver}s, to define
* rich semantics for evaluating an expression.ELResolver is not
* responsible for resolving all possible (base, property) pairs. In fact,
* most resolvers will only handle a base of a single type.
* To indicate that a resolver has successfully resolved a particular
* (base, property) pair, it must set the propertyResolved
* property of the ELContext to true. If it could
* not handle the given pair, it must leave this property alone. The caller
* must ignore the return value of the method if propertyResolved
* is false.FeatureDescriptor that specifies the runtime type of
* the variable or property.FeatureDescriptor that specifies whether the
* variable or property can be resolved at runtime.property object on the given
* base object.
*
* propertyResolved property of the
* ELContext object must be set to true
* by the resolver, before returning. If this property is not
* true after this method is called, the caller should ignore
* the return value.null to resolve a top-level variable.
* @param property The property or variable to be resolved.
* @return If the propertyResolved property of
* ELContext was set to true, then
* the result of the variable or property resolution; otherwise
* undefined.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if the given (base, property) pair
* is handled by this ELResolver but the specified
* variable or property does not exist or is not readable.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public abstract Object getValue(ELContext context,
Object base,
Object property);
/**
* For a given base and property, attempts to
* identify the most general type that is acceptable for an object to be
* passed as the value parameter in a future call
* to the {@link #setValue} method.
*
* propertyResolved property of the
* ELContext object must be set to true
* by the resolver, before returning. If this property is not
* true after this method is called, the caller should ignore
* the return value.getValue().getClass().
* For example, in the case of an {@link ArrayELResolver}, the
* getType method will return the element type of the
* array, which might be a superclass of the type of the actual
* element that is currently in the specified array element.null to analyze a top-level variable.
* @param property The property or variable to return the acceptable
* type for.
* @return If the propertyResolved property of
* ELContext was set to true, then
* the most general acceptable type; otherwise undefined.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if the given (base, property) pair
* is handled by this ELResolver but the specified
* variable or property does not exist or is not readable.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public abstract Class> getType(ELContext context,
Object base,
Object property);
/**
* Attempts to set the value of the given property
* object on the given base object.
*
* propertyResolved property of the
* ELContext object must be set to true
* by the resolver, before returning. If this property is not
* true after this method is called, the caller can
* safely assume no value has been set.null to set a top-level variable.
* @param property The property or variable to be set.
* @param value The value to set the property or variable to.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if the given (base, property) pair
* is handled by this ELResolver but the specified
* variable or property does not exist.
* @throws PropertyNotWritableException if the given (base, property)
* pair is handled by this ELResolver but the specified
* variable or property is not writable.
* @throws ELException if an exception was thrown while attempting to
* set the property or variable. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public abstract void setValue(ELContext context,
Object base,
Object property,
Object value);
/**
* For a given base and property, attempts to
* determine whether a call to {@link #setValue} will always fail.
*
* propertyResolved property of the
* ELContext object must be set to true
* by the resolver, before returning. If this property is not
* true after this method is called, the caller should ignore
* the return value.null to analyze a top-level variable.
* @param property The property or variable to return the read-only status
* for.
* @return If the propertyResolved property of
* ELContext was set to true, then
* true if the property is read-only or
* false if not; otherwise undefined.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if the given (base, property) pair
* is handled by this ELResolver but the specified
* variable or property does not exist.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public abstract boolean isReadOnly(ELContext context,
Object base,
Object property);
/**
* Returns information about the set of variables or properties that
* can be resolved for the given base object. One use for
* this method is to assist tools in auto-completion.
*
* base parameter is null, the
* resolver must enumerate the list of top-level variables it can
* resolve.Iterator returned must contain zero or more
* instances of {@link java.beans.FeatureDescriptor}, in no guaranteed
* order. In the case of primitive types such as int, the
* value null must be returned. This is to prevent the
* useless iteration through all possible primitive values. A
* return value of null indicates that this resolver does
* not handle the given base object or that the results
* are too complex to represent with this method and the
* {@link #getCommonPropertyType} method should be used instead.FeatureDescriptor will contain information about
* a single variable or property. In addition to the standard
* properties, the FeatureDescriptor must have two
* named attributes (as set by the setValue method):
*
*
java.lang.Class and specify the
* runtime type of the variable or property.java.lang.Boolean and indicates whether it is safe
* to attempt to resolve this property at design-time. For
* instance, it may be unsafe to attempt a resolution at design
* time if the ELResolver needs access to a resource
* that is only available at runtime and no acceptable simulated
* value can be provided.Iterator
* returned might iterate through a very large or even infinitely large
* set of properties. Care should be taken by the caller to not get
* stuck in an infinite loop.ELResolvers
* will return completely accurate results, but all must be callable
* at both design-time and runtime (i.e. whether or not
* Beans.isDesignTime() returns true),
* without causing errors.propertyResolved property of the
* ELContext is not relevant to this method.
* The results of all ELResolvers are concatenated
* in the case of composite resolvers.null to enumerate the set of
* top-level variables that this resolver can evaluate.
* @return An Iterator containing zero or more (possibly
* infinitely more) FeatureDescriptor objects, or
* null if this resolver does not handle the given
* base object or that the results are too complex to
* represent with this method
* @see java.beans.FeatureDescriptor
*/
public abstract Iteratorproperty argument, given a base object.
* One use for this method is to assist tools in auto-completion.
*
* int as a
* property, so the return value would be
* Integer.class.null to enumerate the set of
* top-level variables that this resolver can evaluate.
* @return null if this ELResolver does not
* know how to handle the given base object; otherwise
* Object.class if any type of property
* is accepted; otherwise the most general property
* type accepted for the given base.
*/
public abstract Class> getCommonPropertyType(ELContext context,
Object base);
}
beansbinding-1.2.1/src/org/jdesktop/el/ELUtil.java 0000664 0000000 0000000 00000012543 11516446575 0021763 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
*
* ThreadLocal variable used to record the
* {@link FacesContext} instance for each processing thread.context for a Locale. If
* not present, use Locale.getDefault(). Load the
* ResourceBundle "javax.el.Messages" using that locale. Get
* the message string for argument messageId. If not found
* return "Missing Resource in EL implementation ??? messageId ???"
* with messageId substituted with the runtime
* value of argument messageId. If found, and argument
* params is non-null, format the message using the
* params. If formatting fails, return a sensible message including
* the messageId. If argument params is
* null, skip formatting and return the message directly, otherwise
* return the formatted message.equals() and
* hashCode() methods so that two expressions can be compared
* for equality. They are redefined abstract in this class to force their
* implementation in subclasses.Serializable so that they
* can be saved and restored.Expressions are also designed to be immutable so
* that only one instance needs to be created for any given expression
* String / {@link FunctionMapper}. This allows a container to pre-create
* expressions and not have to re-parse them each time they are evaluated.Expression,
* unmodified.
*
* Expression.Expression.
*
* true if and only if the argument is
* not null, is an Expression object that
* is the of the same type (ValueExpression or
* MethodExpression), and has an identical parsed
* representation.${fn1:foo()}
* and ${fn2:foo()} are equal if their corresponding
* FunctionMappers mapped fn1:foo and
* fn2:foo to the same method.Object to test for equality.
* @return true if obj equals this
* Expression; false otherwise.
* @see java.util.Hashtable
* @see java.lang.Object#equals(java.lang.Object)
*/
public abstract boolean equals(Object obj);
/**
* Returns the hash code for this Expression.
*
* equals(Object)
* method, then calling the hashCode method on each of the
* two objects must produce the same integer result. Implementations must
* take special note and implement hashCode correctly.Expression.
* @see #equals
* @see java.util.Hashtable
* @see java.lang.Object#hashCode()
*/
public abstract int hashCode();
/**
* Returns whether this expression was created from only literal text.
*
* true if and only if the
* expression string this expression was created from contained no
* unescaped EL delimeters (${...} or
* #{...}).true if this expression was created from only
* literal text; false otherwise.
*/
public abstract boolean isLiteralText();
public static final class Result {
private final Type type;
private final Object result;
private final ListString into a {@link ValueExpression} or
* {@link MethodExpression} instance for later evaluation.
*
* ValueExpression or MethodExpression.Serializable.createValueExpression and
* createMethodExpression methods must be thread-safe. That is,
* multiple threads may call these methods on the same
* ExpressionFactory object simultaneously. Implementations
* should synchronize access if they depend on transient state.
* Implementations should not, however, assume that only one object of
* each ExpressionFactory type will be instantiated; global
* caching should therefore be static.ExpressionFactory must be able to handle the following
* types of input for the expression parameter:
*
*
${} delimiter
* (e.g. "${employee.lastName}").#{} delimiter
* (e.g. "#{employee.lastName}").${} or #{}
* delimiters (e.g. "John Doe")."${employee.firstName}${employee.lastName}" or
* "#{employee.firstName}#{employee.lastName}")."Name: ${employee.firstName} ${employee.lastName}").
*
"${employee.firstName}#{employee.lastName}")."Name: ${employee.firstName} #{employee.lastName}").ELException.FunctionMapper and VariableMapper
* stored in the ELContext
* are used to resolve functions and variables found in
* the expression. They can be null, in which case
* functions or variables are not supported for this expression.
* The object
* returned must invoke the same functions and access the same
* variable mappings
* regardless of whether
* the mappings in the provided FunctionMapper
* and VariableMapper instances
* change between calling
* ExpressionFactory.createValueExpression() and any
* method on ValueExpression.
* getValue() method, optionally coerced.
*
* @param instance The object instance to be wrapped.
* @param expectedType The type the result of the expression
* will be coerced to after evaluation. There will be no
* coercion if it is Object.class,
*/
public abstract ValueExpression createValueExpression(
Object instance,
Class> expectedType);
/**
* Parses an expression into a {@link MethodExpression} for later
* evaluation. Use this method for expressions that refer to methods.
*
* MethodExpression
* is created, which when invoked, returns the String literal,
* coerced to expectedReturnType. An ELException is thrown if
* expectedReturnType is void or if the coercion of the String literal
* to the expectedReturnType yields an error (see Section "1.16 Type
* Conversion").
* ELException.FunctionMapper and VariableMapper
* stored in the ELContext
* are used to resolve functions and variables found in
* the expression. They can be null, in which
* case functions or variables are not supported for this expression.
* The object
* returned must invoke the same functions and access the same variable
* mappings
* regardless of whether
* the mappings in the provided FunctionMapper
* and VariableMapper instances
* change between calling
* ExpressionFactory.createMethodExpression() and any
* method on MethodExpression.
* MethodExpression must check that the return type of
* the actual method matches this type. Passing in a value of
* null indicates the caller does not care what the
* return type is, and the check is disabled.
* @param expectedParamTypes The expected parameter types for the method to
* be found. Must be an array with no elements if there are
* no parameters expected. It is illegal to pass null.
* @return The parsed expression
* @throws ELException Thrown if there are syntactical errors in the
* provided expression.
* @throws NullPointerException if paramTypes is null.
*/
public abstract MethodExpression createMethodExpression(
ELContext context,
String expression,
Class> expectedReturnType,
Class>[] expectedParamTypes);
/**
* Coerces an object to a specific type according to the
* EL type conversion rules.
*
* ELException is thrown if an error results from
* applying the conversion rules.
* FunctionMapper maps ${prefix:name()}
* style functions to a static method that can execute that function.java.lang.Method.
*
* null if no function could be found that matches
* the given prefix and local name."fn" in ${fn:method()}, or
* "" in ${method()}.
* @param localName the short name of the function. For example,
* "method" in ${fn:method()}.
* @return the static method to invoke, or null if no
* match was found.
*/
public abstract java.lang.reflect.Method resolveFunction(String prefix,
String localName);
}
beansbinding-1.2.1/src/org/jdesktop/el/ListELResolver.java 0000664 0000000 0000000 00000036214 11516446575 0023504 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
import java.util.List;
import java.util.Iterator;
import java.util.Collections;
import java.util.ArrayList;
import java.beans.FeatureDescriptor;
/**
* Defines property resolution behavior on instances of {@link java.util.List}.
*
* java.util.List.
* It accepts any object as a property and coerces that object into an
* integer index into the list. The resulting value is the value in the list
* at that index.true and
* {@link #setValue} will always throw
* PropertyNotWritableException.ELResolvers are combined together using
* {@link CompositeELResolver}s, to define rich semantics for evaluating
* an expression. See the javadocs for {@link ELResolver} for details.ListELResolver.
*/
public ListELResolver() {
this.isReadOnly = false;
}
/**
* Creates a new ListELResolver whose read-only status is
* determined by the given parameter.
*
* @param isReadOnly true if this resolver cannot modify
* lists; false otherwise.
*/
public ListELResolver(boolean isReadOnly) {
this.isReadOnly = isReadOnly;
}
/**
* If the base object is a list, returns the most general acceptable type
* for a value in this list.
*
* List, the propertyResolved
* property of the ELContext object must be set to
* true by this resolver, before returning. If this property
* is not true after this method is called, the caller
* should ignore the return value.List, this method will always
* return Object.class. This is because Lists
* accept any object as an element.List
* are handled by this resolver.
* @param property The index of the element in the list to return the
* acceptable type for. Will be coerced into an integer, but
* otherwise ignored by this resolver.
* @return If the propertyResolved property of
* ELContext was set to true, then
* the most general acceptable type; otherwise undefined.
* @throws PropertyNotFoundException if the given index is out of
* bounds for this list.
* @throws NullPointerException if context is null
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public Class> getType(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base instanceof List) {
context.setPropertyResolved(true);
List list = (List) base;
int index = toInteger(property);
if (index < 0 || index >= list.size()) {
throw new PropertyNotFoundException();
}
return Object.class;
}
return null;
}
/**
* If the base object is a list, returns the value at the given index.
* The index is specified by the property argument, and
* coerced into an integer. If the coercion could not be performed,
* an IllegalArgumentException is thrown. If the index is
* out of bounds, null is returned.
*
* List, the propertyResolved
* property of the ELContext object must be set to
* true by this resolver, before returning. If this property
* is not true after this method is called, the caller
* should ignore the return value.List are handled by this resolver.
* @param property The index of the value to be returned. Will be coerced
* into an integer.
* @return If the propertyResolved property of
* ELContext was set to true, then
* the value at the given index or null
* if the index was out of bounds. Otherwise, undefined.
* @throws IllegalArgumentException if the property could not be coerced
* into an integer.
* @throws NullPointerException if context is null.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public Object getValue(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base instanceof List) {
context.setPropertyResolved(true);
List list = (List) base;
int index = toInteger(property);
if (index < 0 || index >= list.size()) {
return null;
}
return list.get(index);
}
return null;
}
/**
* If the base object is a list, attempts to set the value at the
* given index with the given value. The index is specified by the
* property argument, and coerced into an integer. If the
* coercion could not be performed, an
* IllegalArgumentException is thrown. If the index is
* out of bounds, a PropertyNotFoundException is thrown.
*
* List, the propertyResolved
* property of the ELContext object must be set to
* true by this resolver, before returning. If this property
* is not true after this method is called, the caller
* can safely assume no value was set.PropertyNotWritableException.List was created using
* {@link java.util.Collections#unmodifiableList}, this method must
* throw PropertyNotWritableException. Unfortunately,
* there is no Collections API method to detect this. However, an
* implementation can create a prototype unmodifiable List
* and query its runtime type to see if it matches the runtime type of
* the base object as a workaround.List are handled by this resolver.
* @param property The index of the value to be set. Will be coerced
* into an integer.
* @param val The value to be set at the given index.
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this list.
* @throws NullPointerException if context is null, or
* if the value is null and this List
* does not support null elements.
* @throws IllegalArgumentException if the property could not be coerced
* into an integer, or if some aspect of the specified element
* prevents it from being added to this list.
* @throws PropertyNotWritableException if this resolver was constructed
* in read-only mode, or if the set operation is not supported by
* the underlying list.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public void setValue(ELContext context,
Object base,
Object property,
Object val) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base instanceof List) {
context.setPropertyResolved(true);
List list = (List) base;
int index = toInteger(property);
if (isReadOnly) {
throw new PropertyNotWritableException();
}
try {
list.set(index, val);
} catch (UnsupportedOperationException ex) {
throw new PropertyNotWritableException();
} catch (IndexOutOfBoundsException ex) {
throw new PropertyNotFoundException();
} catch (ClassCastException ex) {
throw ex;
} catch (NullPointerException ex) {
throw ex;
} catch (IllegalArgumentException ex) {
throw ex;
}
}
}
static private Class> theUnmodifiableListClass =
Collections.unmodifiableList(new ArrayList()).getClass();
/**
* If the base object is a list, returns whether a call to
* {@link #setValue} will always fail.
*
* List, the propertyResolved
* property of the ELContext object must be set to
* true by this resolver, before returning. If this property
* is not true after this method is called, the caller
* should ignore the return value.true.List was created using
* {@link java.util.Collections#unmodifiableList}, this method must
* return true. Unfortunately, there is no Collections API
* method to detect this. However, an implementation can create a
* prototype unmodifiable List and query its runtime type
* to see if it matches the runtime type of the base object as a
* workaround.List
* are handled by this resolver.
* @param property The index of the element in the list to return the
* acceptable type for. Will be coerced into an integer, but
* otherwise ignored by this resolver.
* @return If the propertyResolved property of
* ELContext was set to true, then
* true if calling the setValue method
* will always fail or false if it is possible that
* such a call may succeed; otherwise undefined.
* @throws PropertyNotFoundException if the given index is out of
* bounds for this list.
* @throws NullPointerException if context is null
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public boolean isReadOnly(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base instanceof List) {
context.setPropertyResolved(true);
List list = (List) base;
int index = toInteger(property);
if (index < 0 || index >= list.size()) {
throw new PropertyNotFoundException();
}
return list.getClass() == theUnmodifiableListClass || isReadOnly;
}
return false;
}
/**
* Always returns null, since there is no reason to
* iterate through set set of all integers.
*
* List are
* handled by this resolver.
* @return null.
*/
public Iteratorproperty argument.
* Otherwise, returns null.
*
* List, this method will always
* return Integer.class. This is because Lists
* accept integers as their index.List
* are handled by this resolver.
* @return null if base is not a List; otherwise
* Integer.class.
*/
public Class> getCommonPropertyType(ELContext context,
Object base) {
if (base != null && base instanceof List) {
return Integer.class;
}
return null;
}
private int toInteger(Object p) {
if (p instanceof Integer) {
return ((Integer) p).intValue();
}
if (p instanceof Character) {
return ((Character) p).charValue();
}
if (p instanceof Boolean) {
return ((Boolean) p).booleanValue()? 1: 0;
}
if (p instanceof Number) {
return ((Number) p).intValue();
}
if (p instanceof String) {
return Integer.parseInt((String) p);
}
throw new IllegalArgumentException();
}
private boolean isReadOnly;
}
beansbinding-1.2.1/src/org/jdesktop/el/MapELResolver.java 0000664 0000000 0000000 00000036742 11516446575 0023314 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
import java.beans.FeatureDescriptor;
import java.util.Map;
import java.util.Iterator;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
/**
* Defines property resolution behavior on instances of {@link java.util.Map}.
*
* java.util.Map.
* It accepts any object as a property and uses that object as a key in
* the map. The resulting value is the value in the map that is associated with
* that key.true and
* {@link #setValue} will always throw
* PropertyNotWritableException.ELResolvers are combined together using
* {@link CompositeELResolver}s, to define rich semantics for evaluating
* an expression. See the javadocs for {@link ELResolver} for details.MapELResolver.
*/
public MapELResolver() {
this.isReadOnly = false;
}
/**
* Creates a new MapELResolver whose read-only status is
* determined by the given parameter.
*
* @param isReadOnly true if this resolver cannot modify
* maps; false otherwise.
*/
public MapELResolver(boolean isReadOnly) {
this.isReadOnly = isReadOnly;
}
/**
* If the base object is a map, returns the most general acceptable type
* for a value in this map.
*
* Map, the propertyResolved
* property of the ELContext object must be set to
* true by this resolver, before returning. If this property
* is not true after this method is called, the caller
* should ignore the return value.Map, this method will always
* return Object.class. This is because Maps
* accept any object as the value for a given key.Map
* are handled by this resolver.
* @param property The key to return the acceptable type for.
* Ignored by this resolver.
* @return If the propertyResolved property of
* ELContext was set to true, then
* the most general acceptable type; otherwise undefined.
* @throws NullPointerException if context is null
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public Class> getType(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base instanceof Map) {
context.setPropertyResolved(true);
return Object.class;
}
return null;
}
/**
* If the base object is a map, returns the value associated with the
* given key, as specified by the property argument. If the
* key was not found, null is returned.
*
* Map, the propertyResolved
* property of the ELContext object must be set to
* true by this resolver, before returning. If this property
* is not true after this method is called, the caller
* should ignore the return value.null
* is returned doesn't mean there is no mapping for the key; it's also
* possible that the Map explicitly maps the key to
* null.Map
* are handled by this resolver.
* @param property The key whose associated value is to be returned.
* @return If the propertyResolved property of
* ELContext was set to true, then
* the value associated with the given key or null
* if the key was not found. Otherwise, undefined.
* @throws ClassCastException if the key is of an inappropriate type
* for this map (optionally thrown by the underlying Map).
* @throws NullPointerException if context is null, or if
* the key is null and this map does not permit null keys (the
* latter is optionally thrown by the underlying Map).
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public Object getValue(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base instanceof Map) {
context.setPropertyResolved(true);
Map map = (Map) base;
return map.get(property);
}
return null;
}
static private Class> theUnmodifiableMapClass =
Collections.unmodifiableMap(new HashMap()).getClass();
/**
* If the base object is a map, attempts to set the value associated with
* the given key, as specified by the property argument.
*
* Map, the propertyResolved
* property of the ELContext object must be set to
* true by this resolver, before returning. If this property
* is not true after this method is called, the caller
* can safely assume no value was set.PropertyNotWritableException.Map was created using
* {@link java.util.Collections#unmodifiableMap}, this method must
* throw PropertyNotWritableException. Unfortunately,
* there is no Collections API method to detect this. However, an
* implementation can create a prototype unmodifiable Map
* and query its runtime type to see if it matches the runtime type of
* the base object as a workaround.Map
* are handled by this resolver.
* @param property The key with which the specified value is to be
* associated.
* @param val The value to be associated with the specified key.
* @throws ClassCastException if the class of the specified key or
* value prevents it from being stored in this map.
* @throws NullPointerException if context is null, or if
* this map does not permit null keys or values, and
* the specified key or value is null.
* @throws IllegalArgumentException if some aspect of this key or
* value prevents it from being stored in this map.
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
* @throws PropertyNotWritableException if this resolver was constructed
* in read-only mode, or if the put operation is not supported by
* the underlying map.
*/
public void setValue(ELContext context,
Object base,
Object property,
Object val) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base instanceof Map) {
context.setPropertyResolved(true);
Map map = (Map) base;
if (isReadOnly || map.getClass() == theUnmodifiableMapClass) {
throw new PropertyNotWritableException();
}
map.put(property, val);
}
}
/**
* If the base object is a map, returns whether a call to
* {@link #setValue} will always fail.
*
* Map, the propertyResolved
* property of the ELContext object must be set to
* true by this resolver, before returning. If this property
* is not true after this method is called, the caller
* should ignore the return value.true.Map was created using
* {@link java.util.Collections#unmodifiableMap}, this method must
* return true. Unfortunately, there is no Collections API
* method to detect this. However, an implementation can create a
* prototype unmodifiable Map and query its runtime type
* to see if it matches the runtime type of the base object as a
* workaround.Map
* are handled by this resolver.
* @param property The key to return the read-only status for.
* Ignored by this resolver.
* @return If the propertyResolved property of
* ELContext was set to true, then
* true if calling the setValue method
* will always fail or false if it is possible that
* such a call may succeed; otherwise undefined.
* @throws NullPointerException if context is null
* @throws ELException if an exception was thrown while performing
* the property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public boolean isReadOnly(ELContext context,
Object base,
Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base != null && base instanceof Map) {
context.setPropertyResolved(true);
Map map = (Map) base;
return isReadOnly || map.getClass() == theUnmodifiableMapClass;
}
return false;
}
/**
* If the base object is a map, returns an Iterator
* containing the set of keys available in the Map.
* Otherwise, returns null.
*
* Iterator returned must contain zero or more
* instances of {@link java.beans.FeatureDescriptor}. Each info object
* contains information about a key in the Map, and is initialized as
* follows:
*
*
toString method on this key, or
* "null" if the key is null.falsefalsetrueFeatureDescriptors:
*
*
getClass()
* method on this key, or null if the key is
* null.trueMap are handled by this resolver.
* @return An Iterator containing zero or more (possibly
* infinitely more) FeatureDescriptor objects, each
* representing a key in this map, or null if
* the base object is not a map.
*/
public Iteratorproperty argument.
* Otherwise, returns null.
*
* Map, this method will always
* return Object.class. This is because Maps
* accept any object as a key.Map
* are handled by this resolver.
* @return null if base is not a Map; otherwise
* Object.class.
*/
public Class> getCommonPropertyType(ELContext context,
Object base) {
if (base != null && base instanceof Map) {
return Object.class;
}
return null;
}
private boolean isReadOnly;
}
beansbinding-1.2.1/src/org/jdesktop/el/MethodExpression.java 0000664 0000000 0000000 00000012054 11516446575 0024122 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
/**
* An Expression that refers to a method on an object.
*
* MethodExpression that encapsulates the parsed expression.
* The {@link FunctionMapper} is used at parse time, not evaluation time,
* so one is not needed to evaluate an expression using this class.
* However, the {@link ELContext} is needed at evaluation time.ELContext is used to resolve the top-level variables and to
* determine the behavior of the . and []
* operators. For any of the two methods, the {@link ELResolver#getValue}
* method is used to resolve all properties up to but excluding the last
* one. This provides the base object on which the method
* appears. If the base object is null, a
* PropertyNotFoundException must be thrown.
* At the last resolution,
* the final property is then coerced to a String,
* which provides the name of the method to be found. A method matching the
* name and expected parameters provided at parse time is found and it is
* either queried or invoked (depending on the method called on this
* MethodExpression).MethodInfo containing information
* about the method the expression evaluated to.
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if one of the property
* resolutions failed because a specified variable or property
* does not exist or is not readable.
* @throws MethodNotFoundException if no suitable method can be found.
* @throws ELException if an exception was thrown while performing
* property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public abstract MethodInfo getMethodInfo(ELContext context);
/**
* If a String literal is specified as the expression, returns the
* String literal coerced to the expected return type of the method
* signature. An ELException is thrown if
* expectedReturnType is void or if the coercion of the String literal
* to the expectedReturnType yields an error (see Section "1.16 Type
* Conversion" of the EL specification).
*
* If not a String literal, evaluates the expression
* relative to the provided context, invokes the method that was
* found using the supplied parameters, and returns the result of
* the method invocation.
*
* Any parameters passed to this method is ignored if isLiteralText()
* is true.
*
* @param context The context of this evaluation.
* @param params The parameters to pass to the method, or
* null if no parameters.
* @return the result of the method invocation (null if
* the method has a void return type).
* @throws NullPointerException if context is null
* @throws PropertyNotFoundException if one of the property
* resolutions failed because a specified variable or property
* does not exist or is not readable.
* @throws MethodNotFoundException if no suitable method can be found.
* @throws ELException if a String literal is specified and
* expectedReturnType of the MethodExpression is void or if the coercion of the String literal
* to the expectedReturnType yields an error (see Section "1.16 Type
* Conversion").
* @throws ELException if
* an exception was thrown while performing
* property or variable resolution. The thrown exception must be
* included as the cause property of this exception, if
* available. If the exception thrown is an
* InvocationTargetException, extract its
* cause and pass it to the
* ELException constructor.
*/
public abstract Object invoke(ELContext context, Object[] params);
}
beansbinding-1.2.1/src/org/jdesktop/el/MethodInfo.java 0000664 0000000 0000000 00000002702 11516446575 0022655 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
/**
* Holds information about a method that a {@link MethodExpression}
* evaluated to.
*
* @since JSP 2.1
*/
public class MethodInfo {
/**
* Creates a new instance of MethodInfo with the given
* information.
*
* @param name The name of the method
* @param returnType The return type of the method
* @param paramTypes The types of each of the method's parameters
*/
public MethodInfo(String name, Class> returnType, Class>[] paramTypes) {
this.name = name;
this.returnType = returnType;
this.paramTypes = paramTypes;
}
/**
* Returns the name of the method
*
* @return the name of the method
*/
public String getName() {
return this.name;
}
/**
* Returns the return type of the method
*
* @return the return type of the method
*/
public Class> getReturnType() {
return this.returnType;
}
/**
* Returns the parameter types of the method
*
* @return the parameter types of the method
*/
public Class>[] getParamTypes() {
return this.paramTypes;
}
private String name;
private Class> returnType;
private Class>[] paramTypes;
}
beansbinding-1.2.1/src/org/jdesktop/el/MethodNotFoundException.java 0000664 0000000 0000000 00000002621 11516446575 0025375 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
/**
* Thrown when a method could not be found while evaluating a
* {@link MethodExpression}.
*
* @see MethodExpression
* @since JSP 2.1
*/
public class MethodNotFoundException extends ELException {
/**
* Creates a MethodNotFoundException with no detail message.
*/
public MethodNotFoundException() {
super ();
}
/**
* Creates a MethodNotFoundException with the provided
* detail message.
*
* @param message the detail message
*/
public MethodNotFoundException(String message) {
super (message);
}
/**
* Creates a MethodNotFoundException with the given root
* cause.
*
* @param exception the originating cause of this exception
*/
public MethodNotFoundException(Throwable exception) {
super (exception);
}
/**
* Creates a MethodNotFoundException with the given detail
* message and root cause.
*
* @param pMessage the detail message
* @param pRootCause the originating cause of this exception
*/
public MethodNotFoundException(String pMessage, Throwable pRootCause) {
super (pMessage, pRootCause);
}
}
beansbinding-1.2.1/src/org/jdesktop/el/PrivateMessages.properties 0000664 0000000 0000000 00000001262 11516446575 0025176 0 ustar 00root root 0000000 0000000 # Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
# subject to license terms.
#
# This properties file is essentially "package private" but because
# there is no way to attach an access specifier to a properties file we
# are including this comment to serve as such.
setPropertyFailed=Can''t set property ''{0}'' on class ''{1}'' to value ''{2}''.
propertyNotFound=The class ''{0}'' does not have the property ''{1}''.
propertyNotreadable=The class ''{0}'' does not have a readable property ''{1}''.
resolverNotWritable=The ELResolver for the class ''{0}'' is not writable.
propertyNotWritable=The class ''{0}'' does not have a writable property ''{1}''.
beansbinding-1.2.1/src/org/jdesktop/el/PropertyNotFoundException.java 0000664 0000000 0000000 00000003304 11516446575 0026000 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
/**
* Thrown when a property could not be found while evaluating a
* {@link ValueExpression} or {@link MethodExpression}.
*
* PropertyNotFoundException with no detail message.
*/
public PropertyNotFoundException() {
super ();
}
//-------------------------------------
/**
* Creates a PropertyNotFoundException with the provided
* detail message.
*
* @param message the detail message
*/
public PropertyNotFoundException(String message) {
super (message);
}
/**
* Creates a PropertyNotFoundException with the given root
* cause.
*
* @param exception the originating cause of this exception
*/
public PropertyNotFoundException(Throwable exception) {
super (exception);
}
/**
* Creates a PropertyNotFoundException with the given detail
* message and root cause.
*
* @param pMessage the detail message
* @param pRootCause the originating cause of this exception
*/
public PropertyNotFoundException(String pMessage, Throwable pRootCause) {
super (pMessage, pRootCause);
}
}
beansbinding-1.2.1/src/org/jdesktop/el/PropertyNotWritableException.java 0000664 0000000 0000000 00000003345 11516446575 0026503 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
/**
* Thrown when a property could not be written to while setting the
* value on a {@link ValueExpression}.
*
* PropertyNotWritableException with no detail
* message.
*/
public PropertyNotWritableException() {
super ();
}
//-------------------------------------
/**
* Creates a PropertyNotWritableException with the
* provided detail message.
*
* @param pMessage the detail message
*/
public PropertyNotWritableException(String pMessage) {
super (pMessage);
}
//-------------------------------------
/**
* Creates a PropertyNotWritableException with the given root
* cause.
*
* @param exception the originating cause of this exception
*/
public PropertyNotWritableException(Throwable exception) {
super (exception);
}
//-------------------------------------
/**
* Creates a PropertyNotWritableException with the given
* detail message and root cause.
*
* @param pMessage the detail message
* @param pRootCause the originating cause of this exception
*/
public PropertyNotWritableException(String pMessage, Throwable pRootCause) {
super (pMessage, pRootCause);
}
}
beansbinding-1.2.1/src/org/jdesktop/el/ResourceBundleELResolver.java 0000664 0000000 0000000 00000025163 11516446575 0025513 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
import java.beans.FeatureDescriptor;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* Defines property resolution behavior on instances of
* {@link java.util.ResourceBundle}.
*
* java.util.ResourceBundle. It accepts any object as a property
* and coerces it to a java.lang.String for invoking
* {@link java.util.ResourceBundle#getObject(java.lang.String)}.
* setValue is called.
* ELResolvers are combined together using
* {@link CompositeELResolver}s, to define rich semantics for evaluating an
* expression. See the javadocs for {@link ELResolver} for details.
* ResourceBundle,
* the provided property will first be coerced to a String.
* The Object returned by getObject on
* the base ResourceBundle will be returned.
* ResourceBundle, the
* propertyResolved property of the ELContext
* object must be set to true by this resolver, before
* returning. If this property is not true after this method
* is called, the caller should ignore the return value.
* String.
* @return If the propertyResolved property of
* ELContext was set to true, then
* null if property is null;
* otherwise the Object for the given key
* (property coerced to String) from the
* ResourceBundle.
* If no object for the given key can be found, then the
* String "???" + key + "???".
* @throws NullPointerException
* if context is null
* @throws ELException
* if an exception was thrown while performing the property or
* variable resolution. The thrown exception must be included as
* the cause property of this exception, if available.
*/
public Object getValue(ELContext context, Object base, Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base instanceof ResourceBundle) {
context.setPropertyResolved(true);
if (property != null) {
try {
return ((ResourceBundle) base).getObject(property
.toString());
} catch (MissingResourceException e) {
return "???" + property + "???";
}
}
}
return null;
}
/**
* If the base object is an instance of ResourceBundle,
* return null, since the resolver is read only.
*
* ResourceBundle, the
* propertyResolved property of the ELContext
* object must be set to true by this resolver, before
* returning. If this property is not true after this method
* is called, the caller should ignore the return value.
* propertyResolved property of
* ELContext was set to true, then
* null; otherwise undefined.
* @throws NullPointerException
* if context is null
*/
public Class> getType(ELContext context, Object base, Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base instanceof ResourceBundle) {
context.setPropertyResolved(true);
}
return null;
}
/**
* If the base object is a ResourceBundle, throw a
* {@link PropertyNotWritableException}.
*
* @param context
* The context of this evaluation.
* @param base
* The ResourceBundle to be modified. Only bases that are of type
* ResourceBundle are handled.
* @param property
* The String property to use.
* @param value
* The value to be set.
* @throws NullPointerException
* if context is null.
* @throws PropertyNotWritableException
* Always thrown if base is an instance of ReasourceBundle.
*/
public void setValue(ELContext context, Object base, Object property,
Object value) {
if (context == null) {
throw new NullPointerException();
}
if (base instanceof ResourceBundle) {
context.setPropertyResolved(true);
throw new PropertyNotWritableException(
"ResourceBundles are immutable");
}
}
/**
* If the base object is not null and an instanceof {@link ResourceBundle},
* return true.
*
* @param context
* The context of this evaluation.
* @param base
* The ResourceBundle to be modified. Only bases that are of type
* ResourceBundle are handled.
* @param property
* The String property to use.
* @return If the propertyResolved property of
* ELContext was set to true, then
* true; otherwise undefined.
* @throws NullPointerException
* if context is null
*/
public boolean isReadOnly(ELContext context, Object base, Object property) {
if (context == null) {
throw new NullPointerException();
}
if (base instanceof ResourceBundle) {
context.setPropertyResolved(true);
return true;
}
return false;
}
/**
* If the base object is a ResourceBundle, returns an Iterator
* containing the set of keys available in the ResourceBundle.
* Otherwise, returns null.
*
* Iterator returned must contain zero or more instances
* of {@link java.beans.FeatureDescriptor}. Each info object contains
* information about a key in the ResourceBundle, and is initialized as
* follows:
*
*
String key
* falsefalsetrueFeatureDescriptors:
*
*
String.classtrueResourceBundle are handled by this
* resolver.
* @return An Iterator containing zero or more (possibly
* infinitely more) FeatureDescriptor objects, each
* representing a key in this bundle, or null if the
* base object is not a ResourceBundle.
*/
public Iterator getFeatureDescriptors(ELContext context, Object base) {
if (base instanceof ResourceBundle) {
ResourceBundle bundle = (ResourceBundle) base;
List features = new ArrayList();
String key = null;
FeatureDescriptor desc = null;
for (Enumeration e = bundle.getKeys(); e.hasMoreElements();) {
key = (String) e.nextElement();
desc = new FeatureDescriptor();
desc.setDisplayName(key);
desc.setExpert(false);
desc.setHidden(false);
desc.setName(key);
desc.setPreferred(true);
desc.setValue(TYPE, String.class);
desc.setValue(RESOLVABLE_AT_DESIGN_TIME, Boolean.TRUE);
features.add(desc);
}
return features.iterator();
}
return null;
}
/**
* If the base object is a ResourceBundle, returns the most general type
* that this resolver accepts for the property argument.
* Otherwise, returns null.
*
* ResourceBundle, this method will
* always return String.class.
*
* @param context
* The context of this evaluation.
* @param base
* The bundle to analyze. Only bases of type
* ResourceBundle are handled by this resolver.
* @return null if base is not a ResourceBundle;
* otherwise String.class.
*/
public Class> getCommonPropertyType(ELContext context, Object base) {
if (base instanceof ResourceBundle) {
return String.class;
}
return null;
}
}
beansbinding-1.2.1/src/org/jdesktop/el/ValueExpression.java 0000664 0000000 0000000 00000020107 11516446575 0023754 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
import java.util.Collections;
import java.util.List;
/**
* An Expression that can get or set a value.
*
* ValueExpression objects can now be used both to
* retrieve a value and to set a value. Expressions that can have a value
* set on them are referred to as l-value expressions. Those that
* cannot are referred to as r-value expressions. Not all r-value expressions
* can be used as l-value expressions (e.g. "${1+1}" or
* "${firstName} ${lastName}"). See the EL Specification for
* details. Expressions that cannot be used as l-values must always
* return true from isReadOnly().{@link ExpressionFactory#createValueExpression} method
* can be used to parse an expression string and return a concrete instance
* of ValueExpression that encapsulates the parsed expression.
* The {@link FunctionMapper} is used at parse time, not evaluation time,
* so one is not needed to evaluate an expression using this class.
* However, the {@link ELContext} is needed at evaluation time.ELContext is used to
* resolve the top-level variables and to determine the behavior of the
* . and [] operators. For any of the four methods,
* the {@link ELResolver#getValue} method is used to resolve all properties
* up to but excluding the last one. This provides the base
* object. At the last resolution, the ValueExpression will
* call the corresponding {@link ELResolver#getValue},
* {@link ELResolver#setValue}, {@link ELResolver#isReadOnly} or
* {@link ELResolver#getType} method, depending on which was called on
* the ValueExpression.
* getExpectedType(), which was
* provided to the ExpressionFactory when this
* expression was created.null.
* @throws PropertyNotFoundException if one of the property
* resolutions failed because a specified variable or property
* does not exist or is not readable.
* @throws ELException if an exception was thrown while performing
* property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public abstract Object getValue(ELContext context);
public Result getResult(ELContext context, boolean trackResolvedObjects) throws PropertyNotFoundException,
ELException {
Object value = getValue(context);
Listnull.
* @throws PropertyNotFoundException if one of the property
* resolutions failed because a specified variable or property
* does not exist or is not readable.
* @throws PropertyNotWritableException if the final variable or
* property resolution failed because the specified
* variable or property is not writable.
* @throws ELException if an exception was thrown while attempting to
* set the property or variable. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public abstract void setValue(ELContext context, Object value);
/**
* Evaluates the expression relative to the provided context, and
* returns true if a call to {@link #setValue} will
* always fail.
*
* @param context The context of this evaluation.
* @return true if the expression is read-only or
* false if not.
* @throws NullPointerException if context is null.
* @throws PropertyNotFoundException if one of the property
* resolutions failed because a specified variable or property
* does not exist or is not readable.
* @throws ELException if an exception was thrown while performing
* property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
* * @throws NullPointerException if context is null
*/
public abstract boolean isReadOnly(ELContext context);
/**
* Evaluates the expression relative to the provided context, and
* returns the most general type that is acceptable for an object to be
* passed as the value parameter in a future call
* to the {@link #setValue} method.
*
* getValue().getClass().
* For example, in the case of an expression that references an
* array element, the getType method will return the
* element type of the array, which might be a superclass of the type
* of the actual element that is currently in the specified
* array element.null.
* @throws PropertyNotFoundException if one of the property
* resolutions failed because a specified variable or property
* does not exist or is not readable.
* @throws ELException if an exception was thrown while performing
* property or variable resolution. The thrown exception
* must be included as the cause property of this exception, if
* available.
*/
public abstract Class> getType(ELContext context);
/**
* Returns the type the result of the expression will be coerced to
* after evaluation.
*
* @return the expectedType passed to the
* ExpressionFactory.createValueExpression method
* that created this ValueExpression.
*/
public abstract Class> getExpectedType();
}
beansbinding-1.2.1/src/org/jdesktop/el/VariableMapper.java 0000664 0000000 0000000 00000002356 11516446575 0023520 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el;
/**
* The interface to a map between EL variables and the EL expressions
* they are associated with.
*
* @since JSP 2.1
*/
public abstract class VariableMapper {
/**
* @param variable The variable name
* @return the ValueExpression assigned to the variable,
* null if there is no previous assignment to this variable.
*/
public abstract ValueExpression resolveVariable(
String variable);
/**
* Assign a ValueExpression to an EL variable, replacing
* any previously assignment to the same variable.
* The assignment for the variable is removed if
* the expression is null.
*
* @param variable The variable name
* @param expression The ValueExpression to be assigned
* to the variable.
* @return The previous ValueExpression assigned to this variable,
* null if there is no previouse assignment to this variable.
*/
public abstract ValueExpression setVariable(
String variable,
ValueExpression expression);
}
beansbinding-1.2.1/src/org/jdesktop/el/impl/ 0000775 0000000 0000000 00000000000 11516446575 0020716 5 ustar 00root root 0000000 0000000 beansbinding-1.2.1/src/org/jdesktop/el/impl/ExpressionFactoryImpl.java 0000664 0000000 0000000 00000004262 11516446575 0026076 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el.impl;
import org.jdesktop.el.ELContext;
import org.jdesktop.el.ExpressionFactory;
import org.jdesktop.el.MethodExpression;
import org.jdesktop.el.ValueExpression;
import org.jdesktop.el.impl.lang.ExpressionBuilder;
import org.jdesktop.el.impl.lang.ELSupport;
import org.jdesktop.el.impl.util.MessageFactory;
/**
* @see javax.el.ExpressionFactory
*
* @author Jacob Hookom [jacob@hookom.net]
* @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
*/
public class ExpressionFactoryImpl extends ExpressionFactory {
/**
*
*/
public ExpressionFactoryImpl() {
super();
}
public Object coerceToType(Object obj, Class type) {
return ELSupport.coerceToType(obj, type);
}
public MethodExpression createMethodExpression(ELContext context,
String expression, Class expectedReturnType,
Class[] expectedParamTypes) {
if (expectedParamTypes == null) {
throw new NullPointerException(MessageFactory
.get("error.method.nullParms"));
}
ExpressionBuilder builder = new ExpressionBuilder(expression, context);
return builder.createMethodExpression(expectedReturnType,
expectedParamTypes);
}
public ValueExpression createValueExpression(ELContext context,
String expression, Class expectedType) {
if (expectedType == null) {
throw new NullPointerException(MessageFactory
.get("error.value.expectedType"));
}
ExpressionBuilder builder = new ExpressionBuilder(expression, context);
return builder.createValueExpression(expectedType);
}
public ValueExpression createValueExpression(Object instance,
Class expectedType) {
if (expectedType == null) {
throw new NullPointerException(MessageFactory
.get("error.value.expectedType"));
}
return new ValueExpressionLiteral(instance, expectedType);
}
}
beansbinding-1.2.1/src/org/jdesktop/el/impl/Messages.properties 0000664 0000000 0000000 00000004065 11516446575 0024610 0 ustar 00root root 0000000 0000000 # Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
# subject to license terms.
# General Errors
error.convert=Cannot convert {0} of type {1} to {2}
error.compare=Cannot compare {0} to {1}
error.function=Problems calling function ''{0}''
error.unreachable.base=Target Unreachable, identifier ''{0}'' resolved to null
error.unreachable.property=Target Unreachable, ''{0}'' returned null
error.resolver.unhandled=ELResolver did not handle type: {0} with property of ''{1}''
error.resolver.unhandled.null=ELResolver cannot handle a null base Object with identifier ''{0}''
# ValueExpressionLiteral
error.value.literal.write=ValueExpression is a literal and not writable: {0}
# ExpressionFactoryImpl
error.null=Expression cannot be null
error.mixed=Expression cannot contain both '#{..}' and '${..}' : {0}
error.method=Not a valid MethodExpression : {0}
error.method.nullParms=Parameter types cannot be null
error.value.expectedType=Expected type cannot be null
# ExpressionMediator
error.eval=Error Evaluating {0} : {1}
# ValueSetVisitor
error.syntax.set=Illegal Syntax for Set Operation
# ReflectionUtil
error.method.notfound=Method not found: {0}.{1}({2})
error.property.notfound=Property ''{1}'' not found on {0}
# ValidatingVisitor
error.fnMapper.null=Expression uses functions, but no FunctionMapper was provided
error.fnMapper.method=Function ''{0}'' not found
error.fnMapper.paramcount=Function ''{0}'' specifies {1} params, but {2} were declared
# **ExpressionImpl
error.context.null=ELContext was null
# ArrayELResolver
error.array.outofbounds=Index {0} is out of bounds for array of size {1}
# ListELResolver
error.list.outofbounds=Index {0} is out of bounds for list of size {1}
# BeanELResolver
error.property.notfound=Property ''{1}'' not found on type: {0}
error.property.invocation=Property ''{1}'' threw an exception from type: {0}
error.property.notreadable=Property ''{1}'' doesn't have a 'get' specified on type: {0}
error.property.notwritable=Property ''{1}'' doesn't have a 'set' specified on type: {0} beansbinding-1.2.1/src/org/jdesktop/el/impl/MethodExpressionImpl.java 0000664 0000000 0000000 00000027013 11516446575 0025706 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el.impl;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import org.jdesktop.el.ELContext;
import org.jdesktop.el.ELException;
import org.jdesktop.el.ELResolver;
import org.jdesktop.el.Expression;
import org.jdesktop.el.ExpressionFactory;
import org.jdesktop.el.FunctionMapper;
import org.jdesktop.el.MethodExpression;
import org.jdesktop.el.MethodInfo;
import org.jdesktop.el.MethodNotFoundException;
import org.jdesktop.el.PropertyNotFoundException;
import org.jdesktop.el.VariableMapper;
import org.jdesktop.el.impl.lang.ELSupport;
import org.jdesktop.el.impl.lang.EvaluationContext;
import org.jdesktop.el.impl.lang.ExpressionBuilder;
import org.jdesktop.el.impl.parser.Node;
import org.jdesktop.el.impl.util.ReflectionUtil;
/**
* An Expression that refers to a method on an object.
*
* The {@link ExpressionFactory#createMethodExpression} method
* can be used to parse an expression string and return a concrete instance
* of MethodExpression that encapsulates the parsed expression.
* The {@link FunctionMapper} is used at parse time, not evaluation time,
* so one is not needed to evaluate an expression using this class.
* However, the {@link ELContext} is needed at evaluation time.ELContext is used to resolve the top-level variables and to
* determine the behavior of the . and []
* operators. For any of the two methods, the {@link ELResolver#getValue}
* method is used to resolve all properties up to but excluding the last
* one. This provides the base object on which the method
* appears. If the base object is null, a
* NullPointerException must be thrown. At the last resolution,
* the final property is then coerced to a String,
* which provides the name of the method to be found. A method matching the
* name and expected parameters provided at parse time is found and it is
* either queried or invoked (depending on the method called on this
* MethodExpression).Expression.
*
* true if and only if the argument is not
* null, is an Expression object that is the
* of the same type (ValueExpression or
* MethodExpression), and has an identical parsed
* representation.
* ${fn1:foo()} and
* ${fn2:foo()} are equal if their corresponding
* FunctionMappers mapped fn1:foo and
* fn2:foo to the same method.
* Object to test for equality.
* @return true if obj equals this
* Expression; false otherwise.
* @see java.util.Hashtable
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
return (obj instanceof MethodExpressionImpl && obj.hashCode() == this
.hashCode());
}
/**
* Returns the original String used to create this Expression,
* unmodified.
*
* Expression.
* MethodInfo containing information
* about the method the expression evaluated to.
* @throws NullPointerException
* if context is null or the base object is
* null on the last resolution.
* @throws PropertyNotFoundException
* if one of the property resolutions failed because a specified
* variable or property does not exist or is not readable.
* @throws MethodNotFoundException
* if no suitable method can be found.
* @throws ELException
* if an exception was thrown while performing property or
* variable resolution. The thrown exception must be included as
* the cause property of this exception, if available.
* @see javax.el.MethodExpression#getMethodInfo(javax.el.ELContext)
*/
public MethodInfo getMethodInfo(ELContext context)
throws PropertyNotFoundException, MethodNotFoundException,
ELException {
Node n = this.getNode();
EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
this.varMapper, this);
return n.getMethodInfo(ctx, this.paramTypes);
}
/**
* @return
* @throws ELException
*/
private Node getNode() throws ELException {
if (this.node == null) {
this.node = ExpressionBuilder.createNode(this.expr);
}
return this.node;
}
/**
* Returns the hash code for this Expression.
*
* equals(Object) method,
* then calling the hashCode method on each of the two
* objects must produce the same integer result. Implementations must take
* special note and implement hashCode correctly.
* Expression.
* @see #equals
* @see java.util.Hashtable
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return this.expr.hashCode();
}
/**
* Evaluates the expression relative to the provided context, invokes the
* method that was found using the supplied parameters, and returns the
* result of the method invocation.
*
* @param context
* The context of this evaluation.
* @param params
* The parameters to pass to the method, or null
* if no parameters.
* @return the result of the method invocation (null if the
* method has a void return type).
* @throws NullPointerException
* if context is null or the base object is
* null on the last resolution.
* @throws PropertyNotFoundException
* if one of the property resolutions failed because a specified
* variable or property does not exist or is not readable.
* @throws MethodNotFoundException
* if no suitable method can be found.
* @throws ELException
* if an exception was thrown while performing property or
* variable resolution. The thrown exception must be included as
* the cause property of this exception, if available. If the
* exception thrown is an InvocationTargetException,
* extract its cause and pass it to the
* ELException constructor.
* @see javax.el.MethodExpression#invoke(javax.el.ELContext,
* java.lang.Object[])
*/
public Object invoke(ELContext context, Object[] params)
throws PropertyNotFoundException, MethodNotFoundException,
ELException {
EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
this.varMapper, this);
return this.getNode().invoke(ctx, this.paramTypes, params);
}
/*
* (non-Javadoc)
*
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.expr = in.readUTF();
String type = in.readUTF();
if (!"".equals(type)) {
this.expectedType = ReflectionUtil.forName(type);
}
this.paramTypes = ReflectionUtil.toTypeArray(((String[]) in
.readObject()));
this.fnMapper = (FunctionMapper) in.readObject();
this.varMapper = (VariableMapper) in.readObject();
}
/*
* (non-Javadoc)
*
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(this.expr);
out.writeUTF((this.expectedType != null) ? this.expectedType.getName()
: "");
out.writeObject(ReflectionUtil.toTypeNameArray(this.paramTypes));
out.writeObject(this.fnMapper);
out.writeObject(this.varMapper);
}
public boolean isLiteralText() {
return false;
}
}
beansbinding-1.2.1/src/org/jdesktop/el/impl/MethodExpressionLiteral.java 0000664 0000000 0000000 00000004660 11516446575 0026404 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el.impl;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import org.jdesktop.el.ELContext;
import org.jdesktop.el.ELException;
import org.jdesktop.el.MethodExpression;
import org.jdesktop.el.MethodInfo;
import org.jdesktop.el.impl.lang.ELSupport;
import org.jdesktop.el.impl.util.ReflectionUtil;
public class MethodExpressionLiteral extends MethodExpression implements Externalizable {
private Class expectedType;
private String expr;
private Class[] paramTypes;
public MethodExpressionLiteral() {
// do nothing
}
public MethodExpressionLiteral(String expr, Class expectedType, Class[] paramTypes) {
this.expr = expr;
this.expectedType = expectedType;
this.paramTypes = paramTypes;
}
public MethodInfo getMethodInfo(ELContext context) throws ELException {
return new MethodInfo(this.expr, this.expectedType, this.paramTypes);
}
public Object invoke(ELContext context, Object[] params) throws ELException {
if (this.expectedType != null) {
return ELSupport.coerceToType(this.expr, this.expectedType);
} else {
return this.expr;
}
}
public String getExpressionString() {
return this.expr;
}
public boolean equals(Object obj) {
return (obj instanceof MethodExpressionLiteral && this.hashCode() == obj.hashCode());
}
public int hashCode() {
return this.expr.hashCode();
}
public boolean isLiteralText() {
return true;
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.expr = in.readUTF();
String type = in.readUTF();
if (!"".equals(type)) {
this.expectedType = ReflectionUtil.forName(type);
}
this.paramTypes = ReflectionUtil.toTypeArray(((String[]) in
.readObject()));
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(this.expr);
out.writeUTF((this.expectedType != null) ? this.expectedType.getName()
: "");
out.writeObject(ReflectionUtil.toTypeNameArray(this.paramTypes));
}
}
beansbinding-1.2.1/src/org/jdesktop/el/impl/ValueExpressionImpl.java 0000664 0000000 0000000 00000022115 11516446575 0025540 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.el.impl;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.List;
import java.util.Collections;
import org.jdesktop.el.ELContext;
import org.jdesktop.el.ELException;
import org.jdesktop.el.ELResolver;
import org.jdesktop.el.Expression;
import org.jdesktop.el.ExpressionFactory;
import org.jdesktop.el.FunctionMapper;
import org.jdesktop.el.PropertyNotFoundException;
import org.jdesktop.el.PropertyNotWritableException;
import org.jdesktop.el.ValueExpression;
import org.jdesktop.el.VariableMapper;
import org.jdesktop.el.impl.lang.ELSupport;
import org.jdesktop.el.impl.lang.EvaluationContext;
import org.jdesktop.el.impl.lang.ExpressionBuilder;
import org.jdesktop.el.impl.parser.AstLiteralExpression;
import org.jdesktop.el.impl.parser.Node;
import org.jdesktop.el.impl.util.ReflectionUtil;
/**
* An Expression that can get or set a value.
*
* ValueExpression objects can now be used both to retrieve a
* value and to set a value. Expressions that can have a value set on them are
* referred to as l-value expressions. Those that cannot are referred to as
* r-value expressions. Not all r-value expressions can be used as l-value
* expressions (e.g. "${1+1}" or
* "${firstName} ${lastName}"). See the EL Specification for
* details. Expressions that cannot be used as l-values must always return
* true from isReadOnly().
* The {@link ExpressionFactory#createValueExpression} method
* can be used to parse an expression string and return a concrete instance
* of ValueExpression that encapsulates the parsed expression.
* The {@link FunctionMapper} is used at parse time, not evaluation time,
* so one is not needed to evaluate an expression using this class.
* However, the {@link ELContext} is needed at evaluation time.ELContext is used to
* resolve the top-level variables and to determine the behavior of the
* . and [] operators. For any of the four methods,
* the {@link ELResolver#getValue} method is used to resolve all properties
* up to but excluding the last one. This provides the base
* object. At the last resolution, the ValueExpression will
* call the corresponding {@link ELResolver#getValue},
* {@link ELResolver#setValue}, {@link ELResolver#isReadOnly} or
* {@link ELResolver#getType} method, depending on which was called on
* the ValueExpression.
* expectedType passed to the
* ExpressionFactory.createValueExpression method
* that created this ValueExpression.
*
* @see javax.el.Expression#getExpressionString()
*/
public String getExpressionString() {
return this.expr;
}
/**
* @return
* @throws ELException
*/
private Node getNode() throws ELException {
if (this.node == null) {
this.node = ExpressionBuilder.createNode(this.expr);
}
return this.node;
}
/*
* (non-Javadoc)
*
* @see javax.el.ValueExpression#getType(javax.el.ELContext)
*/
public Class getType(ELContext context) throws PropertyNotFoundException,
ELException {
EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
this.varMapper, this);
return this.getNode().getType(ctx);
}
/*
* (non-Javadoc)
*
* @see javax.el.ValueExpression#getValue(javax.el.ELContext)
*/
public Object getValue(ELContext context) throws PropertyNotFoundException,
ELException {
EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
this.varMapper, this);
Object value = this.getNode().getValue(ctx);
if (this.expectedType != null) {
return ELSupport.coerceToType(value, this.expectedType);
}
return value;
}
public Result getResult(ELContext context, boolean trackResolvedObjects) throws PropertyNotFoundException,
ELException {
EvaluationContext ctx = new EvaluationContext(context, this.fnMapper, this.varMapper, this, trackResolvedObjects);
Object value = this.getNode().getValue(ctx);
List
EL Context
ELContext objects is controlled through
the underlying technology. For example, in JSP, the
JspContext.getELContext() factory method is used.ELContext.Expression Objects
"${customer.name}" can be used either
as an rvalue (return the value associated with property name
of the model object customer) or as an lvalue
(set the value of the property name of the model object
customer)."${handler.process}" makes it possible to invoke a method
(process) on a specific model object (handler).equals() and
hashCode(). Morevover, each method on these expression classes
that actually evaluates an expression receives a parameter
of class {@link javax.el.ELContext},
which provides the context required to evaluate the expression.Creation of Expressions
ValueExpression) or signature
(MethodExpression).
The ELContext provides the context necessary to parse an expression.
Specifically, if the expression uses an EL function
(for example ${fn:toUpperCase(customer.name)}) or an
EL variable, then
{@link javax.el.FunctionMapper} and {@link javax.el.VariableMapper}
objects must be available within the ELContext so that EL functions and
EL variables are properly mapped.
Resolution of Model Objects and their Properties
ELResolver supporting
property resolution for common data types which include
arrays ({@link javax.el.ArrayELResolver}), JavaBeans ({@link javax.el.BeanELResolver}), Lists ({@link javax.el.ListELResolver}),
Maps ({@link javax.el.MapELResolver}), and ResourceBundles ({@link javax.el.ResourceBundleELResolver}).getFeatureDescriptors on the ELResolver. This method exposes objects
of type java.beans.FeatureDescriptor, providing all information of interest
on top-level model objects as well as their properties.EL Functions
${fn:toUpperCase(customer.name)}), then a
{@link javax.el.FunctionMapper}
object must also be specified within the ELContext.
The FunctionMapper is responsible to map
${prefix:name()} style functions to
static methods that can execute the specified functions.
EL Variables
ELResolver. Instead, it refers to an EL
expression. The evaluation of that EL expression gives the EL variable
its value.
<h:inputText value="#{handler.customer.name}"/>
handler refers to a model object that can be resolved by an EL Resolver.
<c:forEach var="item" items="#{model.list}">
<h:inputText value="#{item.name}"/>
</c:forEach>
item is an EL variable because it does not refer directly to a model
object. Instead, it refers to another EL expression, namely a
specific item in the collection referred to by the EL expression
#{model.list}.
Assuming that there are three elements in ${model.list}, this means
that for
each invocation of <h:inputText>, the following information
about item must be preserved in the {@link javax.el.VariableMapper}:
first invocation:itemmaps to first element in${model.list}
second invocation:itemmaps to second element in${model.list}
third invocation:itemmaps to third element in${model.list}
VariableMapper provides the mechanisms required to allow the mapping
of an EL variable to the EL expression from which it gets its value.
Provides support for observing changes to collection classes. The classes in this package are not part of JSR 295.
beansbinding-1.2.1/src/org/jdesktop/swingbinding/ 0000775 0000000 0000000 00000000000 11516446575 0022037 5 ustar 00root root 0000000 0000000 beansbinding-1.2.1/src/org/jdesktop/swingbinding/ElementsProperty.java 0000664 0000000 0000000 00000010200 11516446575 0026214 0 ustar 00root root 0000000 0000000 /* * Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is * subject to license terms. */ package org.jdesktop.swingbinding; import java.util.*; import org.jdesktop.beansbinding.Property; import org.jdesktop.beansbinding.PropertyHelper; import org.jdesktop.beansbinding.PropertyStateEvent; /** * @author Shannon Hickey */ class ElementsProperty* Here is an example of creating a binding from a {@code List} of {@code Country} * objects to a {@code JComboBox}: *
*
* // create the country list
* List countries = createCountryList();
*
* // create the binding from List to JComboBox
* JComboBoxBinding cb = SwingBindings.createJComboBoxBinding(READ, countries, jComboBox);
*
* // realize the binding
* cb.bind();
*
* * If the {@code List} is an instance of {@code ObservableList}, then changes to * the {@code List} contents (such as adding, removing or replacing an object) * are reflected in the {@code JComboBox}. Important: Changing the contents * of a non-observable {@code List} while it is participating in a * {@code JComboBoxBinding} is unsupported, resulting in undefined behavior and * possible exceptions. *
* {@code JComboBoxBinding} requires * extra clarification on the operation of the * {@code refresh} and {@code save} methods and the meaning of the update * strategy. The target property of a {@code JComboBoxBinding} is not the * target {@code JComboBox} property provided in the constructor, but rather a * private synthetic property representing the {@code List} of objects to show * in the target {@code JComboBox}. This synthetic property is readable/writeable * only when the {@code JComboBoxBinding} is bound and the target {@code JComboBox} * property is readable with a {@code non-null} value. *
* It is this private synthetic property on which the {@code refresh} and * {@code save} methods operate; meaning that these methods simply cause syncing * between the value of the source {@code List} property and the value of the * synthetic target property (representing the {@code List} to be shown in the * target {@code JComboBox}). These methods do not, therefore, have anything to do * with refreshing values in the {@code JComboBox}. Likewise, the update * strategy, which simply controls when {@code refresh} and {@code save} are * automatically called, also has nothing to do with refreshing values * in the {@code JComboBox}. *
* Note: At the current time, the {@code READ_WRITE} update strategy * is not useful for {@code JComboBoxBinding}. To prevent unwanted confusion, * {@code READ_WRITE} is translated to {@code READ} by {@code JComboBoxBinding's} * constructor. *
* {@code JComboBoxBinding} works by installing a custom model on the target * {@code JComboBox}, as appropriate, to represent the source {@code List}. The * model is installed on a target {@code JComboBox} with the first succesful call * to {@code refresh} with that {@code JComboBox} as the target. Subsequent calls * to {@code refresh} update the elements in this already-installed model. * The model is uninstalled from a target {@code JComboBox} when either the * {@code JComboBoxBinding} is unbound or when the target {@code JComboBox} property * changes to no longer represent that {@code JComboBox}. Note: When the model is * uninstalled from a {@code JComboBox}, it is replaced with a {@code DefaultComboBoxModel}, * in order to leave the {@code JComboBox} functional. *
* Some of the above is easier to understand with an example. Let's consider * a {@code JComboBoxBinding} ({@code binding}), with update strategy * {@code READ}, between a property representing a {@code List} ({@code listP}) * and a property representing a {@code JComboBox} ({@code jComboBoxP}). {@code listP} * and {@code jComboBoxP} both start off readable, referring to a {@code non-null} * {@code List} and {@code non-null} {@code JComboBox} respectively. Let's look at * what happens for each of a sequence of events: *
*
| Sequence | Event | Result |
|---|---|---|
| 1 | *explicit call to {@code binding.bind()} | *
* - synthetic target property becomes readable/writeable
* * - {@code refresh()} is called * * - model is installed on target {@code JComboBox}, representing list of objects * |
*
| 2 | *{@code listP} changes to a new {@code List} | *
* - {@code refresh()} is called
* * - model is updated with new list of objects * |
*
| 3 | *{@code jComboBoxP} changes to a new {@code JComboBox} | ** - model is uninstalled from old {@code JComboBox} * | *
| 4 | *explicit call to {@code binding.refresh()} | ** - model is installed on target {@code JComboBox}, representing list of objects * | *
| 5 | *{@code listP} changes to a new {@code List} | *
* - {@code refresh()} is called
* * - model is updated with new list of objects * |
*
| 6 | *explicit call to {@code binding.unbind()} | ** - model is uninstalled from target {@code JComboBox} * | *
* Notice that in step 3, when the value * of the {@code JComboBox} property changed, the new {@code JComboBox} did not * automatically get the model with the elements applied to it. A change to the * target value should not cause an {@code AutoBinding} to sync the target from * the source. Step 4 forces a sync by explicitly calling {@code refresh}. * Alternatively, it could be caused by any other action that results * in a {@code refresh} (for example, the source property changing value, or an * explicit call to {@code unbind} followed by {@code bind}). *
* In addition to binding the items of a {@code JComboBox}, it is possible to
* bind to the selected item of a {@code JComboBox}.
* See the list of
* interesting swing properties in the package summary for more details.
*
* @param
* Here is an example of creating a binding from a {@code List} of {@code Person}
* objects to a {@code JList}:
*
*
* The {@code JList} target of a {@code JListBinding} acts as a live view of
* the objects in the source {@code List}, regardless of the update strategy (the
* meaning of the update strategy is clarified later
* in this document). {@code JListBinding} listens to the property specified for
* any {@code DetailBinding}, for all objects in the {@code List}, and updates
* the values displayed in the {@code JList} in response to change. If the
* {@code List} is an instance of {@code ObservableList}, then changes to the
* {@code List} contents (such as adding, removing or replacing an object) are
* also reflected in the {@code JList}. Important: Changing the contents
* of a non-observable {@code List} while it is participating in a
* {@code JListBinding} is unsupported, resulting in undefined behavior and
* possible exceptions.
*
* {@code JListBinding} requires
* extra clarification on the operation of the
* {@code refresh} and {@code save} methods and the meaning of the update
* strategy. The target property of a {@code JListBinding} is not the
* target {@code JList} property provided in the constructor, but rather a
* private synthetic property representing the {@code List} of objects to show
* in the target {@code JList}. This synthetic property is readable/writeable
* only when the {@code JListBinding} is bound and the target {@code JList}
* property is readable with a {@code non-null} value.
*
* It is this private synthetic property on which the {@code refresh} and
* {@code save} methods operate; meaning that these methods simply cause syncing
* between the value of the source {@code List} property and the value of the
* synthetic target property (representing the {@code List} to be shown in the
* target {@code JList}). These methods do not, therefore, have anything to do
* with refreshing values in the {@code JList}. Likewise, the update
* strategy, which simply controls when {@code refresh} and {@code save} are
* automatically called, also has nothing to do with refreshing values
* in the {@code JList}.
*
* Note: At the current time, the {@code READ_WRITE} update strategy
* is not useful for {@code JListBinding}. To prevent unwanted confusion,
* {@code READ_WRITE} is translated to {@code READ} by {@code JListBinding's}
* constructor.
*
* {@code JListBinding} works by installing a custom model on the target
* {@code JList}, as appropriate, to represent the source {@code List}. The
* model is installed on a target {@code JList} with the first succesful call
* to {@code refresh} with that {@code JList} as the target. Subsequent calls
* to {@code refresh} update the elements in this already-installed model.
* The model is uninstalled from a target {@code JList} when either the
* {@code JListBinding} is unbound or when the target {@code JList} property
* changes to no longer represent that {@code JList}. Note: When the model is
* uninstalled from a {@code JList}, it is replaced with a {@code DefaultListModel},
* in order to leave the {@code JList} functional.
*
* Some of the above is easier to understand with an example. Let's consider
* a {@code JListBinding} ({@code binding}), with update strategy
* {@code READ}, between a property representing a {@code List} ({@code listP})
* and a property representing a {@code JList} ({@code jListP}). {@code listP}
* and {@code jListP} both start off readable, referring to a {@code non-null}
* {@code List} and {@code non-null} {@code JList} respectively. Let's look at
* what happens for each of a sequence of events:
*
*
* Notice that in step 3, when the value
* of the {@code JList} property changed, the new {@code JList} did not
* automatically get the model with the elements applied to it. A change to the
* target value should not cause an {@code AutoBinding} to sync the target from
* the source. Step 4 forces a sync by explicitly calling {@code refresh}.
* Alternatively, it could be caused by any other action that results
* in a {@code refresh} (for example, the source property changing value, or an
* explicit call to {@code unbind} followed by {@code bind}).
*
* {@code DetailBindings} are managed by the {@code JList}. They are not
* to be explicitly bound, unbound, added to a {@code BindingGroup}, or accessed
* in a way that is not allowed for a managed binding.
*
* In addition to binding the elements of a {@code JList}, it is possible to
* bind to the selection of a {@code JList}. When binding to the selection of a {@code JList}
* backed by a {@code JListBinding}, the selection is always in terms of elements
* from the source {@code List}, regardless of any {@code DetailBinding} specified.
* See the list of
* interesting swing properties in the package summary for more details.
*
* @param
* A {@code Converter} may be specified on a {@code DetailBinding}. Specifying a
* {@code Validator} is also possible, but doesn't make sense since {@code JList}
* values aren't editable.
*
* {@code DetailBindings} are managed by their {@code JListBinding}. They are not
* to be explicitly bound, unbound, added to a {@code BindingGroup}, or accessed
* in a way that is not allowed for a managed binding.
*
* @see org.jdesktop.swingbinding.JListBinding#setDetailBinding(Property, String)
*/
public final class DetailBinding extends AbstractColumnBinding {
private DetailBinding(Property
* Here is an example of creating a binding from a {@code List} of {@code Person}
* objects to a {@code JTable}:
*
*
* The {@code JTable} target of a {@code JTableBinding} acts as a live view of
* the objects in the source {@code List},
* regardless of the update strategy (the meaning of the update strategy is
* clarified later in this document). {@code JTableBinding}
* listens to the properties specified for the {@code ColumnBindings},
* for all objects in the {@code List}, and updates the values
* displayed in the {@code JTable} in response to change. All successful
* edits made to {@code JTable} cell values are immediately committed back to
* corresponding objects in the source {@code List}. If the {@code List} is an
* instance of {@code ObservableList}, then changes to the {@code List} contents
* (such as adding, removing or replacing an object) are also reflected in the
* {@code JTable}. Important: Changing the contents of a non-observable
* {@code List} while it is participating in a {@code JTableBinding} is unsupported,
* resulting in undefined behavior and possible exceptions.
*
* A cell in the {@code JTable} is editable for any given row and
* column when all of the following are true: the property specified for that column
* by its {@code ColumnBinding} is writeable for the object representing that row,
* the {@code "editable"} property of the {@code JTableBinding} is {@code true}
* (the default), and the {@code "editable"} property of the {@code ColumnBinding}
* is {@code true} (the default).
*
* {@code JTableBinding} requires
* extra clarification on the operation of the
* {@code refresh} and {@code save} methods and the meaning of the update
* strategy. The target property of a {@code JTableBinding} is not the
* target {@code JTable} property provided in the constructor, but rather a
* private synthetic property representing the {@code List} of objects to show
* in the target {@code JTable}. This synthetic property is readable/writeable
* only when the {@code JTableBinding} is bound and the target {@code JTable}
* property is readable with a {@code non-null} value.
*
* It is this private synthetic property on which the {@code refresh} and
* {@code save} methods operate; meaning that these methods simply cause syncing
* between the value of the source {@code List} property and the value of the
* synthetic target property (representing the {@code List} to be shown in the
* target {@code JTable}). These methods do not, therefore, have anything to do
* with refreshing or saving values in the {@code JTable}. Likewise, the update
* strategy, which simply controls when {@code refresh} and {@code save} are
* automatically called, also has nothing to do with refreshing or saving
* values in the {@code JTable}.
*
* Note: At the current time, the {@code READ_WRITE} update strategy
* is not useful for {@code JTableBinding}. To prevent unwanted confusion,
* {@code READ_WRITE} is translated to {@code READ} by {@code JTableBinding's}
* constructor.
*
* {@code JTableBinding} works by installing a custom model on the target
* {@code JTable}, as appropriate, to represent the source {@code List}. The
* model is installed on a target {@code JTable} with the first succesful call
* to {@code refresh} with that {@code JTable} as the target. Subsequent calls
* to {@code refresh} update the elements in this already-installed model.
* The model is uninstalled from a target {@code JTable} when either the
* {@code JTableBinding} is unbound or when the target {@code JTable} property
* changes to no longer represent that {@code JTable}. Note: When the model is
* uninstalled from a {@code JTable}, it is replaced with a {@code DefaultTableModel},
* in order to leave the {@code JTable} functional.
*
* Some of the above is easier to understand with an example. Let's consider
* a {@code JTableBinding} ({@code binding}), with update strategy
* {@code READ}, between a property representing a {@code List} ({@code listP})
* and a property representing a {@code JTable} ({@code jTableP}). {@code listP}
* and {@code jTableP} both start off readable, referring to a {@code non-null}
* {@code List} and {@code non-null} {@code JTable} respectively. Let's look at
* what happens for each of a sequence of events:
*
*
* Notice that in step 3, when the value
* of the {@code JTable} property changed, the new {@code JTable} did not
* automatically get the model with the elements applied to it. A change to the
* target value should not cause an {@code AutoBinding} to sync the target from
* the source. Step 4 forces a sync by explicitly calling {@code refresh}.
* Alternatively, it could be caused by any other action that results
* in a {@code refresh} (for example, the source property changing value, or an
* explicit call to {@code unbind} followed by {@code bind}).
*
* {@code ColumnBindings} are managed by the {@code JTableBinding}. They are not
* to be explicitly bound, unbound, added to a {@code BindingGroup}, or accessed
* in a way that is not allowed for a managed binding. {@code BindingListeners}
* added to a {@code ColumnBinding} are notified at the time an edited {@code JTable} value
* is to be committed back to the source {@code List}. They receive notification of either
* {@code synced} or {@code syncFailed}. {@code BindingListeners} added to the
* {@code JTableBinding} itself are also notified of {@code sync} and {@code syncFailed}
* for the {@code JTableBinding's ColumnBindings}.
*
* In addition to binding the elements of a {@code JTable}, it is possible to
* bind to the selection of a {@code JTable}. When binding to the selection of a {@code JTable}
* backed by a {@code JTableBinding}, the selection is always in terms of elements
* from the source {@code List}. See the list of
* interesting swing properties in the package summary for more details.
*
* @param
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param columnProperty the property with which to derive cell values from the
* elements of the source {@code List}
* @return the {@code ColumnBinding}
* @throws IllegalArgumentException if {@code columnProperty} is {@code null}
* @see org.jdesktop.swingbinding.JTableBinding.ColumnBinding
*/
public ColumnBinding addColumnBinding(Property
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param columnProperty the property with which to derive cell values from the
* elements of the source {@code List}
* @param name a name for the column binding
* @return the {@code ColumnBinding}
* @throws IllegalArgumentException if {@code columnProperty} is {@code null}
* @see org.jdesktop.swingbinding.JTableBinding.ColumnBinding
*/
public ColumnBinding addColumnBinding(Property
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param index the index at which to insert the {@code ColumnBinding}
* @param columnProperty the property with which to derive cell values from the
* elements of the source {@code List}
* @return the {@code ColumnBinding}
* @throws IllegalArgumentException if {@code columnProperty} is {@code null}
* @see org.jdesktop.swingbinding.JTableBinding.ColumnBinding
*/
public ColumnBinding addColumnBinding(int index, Property
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param index the index at which to insert the {@code ColumnBinding}
* @param columnProperty the property with which to derive cell values from the
* elements of the source {@code List}
* @param name a name for the {@code ColumnBinding}
* @return the {@code ColumnBinding}
* @throws IllegalArgumentException if {@code columnProperty} is {@code null}
* @see org.jdesktop.swingbinding.JTableBinding.ColumnBinding
*/
public ColumnBinding addColumnBinding(int index, Property
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param binding the {@code ColumnBinding} to remove
* @see #addColumnBinding(Property, String)
*/
public boolean removeColumnBinding(ColumnBinding binding) {
throwIfBound();
boolean retVal = columnBindings.remove(binding);
if (retVal) {
adjustIndices(binding.getColumn(), false);
}
return retVal;
}
/**
* Removes the {@code ColumnBinding} with the given index from the list maintained
* by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param index the index of the {@code ColumnBinding} to remove
* @see #addColumnBinding(Property, String)
*/
public ColumnBinding removeColumnBinding(int index) {
throwIfBound();
ColumnBinding retVal = columnBindings.remove(index);
if (retVal != null) {
adjustIndices(index, false);
}
return retVal;
}
/**
* Returns the {@code ColumnBinding} with the given index in the list maintained
* by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param index the index of the {@code ColumnBinding} to return
* @return the {@code ColumnBinding} at the given index
* @see #addColumnBinding(Property, String)
*/
public ColumnBinding getColumnBinding(int index) {
return columnBindings.get(index);
}
/**
* Returns an unmodifiable copy of the list of {@code ColumnBindings} maintained
* by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @return the list of {@code ColumnBindings}
* @see #addColumnBinding(Property, String)
*/
public List
* A {@code Converter} may be specified on a {@code ColumnBinding}, as may be
* a {@code Validator}. Validation occurs at the time a cell value is to be
* committed back to the source {@code List}.
*
* {@code BindingListeners} registered on
* a {@code ColumnBinding} are notified of successful {@code sync} or
* {@code syncFailure}. These notifications are also sent to the
* {@code JTableBinding's} {@code BindingListeners}.
*
* {@code ColumnBindings} are managed by their {@code JTableBinding}. They are not
* to be explicitly bound, unbound, added to a {@code BindingGroup}, or accessed
* in a way that is not allowed for a managed binding.
*
* @see org.jdesktop.swingbinding.JTableBinding#addColumnBinding(Property, String)
*/
public final class ColumnBinding extends AbstractColumnBinding {
private Class> columnClass;
private boolean editable = true;
private boolean editableSet;
private String columnName;
private Object editingObject;
private ColumnBinding(int column, Property
*
* // create the person list
* List
*
*
* Sequence Event Result
*
* 1
* explicit call to {@code binding.bind()}
*
* - synthetic target property becomes readable/writeable
*
*
* - {@code refresh()} is called
*
* - model is installed on target {@code JList}, representing list of objects
*
*
* 2
* {@code listP} changes to a new {@code List}
*
* - {@code refresh()} is called
*
*
* - model is updated with new list of objects
*
*
* 3
* {@code jListP} changes to a new {@code JList}
*
* - model is uninstalled from old {@code JList}
*
*
*
* 4
* explicit call to {@code binding.refresh()}
*
* - model is installed on target {@code JList}, representing list of objects
*
*
*
* 5
* {@code listP} changes to a new {@code List}
*
* - {@code refresh()} is called
*
*
* - model is updated with new list of objects
*
*
* 6
* explicit call to {@code binding.unbind()}
*
* - model is uninstalled from target {@code JList}
*
*
*
* // create the person List
* List
*
*
* Sequence Event Result
*
* 1
* explicit call to {@code binding.bind()}
*
* - synthetic target property becomes readable/writeable
*
*
* - {@code refresh()} is called
*
* - model is installed on target {@code JTable}, representing list of objects
*
*
* 2
* {@code listP} changes to a new {@code List}
*
* - {@code refresh()} is called
*
*
* - model is updated with new list of objects
*
*
* 3
* {@code jTableP} changes to a new {@code JTable}
*
* - model is uninstalled from old {@code JTable}
*
*
*
* 4
* explicit call to {@code binding.refresh()}
*
* - model is installed on target {@code JTable}, representing list of objects
*
*
*
* 5
* {@code listP} changes to a new {@code List}
*
* - {@code refresh()} is called
*
*
* - model is updated with new list of objects
*
*
* 6
* explicit call to {@code binding.unbind()}
*
* - model is uninstalled from target {@code JTable}
*
* >create(), targetJList, ObjectProperty.
>create(), targetJList, ObjectProperty.
>create(), targetObject, targetJListProperty, null);
}
/**
* Creates a named {@code JListBinding} from a direct reference to a {@code List} and an object and property that resolves to a {@code JList}.
*
* @param strategy the update strategy
* @param sourceList the source {@code List}
* @param targetObject the target object
* @param targetJListProperty a property on the target object that resolves to a {@code JList}
* @return the {@code JListBinding}
* @throws IllegalArgumentException if {@code targetJListProperty} is {@code null}
*/
public static
>create(), targetObject, targetJListProperty, name);
}
/**
* Creates a {@code JListBinding} from an object and property that resolves to a {@code List} and an object and property that resolves to a {@code JList}.
*
* @param strategy the update strategy
* @param sourceObject the source object
* @param sourceListProperty a property on the source object that resolves to a {@code List}
* @param targetObject the target object
* @param targetJListProperty a property on the target object that resolves to a {@code JList}
* @return the {@code JListBinding}
* @throws IllegalArgumentException if {@code sourceListProperty} or {@code targetJListProperty} is {@code null}
*/
public static
>create(), targetJTable, ObjectProperty.
>create(), targetJTable, ObjectProperty.
>create(), targetObject, targetJTableProperty, null);
}
/**
* Creates a named {@code JTableBinding} from a direct reference to a {@code List} and an object and property that resolves to a {@code JTable}.
*
* @param strategy the update strategy
* @param sourceList the source {@code List}
* @param targetObject the target object
* @param targetJTableProperty a property on the target object that resolves to a {@code JTable}
* @return the {@code JTableBinding}
* @throws IllegalArgumentException if {@code targetJTableProperty} is {@code null}
*/
public static
>create(), targetObject, targetJTableProperty, name);
}
/**
* Creates a {@code JTableBinding} from an object and property that resolves to a {@code List} and an object and property that resolves to a {@code JTable}.
*
* @param strategy the update strategy
* @param sourceObject the source object
* @param sourceListProperty a property on the source object that resolves to a {@code List}
* @param targetObject the target object
* @param targetJTableProperty a property on the target object that resolves to a {@code JTable}
* @return the {@code JTableBinding}
* @throws IllegalArgumentException if {@code sourceListProperty} or {@code targetJTableProperty} is {@code null}
*/
public static
>create(), targetJComboBox, ObjectProperty.
>create(), targetJComboBox, ObjectProperty.
>create(), targetObject, targetJComboBoxProperty, null);
}
/**
* Creates a named {@code JComboBoxBinding} from a direct reference to a {@code List} and an object and property that resolves to a {@code JComboBox}.
*
* @param strategy the update strategy
* @param sourceList the source {@code List}
* @param targetObject the target object
* @param targetJComboBoxProperty a property on the target object that resolves to a {@code JComboBox}
* @return the {@code JComboBoxBinding}
* @throws IllegalArgumentException if {@code targetJComboBoxProperty} is {@code null}
*/
public static
>create(), targetObject, targetJComboBoxProperty, name);
}
/**
* Creates a {@code JComboBoxBinding} from an object and property that resolves to a {@code List} and an object and property that resolves to a {@code JComboBox}.
*
* @param strategy the update strategy
* @param sourceObject the source object
* @param sourceListProperty a property on the source object that resolves to a {@code List}
* @param targetObject the target object
* @param targetJComboBoxProperty a property on the target object that resolves to a {@code JComboBox}
* @return the {@code JComboBoxBinding}
* @throws IllegalArgumentException if {@code sourceListProperty} or {@code targetJComboBoxProperty} is {@code null}
*/
public static