JavaBean property population methods.
| Method from org.apache.commons.beanutils.BeanUtilsBean Detail: |
public Object cloneBean(Object bean) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
if (log.isDebugEnabled()) {
log.debug("Cloning bean: " + bean.getClass().getName());
}
Object newBean = null;
if (bean instanceof DynaBean) {
newBean = ((DynaBean) bean).getDynaClass().newInstance();
} else {
newBean = bean.getClass().newInstance();
}
getPropertyUtils().copyProperties(newBean, bean);
return (newBean);
}
Clone a bean based on the available property getters and setters,
even if the bean class itself does not implement Cloneable.
Note: this method creates a shallow clone.
In other words, any objects referred to by the bean are shared with the clone
rather than being cloned in turn.
|
protected Object convert(Object value,
Class type) {
Converter converter = getConvertUtils().lookup(type);
if (converter != null) {
log.trace(" USING CONVERTER " + converter);
return converter.convert(type, value);
} else {
return value;
}
}
|
public void copyProperties(Object dest,
Object orig) throws IllegalAccessException, InvocationTargetException {
// Validate existence of the specified beans
if (dest == null) {
throw new IllegalArgumentException
("No destination bean specified");
}
if (orig == null) {
throw new IllegalArgumentException("No origin bean specified");
}
if (log.isDebugEnabled()) {
log.debug("BeanUtils.copyProperties(" + dest + ", " +
orig + ")");
}
// Copy the properties, converting as necessary
if (orig instanceof DynaBean) {
DynaProperty[] origDescriptors =
((DynaBean) orig).getDynaClass().getDynaProperties();
for (int i = 0; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();
// Need to check isReadable() for WrapDynaBean
// (see Jira issue# BEANUTILS-61)
if (getPropertyUtils().isReadable(orig, name) &&
getPropertyUtils().isWriteable(dest, name)) {
Object value = ((DynaBean) orig).get(name);
copyProperty(dest, name, value);
}
}
} else if (orig instanceof Map) {
Iterator entries = ((Map) orig).entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
String name = (String)entry.getKey();
if (getPropertyUtils().isWriteable(dest, name)) {
copyProperty(dest, name, entry.getValue());
}
}
} else /* if (orig is a standard JavaBean) */ {
PropertyDescriptor[] origDescriptors =
getPropertyUtils().getPropertyDescriptors(orig);
for (int i = 0; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();
if ("class".equals(name)) {
continue; // No point in trying to set an object's class
}
if (getPropertyUtils().isReadable(orig, name) &&
getPropertyUtils().isWriteable(dest, name)) {
try {
Object value =
getPropertyUtils().getSimpleProperty(orig, name);
copyProperty(dest, name, value);
} catch (NoSuchMethodException e) {
// Should not happen
}
}
}
}
}
Copy property values from the origin bean to the destination bean
for all cases where the property names are the same. For each
property, a conversion is attempted as necessary. All combinations of
standard JavaBeans and DynaBeans as origin and destination are
supported. Properties that exist in the origin bean, but do not exist
in the destination bean (or are read-only in the destination bean) are
silently ignored.
If the origin "bean" is actually a Map, it is assumed
to contain String-valued simple property names as the keys, pointing at
the corresponding property values that will be converted (if necessary)
and set in the destination bean. Note that this method
is intended to perform a "shallow copy" of the properties and so complex
properties (for example, nested ones) will not be copied.
This method differs from populate(), which
was primarily designed for populating JavaBeans from the map of request
parameters retrieved on an HTTP request, is that no scalar->indexed
or indexed->scalar manipulations are performed. If the origin property
is indexed, the destination property must be also.
If you know that no type conversions are required, the
copyProperties() method in PropertyUtils will
execute faster than this method.
FIXME - Indexed and mapped properties that do not
have getter and setter methods for the underlying array or Map are not
copied by this method.
|
public void copyProperty(Object bean,
String name,
Object value) throws IllegalAccessException, InvocationTargetException {
// Trace logging (if enabled)
if (log.isTraceEnabled()) {
StringBuffer sb = new StringBuffer(" copyProperty(");
sb.append(bean);
sb.append(", ");
sb.append(name);
sb.append(", ");
if (value == null) {
sb.append("< NULL >");
} else if (value instanceof String) {
sb.append((String) value);
} else if (value instanceof String[]) {
String[] values = (String[]) value;
sb.append('[');
for (int i = 0; i < values.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(values[i]);
}
sb.append(']');
} else {
sb.append(value.toString());
}
sb.append(')');
log.trace(sb.toString());
}
// Resolve any nested expression to get the actual target bean
Object target = bean;
Resolver resolver = getPropertyUtils().getResolver();
while (resolver.hasNested(name)) {
try {
target = getPropertyUtils().getProperty(target, resolver.next(name));
name = resolver.remove(name);
} catch (NoSuchMethodException e) {
return; // Skip this property setter
}
}
if (log.isTraceEnabled()) {
log.trace(" Target bean = " + target);
log.trace(" Target name = " + name);
}
// Declare local variables we will require
String propName = resolver.getProperty(name); // Simple name of target property
Class type = null; // Java type of target property
int index = resolver.getIndex(name); // Indexed subscript value (if any)
String key = resolver.getKey(name); // Mapped key value (if any)
// Calculate the target property type
if (target instanceof DynaBean) {
DynaClass dynaClass = ((DynaBean) target).getDynaClass();
DynaProperty dynaProperty = dynaClass.getDynaProperty(propName);
if (dynaProperty == null) {
return; // Skip this property setter
}
type = dynaProperty.getType();
} else {
PropertyDescriptor descriptor = null;
try {
descriptor =
getPropertyUtils().getPropertyDescriptor(target, name);
if (descriptor == null) {
return; // Skip this property setter
}
} catch (NoSuchMethodException e) {
return; // Skip this property setter
}
type = descriptor.getPropertyType();
if (type == null) {
// Most likely an indexed setter on a POJB only
if (log.isTraceEnabled()) {
log.trace(" target type for property '" +
propName + "' is null, so skipping ths setter");
}
return;
}
}
if (log.isTraceEnabled()) {
log.trace(" target propName=" + propName + ", type=" +
type + ", index=" + index + ", key=" + key);
}
// Convert the specified value to the required type and store it
if (index >= 0) { // Destination must be indexed
value = convert(value, type.getComponentType());
try {
getPropertyUtils().setIndexedProperty(target, propName,
index, value);
} catch (NoSuchMethodException e) {
throw new InvocationTargetException
(e, "Cannot set " + propName);
}
} else if (key != null) { // Destination must be mapped
// Maps do not know what the preferred data type is,
// so perform no conversions at all
// FIXME - should we create or support a TypedMap?
try {
getPropertyUtils().setMappedProperty(target, propName,
key, value);
} catch (NoSuchMethodException e) {
throw new InvocationTargetException
(e, "Cannot set " + propName);
}
} else { // Destination must be simple
value = convert(value, type);
try {
getPropertyUtils().setSimpleProperty(target, propName, value);
} catch (NoSuchMethodException e) {
throw new InvocationTargetException
(e, "Cannot set " + propName);
}
}
}
Copy the specified property value to the specified destination bean,
performing any type conversion that is required. If the specified
bean does not have a property of the specified name, or the property
is read only on the destination bean, return without
doing anything. If you have custom destination property types, register
Converter s for them by calling the register()
method of ConvertUtils .
IMPLEMENTATION RESTRICTIONS:
- Does not support destination properties that are indexed,
but only an indexed setter (as opposed to an array setter)
is available.
- Does not support destination properties that are mapped,
but only a keyed setter (as opposed to a Map setter)
is available.
- The desired property type of a mapped setter cannot be
determined (since Maps support any data type), so no conversion
will be performed.
|
public Map describe(Object bean) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
if (bean == null) {
// return (Collections.EMPTY_MAP);
return (new java.util.HashMap());
}
if (log.isDebugEnabled()) {
log.debug("Describing bean: " + bean.getClass().getName());
}
Map description = new HashMap();
if (bean instanceof DynaBean) {
DynaProperty[] descriptors =
((DynaBean) bean).getDynaClass().getDynaProperties();
for (int i = 0; i < descriptors.length; i++) {
String name = descriptors[i].getName();
description.put(name, getProperty(bean, name));
}
} else {
PropertyDescriptor[] descriptors =
getPropertyUtils().getPropertyDescriptors(bean);
Class clazz = bean.getClass();
for (int i = 0; i < descriptors.length; i++) {
String name = descriptors[i].getName();
if (getPropertyUtils().getReadMethod(clazz, descriptors[i]) != null) {
description.put(name, getProperty(bean, name));
}
}
}
return (description);
}
Return the entire set of properties for which the specified bean
provides a read method. This map contains the to String
converted property values for all properties for which a read method
is provided (i.e. where the getReadMethod() returns non-null).
This map can be fed back to a call to
BeanUtils.populate() to reconsitute the same set of
properties, modulo differences for read-only and write-only
properties, but only if there are no indexed properties.
Warning: if any of the bean property implementations
contain (directly or indirectly) a call to this method then
a stack overflow may result. For example:
class MyBean
{
public Map getParameterMap()
{
BeanUtils.describe(this);
}
}
will result in an infinite regression when getParametersMap
is called. It is recommended that such methods are given alternative
names (for example, parametersMap).
|
public String[] getArrayProperty(Object bean,
String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Object value = getPropertyUtils().getProperty(bean, name);
if (value == null) {
return (null);
} else if (value instanceof Collection) {
ArrayList values = new ArrayList();
Iterator items = ((Collection) value).iterator();
while (items.hasNext()) {
Object item = items.next();
if (item == null) {
values.add((String) null);
} else {
// convert to string using convert utils
values.add(getConvertUtils().convert(item));
}
}
return ((String[]) values.toArray(new String[values.size()]));
} else if (value.getClass().isArray()) {
int n = Array.getLength(value);
String[] results = new String[n];
for (int i = 0; i < n; i++) {
Object item = Array.get(value, i);
if (item == null) {
results[i] = null;
} else {
// convert to string using convert utils
results[i] = getConvertUtils().convert(item);
}
}
return (results);
} else {
String[] results = new String[1];
results[0] = getConvertUtils().convert(value);
return (results);
}
}
Return the value of the specified array property of the specified
bean, as a String array. |
public ConvertUtilsBean getConvertUtils() {
return convertUtilsBean;
}
Gets the ConvertUtilsBean instance used to perform the conversions. |
public String getIndexedProperty(Object bean,
String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Object value = getPropertyUtils().getIndexedProperty(bean, name);
return (getConvertUtils().convert(value));
}
Return the value of the specified indexed property of the specified
bean, as a String. The zero-relative index of the
required value must be included (in square brackets) as a suffix to
the property name, or IllegalArgumentException will be
thrown. |
public String getIndexedProperty(Object bean,
String name,
int index) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Object value = getPropertyUtils().getIndexedProperty(bean, name, index);
return (getConvertUtils().convert(value));
}
Return the value of the specified indexed property of the specified
bean, as a String. The index is specified as a method parameter and
must *not* be included in the property name expression |
public static BeanUtilsBean getInstance() {
return (BeanUtilsBean) BEANS_BY_CLASSLOADER.get();
}
Gets the instance which provides the functionality for BeanUtils .
This is a pseudo-singleton - an single instance is provided per (thread) context classloader.
This mechanism provides isolation for web apps deployed in the same container. |
public String getMappedProperty(Object bean,
String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Object value = getPropertyUtils().getMappedProperty(bean, name);
return (getConvertUtils().convert(value));
}
Return the value of the specified indexed property of the specified
bean, as a String. The String-valued key of the required value
must be included (in parentheses) as a suffix to
the property name, or IllegalArgumentException will be
thrown. |
public String getMappedProperty(Object bean,
String name,
String key) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Object value = getPropertyUtils().getMappedProperty(bean, name, key);
return (getConvertUtils().convert(value));
}
Return the value of the specified mapped property of the specified
bean, as a String. The key is specified as a method parameter and
must *not* be included in the property name expression |
public String getNestedProperty(Object bean,
String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Object value = getPropertyUtils().getNestedProperty(bean, name);
return (getConvertUtils().convert(value));
}
Return the value of the (possibly nested) property of the specified
name, for the specified bean, as a String. |
public String getProperty(Object bean,
String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
return (getNestedProperty(bean, name));
}
Return the value of the specified property of the specified bean,
no matter which property reference format is used, as a String. |
public PropertyUtilsBean getPropertyUtils() {
return propertyUtilsBean;
}
Gets the PropertyUtilsBean instance used to access properties. |
public String getSimpleProperty(Object bean,
String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Object value = getPropertyUtils().getSimpleProperty(bean, name);
return (getConvertUtils().convert(value));
}
Return the value of the specified simple property of the specified
bean, converted to a String. |
public boolean initCause(Throwable throwable,
Throwable cause) {
if (INIT_CAUSE_METHOD != null && cause != null) {
try {
INIT_CAUSE_METHOD.invoke(throwable, new Object[] { cause });
return true;
} catch (Throwable e) {
return false; // can't initialize cause
}
}
return false;
}
If we're running on JDK 1.4 or later, initialize the cause for the given throwable. |
public void populate(Object bean,
Map properties) throws IllegalAccessException, InvocationTargetException {
// Do nothing unless both arguments have been specified
if ((bean == null) || (properties == null)) {
return;
}
if (log.isDebugEnabled()) {
log.debug("BeanUtils.populate(" + bean + ", " +
properties + ")");
}
// Loop through the property name/value pairs to be set
Iterator entries = properties.entrySet().iterator();
while (entries.hasNext()) {
// Identify the property name and value(s) to be assigned
Map.Entry entry = (Map.Entry)entries.next();
String name = (String) entry.getKey();
if (name == null) {
continue;
}
// Perform the assignment for this property
setProperty(bean, name, entry.getValue());
}
}
Populate the JavaBeans properties of the specified bean, based on
the specified name/value pairs. This method uses Java reflection APIs
to identify corresponding "property setter" method names, and deals
with setter arguments of type String, boolean,
int, long, float, and
double. In addition, array setters for these types (or the
corresponding primitive types) can also be identified.
The particular setter method to be called for each property is
determined using the usual JavaBeans introspection mechanisms. Thus,
you may identify custom setter methods using a BeanInfo class that is
associated with the class of the bean itself. If no such BeanInfo
class is available, the standard method name conversion ("set" plus
the capitalized name of the property in question) is used.
NOTE: It is contrary to the JavaBeans Specification
to have more than one setter method (with different argument
signatures) for the same property.
WARNING - The logic of this method is customized
for extracting String-based request parameters from an HTTP request.
It is probably not what you want for general property copying with
type conversion. For that purpose, check out the
copyProperties() method instead.
|
public static void setInstance(BeanUtilsBean newInstance) {
BEANS_BY_CLASSLOADER.set(newInstance);
}
Sets the instance which provides the functionality for BeanUtils .
This is a pseudo-singleton - an single instance is provided per (thread) context classloader.
This mechanism provides isolation for web apps deployed in the same container. |
public void setProperty(Object bean,
String name,
Object value) throws IllegalAccessException, InvocationTargetException {
// Trace logging (if enabled)
if (log.isTraceEnabled()) {
StringBuffer sb = new StringBuffer(" setProperty(");
sb.append(bean);
sb.append(", ");
sb.append(name);
sb.append(", ");
if (value == null) {
sb.append("< NULL >");
} else if (value instanceof String) {
sb.append((String) value);
} else if (value instanceof String[]) {
String[] values = (String[]) value;
sb.append('[');
for (int i = 0; i < values.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(values[i]);
}
sb.append(']');
} else {
sb.append(value.toString());
}
sb.append(')');
log.trace(sb.toString());
}
// Resolve any nested expression to get the actual target bean
Object target = bean;
Resolver resolver = getPropertyUtils().getResolver();
while (resolver.hasNested(name)) {
try {
target = getPropertyUtils().getProperty(target, resolver.next(name));
name = resolver.remove(name);
} catch (NoSuchMethodException e) {
return; // Skip this property setter
}
}
if (log.isTraceEnabled()) {
log.trace(" Target bean = " + target);
log.trace(" Target name = " + name);
}
// Declare local variables we will require
String propName = resolver.getProperty(name); // Simple name of target property
Class type = null; // Java type of target property
int index = resolver.getIndex(name); // Indexed subscript value (if any)
String key = resolver.getKey(name); // Mapped key value (if any)
// Calculate the property type
if (target instanceof DynaBean) {
DynaClass dynaClass = ((DynaBean) target).getDynaClass();
DynaProperty dynaProperty = dynaClass.getDynaProperty(propName);
if (dynaProperty == null) {
return; // Skip this property setter
}
type = dynaProperty.getType();
} else if (target instanceof Map) {
type = Object.class;
} else if (target != null && target.getClass().isArray() && index >= 0) {
type = Array.get(target, index).getClass();
} else {
PropertyDescriptor descriptor = null;
try {
descriptor =
getPropertyUtils().getPropertyDescriptor(target, name);
if (descriptor == null) {
return; // Skip this property setter
}
} catch (NoSuchMethodException e) {
return; // Skip this property setter
}
if (descriptor instanceof MappedPropertyDescriptor) {
if (((MappedPropertyDescriptor) descriptor).getMappedWriteMethod() == null) {
if (log.isDebugEnabled()) {
log.debug("Skipping read-only property");
}
return; // Read-only, skip this property setter
}
type = ((MappedPropertyDescriptor) descriptor).
getMappedPropertyType();
} else if (index >= 0 && descriptor instanceof IndexedPropertyDescriptor) {
if (((IndexedPropertyDescriptor) descriptor).getIndexedWriteMethod() == null) {
if (log.isDebugEnabled()) {
log.debug("Skipping read-only property");
}
return; // Read-only, skip this property setter
}
type = ((IndexedPropertyDescriptor) descriptor).
getIndexedPropertyType();
} else if (key != null) {
if (descriptor.getReadMethod() == null) {
if (log.isDebugEnabled()) {
log.debug("Skipping read-only property");
}
return; // Read-only, skip this property setter
}
type = (value == null) ? Object.class : value.getClass();
} else {
if (descriptor.getWriteMethod() == null) {
if (log.isDebugEnabled()) {
log.debug("Skipping read-only property");
}
return; // Read-only, skip this property setter
}
type = descriptor.getPropertyType();
}
}
// Convert the specified value to the required type
Object newValue = null;
if (type.isArray() && (index < 0)) { // Scalar value into array
if (value == null) {
String[] values = new String[1];
values[0] = null;
newValue = getConvertUtils().convert(values, type);
} else if (value instanceof String) {
newValue = getConvertUtils().convert(value, type);
} else if (value instanceof String[]) {
newValue = getConvertUtils().convert((String[]) value, type);
} else {
newValue = convert(value, type);
}
} else if (type.isArray()) { // Indexed value into array
if (value instanceof String || value == null) {
newValue = getConvertUtils().convert((String) value,
type.getComponentType());
} else if (value instanceof String[]) {
newValue = getConvertUtils().convert(((String[]) value)[0],
type.getComponentType());
} else {
newValue = convert(value, type.getComponentType());
}
} else { // Value into scalar
if (value instanceof String) {
newValue = getConvertUtils().convert((String) value, type);
} else if (value instanceof String[]) {
newValue = getConvertUtils().convert(((String[]) value)[0],
type);
} else {
newValue = convert(value, type);
}
}
// Invoke the setter method
try {
getPropertyUtils().setProperty(target, name, newValue);
} catch (NoSuchMethodException e) {
throw new InvocationTargetException
(e, "Cannot set " + propName);
}
}
Set the specified property value, performing type conversions as
required to conform to the type of the destination property.
If the property is read only then the method returns
without throwing an exception.
If null is passed into a property expecting a primitive value,
then this will be converted as if it were a null string.
WARNING - The logic of this method is customized
to meet the needs of populate(), and is probably not what
you want for general property copying with type conversion. For that
purpose, check out the copyProperty() method instead.
WARNING - PLEASE do not modify the behavior of this
method without consulting with the Struts developer community. There
are some subtleties to its functionality that are not documented in the
Javadoc description above, yet are vital to the way that Struts utilizes
this method.
|