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