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