📄 propertyutilsbean.java
字号:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.beanutils;
import java.beans.BeanInfo;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.expression.DefaultResolver;
import org.apache.commons.beanutils.expression.Resolver;
import org.apache.commons.collections.FastHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Utility methods for using Java Reflection APIs to facilitate generic
* property getter and setter operations on Java objects. Much of this
* code was originally included in <code>BeanUtils</code>, but has been
* separated because of the volume of code involved.
* <p>
* In general, the objects that are examined and modified using these
* methods are expected to conform to the property getter and setter method
* naming conventions described in the JavaBeans Specification (Version 1.0.1).
* No data type conversions are performed, and there are no usage of any
* <code>PropertyEditor</code> classes that have been registered, although
* a convenient way to access the registered classes themselves is included.
* <p>
* For the purposes of this class, five formats for referencing a particular
* property value of a bean are defined, with the <i>default</i> layout of an
* identifying String in parentheses. However the notation for these formats
* and how they are resolved is now (since BeanUtils 1.8.0) controlled by
* the configured {@link Resolver} implementation:
* <ul>
* <li><strong>Simple (<code>name</code>)</strong> - The specified
* <code>name</code> identifies an individual property of a particular
* JavaBean. The name of the actual getter or setter method to be used
* is determined using standard JavaBeans instrospection, so that (unless
* overridden by a <code>BeanInfo</code> class, a property named "xyz"
* will have a getter method named <code>getXyz()</code> or (for boolean
* properties only) <code>isXyz()</code>, and a setter method named
* <code>setXyz()</code>.</li>
* <li><strong>Nested (<code>name1.name2.name3</code>)</strong> The first
* name element is used to select a property getter, as for simple
* references above. The object returned for this property is then
* consulted, using the same approach, for a property getter for a
* property named <code>name2</code>, and so on. The property value that
* is ultimately retrieved or modified is the one identified by the
* last name element.</li>
* <li><strong>Indexed (<code>name[index]</code>)</strong> - The underlying
* property value is assumed to be an array, or this JavaBean is assumed
* to have indexed property getter and setter methods. The appropriate
* (zero-relative) entry in the array is selected. <code>List</code>
* objects are now also supported for read/write. You simply need to define
* a getter that returns the <code>List</code></li>
* <li><strong>Mapped (<code>name(key)</code>)</strong> - The JavaBean
* is assumed to have an property getter and setter methods with an
* additional attribute of type <code>java.lang.String</code>.</li>
* <li><strong>Combined (<code>name1.name2[index].name3(key)</code>)</strong> -
* Combining mapped, nested, and indexed references is also
* supported.</li>
* </ul>
*
* @author Craig R. McClanahan
* @author Ralph Schaer
* @author Chris Audley
* @author Rey Francois
* @author Gregor Rayman
* @author Jan Sorensen
* @author Scott Sanders
* @author Erik Meade
* @version $Revision: 687190 $ $Date: 2008-08-19 23:51:19 +0100 (Tue, 19 Aug 2008) $
* @see Resolver
* @see PropertyUtils
* @since 1.7
*/
public class PropertyUtilsBean {
private Resolver resolver = new DefaultResolver();
// --------------------------------------------------------- Class Methods
/**
* Return the PropertyUtils bean instance.
* @return The PropertyUtils bean instance
*/
protected static PropertyUtilsBean getInstance() {
return BeanUtilsBean.getInstance().getPropertyUtils();
}
// --------------------------------------------------------- Variables
/**
* The cache of PropertyDescriptor arrays for beans we have already
* introspected, keyed by the java.lang.Class of this object.
*/
private WeakFastHashMap descriptorsCache = null;
private WeakFastHashMap mappedDescriptorsCache = null;
private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
private static final Class[] LIST_CLASS_PARAMETER = new Class[] {java.util.List.class};
/** An empty object array */
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
/** Log instance */
private Log log = LogFactory.getLog(PropertyUtils.class);
// ---------------------------------------------------------- Constructors
/** Base constructor */
public PropertyUtilsBean() {
descriptorsCache = new WeakFastHashMap();
descriptorsCache.setFast(true);
mappedDescriptorsCache = new WeakFastHashMap();
mappedDescriptorsCache.setFast(true);
}
// --------------------------------------------------------- Public Methods
/**
* Return the configured {@link Resolver} implementation used by BeanUtils.
* <p>
* The {@link Resolver} handles the <i>property name</i>
* expressions and the implementation in use effectively
* controls the dialect of the <i>expression language</i>
* that BeanUtils recongnises.
* <p>
* {@link DefaultResolver} is the default implementation used.
*
* @return resolver The property expression resolver.
*/
public Resolver getResolver() {
return resolver;
}
/**
* Configure the {@link Resolver} implementation used by BeanUtils.
* <p>
* The {@link Resolver} handles the <i>property name</i>
* expressions and the implementation in use effectively
* controls the dialect of the <i>expression language</i>
* that BeanUtils recongnises.
* <p>
* {@link DefaultResolver} is the default implementation used.
*
* @param resolver The property expression resolver.
*/
public void setResolver(Resolver resolver) {
if (resolver == null) {
this.resolver = new DefaultResolver();
} else {
this.resolver = resolver;
}
}
/**
* Clear any cached property descriptors information for all classes
* loaded by any class loaders. This is useful in cases where class
* loaders are thrown away to implement class reloading.
*/
public void clearDescriptors() {
descriptorsCache.clear();
mappedDescriptorsCache.clear();
Introspector.flushCaches();
}
/**
* <p>Copy property values from the "origin" bean to the "destination" bean
* for all cases where the property names are the same (even though the
* actual getter and setter methods might have been customized via
* <code>BeanInfo</code> classes). No conversions are performed on the
* actual property values -- it is assumed that the values retrieved from
* the origin bean are assignment-compatible with the types expected by
* the destination bean.</p>
*
* <p>If the origin "bean" is actually a <code>Map</code>, it is assumed
* to contain String-valued <strong>simple</strong> property names as the keys, pointing
* at the corresponding property values that will be set in the destination
* bean.<strong>Note</strong> 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.</p>
*
* <p>Note, that this method will not copy a List to a List, or an Object[]
* to an Object[]. It's specifically for copying JavaBean properties. </p>
*
* @param dest Destination bean whose properties are modified
* @param orig Origin bean whose properties are retrieved
*
* @exception IllegalAccessException if the caller does not have
* access to the property accessor method
* @exception IllegalArgumentException if the <code>dest</code> or
* <code>orig</code> argument is null
* @exception InvocationTargetException if the property accessor method
* throws an exception
* @exception NoSuchMethodException if an accessor method for this
* propety cannot be found
*/
public void copyProperties(Object dest, Object orig)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
if (dest == null) {
throw new IllegalArgumentException
("No destination bean specified");
}
if (orig == null) {
throw new IllegalArgumentException("No origin bean specified");
}
if (orig instanceof DynaBean) {
DynaProperty[] origDescriptors =
((DynaBean) orig).getDynaClass().getDynaProperties();
for (int i = 0; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();
if (isReadable(orig, name) && isWriteable(dest, name)) {
try {
Object value = ((DynaBean) orig).get(name);
if (dest instanceof DynaBean) {
((DynaBean) dest).set(name, value);
} else {
setSimpleProperty(dest, name, value);
}
} catch (NoSuchMethodException e) {
if (log.isDebugEnabled()) {
log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
}
}
}
}
} 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 (isWriteable(dest, name)) {
try {
if (dest instanceof DynaBean) {
((DynaBean) dest).set(name, entry.getValue());
} else {
setSimpleProperty(dest, name, entry.getValue());
}
} catch (NoSuchMethodException e) {
if (log.isDebugEnabled()) {
log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
}
}
}
}
} else /* if (orig is a standard JavaBean) */ {
PropertyDescriptor[] origDescriptors =
getPropertyDescriptors(orig);
for (int i = 0; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();
if (isReadable(orig, name) && isWriteable(dest, name)) {
try {
Object value = getSimpleProperty(orig, name);
if (dest instanceof DynaBean) {
((DynaBean) dest).set(name, value);
} else {
setSimpleProperty(dest, name, value);
}
} catch (NoSuchMethodException e) {
if (log.isDebugEnabled()) {
log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
}
}
}
}
}
}
/**
* <p>Return the entire set of properties for which the specified bean
* provides a read method. This map contains the unconverted property
* values for all properties for which a read method is provided
* (i.e. where the <code>getReadMethod()</code> returns non-null).</p>
*
* <p><strong>FIXME</strong> - Does not account for mapped properties.</p>
*
* @param bean Bean whose properties are to be extracted
* @return The set of properties for the bean
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -