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