|
|||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |
See:
Description
Class Summary | |
BeanFilter | The BeanFilter class is the abstract superclass
of all bean filters. |
BeanWrapper | The BeanWrapper class is a stateful business object
wrapper that manages string-to-object value conversions and property
change events for a single business object. |
GenericBeanFilter | Generic default filter created through introspection. |
GenericValidator | Generic validator. |
PropertyFilter | An abstract superclass of property filters. |
Validator | The Validator class is the abstract superclass of all
validators. |
Exception Summary | |
FilterException | Superclass for exceptions in the filter package. |
InitializationException | Exception thrown during filter initialization. |
OperationException | Exception that wraps exceptions from underlying methods. |
PropertyNotFoundException | Exception thrown when a property is not found. |
PropertyNotReadableException | Exception thrown when a property is not readable. |
PropertyNotWriteableException | Exception thrown when a property is not writeable. |
ValidationException | Exception thrown for validation errors. |
A package that manages string-to-data conversions for JavaBeans. The
filter
library is an abstraction layer that is responsible for:
PropertyChangeEvent
messages.It was necessary to develop the filter
library because none
of the existing structures in the Java language adequately address these issues:
java.text
package
address conversions for individual values but not for entire business
objects.Another major open source framework that addresses this issue (the org.apache.commons.beanutils
API) only deals with conversions on a per field basis and doesn't allow
any customization specific to a particular business object class. In the
author's opinion isn't good enough either. For example, what about the case
where a double
value represents a percentage in one object but a
currency value in another?
The filter
library using a syntax similar to both
reflection and the java.beans
classes. The heart of the package is
the abstract BeanFilter
class. This class is
analogous to the java.beans.BeanInfo
classes. Unlike those classes,
however, each BeanFilter
is a bean-specific singleton. All of the
filter's properties
are immutable and all of its methods are stateless, making it thread safe.
The abstract BeanFilter
class has factory methods for retrieving
a BeanFilter
for a specific Java Bean:
BeanFilter filter = BeanFilter.findFilter(Object | Class | String);
The parameter for the findFilter
method can be an
object, its Class
or its fully qualified class name as a string. This
method retrieves the BeanFilter
in one of three ways:
BeanFilter
for that bean
has already been initialized.<beanClassName>+"Filter"
. That is, the filter for the example.Account
class would
be example.Account
Filter
.GenericBeanFilter
object whose values are initialized
from the bean class via reflection.Once located, the bean filter can be used to get/set properties object using string values.
BeanFilter filter = BeanFilter.findFilter(account); filter.set(account, "name", "John Smith"); // account.setName("John Smith"); filter.set(account, "balance", "1234.56"); // account.setBalance(1234.56); filter.set(account, "startDate", "10/11/2001"); // account.setStartDate(<10/11/2001>);
The BeanFilter
object holds a collections of PropertyFilter
objects. The get()
and set()
methods of the BeanFilter
delegate to the appropriate
PropertyFilter
. The property filters can be accessed directly through
the getPropertyFilters()
method. This allows dynamic discovery of
the properties of the bean:
BeanFilter filter = BeanFilter.findFilter(account); PropertyFilter[] propertyFilters = filter.getPropertyFilters(); for (int i=0; i<propertyFilters.length; i++) { String name = propertyFilters[i].getName(); // Do something with that property }
In addition, the PropertyFilter
stores property meta-data. This meta-data can be
useful during user interface generation, especially if the UI is in a different language than
Java, such as HTML/JavaScript.
BeanFilter filter = BeanFilter.findFilter(account); PropertyFilter propertyFilter = filter.getPropertyFilter(property); String attributeValue = propertyFilter.getAttribute(attribute);
The text filter API does not dictate any particular structure to property meta-data, but other layers of the Chrysalis framework uses certain values:
label |
The label for the field. Defaults to the field name. |
datatype |
The field's data type. |
readonly |
Whether the field is read-only. This is useful for calculated fields. |
required |
Whether the field is required. |
max |
The field's maximum value (numeric types only). This value is included in the allowed range. |
min |
The field's minimum value (numeric types only). This value is included in the allowed range. |
maxlength |
The field's maximum length (string types only). This value is included in the allowed range. |
minlength |
The field's minimum length (string types only). This value is included in the allowed range. |
scale |
The number of digits after the decimal place (decimal types only). |
options |
A map containing a list of options for constrained values. The keys in this map should be the valid values for the property, and the values in the map should be human-readable interpretations of these values. This attribute is intended to generate drop-down lists or select lists. |
A few of the above attribute values can be deduced from the business object's structure.
The text filter API places very few restrictions on the structures of the business objects it uses. The only firm requirement is that they have properties encapsulated in get/set methods.
Metadata information can be embedded in the chrysalis.xml
configuration file for the JavaBean package.
<config> <class name="Item"> <property name="name" maxlength="20" /> <property name="stock" min="0" max="1000" /> </class> <!-- Metadata for other beans ... --> </config>
The following property attributes have default values if not specified by the configuration:
label |
Defaults to the field name. |
readonly |
Defaults to true unless (a) the
property has setter method and (b) the property type has Converter (that is, it is a simple type). |
datatype |
Defaults to the property's Java type, or its object wrapper type if
primitive. The default uses the type's short class name, not the fully
qualified name. For example, strings are "String ", java.util.Date
is "Date " and int is "Integer ". |
Business objects that do not following these conventions can have their meta-data defined through custom filters.
Sometimes the default GenericBeanFilter
is not accurate enough to
describe the business object and its string conversions. The filter library
also supports the definition of custom filters. The custom filter must be in the
same package as the bean, and its class name
must be beanClass + "Filter"
. If this class exists, the findFilter()
method returns an instance of this class rather than a dynamically generated
filter. The filter class can be defined in two ways:
GenericBeanFilter
. If so, properties and
methods that are not redefined by the subclass will be initialized through reflection.BeanFilter
. If so, all of the filter's
property and method filters must be explicitly defined.The first technique is generally preferable. The second technique is useful
if some of a beans public properties and methods must be hidden from the user
interface. For the most part, the custom filter
class need only define its constructor. For example, the filter for
the example.Account
class could be defined as:
package example; import org.chwf.filter.*; import java.text.SimpleDateFormat; public class AccountFilter extends GenericBeanFilter { public AccountFilter() { super(Account.class); // startDate property filter as an anonymous inner class: PropertyFilter sdFilter = new PropertyFilter("startDate", java.util.Date.class) { public String get(Object object) { java.util.Date date = ((Account) object).getStartDate(); if (date == null) { return null; } else { SimpleDateFormat df = new SimpleDateFormat("mm/dd/yyyy"); return df.format(date); } } public Object getPropertyAsObject(Object object) { return ((Account) object).getStartDate(); } public void set(Object object, String value) { try { SimpleDateFormat df = new SimpleDateFormat("mm/dd/yyyy"); ((Account) object).setStartDate(df.parse(value)); } catch (java.text.ParseException ex) { throw new IllegalArgumentException(ex.getMessage()); } } }; putPropertyFilter("startDate", sdFilter); // Initialize property attributes from constant values: initializePropertyAttributes(); } }
Any property filters defined in the constructor of the custom
filter class replaces the generic filters created in the GenericBeanFilter
constructor. As noted in the description of the GenericBeanFilter
class, the
initializePropertyAttributes()
method is not automatically called
in the GenericBeanFilter
constructor. It should be called manually
in the custom filter subclass after all property filters have been
put into the bean filter. Changes to property attributes can be made
after the call to initializePropertyAttributes()
:
initializePropertyAttributes(); putPropertyAttribute("balance", "datatype", "Currency");
Performance notes: By eliminating reflection, custom filters have slightly better performance than generic filters. The performance gain may seem significant, in the neighborhood of 33%, but when combined with the other operations that the application is performing (managing remote calls, load and save operations to the database), the effects of this gain will be negligible. Therefore, the developer should only define custom filters when absolutely necessary, to override behavior that is not correctly defined through reflection.
The BeanWrapper
class is a stateful business object wrapper that
manages string-to-object value conversions and property change events for a
single business object. It simplifies BeanFilter
methods by
eliminating the object parameter, but it is not thread-safe. The BeanWrapper
is appropriate for use in non-multi-threaded environments like Java GUIs:
BeanWrapper wrapper = new BeanWrapper(account); wrapper.set("name", "John Smith"); // account.setName("John Smith"); wrapper.set("balance", "1234.56"); // account.setBalance(1234.56); wrapper.set("startDate", "10/11/2001"); // account.setStartDate(<10/11/2001>);
The BeanWrapper
class also allows registration of PropertyChangeListener
objects to handle PropertyChangeEvents
. This means that the
business objects themselves do not need to be encumbered with such GUI
specific code. These property change events are only generated when the
property is changed through the BeanWrapper
, using its
set()
and setAll()
methods.
BeanWrapper wrapper = new BeanWrapper(account); PropertyChangeListener bcl = new BalanceChangeListener(); wrapper.addPropertyChangeListen("balance", bcl); // listener is notified when balance is changed via the wrapper
The bean filter API leverages the internationalization features of the Converter
class that it uses for string conversions. To
specify the user locale:
UserLocale.setLocale(locale);
In addition, you can retrieve localized attribute values (such as for
property labels) using the PropertyFilter.getLocalizedAttribute()
method.
|
|||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |