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