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