    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.lang3.reflect;
    018
    019import org.apache.commons.lang3.ClassUtils;
    020import org.apache.commons.lang3.StringUtils;
    021import org.apache.commons.lang3.Validate;
    022
    023import java.lang.annotation.Annotation;
    024import java.lang.reflect.Field;
    025import java.lang.reflect.Modifier;
    026import java.util.ArrayList;
    027import java.util.Collections;
    028import java.util.List;
    029
    030/**
    031 * Utilities for working with {@link Field}s by reflection. Adapted and refactored from the dormant [reflect] Commons
    032 * sandbox component.
    033 * <p>
    034 * The ability is provided to break the scoping restrictions coded by the programmer. This can allow fields to be
    035 * changed that shouldn't be. This facility should be used with care.
    036 *
    037 * @since 2.5
    038 */
    039public class FieldUtils {
    040
    041    /**
    042     * {@link FieldUtils} instances should NOT be constructed in standard programming.
    043     * <p>
    044     * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
    045     * </p>
    046     */
    047    public FieldUtils() {
    048        super();
    049    }
    050
    051    /**
    052     * Gets an accessible {@link Field} by name respecting scope. Superclasses/interfaces will be considered.
    053     *
    054     * @param cls
    055     *            the {@link Class} to reflect, must not be {@code null}
    056     * @param fieldName
    057     *            the field name to obtain
    058     * @return the Field object
    059     * @throws IllegalArgumentException
    060     *             if the class is {@code null}, or the field name is blank or empty
    061     */
    062    public static Field getField(final Class<?> cls, final String fieldName) {
    063        final Field field = getField(cls, fieldName, false);
    064        MemberUtils.setAccessibleWorkaround(field);
    065        return field;
    066    }
    067
    068    /**
    069     * Gets an accessible {@link Field} by name, breaking scope if requested. Superclasses/interfaces will be
    070     * considered.
    071     *
    072     * @param cls
    073     *            the {@link Class} to reflect, must not be {@code null}
    074     * @param fieldName
    075     *            the field name to obtain
    076     * @param forceAccess
    077     *            whether to break scope restrictions using the
    078     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    079     *            match {@code public} fields.
    080     * @return the Field object
    081     * @throws IllegalArgumentException
    082     *             if the class is {@code null}, or the field name is blank or empty or is matched at multiple places
    083     *             in the inheritance hierarchy
    084     */
    085    public static Field getField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
    086        Validate.isTrue(cls != null, "The class must not be null");
    087        Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
    088        // FIXME is this workaround still needed? lang requires Java 6
    089        // Sun Java 1.3 has a bugged implementation of getField hence we write the
    090        // code ourselves
    091
    092        // getField() will return the Field object with the declaring class
    093        // set correctly to the class that declares the field. Thus requesting the
    094        // field on a subclass will return the field from the superclass.
    095        //
    096        // priority order for lookup:
    097        // searchclass private/protected/package/public
    098        // superclass protected/package/public
    099        // private/different package blocks access to further superclasses
    100        // implementedinterface public
    101
    102        // check up the superclass hierarchy
    103        for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
    104            try {
    105                final Field field = acls.getDeclaredField(fieldName);
    106                // getDeclaredField checks for non-public scopes as well
    107                // and it returns accurate results
    108                if (!Modifier.isPublic(field.getModifiers())) {
    109                    if (forceAccess) {
    110                        field.setAccessible(true);
    111                    } else {
    112                        continue;
    113                    }
    114                }
    115                return field;
    116            } catch (final NoSuchFieldException ex) { // NOPMD
    117                // ignore
    118            }
    119        }
    120        // check the public interface case. This must be manually searched for
    121        // incase there is a public supersuperclass field hidden by a private/package
    122        // superclass field.
    123        Field match = null;
    124        for (final Class<?> class1 : ClassUtils.getAllInterfaces(cls)) {
    125            try {
    126                final Field test = class1.getField(fieldName);
    127                Validate.isTrue(match == null, "Reference to field %s is ambiguous relative to %s"
    128                        + "; a matching field exists on two or more implemented interfaces.", fieldName, cls);
    129                match = test;
    130            } catch (final NoSuchFieldException ex) { // NOPMD
    131                // ignore
    132            }
    133        }
    134        return match;
    135    }
    136
    137    /**
    138     * Gets an accessible {@link Field} by name respecting scope. Only the specified class will be considered.
    139     *
    140     * @param cls
    141     *            the {@link Class} to reflect, must not be {@code null}
    142     * @param fieldName
    143     *            the field name to obtain
    144     * @return the Field object
    145     * @throws IllegalArgumentException
    146     *             if the class is {@code null}, or the field name is blank or empty
    147     */
    148    public static Field getDeclaredField(final Class<?> cls, final String fieldName) {
    149        return getDeclaredField(cls, fieldName, false);
    150    }
    151
    152    /**
    153     * Gets an accessible {@link Field} by name, breaking scope if requested. Only the specified class will be
    154     * considered.
    155     *
    156     * @param cls
    157     *            the {@link Class} to reflect, must not be {@code null}
    158     * @param fieldName
    159     *            the field name to obtain
    160     * @param forceAccess
    161     *            whether to break scope restrictions using the
    162     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    163     *            match {@code public} fields.
    164     * @return the Field object
    165     * @throws IllegalArgumentException
    166     *             if the class is {@code null}, or the field name is blank or empty
    167     */
    168    public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
    169        Validate.isTrue(cls != null, "The class must not be null");
    170        Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
    171        try {
    172            // only consider the specified class by using getDeclaredField()
    173            final Field field = cls.getDeclaredField(fieldName);
    174            if (!MemberUtils.isAccessible(field)) {
    175                if (forceAccess) {
    176                    field.setAccessible(true);
    177                } else {
    178                    return null;
    179                }
    180            }
    181            return field;
    182        } catch (final NoSuchFieldException e) { // NOPMD
    183            // ignore
    184        }
    185        return null;
    186    }
    187
    188    /**
    189     * Gets all fields of the given class and its parents (if any).
    190     *
    191     * @param cls
    192     *            the {@link Class} to query
    193     * @return an array of Fields (possibly empty).
    194     * @throws IllegalArgumentException
    195     *             if the class is {@code null}
    196     * @since 3.2
    197     */
    198    public static Field[] getAllFields(final Class<?> cls) {
    199        final List<Field> allFieldsList = getAllFieldsList(cls);
    200        return allFieldsList.toArray(new Field[allFieldsList.size()]);
    201    }
    202
    203    /**
    204     * Gets all fields of the given class and its parents (if any).
    205     *
    206     * @param cls
    207     *            the {@link Class} to query
    208     * @return an array of Fields (possibly empty).
    209     * @throws IllegalArgumentException
    210     *             if the class is {@code null}
    211     * @since 3.2
    212     */
    213    public static List<Field> getAllFieldsList(final Class<?> cls) {
    214        Validate.isTrue(cls != null, "The class must not be null");
    215        final List<Field> allFields = new ArrayList<>();
    216        Class<?> currentClass = cls;
    217        while (currentClass != null) {
    218            final Field[] declaredFields = currentClass.getDeclaredFields();
    219            Collections.addAll(allFields, declaredFields);
    220            currentClass = currentClass.getSuperclass();
    221        }
    222        return allFields;
    223    }
    224
    225    /**
    226     * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
    227     * @param cls
    228     *            the {@link Class} to query
    229     * @param annotationCls
    230     *            the {@link Annotation} that must be present on a field to be matched
    231     * @return an array of Fields (possibly empty).
    232     * @throws IllegalArgumentException
    233     *            if the class or annotation are {@code null}
    234     * @since 3.4
    235     */
    236    public static Field[] getFieldsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
    237        final List<Field> annotatedFieldsList = getFieldsListWithAnnotation(cls, annotationCls);
    238        return annotatedFieldsList.toArray(new Field[annotatedFieldsList.size()]);
    239    }
    240
    241    /**
    242     * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
    243     * @param cls
    244     *            the {@link Class} to query
    245     * @param annotationCls
    246     *            the {@link Annotation} that must be present on a field to be matched
    247     * @return a list of Fields (possibly empty).
    248     * @throws IllegalArgumentException
    249     *            if the class or annotation are {@code null}
    250     * @since 3.4
    251     */
    252    public static List<Field> getFieldsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
    253        Validate.isTrue(annotationCls != null, "The annotation class must not be null");
    254        final List<Field> allFields = getAllFieldsList(cls);
    255        final List<Field> annotatedFields = new ArrayList<>();
    256        for (final Field field : allFields) {
    257            if (field.getAnnotation(annotationCls) != null) {
    258                annotatedFields.add(field);
    259            }
    260        }
    261        return annotatedFields;
    262    }
    263
    264    /**
    265     * Reads an accessible {@code static} {@link Field}.
    266     *
    267     * @param field
    268     *            to read
    269     * @return the field value
    270     * @throws IllegalArgumentException
    271     *             if the field is {@code null}, or not {@code static}
    272     * @throws IllegalAccessException
    273     *             if the field is not accessible
    274     */
    275    public static Object readStaticField(final Field field) throws IllegalAccessException {
    276        return readStaticField(field, false);
    277    }
    278
    279    /**
    280     * Reads a static {@link Field}.
    281     *
    282     * @param field
    283     *            to read
    284     * @param forceAccess
    285     *            whether to break scope restrictions using the
    286     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
    287     * @return the field value
    288     * @throws IllegalArgumentException
    289     *             if the field is {@code null} or not {@code static}
    290     * @throws IllegalAccessException
    291     *             if the field is not made accessible
    292     */
    293    public static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException {
    294        Validate.isTrue(field != null, "The field must not be null");
    295        Validate.isTrue(Modifier.isStatic(field.getModifiers()), "The field '%s' is not static", field.getName());
    296        return readField(field, (Object) null, forceAccess);
    297    }
    298
    299    /**
    300     * Reads the named {@code public static} {@link Field}. Superclasses will be considered.
    301     *
    302     * @param cls
    303     *            the {@link Class} to reflect, must not be {@code null}
    304     * @param fieldName
    305     *            the field name to obtain
    306     * @return the value of the field
    307     * @throws IllegalArgumentException
    308     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
    309     *             not be found
    310     * @throws IllegalAccessException
    311     *             if the field is not accessible
    312     */
    313    public static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
    314        return readStaticField(cls, fieldName, false);
    315    }
    316
    317    /**
    318     * Reads the named {@code static} {@link Field}. Superclasses will be considered.
    319     *
    320     * @param cls
    321     *            the {@link Class} to reflect, must not be {@code null}
    322     * @param fieldName
    323     *            the field name to obtain
    324     * @param forceAccess
    325     *            whether to break scope restrictions using the
    326     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    327     *            match {@code public} fields.
    328     * @return the Field object
    329     * @throws IllegalArgumentException
    330     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
    331     *             not be found
    332     * @throws IllegalAccessException
    333     *             if the field is not made accessible
    334     */
    335    public static Object readStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
    336        final Field field = getField(cls, fieldName, forceAccess);
    337        Validate.isTrue(field != null, "Cannot locate field '%s' on %s", fieldName, cls);
    338        // already forced access above, don't repeat it here:
    339        return readStaticField(field, false);
    340    }
    341
    342    /**
    343     * Gets the value of a {@code static} {@link Field} by name. The field must be {@code public}. Only the specified
    344     * class will be considered.
    345     *
    346     * @param cls
    347     *            the {@link Class} to reflect, must not be {@code null}
    348     * @param fieldName
    349     *            the field name to obtain
    350     * @return the value of the field
    351     * @throws IllegalArgumentException
    352     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
    353     *             not be found
    354     * @throws IllegalAccessException
    355     *             if the field is not accessible
    356     */
    357    public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
    358        return readDeclaredStaticField(cls, fieldName, false);
    359    }
    360
    361    /**
    362     * Gets the value of a {@code static} {@link Field} by name. Only the specified class will be considered.
    363     *
    364     * @param cls
    365     *            the {@link Class} to reflect, must not be {@code null}
    366     * @param fieldName
    367     *            the field name to obtain
    368     * @param forceAccess
    369     *            whether to break scope restrictions using the
    370     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    371     *            match {@code public} fields.
    372     * @return the Field object
    373     * @throws IllegalArgumentException
    374     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
    375     *             not be found
    376     * @throws IllegalAccessException
    377     *             if the field is not made accessible
    378     */
    379    public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
    380        final Field field = getDeclaredField(cls, fieldName, forceAccess);
    381        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
    382        // already forced access above, don't repeat it here:
    383        return readStaticField(field, false);
    384    }
    385
    386    /**
    387     * Reads an accessible {@link Field}.
    388     *
    389     * @param field
    390     *            the field to use
    391     * @param target
    392     *            the object to call on, may be {@code null} for {@code static} fields
    393     * @return the field value
    394     * @throws IllegalArgumentException
    395     *             if the field is {@code null}
    396     * @throws IllegalAccessException
    397     *             if the field is not accessible
    398     */
    399    public static Object readField(final Field field, final Object target) throws IllegalAccessException {
    400        return readField(field, target, false);
    401    }
    402
    403    /**
    404     * Reads a {@link Field}.
    405     *
    406     * @param field
    407     *            the field to use
    408     * @param target
    409     *            the object to call on, may be {@code null} for {@code static} fields
    410     * @param forceAccess
    411     *            whether to break scope restrictions using the
    412     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
    413     * @return the field value
    414     * @throws IllegalArgumentException
    415     *             if the field is {@code null}
    416     * @throws IllegalAccessException
    417     *             if the field is not made accessible
    418     */
    419    public static Object readField(final Field field, final Object target, final boolean forceAccess) throws IllegalAccessException {
    420        Validate.isTrue(field != null, "The field must not be null");
    421        if (forceAccess && !field.isAccessible()) {
    422            field.setAccessible(true);
    423        } else {
    424            MemberUtils.setAccessibleWorkaround(field);
    425        }
    426        return field.get(target);
    427    }
    428
    429    /**
    430     * Reads the named {@code public} {@link Field}. Superclasses will be considered.
    431     *
    432     * @param target
    433     *            the object to reflect, must not be {@code null}
    434     * @param fieldName
    435     *            the field name to obtain
    436     * @return the value of the field
    437     * @throws IllegalArgumentException
    438     *             if the class is {@code null}, or the field name is blank or empty or could not be found
    439     * @throws IllegalAccessException
    440     *             if the named field is not {@code public}
    441     */
    442    public static Object readField(final Object target, final String fieldName) throws IllegalAccessException {
    443        return readField(target, fieldName, false);
    444    }
    445
    446    /**
    447     * Reads the named {@link Field}. Superclasses will be considered.
    448     *
    449     * @param target
    450     *            the object to reflect, must not be {@code null}
    451     * @param fieldName
    452     *            the field name to obtain
    453     * @param forceAccess
    454     *            whether to break scope restrictions using the
    455     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    456     *            match {@code public} fields.
    457     * @return the field value
    458     * @throws IllegalArgumentException
    459     *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
    460     * @throws IllegalAccessException
    461     *             if the named field is not made accessible
    462     */
    463    public static Object readField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
    464        Validate.isTrue(target != null, "target object must not be null");
    465        final Class<?> cls = target.getClass();
    466        final Field field = getField(cls, fieldName, forceAccess);
    467        Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
    468        // already forced access above, don't repeat it here:
    469        return readField(field, target, false);
    470    }
    471
    472    /**
    473     * Reads the named {@code public} {@link Field}. Only the class of the specified object will be considered.
    474     *
    475     * @param target
    476     *            the object to reflect, must not be {@code null}
    477     * @param fieldName
    478     *            the field name to obtain
    479     * @return the value of the field
    480     * @throws IllegalArgumentException
    481     *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
    482     * @throws IllegalAccessException
    483     *             if the named field is not {@code public}
    484     */
    485    public static Object readDeclaredField(final Object target, final String fieldName) throws IllegalAccessException {
    486        return readDeclaredField(target, fieldName, false);
    487    }
    488
    489    /**
    490     * Gets a {@link Field} value by name. Only the class of the specified object will be considered.
    491     *
    492     * @param target
    493     *            the object to reflect, must not be {@code null}
    494     * @param fieldName
    495     *            the field name to obtain
    496     * @param forceAccess
    497     *            whether to break scope restrictions using the
    498     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    499     *            match public fields.
    500     * @return the Field object
    501     * @throws IllegalArgumentException
    502     *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
    503     * @throws IllegalAccessException
    504     *             if the field is not made accessible
    505     */
    506    public static Object readDeclaredField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
    507        Validate.isTrue(target != null, "target object must not be null");
    508        final Class<?> cls = target.getClass();
    509        final Field field = getDeclaredField(cls, fieldName, forceAccess);
    510        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls, fieldName);
    511        // already forced access above, don't repeat it here:
    512        return readField(field, target, false);
    513    }
    514
    515    /**
    516     * Writes a {@code public static} {@link Field}.
    517     *
    518     * @param field
    519     *            to write
    520     * @param value
    521     *            to set
    522     * @throws IllegalArgumentException
    523     *             if the field is {@code null} or not {@code static}, or {@code value} is not assignable
    524     * @throws IllegalAccessException
    525     *             if the field is not {@code public} or is {@code final}
    526     */
    527    public static void writeStaticField(final Field field, final Object value) throws IllegalAccessException {
    528        writeStaticField(field, value, false);
    529    }
    530
    531    /**
    532     * Writes a static {@link Field}.
    533     *
    534     * @param field
    535     *            to write
    536     * @param value
    537     *            to set
    538     * @param forceAccess
    539     *            whether to break scope restrictions using the
    540     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    541     *            match {@code public} fields.
    542     * @throws IllegalArgumentException
    543     *             if the field is {@code null} or not {@code static}, or {@code value} is not assignable
    544     * @throws IllegalAccessException
    545     *             if the field is not made accessible or is {@code final}
    546     */
    547    public static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException {
    548        Validate.isTrue(field != null, "The field must not be null");
    549        Validate.isTrue(Modifier.isStatic(field.getModifiers()), "The field %s.%s is not static", field.getDeclaringClass().getName(),
    550                field.getName());
    551        writeField(field, (Object) null, value, forceAccess);
    552    }
    553
    554    /**
    555     * Writes a named {@code public static} {@link Field}. Superclasses will be considered.
    556     *
    557     * @param cls
    558     *            {@link Class} on which the field is to be found
    559     * @param fieldName
    560     *            to write
    561     * @param value
    562     *            to set
    563     * @throws IllegalArgumentException
    564     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
    565     *             not {@code static}, or {@code value} is not assignable
    566     * @throws IllegalAccessException
    567     *             if the field is not {@code public} or is {@code final}
    568     */
    569    public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
    570        writeStaticField(cls, fieldName, value, false);
    571    }
    572
    573    /**
    574     * Writes a named {@code static} {@link Field}. Superclasses will be considered.
    575     *
    576     * @param cls
    577     *            {@link Class} on which the field is to be found
    578     * @param fieldName
    579     *            to write
    580     * @param value
    581     *            to set
    582     * @param forceAccess
    583     *            whether to break scope restrictions using the
    584     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    585     *            match {@code public} fields.
    586     * @throws IllegalArgumentException
    587     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
    588     *             not {@code static}, or {@code value} is not assignable
    589     * @throws IllegalAccessException
    590     *             if the field is not made accessible or is {@code final}
    591     */
    592    public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
    593            throws IllegalAccessException {
    594        final Field field = getField(cls, fieldName, forceAccess);
    595        Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
    596        // already forced access above, don't repeat it here:
    597        writeStaticField(field, value, false);
    598    }
    599
    600    /**
    601     * Writes a named {@code public static} {@link Field}. Only the specified class will be considered.
    602     *
    603     * @param cls
    604     *            {@link Class} on which the field is to be found
    605     * @param fieldName
    606     *            to write
    607     * @param value
    608     *            to set
    609     * @throws IllegalArgumentException
    610     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
    611     *             not {@code static}, or {@code value} is not assignable
    612     * @throws IllegalAccessException
    613     *             if the field is not {@code public} or is {@code final}
    614     */
    615    public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
    616        writeDeclaredStaticField(cls, fieldName, value, false);
    617    }
    618
    619    /**
    620     * Writes a named {@code static} {@link Field}. Only the specified class will be considered.
    621     *
    622     * @param cls
    623     *            {@link Class} on which the field is to be found
    624     * @param fieldName
    625     *            to write
    626     * @param value
    627     *            to set
    628     * @param forceAccess
    629     *            whether to break scope restrictions using the {@code AccessibleObject#setAccessible(boolean)} method.
    630     *            {@code false} will only match {@code public} fields.
    631     * @throws IllegalArgumentException
    632     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
    633     *             not {@code static}, or {@code value} is not assignable
    634     * @throws IllegalAccessException
    635     *             if the field is not made accessible or is {@code final}
    636     */
    637    public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
    638            throws IllegalAccessException {
    639        final Field field = getDeclaredField(cls, fieldName, forceAccess);
    640        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
    641        // already forced access above, don't repeat it here:
    642        writeField(field, (Object) null, value, false);
    643    }
    644
    645    /**
    646     * Writes an accessible {@link Field}.
    647     *
    648     * @param field
    649     *            to write
    650     * @param target
    651     *            the object to call on, may be {@code null} for {@code static} fields
    652     * @param value
    653     *            to set
    654     * @throws IllegalAccessException
    655     *             if the field or target is {@code null}, the field is not accessible or is {@code final}, or
    656     *             {@code value} is not assignable
    657     */
    658    public static void writeField(final Field field, final Object target, final Object value) throws IllegalAccessException {
    659        writeField(field, target, value, false);
    660    }
    661
    662    /**
    663     * Writes a {@link Field}.
    664     *
    665     * @param field
    666     *            to write
    667     * @param target
    668     *            the object to call on, may be {@code null} for {@code static} fields
    669     * @param value
    670     *            to set
    671     * @param forceAccess
    672     *            whether to break scope restrictions using the
    673     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    674     *            match {@code public} fields.
    675     * @throws IllegalArgumentException
    676     *             if the field is {@code null} or {@code value} is not assignable
    677     * @throws IllegalAccessException
    678     *             if the field is not made accessible or is {@code final}
    679     */
    680    public static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess)
    681            throws IllegalAccessException {
    682        Validate.isTrue(field != null, "The field must not be null");
    683        if (forceAccess && !field.isAccessible()) {
    684            field.setAccessible(true);
    685        } else {
    686            MemberUtils.setAccessibleWorkaround(field);
    687        }
    688        field.set(target, value);
    689    }
    690
    691    /**
    692     * Removes the final modifier from a {@link Field}.
    693     *
    694     * @param field
    695     *            to remove the final modifier
    696     * @throws IllegalArgumentException
    697     *             if the field is {@code null}
    698     * @since 3.2
    699     */
    700    public static void removeFinalModifier(final Field field) {
    701        removeFinalModifier(field, true);
    702    }
    703
    704    /**
    705     * Removes the final modifier from a {@link Field}.
    706     *
    707     * @param field
    708     *            to remove the final modifier
    709     * @param forceAccess
    710     *            whether to break scope restrictions using the
    711     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    712     *            match {@code public} fields.
    713     * @throws IllegalArgumentException
    714     *             if the field is {@code null}
    715     * @since 3.3
    716     */
    717    public static void removeFinalModifier(final Field field, final boolean forceAccess) {
    718        Validate.isTrue(field != null, "The field must not be null");
    719
    720        try {
    721            if (Modifier.isFinal(field.getModifiers())) {
    722                // Do all JREs implement Field with a private ivar called "modifiers"?
    723                final Field modifiersField = Field.class.getDeclaredField("modifiers");
    724                final boolean doForceAccess = forceAccess && !modifiersField.isAccessible();
    725                if (doForceAccess) {
    726                    modifiersField.setAccessible(true);
    727                }
    728                try {
    729                    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    730                } finally {
    731                    if (doForceAccess) {
    732                        modifiersField.setAccessible(false);
    733                    }
    734                }
    735            }
    736        } catch (final NoSuchFieldException ignored) {
    737            // The field class contains always a modifiers field
    738        } catch (final IllegalAccessException ignored) {
    739            // The modifiers field is made accessible
    740        }
    741    }
    742
    743    /**
    744     * Writes a {@code public} {@link Field}. Superclasses will be considered.
    745     *
    746     * @param target
    747     *            the object to reflect, must not be {@code null}
    748     * @param fieldName
    749     *            the field name to obtain
    750     * @param value
    751     *            to set
    752     * @throws IllegalArgumentException
    753     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
    754     *             {@code value} is not assignable
    755     * @throws IllegalAccessException
    756     *             if the field is not accessible
    757     */
    758    public static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
    759        writeField(target, fieldName, value, false);
    760    }
    761
    762    /**
    763     * Writes a {@link Field}. Superclasses will be considered.
    764     *
    765     * @param target
    766     *            the object to reflect, must not be {@code null}
    767     * @param fieldName
    768     *            the field name to obtain
    769     * @param value
    770     *            to set
    771     * @param forceAccess
    772     *            whether to break scope restrictions using the
    773     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    774     *            match {@code public} fields.
    775     * @throws IllegalArgumentException
    776     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
    777     *             {@code value} is not assignable
    778     * @throws IllegalAccessException
    779     *             if the field is not made accessible
    780     */
    781    public static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
    782            throws IllegalAccessException {
    783        Validate.isTrue(target != null, "target object must not be null");
    784        final Class<?> cls = target.getClass();
    785        final Field field = getField(cls, fieldName, forceAccess);
    786        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
    787        // already forced access above, don't repeat it here:
    788        writeField(field, target, value, false);
    789    }
    790
    791    /**
    792     * Writes a {@code public} {@link Field}. Only the specified class will be considered.
    793     *
    794     * @param target
    795     *            the object to reflect, must not be {@code null}
    796     * @param fieldName
    797     *            the field name to obtain
    798     * @param value
    799     *            to set
    800     * @throws IllegalArgumentException
    801     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
    802     *             {@code value} is not assignable
    803     * @throws IllegalAccessException
    804     *             if the field is not made accessible
    805     */
    806    public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
    807        writeDeclaredField(target, fieldName, value, false);
    808    }
    809
    810    /**
    811     * Writes a {@code public} {@link Field}. Only the specified class will be considered.
    812     *
    813     * @param target
    814     *            the object to reflect, must not be {@code null}
    815     * @param fieldName
    816     *            the field name to obtain
    817     * @param value
    818     *            to set
    819     * @param forceAccess
    820     *            whether to break scope restrictions using the
    821     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
    822     *            match {@code public} fields.
    823     * @throws IllegalArgumentException
    824     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
    825     *             {@code value} is not assignable
    826     * @throws IllegalAccessException
    827     *             if the field is not made accessible
    828     */
    829    public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
    830            throws IllegalAccessException {
    831        Validate.isTrue(target != null, "target object must not be null");
    832        final Class<?> cls = target.getClass();
    833        final Field field = getDeclaredField(cls, fieldName, forceAccess);
    834        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
    835        // already forced access above, don't repeat it here:
    836        writeField(field, target, value, false);
    837    }
    838}