diff options
Diffstat (limited to 'src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java')
-rw-r--r-- | src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java | 837 |
1 files changed, 837 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java b/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java new file mode 100644 index 000000000..a2566512a --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java @@ -0,0 +1,837 @@ +/* + * 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.lang3.reflect; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.JavaVersion; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.commons.lang3.Validate; + +/** + * Utilities for working with {@link Field}s 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. + * </p> + * @since 2.5 + */ +public class FieldUtils { + + /** + * {@link FieldUtils} instances should NOT be constructed in standard programming. + * <p> + * This constructor is {@code public} to permit tools that require a JavaBean instance to operate. + * </p> + */ + public FieldUtils() { + } + + /** + * Gets an accessible {@link Field} by name respecting scope. Superclasses/interfaces will be considered. + * + * @param cls + * the {@link Class} to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @return the Field object + * @throws IllegalArgumentException + * if the class is {@code null}, or the field name is blank or empty + */ + public static Field getField(final Class<?> cls, final String fieldName) { + return MemberUtils.setAccessibleWorkaround(getField(cls, fieldName, false)); + } + + /** + * Gets an accessible {@link Field} by name, breaking scope if requested. Superclasses/interfaces will be + * considered. + * + * @param cls + * the {@link Class} to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @return the Field object + * @throws NullPointerException if the class is {@code null} + * @throws IllegalArgumentException if the field name is blank or empty or is matched at multiple places + * in the inheritance hierarchy + */ + public static Field getField(final Class<?> cls, final String fieldName, final boolean forceAccess) { + Objects.requireNonNull(cls, "cls"); + Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty"); + // FIXME is this workaround still needed? lang requires Java 6 + // 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 { + final Field field = acls.getDeclaredField(fieldName); + // getDeclaredField checks for non-public scopes as well + // and it returns accurate results + if (!MemberUtils.isPublic(field)) { + if (!forceAccess) { + continue; + } + field.setAccessible(true); + } + return field; + } catch (final NoSuchFieldException ignored) { + // 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 (final Class<?> class1 : ClassUtils.getAllInterfaces(cls)) { + try { + final Field test = class1.getField(fieldName); + Validate.isTrue(match == null, "Reference to field %s is ambiguous relative to %s" + + "; a matching field exists on two or more implemented interfaces.", fieldName, cls); + match = test; + } catch (final NoSuchFieldException ignored) { + // ignore + } + } + return match; + } + + /** + * Gets an accessible {@link Field} by name respecting scope. Only the specified class will be considered. + * + * @param cls + * the {@link Class} to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @return the Field object + * @throws IllegalArgumentException + * if the class is {@code null}, or the field name is blank or empty + */ + public static Field getDeclaredField(final Class<?> cls, final String fieldName) { + return getDeclaredField(cls, fieldName, false); + } + + /** + * Gets an accessible {@link Field} by name, breaking scope if requested. Only the specified class will be + * considered. + * + * @param cls + * the {@link Class} to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @return the Field object + * @throws IllegalArgumentException + * if the class is {@code null}, or the field name is blank or empty + */ + public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) { + Objects.requireNonNull(cls, "cls"); + Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty"); + try { + // only consider the specified class by using getDeclaredField() + final Field field = cls.getDeclaredField(fieldName); + if (!MemberUtils.isAccessible(field)) { + if (!forceAccess) { + return null; + } + field.setAccessible(true); + } + return field; + } catch (final NoSuchFieldException ignored) { + // ignore + } + return null; + } + + /** + * Gets all fields of the given class and its parents (if any). + * + * @param cls + * the {@link Class} to query + * @return an array of Fields (possibly empty). + * @throws IllegalArgumentException + * if the class is {@code null} + * @since 3.2 + */ + public static Field[] getAllFields(final Class<?> cls) { + return getAllFieldsList(cls).toArray(ArrayUtils.EMPTY_FIELD_ARRAY); + } + + /** + * Gets all fields of the given class and its parents (if any). + * + * @param cls + * the {@link Class} to query + * @return a list of Fields (possibly empty). + * @throws IllegalArgumentException + * if the class is {@code null} + * @since 3.2 + */ + public static List<Field> getAllFieldsList(final Class<?> cls) { + Objects.requireNonNull(cls, "cls"); + final List<Field> allFields = new ArrayList<>(); + Class<?> currentClass = cls; + while (currentClass != null) { + final Field[] declaredFields = currentClass.getDeclaredFields(); + Collections.addAll(allFields, declaredFields); + currentClass = currentClass.getSuperclass(); + } + return allFields; + } + + /** + * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation. + * @param cls + * the {@link Class} to query + * @param annotationCls + * the {@link Annotation} that must be present on a field to be matched + * @return an array of Fields (possibly empty). + * @throws IllegalArgumentException + * if the class or annotation are {@code null} + * @since 3.4 + */ + public static Field[] getFieldsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) { + return getFieldsListWithAnnotation(cls, annotationCls).toArray(ArrayUtils.EMPTY_FIELD_ARRAY); + } + + /** + * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation. + * @param cls + * the {@link Class} to query + * @param annotationCls + * the {@link Annotation} that must be present on a field to be matched + * @return a list of Fields (possibly empty). + * @throws IllegalArgumentException + * if the class or annotation are {@code null} + * @since 3.4 + */ + public static List<Field> getFieldsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) { + Objects.requireNonNull(annotationCls, "annotationCls"); + return getAllFieldsList(cls).stream().filter(field -> field.getAnnotation(annotationCls) != null).collect(Collectors.toList()); + } + + /** + * Reads an accessible {@code static} {@link Field}. + * + * @param field + * to read + * @return the field value + * @throws IllegalArgumentException + * if the field is {@code null}, or not {@code static} + * @throws IllegalAccessException + * if the field is not accessible + */ + public static Object readStaticField(final Field field) throws IllegalAccessException { + return readStaticField(field, false); + } + + /** + * Reads a static {@link Field}. + * + * @param field + * to read + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. + * @return the field value + * @throws IllegalArgumentException + * if the field is {@code null} or not {@code static} + * @throws IllegalAccessException + * if the field is not made accessible + */ + public static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException { + Objects.requireNonNull(field, "field"); + Validate.isTrue(MemberUtils.isStatic(field), "The field '%s' is not static", field.getName()); + return readField(field, (Object) null, forceAccess); + } + + /** + * Reads the named {@code public static} {@link Field}. Superclasses will be considered. + * + * @param cls + * the {@link Class} to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException + * if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could + * not be found + * @throws IllegalAccessException + * if the field is not accessible + */ + public static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException { + return readStaticField(cls, fieldName, false); + } + + /** + * Reads the named {@code static} {@link Field}. Superclasses will be considered. + * + * @param cls + * the {@link Class} to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @return the Field object + * @throws IllegalArgumentException + * if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could + * not be found + * @throws IllegalAccessException + * if the field is not made accessible + */ + public static Object readStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException { + final Field field = getField(cls, fieldName, forceAccess); + Validate.notNull(field, "Cannot locate field '%s' on %s", fieldName, cls); + // already forced access above, don't repeat it here: + return readStaticField(field, false); + } + + /** + * Gets the value of a {@code static} {@link Field} by name. The field must be {@code public}. Only the specified + * class will be considered. + * + * @param cls + * the {@link Class} to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException + * if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could + * not be found + * @throws IllegalAccessException + * if the field is not accessible + */ + public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException { + return readDeclaredStaticField(cls, fieldName, false); + } + + /** + * Gets the value of a {@code static} {@link Field} by name. Only the specified class will be considered. + * + * @param cls + * the {@link Class} to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @return the Field object + * @throws IllegalArgumentException + * if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could + * not be found + * @throws IllegalAccessException + * if the field is not made accessible + */ + public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException { + final Field field = getDeclaredField(cls, fieldName, forceAccess); + Validate.notNull(field, "Cannot locate declared field %s.%s", cls.getName(), fieldName); + // already forced access above, don't repeat it here: + return readStaticField(field, false); + } + + /** + * Reads an accessible {@link Field}. + * + * @param field + * the field to use + * @param target + * the object to call on, may be {@code null} for {@code static} fields + * @return the field value + * @throws IllegalArgumentException + * if the field is {@code null} + * @throws IllegalAccessException + * if the field is not accessible + */ + public static Object readField(final Field field, final Object target) throws IllegalAccessException { + return readField(field, target, false); + } + + /** + * Reads a {@link Field}. + * + * @param field + * the field to use + * @param target + * the object to call on, may be {@code null} for {@code static} fields + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. + * @return the field value + * @throws IllegalArgumentException + * if the field is {@code null} + * @throws IllegalAccessException + * if the field is not made accessible + */ + public static Object readField(final Field field, final Object target, final boolean forceAccess) throws IllegalAccessException { + Objects.requireNonNull(field, "field"); + if (forceAccess && !field.isAccessible()) { + field.setAccessible(true); + } else { + MemberUtils.setAccessibleWorkaround(field); + } + return field.get(target); + } + + /** + * Reads the named {@code public} {@link Field}. Superclasses will be considered. + * + * @param target + * the object to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException + * if the class is {@code null}, or the field name is blank or empty or could not be found + * @throws IllegalAccessException + * if the named field is not {@code public} + */ + public static Object readField(final Object target, final String fieldName) throws IllegalAccessException { + return readField(target, fieldName, false); + } + + /** + * Reads the named {@link Field}. Superclasses will be considered. + * + * @param target + * the object to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @return the field value + * @throws IllegalArgumentException + * if {@code target} is {@code null}, or the field name is blank or empty or could not be found + * @throws IllegalAccessException + * if the named field is not made accessible + */ + public static Object readField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException { + Objects.requireNonNull(target, "target"); + final Class<?> cls = target.getClass(); + final Field field = getField(cls, fieldName, forceAccess); + Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls); + // already forced access above, don't repeat it here: + return readField(field, target, false); + } + + /** + * Reads the named {@code public} {@link Field}. Only the class of the specified object will be considered. + * + * @param target + * the object to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException + * if {@code target} is {@code null}, or the field name is blank or empty or could not be found + * @throws IllegalAccessException + * if the named field is not {@code public} + */ + public static Object readDeclaredField(final Object target, final String fieldName) throws IllegalAccessException { + return readDeclaredField(target, fieldName, false); + } + + /** + * Gets a {@link Field} value by name. Only the class of the specified object will be considered. + * + * @param target + * the object to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match public fields. + * @return the Field object + * @throws IllegalArgumentException + * if {@code target} is {@code null}, or the field name is blank or empty or could not be found + * @throws IllegalAccessException + * if the field is not made accessible + */ + public static Object readDeclaredField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException { + Objects.requireNonNull(target, "target"); + final Class<?> cls = target.getClass(); + final Field field = getDeclaredField(cls, fieldName, forceAccess); + Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls, fieldName); + // already forced access above, don't repeat it here: + return readField(field, target, false); + } + + /** + * Writes a {@code public static} {@link Field}. + * + * @param field + * to write + * @param value + * to set + * @throws IllegalArgumentException + * if the field is {@code null} or not {@code static}, or {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not {@code public} or is {@code final} + */ + public static void writeStaticField(final Field field, final Object value) throws IllegalAccessException { + writeStaticField(field, value, false); + } + + /** + * Writes a static {@link Field}. + * + * @param field + * to write + * @param value + * to set + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @throws IllegalArgumentException + * if the field is {@code null} or not {@code static}, or {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not made accessible or is {@code final} + */ + public static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException { + Objects.requireNonNull(field, "field"); + Validate.isTrue(MemberUtils.isStatic(field), "The field %s.%s is not static", field.getDeclaringClass().getName(), + field.getName()); + writeField(field, (Object) null, value, forceAccess); + } + + /** + * Writes a named {@code public static} {@link Field}. Superclasses will be considered. + * + * @param cls + * {@link Class} on which the field is to be found + * @param fieldName + * to write + * @param value + * to set + * @throws IllegalArgumentException + * if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is + * not {@code static}, or {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not {@code public} or is {@code final} + */ + public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException { + writeStaticField(cls, fieldName, value, false); + } + + /** + * Writes a named {@code static} {@link Field}. Superclasses will be considered. + * + * @param cls + * {@link 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 + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @throws IllegalArgumentException + * if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is + * not {@code static}, or {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not made accessible or is {@code final} + */ + public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess) + throws IllegalAccessException { + final Field field = getField(cls, fieldName, forceAccess); + Validate.notNull(field, "Cannot locate field %s on %s", fieldName, cls); + // already forced access above, don't repeat it here: + writeStaticField(field, value, false); + } + + /** + * Writes a named {@code public static} {@link Field}. Only the specified class will be considered. + * + * @param cls + * {@link Class} on which the field is to be found + * @param fieldName + * to write + * @param value + * to set + * @throws IllegalArgumentException + * if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is + * not {@code static}, or {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not {@code public} or is {@code final} + */ + public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException { + writeDeclaredStaticField(cls, fieldName, value, false); + } + + /** + * Writes a named {@code static} {@link Field}. Only the specified class will be considered. + * + * @param cls + * {@link 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 AccessibleObject#setAccessible(boolean)} method. + * {@code false} will only match {@code public} fields. + * @throws IllegalArgumentException + * if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is + * not {@code static}, or {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not made accessible or is {@code final} + */ + public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess) + throws IllegalAccessException { + final Field field = getDeclaredField(cls, fieldName, forceAccess); + Validate.notNull(field, "Cannot locate declared field %s.%s", cls.getName(), fieldName); + // already forced access above, don't repeat it here: + writeField(field, (Object) null, value, false); + } + + /** + * Writes an accessible {@link Field}. + * + * @param field + * to write + * @param target + * the object to call on, may be {@code null} for {@code static} fields + * @param value + * to set + * @throws IllegalAccessException + * if the field or target is {@code null}, the field is not accessible or is {@code final}, or + * {@code value} is not assignable + */ + public static void writeField(final Field field, final Object target, final Object value) throws IllegalAccessException { + writeField(field, target, value, false); + } + + /** + * Writes a {@link Field}. + * + * @param field + * to write + * @param target + * the object to call on, may be {@code null} for {@code static} fields + * @param value + * to set + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @throws IllegalArgumentException + * if the field is {@code null} or {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not made accessible or is {@code final} + */ + public static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess) + throws IllegalAccessException { + Objects.requireNonNull(field, "field"); + if (forceAccess && !field.isAccessible()) { + field.setAccessible(true); + } else { + MemberUtils.setAccessibleWorkaround(field); + } + field.set(target, value); + } + + /** + * Removes the final modifier from a {@link Field}. + * + * @param field + * to remove the final modifier + * @throws IllegalArgumentException + * if the field is {@code null} + * @since 3.2 + */ + public static void removeFinalModifier(final Field field) { + removeFinalModifier(field, true); + } + + /** + * Removes the final modifier from a {@link Field}. + * + * @param field + * to remove the final modifier + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @throws IllegalArgumentException + * if the field is {@code null} + * @deprecated As of Java 12, we can no longer drop the {@code final} modifier, thus + * rendering this method obsolete. The JDK discussion about this change can be found + * here: https://mail.openjdk.java.net/pipermail/core-libs-dev/2018-November/056486.html + * @since 3.3 + */ + @Deprecated + public static void removeFinalModifier(final Field field, final boolean forceAccess) { + Objects.requireNonNull(field, "field"); + + try { + if (Modifier.isFinal(field.getModifiers())) { + // Do all JREs implement Field with a private ivar called "modifiers"? + final Field modifiersField = Field.class.getDeclaredField("modifiers"); + final boolean doForceAccess = forceAccess && !modifiersField.isAccessible(); + if (doForceAccess) { + modifiersField.setAccessible(true); + } + try { + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + } finally { + if (doForceAccess) { + modifiersField.setAccessible(false); + } + } + } + } catch (final NoSuchFieldException | IllegalAccessException e) { + if (SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_12)) { + throw new UnsupportedOperationException( + "In java 12+ final cannot be removed.", + e + ); + } + // else no exception is thrown because we can modify final. + } + } + + /** + * Writes a {@code public} {@link Field}. Superclasses will be considered. + * + * @param target + * the object to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @param value + * to set + * @throws IllegalArgumentException + * if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or + * {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not accessible + */ + public static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException { + writeField(target, fieldName, value, false); + } + + /** + * Writes a {@link Field}. Superclasses will be considered. + * + * @param target + * the object to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @param value + * to set + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @throws IllegalArgumentException + * if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or + * {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not made accessible + */ + public static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess) + throws IllegalAccessException { + Objects.requireNonNull(target, "target"); + final Class<?> cls = target.getClass(); + final Field field = getField(cls, fieldName, forceAccess); + Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName); + // already forced access above, don't repeat it here: + writeField(field, target, value, false); + } + + /** + * Writes a {@code public} {@link Field}. Only the specified class will be considered. + * + * @param target + * the object to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @param value + * to set + * @throws IllegalArgumentException + * if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or + * {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not made accessible + */ + public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException { + writeDeclaredField(target, fieldName, value, false); + } + + /** + * Writes a {@code public} {@link Field}. Only the specified class will be considered. + * + * @param target + * the object to reflect, must not be {@code null} + * @param fieldName + * the field name to obtain + * @param value + * to set + * @param forceAccess + * whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only + * match {@code public} fields. + * @throws IllegalArgumentException + * if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or + * {@code value} is not assignable + * @throws IllegalAccessException + * if the field is not made accessible + */ + public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess) + throws IllegalAccessException { + Objects.requireNonNull(target, "target"); + final Class<?> cls = target.getClass(); + final Field field = getDeclaredField(cls, fieldName, forceAccess); + Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName); + // already forced access above, don't repeat it here: + writeField(field, target, value, false); + } +} |