View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.reflect;
18  
19  import org.apache.commons.lang3.ClassUtils;
20  import org.apache.commons.lang3.StringUtils;
21  import org.apache.commons.lang3.Validate;
22  
23  import java.lang.reflect.Field;
24  import java.lang.reflect.Modifier;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  /**
29   * Utilities for working with {@link Field}s by reflection. Adapted and refactored from the dormant [reflect] Commons
30   * sandbox component.
31   * <p>
32   * The ability is provided to break the scoping restrictions coded by the programmer. This can allow fields to be
33   * changed that shouldn't be. This facility should be used with care.
34   * 
35   * @since 2.5
36   * @version $Id: FieldUtils.java 1583482 2014-03-31 22:54:57Z niallp $
37   */
38  public class FieldUtils {
39  
40      /**
41       * {@link FieldUtils} instances should NOT be constructed in standard programming.
42       * <p>
43       * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
44       * </p>
45       */
46      public FieldUtils() {
47          super();
48      }
49  
50      /**
51       * Gets an accessible {@link Field} by name respecting scope. Superclasses/interfaces will be considered.
52       * 
53       * @param cls
54       *            the {@link Class} to reflect, must not be {@code null}
55       * @param fieldName
56       *            the field name to obtain
57       * @return the Field object
58       * @throws IllegalArgumentException
59       *             if the class is {@code null}, or the field name is blank or empty
60       */
61      public static Field getField(final Class<?> cls, final String fieldName) {
62          final Field field = getField(cls, fieldName, false);
63          MemberUtils.setAccessibleWorkaround(field);
64          return field;
65      }
66  
67      /**
68       * Gets an accessible {@link Field} by name, breaking scope if requested. Superclasses/interfaces will be
69       * considered.
70       * 
71       * @param cls
72       *            the {@link Class} to reflect, must not be {@code null}
73       * @param fieldName
74       *            the field name to obtain
75       * @param forceAccess
76       *            whether to break scope restrictions using the
77       *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
78       *            match {@code public} fields.
79       * @return the Field object
80       * @throws IllegalArgumentException
81       *             if the class is {@code null}, or the field name is blank or empty or is matched at multiple places
82       *             in the inheritance hierarchy
83       */
84      public static Field getField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
85          Validate.isTrue(cls != null, "The class must not be null");
86          Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
87          // Sun Java 1.3 has a bugged implementation of getField hence we write the
88          // code ourselves
89  
90          // getField() will return the Field object with the declaring class
91          // set correctly to the class that declares the field. Thus requesting the
92          // field on a subclass will return the field from the superclass.
93          //
94          // priority order for lookup:
95          // searchclass private/protected/package/public
96          // superclass protected/package/public
97          // private/different package blocks access to further superclasses
98          // implementedinterface public
99  
100         // check up the superclass hierarchy
101         for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
102             try {
103                 final Field field = acls.getDeclaredField(fieldName);
104                 // getDeclaredField checks for non-public scopes as well
105                 // and it returns accurate results
106                 if (!Modifier.isPublic(field.getModifiers())) {
107                     if (forceAccess) {
108                         field.setAccessible(true);
109                     } else {
110                         continue;
111                     }
112                 }
113                 return field;
114             } catch (final NoSuchFieldException ex) { // NOPMD
115                 // ignore
116             }
117         }
118         // check the public interface case. This must be manually searched for
119         // incase there is a public supersuperclass field hidden by a private/package
120         // superclass field.
121         Field match = null;
122         for (final Class<?> class1 : ClassUtils.getAllInterfaces(cls)) {
123             try {
124                 final Field test = class1.getField(fieldName);
125                 Validate.isTrue(match == null, "Reference to field %s is ambiguous relative to %s"
126                         + "; a matching field exists on two or more implemented interfaces.", fieldName, cls);
127                 match = test;
128             } catch (final NoSuchFieldException ex) { // NOPMD
129                 // ignore
130             }
131         }
132         return match;
133     }
134 
135     /**
136      * Gets an accessible {@link Field} by name respecting scope. Only the specified class will be considered.
137      * 
138      * @param cls
139      *            the {@link Class} to reflect, must not be {@code null}
140      * @param fieldName
141      *            the field name to obtain
142      * @return the Field object
143      * @throws IllegalArgumentException
144      *             if the class is {@code null}, or the field name is blank or empty
145      */
146     public static Field getDeclaredField(final Class<?> cls, final String fieldName) {
147         return getDeclaredField(cls, fieldName, false);
148     }
149 
150     /**
151      * Gets an accessible {@link Field} by name, breaking scope if requested. Only the specified class will be
152      * considered.
153      * 
154      * @param cls
155      *            the {@link Class} to reflect, must not be {@code null}
156      * @param fieldName
157      *            the field name to obtain
158      * @param forceAccess
159      *            whether to break scope restrictions using the
160      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
161      *            match {@code public} fields.
162      * @return the Field object
163      * @throws IllegalArgumentException
164      *             if the class is {@code null}, or the field name is blank or empty
165      */
166     public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
167         Validate.isTrue(cls != null, "The class must not be null");
168         Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
169         try {
170             // only consider the specified class by using getDeclaredField()
171             final Field field = cls.getDeclaredField(fieldName);
172             if (!MemberUtils.isAccessible(field)) {
173                 if (forceAccess) {
174                     field.setAccessible(true);
175                 } else {
176                     return null;
177                 }
178             }
179             return field;
180         } catch (final NoSuchFieldException e) { // NOPMD
181             // ignore
182         }
183         return null;
184     }
185 
186     /**
187      * Gets all fields of the given class and its parents (if any).
188      * 
189      * @param cls
190      *            the {@link Class} to query
191      * @return an array of Fields (possibly empty).
192      * @throws IllegalArgumentException
193      *             if the class is {@code null}
194      * @since 3.2
195      */
196     public static Field[] getAllFields(Class<?> cls) {
197         final List<Field> allFieldsList = getAllFieldsList(cls);
198         return allFieldsList.toArray(new Field[allFieldsList.size()]);
199     }
200 
201     /**
202      * Gets all fields of the given class and its parents (if any).
203      * 
204      * @param cls
205      *            the {@link Class} to query
206      * @return an array of Fields (possibly empty).
207      * @throws IllegalArgumentException
208      *             if the class is {@code null}
209      * @since 3.2
210      */
211     public static List<Field> getAllFieldsList(Class<?> cls) {
212         Validate.isTrue(cls != null, "The class must not be null");
213         final List<Field> allFields = new ArrayList<Field>();
214         Class<?> currentClass = cls;
215         while (currentClass != null) {
216             final Field[] declaredFields = currentClass.getDeclaredFields();
217             for (Field field : declaredFields) {
218                 allFields.add(field);
219             }
220             currentClass = currentClass.getSuperclass();
221         }
222         return allFields;
223     }
224 
225     /**
226      * Reads an accessible {@code static} {@link Field}.
227      * 
228      * @param field
229      *            to read
230      * @return the field value
231      * @throws IllegalArgumentException
232      *             if the field is {@code null}, or not {@code static}
233      * @throws IllegalAccessException
234      *             if the field is not accessible
235      */
236     public static Object readStaticField(final Field field) throws IllegalAccessException {
237         return readStaticField(field, false);
238     }
239 
240     /**
241      * Reads a static {@link Field}.
242      * 
243      * @param field
244      *            to read
245      * @param forceAccess
246      *            whether to break scope restrictions using the
247      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
248      * @return the field value
249      * @throws IllegalArgumentException
250      *             if the field is {@code null} or not {@code static}
251      * @throws IllegalAccessException
252      *             if the field is not made accessible
253      */
254     public static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException {
255         Validate.isTrue(field != null, "The field must not be null");
256         Validate.isTrue(Modifier.isStatic(field.getModifiers()), "The field '%s' is not static", field.getName());
257         return readField(field, (Object) null, forceAccess);
258     }
259 
260     /**
261      * Reads the named {@code public static} {@link Field}. Superclasses will be considered.
262      * 
263      * @param cls
264      *            the {@link Class} to reflect, must not be {@code null}
265      * @param fieldName
266      *            the field name to obtain
267      * @return the value of the field
268      * @throws IllegalArgumentException
269      *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
270      *             not be found
271      * @throws IllegalAccessException
272      *             if the field is not accessible
273      */
274     public static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
275         return readStaticField(cls, fieldName, false);
276     }
277 
278     /**
279      * Reads the named {@code static} {@link Field}. Superclasses will be considered.
280      * 
281      * @param cls
282      *            the {@link Class} to reflect, must not be {@code null}
283      * @param fieldName
284      *            the field name to obtain
285      * @param forceAccess
286      *            whether to break scope restrictions using the
287      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
288      *            match {@code public} fields.
289      * @return the Field object
290      * @throws IllegalArgumentException
291      *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
292      *             not be found
293      * @throws IllegalAccessException
294      *             if the field is not made accessible
295      */
296     public static Object readStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
297         final Field field = getField(cls, fieldName, forceAccess);
298         Validate.isTrue(field != null, "Cannot locate field '%s' on %s", fieldName, cls);
299         // already forced access above, don't repeat it here:
300         return readStaticField(field, false);
301     }
302 
303     /**
304      * Gets the value of a {@code static} {@link Field} by name. The field must be {@code public}. Only the specified
305      * class will be considered.
306      * 
307      * @param cls
308      *            the {@link Class} to reflect, must not be {@code null}
309      * @param fieldName
310      *            the field name to obtain
311      * @return the value of the field
312      * @throws IllegalArgumentException
313      *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
314      *             not be found
315      * @throws IllegalAccessException
316      *             if the field is not accessible
317      */
318     public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
319         return readDeclaredStaticField(cls, fieldName, false);
320     }
321 
322     /**
323      * Gets the value of a {@code static} {@link Field} by name. Only the specified class will be considered.
324      * 
325      * @param cls
326      *            the {@link Class} to reflect, must not be {@code null}
327      * @param fieldName
328      *            the field name to obtain
329      * @param forceAccess
330      *            whether to break scope restrictions using the
331      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
332      *            match {@code public} fields.
333      * @return the Field object
334      * @throws IllegalArgumentException
335      *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
336      *             not be found
337      * @throws IllegalAccessException
338      *             if the field is not made accessible
339      */
340     public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
341         final Field field = getDeclaredField(cls, fieldName, forceAccess);
342         Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
343         // already forced access above, don't repeat it here:
344         return readStaticField(field, false);
345     }
346 
347     /**
348      * Reads an accessible {@link Field}.
349      * 
350      * @param field
351      *            the field to use
352      * @param target
353      *            the object to call on, may be {@code null} for {@code static} fields
354      * @return the field value
355      * @throws IllegalArgumentException
356      *             if the field is {@code null}
357      * @throws IllegalAccessException
358      *             if the field is not accessible
359      */
360     public static Object readField(final Field field, final Object target) throws IllegalAccessException {
361         return readField(field, target, false);
362     }
363 
364     /**
365      * Reads a {@link Field}.
366      * 
367      * @param field
368      *            the field to use
369      * @param target
370      *            the object to call on, may be {@code null} for {@code static} fields
371      * @param forceAccess
372      *            whether to break scope restrictions using the
373      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
374      * @return the field value
375      * @throws IllegalArgumentException
376      *             if the field is {@code null}
377      * @throws IllegalAccessException
378      *             if the field is not made accessible
379      */
380     public static Object readField(final Field field, final Object target, final boolean forceAccess) throws IllegalAccessException {
381         Validate.isTrue(field != null, "The field must not be null");
382         if (forceAccess && !field.isAccessible()) {
383             field.setAccessible(true);
384         } else {
385             MemberUtils.setAccessibleWorkaround(field);
386         }
387         return field.get(target);
388     }
389 
390     /**
391      * Reads the named {@code public} {@link Field}. Superclasses will be considered.
392      * 
393      * @param target
394      *            the object to reflect, must not be {@code null}
395      * @param fieldName
396      *            the field name to obtain
397      * @return the value of the field
398      * @throws IllegalArgumentException
399      *             if the class is {@code null}, or the field name is blank or empty or could not be found
400      * @throws IllegalAccessException
401      *             if the named field is not {@code public}
402      */
403     public static Object readField(final Object target, final String fieldName) throws IllegalAccessException {
404         return readField(target, fieldName, false);
405     }
406 
407     /**
408      * Reads the named {@link Field}. Superclasses will be considered.
409      * 
410      * @param target
411      *            the object to reflect, must not be {@code null}
412      * @param fieldName
413      *            the field name to obtain
414      * @param forceAccess
415      *            whether to break scope restrictions using the
416      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
417      *            match {@code public} fields.
418      * @return the field value
419      * @throws IllegalArgumentException
420      *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
421      * @throws IllegalAccessException
422      *             if the named field is not made accessible
423      */
424     public static Object readField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
425         Validate.isTrue(target != null, "target object must not be null");
426         final Class<?> cls = target.getClass();
427         final Field field = getField(cls, fieldName, forceAccess);
428         Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
429         // already forced access above, don't repeat it here:
430         return readField(field, target, false);
431     }
432 
433     /**
434      * Reads the named {@code public} {@link Field}. Only the class of the specified object will be considered.
435      * 
436      * @param target
437      *            the object to reflect, must not be {@code null}
438      * @param fieldName
439      *            the field name to obtain
440      * @return the value of the field
441      * @throws IllegalArgumentException
442      *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
443      * @throws IllegalAccessException
444      *             if the named field is not {@code public}
445      */
446     public static Object readDeclaredField(final Object target, final String fieldName) throws IllegalAccessException {
447         return readDeclaredField(target, fieldName, false);
448     }
449 
450     /**
451      * Gets a {@link Field} value by name. Only the class of the specified object will be considered.
452      * 
453      * @param target
454      *            the object to reflect, must not be {@code null}
455      * @param fieldName
456      *            the field name to obtain
457      * @param forceAccess
458      *            whether to break scope restrictions using the
459      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
460      *            match public fields.
461      * @return the Field object
462      * @throws IllegalArgumentException
463      *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
464      * @throws IllegalAccessException
465      *             if the field is not made accessible
466      */
467     public static Object readDeclaredField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
468         Validate.isTrue(target != null, "target object must not be null");
469         final Class<?> cls = target.getClass();
470         final Field field = getDeclaredField(cls, fieldName, forceAccess);
471         Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls, fieldName);
472         // already forced access above, don't repeat it here:
473         return readField(field, target, false);
474     }
475 
476     /**
477      * Writes a {@code public static} {@link Field}.
478      * 
479      * @param field
480      *            to write
481      * @param value
482      *            to set
483      * @throws IllegalArgumentException
484      *             if the field is {@code null} or not {@code static}, or {@code value} is not assignable
485      * @throws IllegalAccessException
486      *             if the field is not {@code public} or is {@code final}
487      */
488     public static void writeStaticField(final Field field, final Object value) throws IllegalAccessException {
489         writeStaticField(field, value, false);
490     }
491 
492     /**
493      * Writes a static {@link Field}.
494      * 
495      * @param field
496      *            to write
497      * @param value
498      *            to set
499      * @param forceAccess
500      *            whether to break scope restrictions using the
501      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
502      *            match {@code public} fields.
503      * @throws IllegalArgumentException
504      *             if the field is {@code null} or not {@code static}, or {@code value} is not assignable
505      * @throws IllegalAccessException
506      *             if the field is not made accessible or is {@code final}
507      */
508     public static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException {
509         Validate.isTrue(field != null, "The field must not be null");
510         Validate.isTrue(Modifier.isStatic(field.getModifiers()), "The field %s.%s is not static", field.getDeclaringClass().getName(),
511                 field.getName());
512         writeField(field, (Object) null, value, forceAccess);
513     }
514 
515     /**
516      * Writes a named {@code public static} {@link Field}. Superclasses will be considered.
517      * 
518      * @param cls
519      *            {@link Class} on which the field is to be found
520      * @param fieldName
521      *            to write
522      * @param value
523      *            to set
524      * @throws IllegalArgumentException
525      *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
526      *             not {@code static}, or {@code value} is not assignable
527      * @throws IllegalAccessException
528      *             if the field is not {@code public} or is {@code final}
529      */
530     public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
531         writeStaticField(cls, fieldName, value, false);
532     }
533 
534     /**
535      * Writes a named {@code static} {@link Field}. Superclasses will be considered.
536      * 
537      * @param cls
538      *            {@link Class} on which the field is to be found
539      * @param fieldName
540      *            to write
541      * @param value
542      *            to set
543      * @param forceAccess
544      *            whether to break scope restrictions using the
545      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
546      *            match {@code public} fields.
547      * @throws IllegalArgumentException
548      *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
549      *             not {@code static}, or {@code value} is not assignable
550      * @throws IllegalAccessException
551      *             if the field is not made accessible or is {@code final}
552      */
553     public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
554             throws IllegalAccessException {
555         final Field field = getField(cls, fieldName, forceAccess);
556         Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
557         // already forced access above, don't repeat it here:
558         writeStaticField(field, value, false);
559     }
560 
561     /**
562      * Writes a named {@code public static} {@link Field}. Only the specified class will be considered.
563      * 
564      * @param cls
565      *            {@link Class} on which the field is to be found
566      * @param fieldName
567      *            to write
568      * @param value
569      *            to set
570      * @throws IllegalArgumentException
571      *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
572      *             not {@code static}, or {@code value} is not assignable
573      * @throws IllegalAccessException
574      *             if the field is not {@code public} or is {@code final}
575      */
576     public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
577         writeDeclaredStaticField(cls, fieldName, value, false);
578     }
579 
580     /**
581      * Writes a named {@code static} {@link Field}. Only the specified class will be considered.
582      * 
583      * @param cls
584      *            {@link Class} on which the field is to be found
585      * @param fieldName
586      *            to write
587      * @param value
588      *            to set
589      * @param forceAccess
590      *            whether to break scope restrictions using the {@code AccessibleObject#setAccessible(boolean)} method.
591      *            {@code false} will only match {@code public} fields.
592      * @throws IllegalArgumentException
593      *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
594      *             not {@code static}, or {@code value} is not assignable
595      * @throws IllegalAccessException
596      *             if the field is not made accessible or is {@code final}
597      */
598     public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
599             throws IllegalAccessException {
600         final Field field = getDeclaredField(cls, fieldName, forceAccess);
601         Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
602         // already forced access above, don't repeat it here:
603         writeField(field, (Object) null, value, false);
604     }
605 
606     /**
607      * Writes an accessible {@link Field}.
608      * 
609      * @param field
610      *            to write
611      * @param target
612      *            the object to call on, may be {@code null} for {@code static} fields
613      * @param value
614      *            to set
615      * @throws IllegalAccessException
616      *             if the field or target is {@code null}, the field is not accessible or is {@code final}, or
617      *             {@code value} is not assignable
618      */
619     public static void writeField(final Field field, final Object target, final Object value) throws IllegalAccessException {
620         writeField(field, target, value, false);
621     }
622 
623     /**
624      * Writes a {@link Field}.
625      * 
626      * @param field
627      *            to write
628      * @param target
629      *            the object to call on, may be {@code null} for {@code static} fields
630      * @param value
631      *            to set
632      * @param forceAccess
633      *            whether to break scope restrictions using the
634      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
635      *            match {@code public} fields.
636      * @throws IllegalArgumentException
637      *             if the field is {@code null} or {@code value} is not assignable
638      * @throws IllegalAccessException
639      *             if the field is not made accessible or is {@code final}
640      */
641     public static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess)
642             throws IllegalAccessException {
643         Validate.isTrue(field != null, "The field must not be null");
644         if (forceAccess && !field.isAccessible()) {
645             field.setAccessible(true);
646         } else {
647             MemberUtils.setAccessibleWorkaround(field);
648         }
649         field.set(target, value);
650     }
651 
652     /**
653      * Removes the final modifier from a {@link Field}.
654      * 
655      * @param field
656      *            to remove the final modifier
657      * @throws IllegalArgumentException
658      *             if the field is {@code null}
659      * @since 3.2
660      */
661     public static void removeFinalModifier(Field field) {
662         removeFinalModifier(field, true);
663     }
664 
665     /**
666      * Removes the final modifier from a {@link Field}.
667      * 
668      * @param field
669      *            to remove the final modifier
670      * @param forceAccess
671      *            whether to break scope restrictions using the
672      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
673      *            match {@code public} fields.
674      * @throws IllegalArgumentException
675      *             if the field is {@code null}
676      * @since 3.3
677      */
678     public static void removeFinalModifier(Field field, boolean forceAccess) {
679         Validate.isTrue(field != null, "The field must not be null");
680 
681         try {
682             if (Modifier.isFinal(field.getModifiers())) {
683                 // Do all JREs implement Field with a private ivar called "modifiers"?
684                 Field modifiersField = Field.class.getDeclaredField("modifiers");
685                 final boolean doForceAccess = forceAccess && !modifiersField.isAccessible();
686                 if (doForceAccess) {
687                     modifiersField.setAccessible(true);
688                 }
689                 try {
690                     modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
691                 } finally {
692                     if (doForceAccess) {
693                         modifiersField.setAccessible(false);
694                     }
695                 }
696             }
697         } catch (NoSuchFieldException ignored) {
698             // The field class contains always a modifiers field
699         } catch (IllegalAccessException ignored) {
700             // The modifiers field is made accessible
701         }
702     }
703 
704     /**
705      * Writes a {@code public} {@link Field}. Superclasses will be considered.
706      * 
707      * @param target
708      *            the object to reflect, must not be {@code null}
709      * @param fieldName
710      *            the field name to obtain
711      * @param value
712      *            to set
713      * @throws IllegalArgumentException
714      *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
715      *             {@code value} is not assignable
716      * @throws IllegalAccessException
717      *             if the field is not accessible
718      */
719     public static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
720         writeField(target, fieldName, value, false);
721     }
722 
723     /**
724      * Writes a {@link Field}. Superclasses will be considered.
725      * 
726      * @param target
727      *            the object to reflect, must not be {@code null}
728      * @param fieldName
729      *            the field name to obtain
730      * @param value
731      *            to set
732      * @param forceAccess
733      *            whether to break scope restrictions using the
734      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
735      *            match {@code public} fields.
736      * @throws IllegalArgumentException
737      *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
738      *             {@code value} is not assignable
739      * @throws IllegalAccessException
740      *             if the field is not made accessible
741      */
742     public static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
743             throws IllegalAccessException {
744         Validate.isTrue(target != null, "target object must not be null");
745         final Class<?> cls = target.getClass();
746         final Field field = getField(cls, fieldName, forceAccess);
747         Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
748         // already forced access above, don't repeat it here:
749         writeField(field, target, value, false);
750     }
751 
752     /**
753      * Writes a {@code public} {@link Field}. Only the specified class will be considered.
754      * 
755      * @param target
756      *            the object to reflect, must not be {@code null}
757      * @param fieldName
758      *            the field name to obtain
759      * @param value
760      *            to set
761      * @throws IllegalArgumentException
762      *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
763      *             {@code value} is not assignable
764      * @throws IllegalAccessException
765      *             if the field is not made accessible
766      */
767     public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
768         writeDeclaredField(target, fieldName, value, false);
769     }
770 
771     /**
772      * Writes a {@code public} {@link Field}. Only the specified class will be considered.
773      * 
774      * @param target
775      *            the object to reflect, must not be {@code null}
776      * @param fieldName
777      *            the field name to obtain
778      * @param value
779      *            to set
780      * @param forceAccess
781      *            whether to break scope restrictions using the
782      *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
783      *            match {@code public} fields.
784      * @throws IllegalArgumentException
785      *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
786      *             {@code value} is not assignable
787      * @throws IllegalAccessException
788      *             if the field is not made accessible
789      */
790     public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
791             throws IllegalAccessException {
792         Validate.isTrue(target != null, "target object must not be null");
793         final Class<?> cls = target.getClass();
794         final Field field = getDeclaredField(cls, fieldName, forceAccess);
795         Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
796         // already forced access above, don't repeat it here:
797         writeField(field, target, value, false);
798     }
799 }