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    }