ValueModels can be created for a property name using {@link #getValueModel(String)} or for a triple of (property name, getter name, setter name) using {@link #getValueModel(String,String,String)}. If you just specify the property name, the adapter uses the standard Java Bean introspection to lookup the available properties and how to read and write the property value. In case of custom readers and writers you may specify a custom BeanInfo class, or as a shortcut use the method that accepts the optional getter and setter name. If these are specified, introspection will be bypassed and a PropertyDescriptor will be created for the given property name, getter and setter name. Note: For each property name subsequent calls to these methods must use the same getter and setter names. Attempts to violate this constraint are rejected with an UsageException.
Property values for a given property name can be read using {@link #getValue(String)}. To set a value for a for a property name invoke {@link #setValue(String,Object)}.
Optionally the BeanAdapter can observe changes in bound properties as described in section 7.4 of the Bean specification. The bean then must provide support for listening on properties as described in section 7.4 of this specification. You can enable this feature by setting the constructor parameter observeChanges
to true
. If the adapter observes changes, the ValueModels returned by #getValueModel
will fire value change events, i.e. PropertyChangeEvents for the property "value"
. Even if you ignore property changes, you can access the adapted property value via #getValue()
. It's just that you won't be notified about changes.
In addition you can observe the bean's bound properties by registering PropertyChangeListeners with the bean using #addBeanPropertyChangeListener
. These listeners will be removed from the old bean before the bean changes and will be re-added after the new bean has been set. Therefore these listeners will be notified about changes only if the current bean changes a property. They won't be notified if the bean changes - and in turn the property value. If you want to observes property changes caused by bean changes too, register with the adapting ValueModel as returned by #getValueModel(String)
.
The BeanAdapter provides two access styles to the target bean that holds the adapted property: you can specify a bean directly, or you can use a bean channel to access the bean indirectly. In the latter case you specify a ValueModel
that holds the bean that in turn holds the adapted properties.
If the adapted bean is null
the BeanAdapter can neither read nor set a value. In this case #getValue
returns null
and #setValue
will silently ignore the new value.
This adapter throws three PropertyChangeEvents if the bean changes: beforeBean, bean and afterBean. This is useful when sharing a bean channel and you must perform an operation before or after other listeners handle a bean change. Since you cannot rely on the order listeners will be notified, only the beforeBean and afterBean events are guaranteed to be fired before and after the bean change is fired. Note that #getBean()
returns the new bean before any of these three PropertyChangeEvents is fired. Therefore listeners that handle these events must use the event's old and new value to determine the old and new bean. The order of events fired during a bean change is:
Unused BeanAdapters that observe changes have a PropertyChangeListener registered with the target bean if the bean is not null. It is recommended to remove this listener by invoking #release
if the observed bean lives much longer than the adapter. After #release has been called you cannot use the BeanAdapter instance any longer. As an alternative you can use event listener lists that implement references with WeakReference
.
Constraints: If property changes shall be observed, the bean class must support bound properties, i. e. it must provide the following pair of methods for registration of multicast property change event listeners:
public void addPropertyChangeListener(PropertyChangeListener x); public void removePropertyChangeListener(PropertyChangeListener x);PropertyAdapter vs. BeanAdapter vs. PresentationModel
Basic Examples:
// Direct access, ignores changes Address address = new Address() BeanAdapter adapter = new BeanAdapter(address); adapter.setValue("street", "Broadway"); System.out.println(address.getStreet()); // Prints "Broadway" address.setStreet("Franz-Josef-Str."); System.out.println(adapter.getValue("street")); // Prints "Franz-Josef-Str." //Direct access, observes changes BeanAdapter adapter = new BeanAdapter(address, true); // Indirect access, ignores changes ValueHolder addressHolder = new ValueHolder(address1); BeanAdapter adapter = new BeanAdapter(addressHolder); adapter.setValue("street", "Broadway"); // Sets the street in address1 System.out.println(address1.getStreet()); // Prints "Broadway" adapter.setBean(address2); adapter.setValue("street", "Robert-Koch-Str."); // Sets the street in address2 System.out.println(address2.getStreet()); // Prints "Robert-Koch-Str." // Indirect access, observes changes ValueHolder addressHolder = new ValueHolder(); BeanAdapter adapter = new BeanAdapter(addressHolder, true); addressHolder.setValue(address1); address1.setStreet("Broadway"); System.out.println(adapter.getValue("street")); // Prints "Broadway" // Access through ValueModels Address address = new Address(); BeanAdapter adapter = new BeanAdapter(address); ValueModel streetModel = adapter.getValueModel("street"); ValueModel cityModel = adapter.getValueModel("city"); streetModel.setValue("Broadway"); System.out.println(address.getStreet()); // Prints "Broadway" address.setCity("Hamburg"); System.out.println(cityModel.getValue()); // Prints "Hamburg"Adapter Chain Example:
Country country = new Country(); country.setName("Germany"); country.setEuMember(true); BeanAdapter countryAdapter = new BeanAdapter(country, true); JTextField nameField = new JTextField(); nameField.setDocument(new DocumentAdapter( countryAdapter.getValueModel("name"))); JCheckBox euMemberBox = new JCheckBox("Is EU Member"); euMemberBox.setModel(new ToggleButtonAdapter( countryAdapter.getValueModel("euMember"))); // Using factory methods JTextField nameField = Factory.createTextField(country, "name"); JCheckBox euMemberBox = Factory.createCheckBox (country, "euMember"); euMemberBox.setText("Is EU Member");
are performed in the event dispatch thread. In case the adapted bean is changed in a thread other than the event dispatch thread, such a feature would help complying with Swing's single thread rule. The feature could be implemented by an extended PropertyChangeSupport.
PropertyChangeEvents. This affects the classes PropertyAdapter, BeanAdapter, and PresentationModel. Basically the PropertyAdapter and the BeanAdapter's internal SimplePropertyAdapter's shall be able to optionally self-fire a PropertyChangeEvent in case the bean does not. There are several downsides with self-firing events compared to bound bean properties. See Issue 49 for more information about the downsides.
The observeChanges constructor parameter shall be replaced by a more fine-grained choice to not observe (former observeChanges=false), to observe bound properties (former observeChanges=true), and a new setting for self-firing PropertyChangeEvents if a value is set. The latter case may be further splitted up to specify how the self-fired PropertyChangeEvent is created:
|
|
|
|