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