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 1057009 2011-01-09 19:48:06Z 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 respecting 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    }