    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     */
    017    package org.apache.commons.lang3.reflect;
    018
    019    import java.lang.reflect.Field;
    020    import java.lang.reflect.Modifier;
    021
    022    import org.apache.commons.lang3.ClassUtils;
    023
    024    /**
    025     * Utilities for working with fields by reflection. Adapted and refactored
    026     * from the dormant [reflect] Commons sandbox component.
    027     * <p>
    028     * The ability is provided to break the scoping restrictions coded by the
    029     * programmer. This can allow fields to be changed that shouldn't be. This
    030     * facility should be used with care.
    031     *
    032     * @since 2.5
    033     * @version $Id: FieldUtils.java 1144929 2011-07-10 18:26:16Z ggregory $
    034     */
    035    public class FieldUtils {
    036
    037        /**
    038         * FieldUtils instances should NOT be constructed in standard programming.
    039         * <p>
    040         * This constructor is public to permit tools that require a JavaBean instance
    041         * to operate.
    042         */
    043        public FieldUtils() {
    044            super();
    045        }
    046
    047        /**
    048         * Gets an accessible <code>Field</code> by name respecting scope.
    049         * Superclasses/interfaces will be considered.
    050         *
    051         * @param cls  the class to reflect, must not be null
    052         * @param fieldName  the field name to obtain
    053         * @return the Field object
    054         * @throws IllegalArgumentException if the class or field name is null
    055         */
    056        public static Field getField(Class<?> cls, String fieldName) {
    057            Field field = getField(cls, fieldName, false);
    058            MemberUtils.setAccessibleWorkaround(field);
    059            return field;
    060        }
    061
    062        /**
    063         * Gets an accessible <code>Field</code> by name breaking scope
    064         * if requested. Superclasses/interfaces will be considered.
    065         *
    066         * @param cls  the class to reflect, must not be null
    067         * @param fieldName  the field name to obtain
    068         * @param forceAccess  whether to break scope restrictions using the
    069         *  <code>setAccessible</code> method. <code>False</code> will only
    070         *  match public fields.
    071         * @return the Field object
    072         * @throws IllegalArgumentException if the class or field name is null
    073         */
    074        public static Field getField(final Class<?> cls, String fieldName, boolean forceAccess) {
    075            if (cls == null) {
    076                throw new IllegalArgumentException("The class must not be null");
    077            }
    078            if (fieldName == null) {
    079                throw new IllegalArgumentException("The field name must not be null");
    080            }
    081            // Sun Java 1.3 has a bugged implementation of getField hence we write the
    082            // code ourselves
    083
    084            // getField() will return the Field object with the declaring class
    085            // set correctly to the class that declares the field. Thus requesting the
    086            // field on a subclass will return the field from the superclass.
    087            //
    088            // priority order for lookup:
    089            // searchclass private/protected/package/public
    090            // superclass protected/package/public
    091            //  private/different package blocks access to further superclasses
    092            // implementedinterface public
    093
    094            // check up the superclass hierarchy
    095            for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
    096                try {
    097                    Field field = acls.getDeclaredField(fieldName);
    098                    // getDeclaredField checks for non-public scopes as well
    099                    // and it returns accurate results
    100                    if (!Modifier.isPublic(field.getModifiers())) {
    101                        if (forceAccess) {
    102                            field.setAccessible(true);
    103                        } else {
    104                            continue;
    105                        }
    106                    }
    107                    return field;
    108                } catch (NoSuchFieldException ex) { // NOPMD
    109                    // ignore
    110                }
    111            }
    112            // check the public interface case. This must be manually searched for
    113            // incase there is a public supersuperclass field hidden by a private/package
    114            // superclass field.
    115            Field match = null;
    116            for (Class<?> class1 : ClassUtils.getAllInterfaces(cls)) {
    117                try {
    118                    Field test = ((Class<?>) class1).getField(fieldName);
    119                    if (match != null) {
    120                        throw new IllegalArgumentException("Reference to field " + fieldName + " is ambiguous relative to " + cls
    121                                + "; a matching field exists on two or more implemented interfaces.");
    122                    }
    123                    match = test;
    124                } catch (NoSuchFieldException ex) { // NOPMD
    125                    // ignore
    126                }
    127            }
    128            return match;
    129        }
    130
    131        /**
    132         * Gets an accessible <code>Field</code> by name respecting scope.
    133         * Only the specified class will be considered.
    134         *
    135         * @param cls  the class to reflect, must not be null
    136         * @param fieldName  the field name to obtain
    137         * @return the Field object
    138         * @throws IllegalArgumentException if the class or field name is null
    139         */
    140        public static Field getDeclaredField(Class<?> cls, String fieldName) {
    141            return getDeclaredField(cls, fieldName, false);
    142        }
    143
    144        /**
    145         * Gets an accessible <code>Field</code> by name breaking scope
    146         * if requested. Only the specified class will be considered.
    147         *
    148         * @param cls  the class to reflect, must not be null
    149         * @param fieldName  the field name to obtain
    150         * @param forceAccess  whether to break scope restrictions using the
    151         *  <code>setAccessible</code> method. False will only match public fields.
    152         * @return the Field object
    153         * @throws IllegalArgumentException if the class or field name is null
    154         */
    155        public static Field getDeclaredField(Class<?> cls, String fieldName, boolean forceAccess) {
    156            if (cls == null) {
    157                throw new IllegalArgumentException("The class must not be null");
    158            }
    159            if (fieldName == null) {
    160                throw new IllegalArgumentException("The field name must not be null");
    161            }
    162            try {
    163                // only consider the specified class by using getDeclaredField()
    164                Field field = cls.getDeclaredField(fieldName);
    165                if (!MemberUtils.isAccessible(field)) {
    166                    if (forceAccess) {
    167                        field.setAccessible(true);
    168                    } else {
    169                        return null;
    170                    }
    171                }
    172                return field;
    173            } catch (NoSuchFieldException e) { // NOPMD
    174                // ignore
    175            }
    176            return null;
    177        }
    178
    179        /**
    180         * Reads an accessible static Field.
    181         * @param field to read
    182         * @return the field value
    183         * @throws IllegalArgumentException if the field is null or not static
    184         * @throws IllegalAccessException if the field is not accessible
    185         */
    186        public static Object readStaticField(Field field) throws IllegalAccessException {
    187            return readStaticField(field, false);
    188        }
    189
    190        /**
    191         * Reads a static Field.
    192         * @param field to read
    193         * @param forceAccess  whether to break scope restrictions using the
    194         *  <code>setAccessible</code> method.
    195         * @return the field value
    196         * @throws IllegalArgumentException if the field is null or not static
    197         * @throws IllegalAccessException if the field is not made accessible
    198         */
    199        public static Object readStaticField(Field field, boolean forceAccess) throws IllegalAccessException {
    200            if (field == null) {
    201                throw new IllegalArgumentException("The field must not be null");
    202            }
    203            if (!Modifier.isStatic(field.getModifiers())) {
    204                throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
    205            }
    206            return readField(field, (Object) null, forceAccess);
    207        }
    208
    209        /**
    210         * Reads the named public static field. Superclasses will be considered.
    211         * @param cls  the class to reflect, must not be null
    212         * @param fieldName  the field name to obtain
    213         * @return the value of the field
    214         * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found
    215         * @throws IllegalAccessException if the field is not accessible
    216         */
    217        public static Object readStaticField(Class<?> cls, String fieldName) throws IllegalAccessException {
    218            return readStaticField(cls, fieldName, false);
    219        }
    220
    221        /**
    222         * Reads the named static field. Superclasses will be considered.
    223         * @param cls  the class to reflect, must not be null
    224         * @param fieldName  the field name to obtain
    225         * @param forceAccess  whether to break scope restrictions using the
    226         *  <code>setAccessible</code> method. <code>False</code> will only
    227         *  match public fields.
    228         * @return the Field object
    229         * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found
    230         * @throws IllegalAccessException if the field is not made accessible
    231         */
    232        public static Object readStaticField(Class<?> cls, String fieldName, boolean forceAccess)
    233            throws IllegalAccessException {
    234            Field field = getField(cls, fieldName, forceAccess);
    235            if (field == null) {
    236                throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
    237            }
    238            //already forced access above, don't repeat it here:
    239            return readStaticField(field, false);
    240        }
    241
    242        /**
    243         * Gets a static Field value by name. The field must be public.
    244         * Only the specified class will be considered.
    245         *
    246         * @param cls  the class to reflect, must not be null
    247         * @param fieldName  the field name to obtain
    248         * @return the value of the field
    249         * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found
    250         * @throws IllegalAccessException if the field is not accessible
    251         */
    252        public static Object readDeclaredStaticField(Class<?> cls, String fieldName) throws IllegalAccessException {
    253            return readDeclaredStaticField(cls, fieldName, false);
    254        }
    255
    256        /**
    257         * Gets a static Field value by name. Only the specified class will
    258         * be considered.
    259         *
    260         * @param cls  the class to reflect, must not be null
    261         * @param fieldName  the field name to obtain
    262         * @param forceAccess  whether to break scope restrictions using the
    263         *  <code>setAccessible</code> method. <code>False</code> will only
    264         *  match public fields.
    265         * @return the Field object
    266         * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found
    267         * @throws IllegalAccessException if the field is not made accessible
    268         */
    269        public static Object readDeclaredStaticField(Class<?> cls, String fieldName, boolean forceAccess)
    270                throws IllegalAccessException {
    271            Field field = getDeclaredField(cls, fieldName, forceAccess);
    272            if (field == null) {
    273                throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
    274            }
    275            //already forced access above, don't repeat it here:
    276            return readStaticField(field, false);
    277        }
    278
    279        /**
    280         * Reads an accessible Field.
    281         * @param field  the field to use
    282         * @param target  the object to call on, may be null for static fields
    283         * @return the field value
    284         * @throws IllegalArgumentException if the field is null
    285         * @throws IllegalAccessException if the field is not accessible
    286         */
    287        public static Object readField(Field field, Object target) throws IllegalAccessException {
    288            return readField(field, target, false);
    289        }
    290
    291        /**
    292         * Reads a Field.
    293         * @param field  the field to use
    294         * @param target  the object to call on, may be null for static fields
    295         * @param forceAccess  whether to break scope restrictions using the
    296         *  <code>setAccessible</code> method.
    297         * @return the field value
    298         * @throws IllegalArgumentException if the field is null
    299         * @throws IllegalAccessException if the field is not made accessible
    300         */
    301        public static Object readField(Field field, Object target, boolean forceAccess) throws IllegalAccessException {
    302            if (field == null) {
    303                throw new IllegalArgumentException("The field must not be null");
    304            }
    305            if (forceAccess && !field.isAccessible()) {
    306                field.setAccessible(true);
    307            } else {
    308                MemberUtils.setAccessibleWorkaround(field);
    309            }
    310            return field.get(target);
    311        }
    312
    313        /**
    314         * Reads the named public field. Superclasses will be considered.
    315         * @param target  the object to reflect, must not be null
    316         * @param fieldName  the field name to obtain
    317         * @return the value of the field
    318         * @throws IllegalArgumentException if the class or field name is null
    319         * @throws IllegalAccessException if the named field is not public
    320         */
    321        public static Object readField(Object target, String fieldName) throws IllegalAccessException {
    322            return readField(target, fieldName, false);
    323        }
    324
    325        /**
    326         * Reads the named field. Superclasses will be considered.
    327         * @param target  the object to reflect, must not be null
    328         * @param fieldName  the field name to obtain
    329         * @param forceAccess  whether to break scope restrictions using the
    330         *  <code>setAccessible</code> method. <code>False</code> will only
    331         *  match public fields.
    332         * @return the field value
    333         * @throws IllegalArgumentException if the class or field name is null
    334         * @throws IllegalAccessException if the named field is not made accessible
    335         */
    336        public static Object readField(Object target, String fieldName, boolean forceAccess) throws IllegalAccessException {
    337            if (target == null) {
    338                throw new IllegalArgumentException("target object must not be null");
    339            }
    340            Class<?> cls = target.getClass();
    341            Field field = getField(cls, fieldName, forceAccess);
    342            if (field == null) {
    343                throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
    344            }
    345            //already forced access above, don't repeat it here:
    346            return readField(field, target);
    347        }
    348
    349        /**
    350         * Reads the named public field. Only the class of the specified object will be considered.
    351         * @param target  the object to reflect, must not be null
    352         * @param fieldName  the field name to obtain
    353         * @return the value of the field
    354         * @throws IllegalArgumentException if the class or field name is null
    355         * @throws IllegalAccessException if the named field is not public
    356         */
    357        public static Object readDeclaredField(Object target, String fieldName) throws IllegalAccessException {
    358            return readDeclaredField(target, fieldName, false);
    359        }
    360
    361        /**
    362         * <p<>Gets a Field value by name. Only the class of the specified
    363         * object will be considered.
    364         *
    365         * @param target  the object to reflect, must not be null
    366         * @param fieldName  the field name to obtain
    367         * @param forceAccess  whether to break scope restrictions using the
    368         *  <code>setAccessible</code> method. <code>False</code> will only
    369         *  match public fields.
    370         * @return the Field object
    371         * @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
    372         * @throws IllegalAccessException if the field is not made accessible
    373         */
    374        public static Object readDeclaredField(Object target, String fieldName, boolean forceAccess)
    375            throws IllegalAccessException {
    376            if (target == null) {
    377                throw new IllegalArgumentException("target object must not be null");
    378            }
    379            Class<?> cls = target.getClass();
    380            Field field = getDeclaredField(cls, fieldName, forceAccess);
    381            if (field == null) {
    382                throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
    383            }
    384            //already forced access above, don't repeat it here:
    385            return readField(field, target);
    386        }
    387
    388        /**
    389         * Writes a public static Field.
    390         * @param field to write
    391         * @param value to set
    392         * @throws IllegalArgumentException if the field is null or not static
    393         * @throws IllegalAccessException if the field is not public or is final
    394         */
    395        public static void writeStaticField(Field field, Object value) throws IllegalAccessException {
    396            writeStaticField(field, value, false);
    397        }
    398
    399        /**
    400         * Writes a static Field.
    401         * @param field to write
    402         * @param value to set
    403         * @param forceAccess  whether to break scope restrictions using the
    404         *  <code>setAccessible</code> method. <code>False</code> will only
    405         *  match public fields.
    406         * @throws IllegalArgumentException if the field is null or not static
    407         * @throws IllegalAccessException if the field is not made accessible or is final
    408         */
    409        public static void writeStaticField(Field field, Object value, boolean forceAccess) throws IllegalAccessException {
    410            if (field == null) {
    411                throw new IllegalArgumentException("The field must not be null");
    412            }
    413            if (!Modifier.isStatic(field.getModifiers())) {
    414                throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
    415            }
    416            writeField(field, (Object) null, value, forceAccess);
    417        }
    418
    419        /**
    420         * Writes a named public static Field. Superclasses will be considered.
    421         * @param cls Class on which the Field is to be found
    422         * @param fieldName to write
    423         * @param value to set
    424         * @throws IllegalArgumentException if the field cannot be located or is not static
    425         * @throws IllegalAccessException if the field is not public or is final
    426         */
    427        public static void writeStaticField(Class<?> cls, String fieldName, Object value) throws IllegalAccessException {
    428            writeStaticField(cls, fieldName, value, false);
    429        }
    430
    431        /**
    432         * Writes a named static Field. Superclasses will be considered.
    433         * @param cls Class on which the Field is to be found
    434         * @param fieldName to write
    435         * @param value to set
    436         * @param forceAccess  whether to break scope restrictions using the
    437         *  <code>setAccessible</code> method. <code>False</code> will only
    438         *  match public fields.
    439         * @throws IllegalArgumentException if the field cannot be located or is not static
    440         * @throws IllegalAccessException if the field is not made accessible or is final
    441         */
    442        public static void writeStaticField(Class<?> cls, String fieldName, Object value, boolean forceAccess)
    443                throws IllegalAccessException {
    444            Field field = getField(cls, fieldName, forceAccess);
    445            if (field == null) {
    446                throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
    447            }
    448            //already forced access above, don't repeat it here:
    449            writeStaticField(field, value);
    450        }
    451
    452        /**
    453         * Writes a named public static Field. Only the specified class will be considered.
    454         * @param cls Class on which the Field is to be found
    455         * @param fieldName to write
    456         * @param value to set
    457         * @throws IllegalArgumentException if the field cannot be located or is not static
    458         * @throws IllegalAccessException if the field is not public or is final
    459         */
    460        public static void writeDeclaredStaticField(Class<?> cls, String fieldName, Object value)
    461                throws IllegalAccessException {
    462            writeDeclaredStaticField(cls, fieldName, value, false);
    463        }
    464
    465        /**
    466         * Writes a named static Field. Only the specified class will be considered.
    467         * @param cls Class on which the Field is to be found
    468         * @param fieldName to write
    469         * @param value to set
    470         * @param forceAccess  whether to break scope restrictions using the
    471         *  <code>setAccessible</code> method. <code>False</code> will only
    472         *  match public fields.
    473         * @throws IllegalArgumentException if the field cannot be located or is not static
    474         * @throws IllegalAccessException if the field is not made accessible or is final
    475          */
    476        public static void writeDeclaredStaticField(Class<?> cls, String fieldName, Object value, boolean forceAccess)
    477                throws IllegalAccessException {
    478            Field field = getDeclaredField(cls, fieldName, forceAccess);
    479            if (field == null) {
    480                throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
    481            }
    482            //already forced access above, don't repeat it here:
    483            writeField(field, (Object) null, value);
    484        }
    485
    486        /**
    487         * Writes an accessible field.
    488         * @param field to write
    489         * @param target  the object to call on, may be null for static fields
    490         * @param value to set
    491         * @throws IllegalArgumentException if the field is null
    492         * @throws IllegalAccessException if the field is not accessible or is final
    493         */
    494        public static void writeField(Field field, Object target, Object value) throws IllegalAccessException {
    495            writeField(field, target, value, false);
    496        }
    497
    498        /**
    499         * Writes a field.
    500         * @param field to write
    501         * @param target  the object to call on, may be null for static fields
    502         * @param value to set
    503         * @param forceAccess  whether to break scope restrictions using the
    504         *  <code>setAccessible</code> method. <code>False</code> will only
    505         *  match public fields.
    506         * @throws IllegalArgumentException if the field is null
    507         * @throws IllegalAccessException if the field is not made accessible or is final
    508         */
    509        public static void writeField(Field field, Object target, Object value, boolean forceAccess)
    510            throws IllegalAccessException {
    511            if (field == null) {
    512                throw new IllegalArgumentException("The field must not be null");
    513            }
    514            if (forceAccess && !field.isAccessible()) {
    515                field.setAccessible(true);
    516            } else {
    517                MemberUtils.setAccessibleWorkaround(field);
    518            }
    519            field.set(target, value);
    520        }
    521
    522        /**
    523         * Writes a public field. Superclasses will be considered.
    524         * @param target  the object to reflect, must not be null
    525         * @param fieldName  the field name to obtain
    526         * @param value to set
    527         * @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
    528         * @throws IllegalAccessException if the field is not accessible
    529         */
    530        public static void writeField(Object target, String fieldName, Object value) throws IllegalAccessException {
    531            writeField(target, fieldName, value, false);
    532        }
    533
    534        /**
    535         * Writes a field. Superclasses will be considered.
    536         * @param target  the object to reflect, must not be null
    537         * @param fieldName  the field name to obtain
    538         * @param value to set
    539         * @param forceAccess  whether to break scope restrictions using the
    540         *  <code>setAccessible</code> method. <code>False</code> will only
    541         *  match public fields.
    542         * @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
    543         * @throws IllegalAccessException if the field is not made accessible
    544         */
    545        public static void writeField(Object target, String fieldName, Object value, boolean forceAccess)
    546                throws IllegalAccessException {
    547            if (target == null) {
    548                throw new IllegalArgumentException("target object must not be null");
    549            }
    550            Class<?> cls = target.getClass();
    551            Field field = getField(cls, fieldName, forceAccess);
    552            if (field == null) {
    553                throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
    554            }
    555            //already forced access above, don't repeat it here:
    556            writeField(field, target, value);
    557        }
    558
    559        /**
    560         * Writes a public field. Only the specified class will be considered.
    561         * @param target  the object to reflect, must not be null
    562         * @param fieldName  the field name to obtain
    563         * @param value to set
    564         * @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
    565         * @throws IllegalAccessException if the field is not made accessible
    566         */
    567        public static void writeDeclaredField(Object target, String fieldName, Object value) throws IllegalAccessException {
    568            writeDeclaredField(target, fieldName, value, false);
    569        }
    570
    571        /**
    572         * Writes a public field. Only the specified class will be considered.
    573         * @param target  the object to reflect, must not be null
    574         * @param fieldName  the field name to obtain
    575         * @param value to set
    576         * @param forceAccess  whether to break scope restrictions using the
    577         *  <code>setAccessible</code> method. <code>False</code> will only
    578         *  match public fields.
    579         * @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
    580         * @throws IllegalAccessException if the field is not made accessible
    581         */
    582        public static void writeDeclaredField(Object target, String fieldName, Object value, boolean forceAccess)
    583                throws IllegalAccessException {
    584            if (target == null) {
    585                throw new IllegalArgumentException("target object must not be null");
    586            }
    587            Class<?> cls = target.getClass();
    588            Field field = getDeclaredField(cls, fieldName, forceAccess);
    589            if (field == null) {
    590                throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
    591            }
    592            //already forced access above, don't repeat it here:
    593            writeField(field, target, value);
    594        }
    595    }