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 */
017package org.apache.commons.lang3.reflect;
018
019import org.apache.commons.lang3.ClassUtils;
020import org.apache.commons.lang3.StringUtils;
021import org.apache.commons.lang3.Validate;
022
023import java.lang.annotation.Annotation;
024import java.lang.reflect.Field;
025import java.lang.reflect.Modifier;
026import java.util.ArrayList;
027import java.util.List;
028
029/**
030 * Utilities for working with {@link Field}s by reflection. Adapted and refactored from the dormant [reflect] Commons
031 * sandbox component.
032 * <p>
033 * The ability is provided to break the scoping restrictions coded by the programmer. This can allow fields to be
034 * changed that shouldn't be. This facility should be used with care.
035 *
036 * @since 2.5
037 */
038public class FieldUtils {
039
040    /**
041     * {@link FieldUtils} instances should NOT be constructed in standard programming.
042     * <p>
043     * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
044     * </p>
045     */
046    public FieldUtils() {
047        super();
048    }
049
050    /**
051     * Gets an accessible {@link Field} by name respecting scope. Superclasses/interfaces will be considered.
052     *
053     * @param cls
054     *            the {@link Class} to reflect, must not be {@code null}
055     * @param fieldName
056     *            the field name to obtain
057     * @return the Field object
058     * @throws IllegalArgumentException
059     *             if the class is {@code null}, or the field name is blank or empty
060     */
061    public static Field getField(final Class<?> cls, final String fieldName) {
062        final Field field = getField(cls, fieldName, false);
063        MemberUtils.setAccessibleWorkaround(field);
064        return field;
065    }
066
067    /**
068     * Gets an accessible {@link Field} by name, breaking scope if requested. Superclasses/interfaces will be
069     * considered.
070     *
071     * @param cls
072     *            the {@link Class} to reflect, must not be {@code null}
073     * @param fieldName
074     *            the field name to obtain
075     * @param forceAccess
076     *            whether to break scope restrictions using the
077     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
078     *            match {@code public} fields.
079     * @return the Field object
080     * @throws IllegalArgumentException
081     *             if the class is {@code null}, or the field name is blank or empty or is matched at multiple places
082     *             in the inheritance hierarchy
083     */
084    public static Field getField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
085        Validate.isTrue(cls != null, "The class must not be null");
086        Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
087        // FIXME is this workaround still needed? lang requires Java 6
088        // Sun Java 1.3 has a bugged implementation of getField hence we write the
089        // code ourselves
090
091        // getField() will return the Field object with the declaring class
092        // set correctly to the class that declares the field. Thus requesting the
093        // field on a subclass will return the field from the superclass.
094        //
095        // priority order for lookup:
096        // searchclass private/protected/package/public
097        // superclass protected/package/public
098        // private/different package blocks access to further superclasses
099        // implementedinterface public
100
101        // check up the superclass hierarchy
102        for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
103            try {
104                final Field field = acls.getDeclaredField(fieldName);
105                // getDeclaredField checks for non-public scopes as well
106                // and it returns accurate results
107                if (!Modifier.isPublic(field.getModifiers())) {
108                    if (forceAccess) {
109                        field.setAccessible(true);
110                    } else {
111                        continue;
112                    }
113                }
114                return field;
115            } catch (final NoSuchFieldException ex) { // NOPMD
116                // ignore
117            }
118        }
119        // check the public interface case. This must be manually searched for
120        // incase there is a public supersuperclass field hidden by a private/package
121        // superclass field.
122        Field match = null;
123        for (final Class<?> class1 : ClassUtils.getAllInterfaces(cls)) {
124            try {
125                final Field test = class1.getField(fieldName);
126                Validate.isTrue(match == null, "Reference to field %s is ambiguous relative to %s"
127                        + "; a matching field exists on two or more implemented interfaces.", fieldName, cls);
128                match = test;
129            } catch (final NoSuchFieldException ex) { // NOPMD
130                // ignore
131            }
132        }
133        return match;
134    }
135
136    /**
137     * Gets an accessible {@link Field} by name respecting scope. Only the specified class will be considered.
138     *
139     * @param cls
140     *            the {@link Class} to reflect, must not be {@code null}
141     * @param fieldName
142     *            the field name to obtain
143     * @return the Field object
144     * @throws IllegalArgumentException
145     *             if the class is {@code null}, or the field name is blank or empty
146     */
147    public static Field getDeclaredField(final Class<?> cls, final String fieldName) {
148        return getDeclaredField(cls, fieldName, false);
149    }
150
151    /**
152     * Gets an accessible {@link Field} by name, breaking scope if requested. Only the specified class will be
153     * considered.
154     *
155     * @param cls
156     *            the {@link Class} to reflect, must not be {@code null}
157     * @param fieldName
158     *            the field name to obtain
159     * @param forceAccess
160     *            whether to break scope restrictions using the
161     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
162     *            match {@code public} fields.
163     * @return the Field object
164     * @throws IllegalArgumentException
165     *             if the class is {@code null}, or the field name is blank or empty
166     */
167    public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
168        Validate.isTrue(cls != null, "The class must not be null");
169        Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
170        try {
171            // only consider the specified class by using getDeclaredField()
172            final 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 (final NoSuchFieldException e) { // NOPMD
182            // ignore
183        }
184        return null;
185    }
186
187    /**
188     * Gets all fields of the given class and its parents (if any).
189     *
190     * @param cls
191     *            the {@link Class} to query
192     * @return an array of Fields (possibly empty).
193     * @throws IllegalArgumentException
194     *             if the class is {@code null}
195     * @since 3.2
196     */
197    public static Field[] getAllFields(final Class<?> cls) {
198        final List<Field> allFieldsList = getAllFieldsList(cls);
199        return allFieldsList.toArray(new Field[allFieldsList.size()]);
200    }
201
202    /**
203     * Gets all fields of the given class and its parents (if any).
204     *
205     * @param cls
206     *            the {@link Class} to query
207     * @return an array of Fields (possibly empty).
208     * @throws IllegalArgumentException
209     *             if the class is {@code null}
210     * @since 3.2
211     */
212    public static List<Field> getAllFieldsList(final Class<?> cls) {
213        Validate.isTrue(cls != null, "The class must not be null");
214        final List<Field> allFields = new ArrayList<>();
215        Class<?> currentClass = cls;
216        while (currentClass != null) {
217            final Field[] declaredFields = currentClass.getDeclaredFields();
218            for (final Field field : declaredFields) {
219                allFields.add(field);
220            }
221            currentClass = currentClass.getSuperclass();
222        }
223        return allFields;
224    }
225
226    /**
227     * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
228     * @param cls
229     *            the {@link Class} to query
230     * @param annotationCls
231     *            the {@link Annotation} that must be present on a field to be matched
232     * @return an array of Fields (possibly empty).
233     * @throws IllegalArgumentException
234     *            if the class or annotation are {@code null}
235     * @since 3.4
236     */
237    public static Field[] getFieldsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
238        final List<Field> annotatedFieldsList = getFieldsListWithAnnotation(cls, annotationCls);
239        return annotatedFieldsList.toArray(new Field[annotatedFieldsList.size()]);
240    }
241
242    /**
243     * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
244     * @param cls
245     *            the {@link Class} to query
246     * @param annotationCls
247     *            the {@link Annotation} that must be present on a field to be matched
248     * @return a list of Fields (possibly empty).
249     * @throws IllegalArgumentException
250     *            if the class or annotation are {@code null}
251     * @since 3.4
252     */
253    public static List<Field> getFieldsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
254        Validate.isTrue(annotationCls != null, "The annotation class must not be null");
255        final List<Field> allFields = getAllFieldsList(cls);
256        final List<Field> annotatedFields = new ArrayList<>();
257        for (final Field field : allFields) {
258            if (field.getAnnotation(annotationCls) != null) {
259                annotatedFields.add(field);
260            }
261        }
262        return annotatedFields;
263    }
264
265    /**
266     * Reads an accessible {@code static} {@link Field}.
267     *
268     * @param field
269     *            to read
270     * @return the field value
271     * @throws IllegalArgumentException
272     *             if the field is {@code null}, or not {@code static}
273     * @throws IllegalAccessException
274     *             if the field is not accessible
275     */
276    public static Object readStaticField(final Field field) throws IllegalAccessException {
277        return readStaticField(field, false);
278    }
279
280    /**
281     * Reads a static {@link Field}.
282     *
283     * @param field
284     *            to read
285     * @param forceAccess
286     *            whether to break scope restrictions using the
287     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
288     * @return the field value
289     * @throws IllegalArgumentException
290     *             if the field is {@code null} or not {@code static}
291     * @throws IllegalAccessException
292     *             if the field is not made accessible
293     */
294    public static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException {
295        Validate.isTrue(field != null, "The field must not be null");
296        Validate.isTrue(Modifier.isStatic(field.getModifiers()), "The field '%s' is not static", field.getName());
297        return readField(field, (Object) null, forceAccess);
298    }
299
300    /**
301     * Reads the named {@code public static} {@link Field}. Superclasses will be considered.
302     *
303     * @param cls
304     *            the {@link Class} to reflect, must not be {@code null}
305     * @param fieldName
306     *            the field name to obtain
307     * @return the value of the field
308     * @throws IllegalArgumentException
309     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
310     *             not be found
311     * @throws IllegalAccessException
312     *             if the field is not accessible
313     */
314    public static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
315        return readStaticField(cls, fieldName, false);
316    }
317
318    /**
319     * Reads the named {@code static} {@link Field}. Superclasses will be considered.
320     *
321     * @param cls
322     *            the {@link Class} to reflect, must not be {@code null}
323     * @param fieldName
324     *            the field name to obtain
325     * @param forceAccess
326     *            whether to break scope restrictions using the
327     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
328     *            match {@code public} fields.
329     * @return the Field object
330     * @throws IllegalArgumentException
331     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
332     *             not be found
333     * @throws IllegalAccessException
334     *             if the field is not made accessible
335     */
336    public static Object readStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
337        final Field field = getField(cls, fieldName, forceAccess);
338        Validate.isTrue(field != null, "Cannot locate field '%s' on %s", fieldName, cls);
339        // already forced access above, don't repeat it here:
340        return readStaticField(field, false);
341    }
342
343    /**
344     * Gets the value of a {@code static} {@link Field} by name. The field must be {@code public}. Only the specified
345     * class will be considered.
346     *
347     * @param cls
348     *            the {@link Class} to reflect, must not be {@code null}
349     * @param fieldName
350     *            the field name to obtain
351     * @return the value of the field
352     * @throws IllegalArgumentException
353     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
354     *             not be found
355     * @throws IllegalAccessException
356     *             if the field is not accessible
357     */
358    public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
359        return readDeclaredStaticField(cls, fieldName, false);
360    }
361
362    /**
363     * Gets the value of a {@code static} {@link Field} by name. Only the specified class will be considered.
364     *
365     * @param cls
366     *            the {@link Class} to reflect, must not be {@code null}
367     * @param fieldName
368     *            the field name to obtain
369     * @param forceAccess
370     *            whether to break scope restrictions using the
371     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
372     *            match {@code public} fields.
373     * @return the Field object
374     * @throws IllegalArgumentException
375     *             if the class is {@code null}, or the field name is blank or empty, is not {@code static}, or could
376     *             not be found
377     * @throws IllegalAccessException
378     *             if the field is not made accessible
379     */
380    public static Object readDeclaredStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
381        final Field field = getDeclaredField(cls, fieldName, forceAccess);
382        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
383        // already forced access above, don't repeat it here:
384        return readStaticField(field, false);
385    }
386
387    /**
388     * Reads an accessible {@link Field}.
389     *
390     * @param field
391     *            the field to use
392     * @param target
393     *            the object to call on, may be {@code null} for {@code static} fields
394     * @return the field value
395     * @throws IllegalArgumentException
396     *             if the field is {@code null}
397     * @throws IllegalAccessException
398     *             if the field is not accessible
399     */
400    public static Object readField(final Field field, final Object target) throws IllegalAccessException {
401        return readField(field, target, false);
402    }
403
404    /**
405     * Reads a {@link Field}.
406     *
407     * @param field
408     *            the field to use
409     * @param target
410     *            the object to call on, may be {@code null} for {@code static} fields
411     * @param forceAccess
412     *            whether to break scope restrictions using the
413     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
414     * @return the field value
415     * @throws IllegalArgumentException
416     *             if the field is {@code null}
417     * @throws IllegalAccessException
418     *             if the field is not made accessible
419     */
420    public static Object readField(final Field field, final Object target, final boolean forceAccess) throws IllegalAccessException {
421        Validate.isTrue(field != null, "The field must not be null");
422        if (forceAccess && !field.isAccessible()) {
423            field.setAccessible(true);
424        } else {
425            MemberUtils.setAccessibleWorkaround(field);
426        }
427        return field.get(target);
428    }
429
430    /**
431     * Reads the named {@code public} {@link Field}. Superclasses will be considered.
432     *
433     * @param target
434     *            the object to reflect, must not be {@code null}
435     * @param fieldName
436     *            the field name to obtain
437     * @return the value of the field
438     * @throws IllegalArgumentException
439     *             if the class is {@code null}, or the field name is blank or empty or could not be found
440     * @throws IllegalAccessException
441     *             if the named field is not {@code public}
442     */
443    public static Object readField(final Object target, final String fieldName) throws IllegalAccessException {
444        return readField(target, fieldName, false);
445    }
446
447    /**
448     * Reads the named {@link Field}. Superclasses will be considered.
449     *
450     * @param target
451     *            the object to reflect, must not be {@code null}
452     * @param fieldName
453     *            the field name to obtain
454     * @param forceAccess
455     *            whether to break scope restrictions using the
456     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
457     *            match {@code public} fields.
458     * @return the field value
459     * @throws IllegalArgumentException
460     *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
461     * @throws IllegalAccessException
462     *             if the named field is not made accessible
463     */
464    public static Object readField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
465        Validate.isTrue(target != null, "target object must not be null");
466        final Class<?> cls = target.getClass();
467        final Field field = getField(cls, fieldName, forceAccess);
468        Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
469        // already forced access above, don't repeat it here:
470        return readField(field, target, false);
471    }
472
473    /**
474     * Reads the named {@code public} {@link Field}. Only the class of the specified object will be considered.
475     *
476     * @param target
477     *            the object to reflect, must not be {@code null}
478     * @param fieldName
479     *            the field name to obtain
480     * @return the value of the field
481     * @throws IllegalArgumentException
482     *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
483     * @throws IllegalAccessException
484     *             if the named field is not {@code public}
485     */
486    public static Object readDeclaredField(final Object target, final String fieldName) throws IllegalAccessException {
487        return readDeclaredField(target, fieldName, false);
488    }
489
490    /**
491     * Gets a {@link Field} value by name. Only the class of the specified object will be considered.
492     *
493     * @param target
494     *            the object to reflect, must not be {@code null}
495     * @param fieldName
496     *            the field name to obtain
497     * @param forceAccess
498     *            whether to break scope restrictions using the
499     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
500     *            match public fields.
501     * @return the Field object
502     * @throws IllegalArgumentException
503     *             if {@code target} is {@code null}, or the field name is blank or empty or could not be found
504     * @throws IllegalAccessException
505     *             if the field is not made accessible
506     */
507    public static Object readDeclaredField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
508        Validate.isTrue(target != null, "target object must not be null");
509        final Class<?> cls = target.getClass();
510        final Field field = getDeclaredField(cls, fieldName, forceAccess);
511        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls, fieldName);
512        // already forced access above, don't repeat it here:
513        return readField(field, target, false);
514    }
515
516    /**
517     * Writes a {@code public static} {@link Field}.
518     *
519     * @param field
520     *            to write
521     * @param value
522     *            to set
523     * @throws IllegalArgumentException
524     *             if the field is {@code null} or not {@code static}, or {@code value} is not assignable
525     * @throws IllegalAccessException
526     *             if the field is not {@code public} or is {@code final}
527     */
528    public static void writeStaticField(final Field field, final Object value) throws IllegalAccessException {
529        writeStaticField(field, value, false);
530    }
531
532    /**
533     * Writes a static {@link Field}.
534     *
535     * @param field
536     *            to write
537     * @param value
538     *            to set
539     * @param forceAccess
540     *            whether to break scope restrictions using the
541     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
542     *            match {@code public} fields.
543     * @throws IllegalArgumentException
544     *             if the field is {@code null} or not {@code static}, or {@code value} is not assignable
545     * @throws IllegalAccessException
546     *             if the field is not made accessible or is {@code final}
547     */
548    public static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException {
549        Validate.isTrue(field != null, "The field must not be null");
550        Validate.isTrue(Modifier.isStatic(field.getModifiers()), "The field %s.%s is not static", field.getDeclaringClass().getName(),
551                field.getName());
552        writeField(field, (Object) null, value, forceAccess);
553    }
554
555    /**
556     * Writes a named {@code public static} {@link Field}. Superclasses will be considered.
557     *
558     * @param cls
559     *            {@link Class} on which the field is to be found
560     * @param fieldName
561     *            to write
562     * @param value
563     *            to set
564     * @throws IllegalArgumentException
565     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
566     *             not {@code static}, or {@code value} is not assignable
567     * @throws IllegalAccessException
568     *             if the field is not {@code public} or is {@code final}
569     */
570    public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
571        writeStaticField(cls, fieldName, value, false);
572    }
573
574    /**
575     * Writes a named {@code static} {@link Field}. Superclasses will be considered.
576     *
577     * @param cls
578     *            {@link Class} on which the field is to be found
579     * @param fieldName
580     *            to write
581     * @param value
582     *            to set
583     * @param forceAccess
584     *            whether to break scope restrictions using the
585     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
586     *            match {@code public} fields.
587     * @throws IllegalArgumentException
588     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
589     *             not {@code static}, or {@code value} is not assignable
590     * @throws IllegalAccessException
591     *             if the field is not made accessible or is {@code final}
592     */
593    public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
594            throws IllegalAccessException {
595        final Field field = getField(cls, fieldName, forceAccess);
596        Validate.isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
597        // already forced access above, don't repeat it here:
598        writeStaticField(field, value, false);
599    }
600
601    /**
602     * Writes a named {@code public static} {@link Field}. Only the specified class will be considered.
603     *
604     * @param cls
605     *            {@link Class} on which the field is to be found
606     * @param fieldName
607     *            to write
608     * @param value
609     *            to set
610     * @throws IllegalArgumentException
611     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
612     *             not {@code static}, or {@code value} is not assignable
613     * @throws IllegalAccessException
614     *             if the field is not {@code public} or is {@code final}
615     */
616    public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
617        writeDeclaredStaticField(cls, fieldName, value, false);
618    }
619
620    /**
621     * Writes a named {@code static} {@link Field}. Only the specified class will be considered.
622     *
623     * @param cls
624     *            {@link Class} on which the field is to be found
625     * @param fieldName
626     *            to write
627     * @param value
628     *            to set
629     * @param forceAccess
630     *            whether to break scope restrictions using the {@code AccessibleObject#setAccessible(boolean)} method.
631     *            {@code false} will only match {@code public} fields.
632     * @throws IllegalArgumentException
633     *             if {@code cls} is {@code null}, the field name is blank or empty, the field cannot be located or is
634     *             not {@code static}, or {@code value} is not assignable
635     * @throws IllegalAccessException
636     *             if the field is not made accessible or is {@code final}
637     */
638    public static void writeDeclaredStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
639            throws IllegalAccessException {
640        final Field field = getDeclaredField(cls, fieldName, forceAccess);
641        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
642        // already forced access above, don't repeat it here:
643        writeField(field, (Object) null, value, false);
644    }
645
646    /**
647     * Writes an accessible {@link Field}.
648     *
649     * @param field
650     *            to write
651     * @param target
652     *            the object to call on, may be {@code null} for {@code static} fields
653     * @param value
654     *            to set
655     * @throws IllegalAccessException
656     *             if the field or target is {@code null}, the field is not accessible or is {@code final}, or
657     *             {@code value} is not assignable
658     */
659    public static void writeField(final Field field, final Object target, final Object value) throws IllegalAccessException {
660        writeField(field, target, value, false);
661    }
662
663    /**
664     * Writes a {@link Field}.
665     *
666     * @param field
667     *            to write
668     * @param target
669     *            the object to call on, may be {@code null} for {@code static} fields
670     * @param value
671     *            to set
672     * @param forceAccess
673     *            whether to break scope restrictions using the
674     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
675     *            match {@code public} fields.
676     * @throws IllegalArgumentException
677     *             if the field is {@code null} or {@code value} is not assignable
678     * @throws IllegalAccessException
679     *             if the field is not made accessible or is {@code final}
680     */
681    public static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess)
682            throws IllegalAccessException {
683        Validate.isTrue(field != null, "The field must not be null");
684        if (forceAccess && !field.isAccessible()) {
685            field.setAccessible(true);
686        } else {
687            MemberUtils.setAccessibleWorkaround(field);
688        }
689        field.set(target, value);
690    }
691
692    /**
693     * Removes the final modifier from a {@link Field}.
694     *
695     * @param field
696     *            to remove the final modifier
697     * @throws IllegalArgumentException
698     *             if the field is {@code null}
699     * @since 3.2
700     */
701    public static void removeFinalModifier(final Field field) {
702        removeFinalModifier(field, true);
703    }
704
705    /**
706     * Removes the final modifier from a {@link Field}.
707     *
708     * @param field
709     *            to remove the final modifier
710     * @param forceAccess
711     *            whether to break scope restrictions using the
712     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
713     *            match {@code public} fields.
714     * @throws IllegalArgumentException
715     *             if the field is {@code null}
716     * @since 3.3
717     */
718    public static void removeFinalModifier(final Field field, final boolean forceAccess) {
719        Validate.isTrue(field != null, "The field must not be null");
720
721        try {
722            if (Modifier.isFinal(field.getModifiers())) {
723                // Do all JREs implement Field with a private ivar called "modifiers"?
724                final Field modifiersField = Field.class.getDeclaredField("modifiers");
725                final boolean doForceAccess = forceAccess && !modifiersField.isAccessible();
726                if (doForceAccess) {
727                    modifiersField.setAccessible(true);
728                }
729                try {
730                    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
731                } finally {
732                    if (doForceAccess) {
733                        modifiersField.setAccessible(false);
734                    }
735                }
736            }
737        } catch (final NoSuchFieldException ignored) {
738            // The field class contains always a modifiers field
739        } catch (final IllegalAccessException ignored) {
740            // The modifiers field is made accessible
741        }
742    }
743
744    /**
745     * Writes a {@code public} {@link Field}. Superclasses will be considered.
746     *
747     * @param target
748     *            the object to reflect, must not be {@code null}
749     * @param fieldName
750     *            the field name to obtain
751     * @param value
752     *            to set
753     * @throws IllegalArgumentException
754     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
755     *             {@code value} is not assignable
756     * @throws IllegalAccessException
757     *             if the field is not accessible
758     */
759    public static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
760        writeField(target, fieldName, value, false);
761    }
762
763    /**
764     * Writes a {@link Field}. Superclasses will be considered.
765     *
766     * @param target
767     *            the object to reflect, must not be {@code null}
768     * @param fieldName
769     *            the field name to obtain
770     * @param value
771     *            to set
772     * @param forceAccess
773     *            whether to break scope restrictions using the
774     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
775     *            match {@code public} fields.
776     * @throws IllegalArgumentException
777     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
778     *             {@code value} is not assignable
779     * @throws IllegalAccessException
780     *             if the field is not made accessible
781     */
782    public static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
783            throws IllegalAccessException {
784        Validate.isTrue(target != null, "target object must not be null");
785        final Class<?> cls = target.getClass();
786        final Field field = getField(cls, fieldName, forceAccess);
787        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
788        // already forced access above, don't repeat it here:
789        writeField(field, target, value, false);
790    }
791
792    /**
793     * Writes a {@code public} {@link Field}. Only the specified class will be considered.
794     *
795     * @param target
796     *            the object to reflect, must not be {@code null}
797     * @param fieldName
798     *            the field name to obtain
799     * @param value
800     *            to set
801     * @throws IllegalArgumentException
802     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
803     *             {@code value} is not assignable
804     * @throws IllegalAccessException
805     *             if the field is not made accessible
806     */
807    public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
808        writeDeclaredField(target, fieldName, value, false);
809    }
810
811    /**
812     * Writes a {@code public} {@link Field}. Only the specified class will be considered.
813     *
814     * @param target
815     *            the object to reflect, must not be {@code null}
816     * @param fieldName
817     *            the field name to obtain
818     * @param value
819     *            to set
820     * @param forceAccess
821     *            whether to break scope restrictions using the
822     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
823     *            match {@code public} fields.
824     * @throws IllegalArgumentException
825     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
826     *             {@code value} is not assignable
827     * @throws IllegalAccessException
828     *             if the field is not made accessible
829     */
830    public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
831            throws IllegalAccessException {
832        Validate.isTrue(target != null, "target object must not be null");
833        final Class<?> cls = target.getClass();
834        final Field field = getDeclaredField(cls, fieldName, forceAccess);
835        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
836        // already forced access above, don't repeat it here:
837        writeField(field, target, value, false);
838    }
839}