001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3.reflect;
018
019import org.apache.commons.lang3.ClassUtils;
020import org.apache.commons.lang3.StringUtils;
021import org.apache.commons.lang3.Validate;
022
023import java.lang.annotation.Annotation;
024import java.lang.reflect.Field;
025import java.lang.reflect.Modifier;
026import java.util.ArrayList;
027import java.util.List;
028
029/**
030 * Utilities for working with {@link Field}s by reflection. Adapted and refactored from the dormant [reflect] Commons
031 * sandbox component.
032 * <p>
033 * The ability is provided to break the scoping restrictions coded by the programmer. This can allow fields to be
034 * changed that shouldn't be. This facility should be used with care.
035 * 
036 * @since 2.5
037 * @version $Id: FieldUtils.java 1620579 2014-08-26 11:53:51Z britter $
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<Field>();
216        Class<?> currentClass = cls;
217        while (currentClass != null) {
218            final Field[] declaredFields = currentClass.getDeclaredFields();
219            for (final Field field : declaredFields) {
220                allFields.add(field);
221            }
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(new Field[annotatedFieldsList.size()]);
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.isTrue(annotationCls != null, "The annotation class must not be null");
256        final List<Field> allFields = getAllFieldsList(cls);
257        final List<Field> annotatedFields = new ArrayList<Field>();
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.isTrue(field != null, "The field must not be null");
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.isTrue(field != null, "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.isTrue(field != null, "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.isTrue(field != null, "The field must not be null");
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.isTrue(target != null, "target object must not be null");
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.isTrue(target != null, "target object must not be null");
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.isTrue(field != null, "The field must not be null");
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.isTrue(field != null, "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.isTrue(field != null, "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.isTrue(field != null, "The field must not be null");
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     * @since 3.3
718     */
719    public static void removeFinalModifier(final Field field, final boolean forceAccess) {
720        Validate.isTrue(field != null, "The field must not be null");
721
722        try {
723            if (Modifier.isFinal(field.getModifiers())) {
724                // Do all JREs implement Field with a private ivar called "modifiers"?
725                final Field modifiersField = Field.class.getDeclaredField("modifiers");
726                final boolean doForceAccess = forceAccess && !modifiersField.isAccessible();
727                if (doForceAccess) {
728                    modifiersField.setAccessible(true);
729                }
730                try {
731                    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
732                } finally {
733                    if (doForceAccess) {
734                        modifiersField.setAccessible(false);
735                    }
736                }
737            }
738        } catch (final NoSuchFieldException ignored) {
739            // The field class contains always a modifiers field
740        } catch (final IllegalAccessException ignored) {
741            // The modifiers field is made accessible
742        }
743    }
744
745    /**
746     * Writes a {@code public} {@link Field}. Superclasses will be considered.
747     * 
748     * @param target
749     *            the object to reflect, must not be {@code null}
750     * @param fieldName
751     *            the field name to obtain
752     * @param value
753     *            to set
754     * @throws IllegalArgumentException
755     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
756     *             {@code value} is not assignable
757     * @throws IllegalAccessException
758     *             if the field is not accessible
759     */
760    public static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
761        writeField(target, fieldName, value, false);
762    }
763
764    /**
765     * Writes a {@link Field}. Superclasses will be considered.
766     * 
767     * @param target
768     *            the object to reflect, must not be {@code null}
769     * @param fieldName
770     *            the field name to obtain
771     * @param value
772     *            to set
773     * @param forceAccess
774     *            whether to break scope restrictions using the
775     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
776     *            match {@code public} fields.
777     * @throws IllegalArgumentException
778     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
779     *             {@code value} is not assignable
780     * @throws IllegalAccessException
781     *             if the field is not made accessible
782     */
783    public static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
784            throws IllegalAccessException {
785        Validate.isTrue(target != null, "target object must not be null");
786        final Class<?> cls = target.getClass();
787        final Field field = getField(cls, fieldName, forceAccess);
788        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
789        // already forced access above, don't repeat it here:
790        writeField(field, target, value, false);
791    }
792
793    /**
794     * Writes a {@code public} {@link Field}. Only the specified class will be considered.
795     * 
796     * @param target
797     *            the object to reflect, must not be {@code null}
798     * @param fieldName
799     *            the field name to obtain
800     * @param value
801     *            to set
802     * @throws IllegalArgumentException
803     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
804     *             {@code value} is not assignable
805     * @throws IllegalAccessException
806     *             if the field is not made accessible
807     */
808    public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
809        writeDeclaredField(target, fieldName, value, false);
810    }
811
812    /**
813     * Writes a {@code public} {@link Field}. Only the specified class will be considered.
814     * 
815     * @param target
816     *            the object to reflect, must not be {@code null}
817     * @param fieldName
818     *            the field name to obtain
819     * @param value
820     *            to set
821     * @param forceAccess
822     *            whether to break scope restrictions using the
823     *            {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
824     *            match {@code public} fields.
825     * @throws IllegalArgumentException
826     *             if {@code target} is {@code null}, {@code fieldName} is blank or empty or could not be found, or
827     *             {@code value} is not assignable
828     * @throws IllegalAccessException
829     *             if the field is not made accessible
830     */
831    public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
832            throws IllegalAccessException {
833        Validate.isTrue(target != null, "target object must not be null");
834        final Class<?> cls = target.getClass();
835        final Field field = getDeclaredField(cls, fieldName, forceAccess);
836        Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
837        // already forced access above, don't repeat it here:
838        writeField(field, target, value, false);
839    }
840}