blob: f170816a8b7de03469139915852d3eecc5c35707 [file] [log] [blame]
/*
* 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.lang.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import org.apache.commons.lang.ClassUtils;
/**
* Utilities for working with fields by reflection. Adapted and refactored
* from the dormant [reflect] Commons sandbox component.
* <p>
* The ability is provided to break the scoping restrictions coded by the
* programmer. This can allow fields to be changed that shouldn't be. This
* facility should be used with care.
*
* @author Apache Software Foundation
* @author Matt Benson
* @since 2.5
* @version $Id$
*/
public class FieldUtils {
/**
* FieldUtils instances should NOT be constructed in standard programming.
* <p>
* This constructor is public to permit tools that require a JavaBean instance
* to operate.
*/
public FieldUtils() {
super();
}
/**
* Gets an accessible <code>Field</code> by name respecting scope.
* Superclasses/interfaces will be considered.
*
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @return the Field object
* @throws IllegalArgumentException if the class or field name is null
*/
public static Field getField(Class cls, String fieldName) {
Field field = getField(cls, fieldName, false);
MemberUtils.setAccessibleWorkaround(field);
return field;
}
/**
* Gets an accessible <code>Field</code> by name breaking scope
* if requested. Superclasses/interfaces will be considered.
*
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @return the Field object
* @throws IllegalArgumentException if the class or field name is null
*/
public static Field getField(final Class cls, String fieldName, boolean forceAccess) {
if (cls == null) {
throw new IllegalArgumentException("The class must not be null");
}
if (fieldName == null) {
throw new IllegalArgumentException("The field name must not be null");
}
// Sun Java 1.3 has a bugged implementation of getField hence we write the
// code ourselves
// getField() will return the Field object with the declaring class
// set correctly to the class that declares the field. Thus requesting the
// field on a subclass will return the field from the superclass.
//
// priority order for lookup:
// searchclass private/protected/package/public
// superclass protected/package/public
// private/different package blocks access to further superclasses
// implementedinterface public
// check up the superclass hierarchy
for (Class acls = cls; acls != null; acls = acls.getSuperclass()) {
try {
Field field = acls.getDeclaredField(fieldName);
// getDeclaredField checks for non-public scopes as well
// and it returns accurate results
if (!Modifier.isPublic(field.getModifiers())) {
if (forceAccess) {
field.setAccessible(true);
} else {
continue;
}
}
return field;
} catch (NoSuchFieldException ex) {
// ignore
}
}
// check the public interface case. This must be manually searched for
// incase there is a public supersuperclass field hidden by a private/package
// superclass field.
Field match = null;
for (Iterator intf = ClassUtils.getAllInterfaces(cls).iterator(); intf
.hasNext();) {
try {
Field test = ((Class) intf.next()).getField(fieldName);
if (match != null) {
throw new IllegalArgumentException(
"Reference to field "
+ fieldName
+ " is ambiguous relative to "
+ cls
+ "; a matching field exists on two or more implemented interfaces.");
}
match = test;
} catch (NoSuchFieldException ex) {
// ignore
}
}
return match;
}
/**
* Gets an accessible <code>Field</code> by name respecting scope.
* Only the specified class will be considered.
*
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @return the Field object
* @throws IllegalArgumentException if the class or field name is null
*/
public static Field getDeclaredField(Class cls, String fieldName) {
return getDeclaredField(cls, fieldName, false);
}
/**
* Gets an accessible <code>Field</code> by name breaking scope
* if requested. Only the specified class will be considered.
*
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. False will only match public fields.
* @return the Field object
* @throws IllegalArgumentException if the class or field name is null
*/
public static Field getDeclaredField(Class cls, String fieldName, boolean forceAccess) {
if (cls == null) {
throw new IllegalArgumentException("The class must not be null");
}
if (fieldName == null) {
throw new IllegalArgumentException("The field name must not be null");
}
try {
// only consider the specified class by using getDeclaredField()
Field field = cls.getDeclaredField(fieldName);
if (!MemberUtils.isAccessible(field)) {
if (forceAccess) {
field.setAccessible(true);
} else {
return null;
}
}
return field;
} catch (NoSuchFieldException e) {
}
return null;
}
/**
* Read an accessible static Field.
* @param field to read
* @return the field value
* @throws IllegalArgumentException if the field is null or not static
* @throws IllegalAccessException if the field is not accessible
*/
public static Object readStaticField(Field field) throws IllegalAccessException {
return readStaticField(field, false);
}
/**
* Read a static Field.
* @param field to read
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method.
* @return the field value
* @throws IllegalArgumentException if the field is null or not static
* @throws IllegalAccessException if the field is not made accessible
*/
public static Object readStaticField(Field field, boolean forceAccess) throws IllegalAccessException {
if (field == null) {
throw new IllegalArgumentException("The field must not be null");
}
if (!Modifier.isStatic(field.getModifiers())) {
throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
}
return readField(field, (Object) null, forceAccess);
}
/**
* Read the named public static field. Superclasses will be considered.
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @return the value of the field
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the field is not accessible
*/
public static Object readStaticField(Class cls, String fieldName) throws IllegalAccessException {
return readStaticField(cls, fieldName, false);
}
/**
* Read the named static field. Superclasses will be considered.
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @return the Field object
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the field is not made accessible
*/
public static Object readStaticField(Class cls, String fieldName, boolean forceAccess) throws IllegalAccessException {
Field field = getField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
}
//already forced access above, don't repeat it here:
return readStaticField(field, false);
}
/**
* Gets a static Field value by name. The field must be public.
* Only the specified class will be considered.
*
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @return the value of the field
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the field is not accessible
*/
public static Object readDeclaredStaticField(Class cls, String fieldName) throws IllegalAccessException {
return readDeclaredStaticField(cls, fieldName, false);
}
/**
* Gets a static Field value by name. Only the specified class will
* be considered.
*
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @return the Field object
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the field is not made accessible
*/
public static Object readDeclaredStaticField(Class cls, String fieldName, boolean forceAccess)
throws IllegalAccessException {
Field field = getDeclaredField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
}
//already forced access above, don't repeat it here:
return readStaticField(field, false);
}
/**
* Read an accessible Field.
* @param field the field to use
* @param target the object to call on, may be null for static fields
* @return the field value
* @throws IllegalArgumentException if the field is null
* @throws IllegalAccessException if the field is not accessible
*/
public static Object readField(Field field, Object target) throws IllegalAccessException {
return readField(field, target, false);
}
/**
* Read a Field.
* @param field the field to use
* @param target the object to call on, may be null for static fields
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method.
* @return the field value
* @throws IllegalArgumentException if the field is null
* @throws IllegalAccessException if the field is not made accessible
*/
public static Object readField(Field field, Object target, boolean forceAccess) throws IllegalAccessException {
if (field == null) {
throw new IllegalArgumentException("The field must not be null");
}
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
MemberUtils.setAccessibleWorkaround(field);
}
return field.get(target);
}
/**
* Read the named public field. Superclasses will be considered.
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @return the value of the field
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the named field is not public
*/
public static Object readField(Object target, String fieldName) throws IllegalAccessException {
return readField(target, fieldName, false);
}
/**
* Read the named field. Superclasses will be considered.
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @return the field value
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the named field is not made accessible
*/
public static Object readField(Object target, String fieldName, boolean forceAccess) throws IllegalAccessException {
if (target == null) {
throw new IllegalArgumentException("target object must not be null");
}
Class cls = target.getClass();
Field field = getField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
}
//already forced access above, don't repeat it here:
return readField(field, target);
}
/**
* Read the named public field. Only the class of the specified object will be considered.
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @return the value of the field
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the named field is not public
*/
public static Object readDeclaredField(Object target, String fieldName) throws IllegalAccessException {
return readDeclaredField(target, fieldName, false);
}
/**
* <p<>Gets a Field value by name. Only the class of the specified
* object will be considered.
*
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @return the Field object
* @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
* @throws IllegalAccessException if the field is not made accessible
*/
public static Object readDeclaredField(Object target, String fieldName, boolean forceAccess) throws IllegalAccessException {
if (target == null) {
throw new IllegalArgumentException("target object must not be null");
}
Class cls = target.getClass();
Field field = getDeclaredField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
}
//already forced access above, don't repeat it here:
return readField(field, target);
}
/**
* Write a public static Field.
* @param field to write
* @param value to set
* @throws IllegalArgumentException if the field is null or not static
* @throws IllegalAccessException if the field is not public or is final
*/
public static void writeStaticField(Field field, Object value) throws IllegalAccessException {
writeStaticField(field, value, false);
}
/**
* Write a static Field.
* @param field to write
* @param value to set
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @throws IllegalArgumentException if the field is null or not static
* @throws IllegalAccessException if the field is not made accessible or is final
*/
public static void writeStaticField(Field field, Object value, boolean forceAccess) throws IllegalAccessException {
if (field == null) {
throw new IllegalArgumentException("The field must not be null");
}
if (!Modifier.isStatic(field.getModifiers())) {
throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
}
writeField(field, (Object) null, value, forceAccess);
}
/**
* Write a named public static Field. Superclasses will be considered.
* @param cls Class on which the Field is to be found
* @param fieldName to write
* @param value to set
* @throws IllegalArgumentException if the field cannot be located or is not static
* @throws IllegalAccessException if the field is not public or is final
*/
public static void writeStaticField(Class cls, String fieldName, Object value) throws IllegalAccessException {
writeStaticField(cls, fieldName, value, false);
}
/**
* Write a named static Field. Superclasses will be considered.
* @param cls Class on which the Field is to be found
* @param fieldName to write
* @param value to set
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @throws IllegalArgumentException if the field cannot be located or is not static
* @throws IllegalAccessException if the field is not made accessible or is final
*/
public static void writeStaticField(Class cls, String fieldName, Object value, boolean forceAccess)
throws IllegalAccessException {
Field field = getField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
}
//already forced access above, don't repeat it here:
writeStaticField(field, value);
}
/**
* Write a named public static Field. Only the specified class will be considered.
* @param cls Class on which the Field is to be found
* @param fieldName to write
* @param value to set
* @throws IllegalArgumentException if the field cannot be located or is not static
* @throws IllegalAccessException if the field is not public or is final
*/
public static void writeDeclaredStaticField(Class cls, String fieldName, Object value)
throws IllegalAccessException {
writeDeclaredStaticField(cls, fieldName, value, false);
}
/**
* Write a named static Field. Only the specified class will be considered.
* @param cls Class on which the Field is to be found
* @param fieldName to write
* @param value to set
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @throws IllegalArgumentException if the field cannot be located or is not static
* @throws IllegalAccessException if the field is not made accessible or is final
*/
public static void writeDeclaredStaticField(Class cls, String fieldName, Object value, boolean forceAccess)
throws IllegalAccessException {
Field field = getDeclaredField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
}
//already forced access above, don't repeat it here:
writeField(field, (Object) null, value);
}
/**
* Write an accessible field.
* @param field to write
* @param target the object to call on, may be null for static fields
* @param value to set
* @throws IllegalArgumentException if the field is null
* @throws IllegalAccessException if the field is not accessible or is final
*/
public static void writeField(Field field, Object target, Object value) throws IllegalAccessException {
writeField(field, target, value, false);
}
/**
* Write a field.
* @param field to write
* @param target the object to call on, may be null for static fields
* @param value to set
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @throws IllegalArgumentException if the field is null
* @throws IllegalAccessException if the field is not made accessible or is final
*/
public static void writeField(Field field, Object target, Object value, boolean forceAccess) throws IllegalAccessException {
if (field == null) {
throw new IllegalArgumentException("The field must not be null");
}
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
MemberUtils.setAccessibleWorkaround(field);
}
field.set(target, value);
}
/**
* Write a public field. Superclasses will be considered.
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @param value to set
* @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
* @throws IllegalAccessException if the field is not accessible
*/
public static void writeField(Object target, String fieldName, Object value) throws IllegalAccessException {
writeField(target, fieldName, value, false);
}
/**
* Write a field. Superclasses will be considered.
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @param value to set
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
* @throws IllegalAccessException if the field is not made accessible
*/
public static void writeField(Object target, String fieldName, Object value, boolean forceAccess)
throws IllegalAccessException {
if (target == null) {
throw new IllegalArgumentException("target object must not be null");
}
Class cls = target.getClass();
Field field = getField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
}
//already forced access above, don't repeat it here:
writeField(field, target, value);
}
/**
* Write a public field. Only the specified class will be considered.
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @param value to set
* @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
* @throws IllegalAccessException if the field is not made accessible
*/
public static void writeDeclaredField(Object target, String fieldName, Object value) throws IllegalAccessException {
writeDeclaredField(target, fieldName, value, false);
}
/**
* Write a public field. Only the specified class will be considered.
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @param value to set
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will only
* match public fields.
* @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
* @throws IllegalAccessException if the field is not made accessible
*/
public static void writeDeclaredField(Object target, String fieldName, Object value, boolean forceAccess)
throws IllegalAccessException {
if (target == null) {
throw new IllegalArgumentException("target object must not be null");
}
Class cls = target.getClass();
Field field = getDeclaredField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
}
//already forced access above, don't repeat it here:
writeField(field, target, value);
}
}