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 */
017 package org.apache.commons.lang3.reflect;
018
019 import java.lang.reflect.Field;
020 import java.lang.reflect.Modifier;
021
022 import org.apache.commons.lang3.ClassUtils;
023
024 /**
025 * Utilities for working with fields by reflection. Adapted and refactored
026 * from the dormant [reflect] Commons sandbox component.
027 * <p>
028 * The ability is provided to break the scoping restrictions coded by the
029 * programmer. This can allow fields to be changed that shouldn't be. This
030 * facility should be used with care.
031 *
032 * @since 2.5
033 * @version $Id: FieldUtils.java 1144929 2011-07-10 18:26:16Z ggregory $
034 */
035 public class FieldUtils {
036
037 /**
038 * FieldUtils instances should NOT be constructed in standard programming.
039 * <p>
040 * This constructor is public to permit tools that require a JavaBean instance
041 * to operate.
042 */
043 public FieldUtils() {
044 super();
045 }
046
047 /**
048 * Gets an accessible <code>Field</code> by name respecting scope.
049 * Superclasses/interfaces will be considered.
050 *
051 * @param cls the class to reflect, must not be null
052 * @param fieldName the field name to obtain
053 * @return the Field object
054 * @throws IllegalArgumentException if the class or field name is null
055 */
056 public static Field getField(Class<?> cls, String fieldName) {
057 Field field = getField(cls, fieldName, false);
058 MemberUtils.setAccessibleWorkaround(field);
059 return field;
060 }
061
062 /**
063 * Gets an accessible <code>Field</code> by name breaking scope
064 * if requested. Superclasses/interfaces will be considered.
065 *
066 * @param cls the class to reflect, must not be null
067 * @param fieldName the field name to obtain
068 * @param forceAccess whether to break scope restrictions using the
069 * <code>setAccessible</code> method. <code>False</code> will only
070 * match public fields.
071 * @return the Field object
072 * @throws IllegalArgumentException if the class or field name is null
073 */
074 public static Field getField(final Class<?> cls, String fieldName, boolean forceAccess) {
075 if (cls == null) {
076 throw new IllegalArgumentException("The class must not be null");
077 }
078 if (fieldName == null) {
079 throw new IllegalArgumentException("The field name must not be null");
080 }
081 // Sun Java 1.3 has a bugged implementation of getField hence we write the
082 // code ourselves
083
084 // getField() will return the Field object with the declaring class
085 // set correctly to the class that declares the field. Thus requesting the
086 // field on a subclass will return the field from the superclass.
087 //
088 // priority order for lookup:
089 // searchclass private/protected/package/public
090 // superclass protected/package/public
091 // private/different package blocks access to further superclasses
092 // implementedinterface public
093
094 // check up the superclass hierarchy
095 for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
096 try {
097 Field field = acls.getDeclaredField(fieldName);
098 // getDeclaredField checks for non-public scopes as well
099 // and it returns accurate results
100 if (!Modifier.isPublic(field.getModifiers())) {
101 if (forceAccess) {
102 field.setAccessible(true);
103 } else {
104 continue;
105 }
106 }
107 return field;
108 } catch (NoSuchFieldException ex) { // NOPMD
109 // ignore
110 }
111 }
112 // check the public interface case. This must be manually searched for
113 // incase there is a public supersuperclass field hidden by a private/package
114 // superclass field.
115 Field match = null;
116 for (Class<?> class1 : ClassUtils.getAllInterfaces(cls)) {
117 try {
118 Field test = ((Class<?>) class1).getField(fieldName);
119 if (match != null) {
120 throw new IllegalArgumentException("Reference to field " + fieldName + " is ambiguous relative to " + cls
121 + "; a matching field exists on two or more implemented interfaces.");
122 }
123 match = test;
124 } catch (NoSuchFieldException ex) { // NOPMD
125 // ignore
126 }
127 }
128 return match;
129 }
130
131 /**
132 * Gets an accessible <code>Field</code> by name respecting scope.
133 * Only the specified class will be considered.
134 *
135 * @param cls the class to reflect, must not be null
136 * @param fieldName the field name to obtain
137 * @return the Field object
138 * @throws IllegalArgumentException if the class or field name is null
139 */
140 public static Field getDeclaredField(Class<?> cls, String fieldName) {
141 return getDeclaredField(cls, fieldName, false);
142 }
143
144 /**
145 * Gets an accessible <code>Field</code> by name breaking scope
146 * if requested. Only the specified class will be considered.
147 *
148 * @param cls the class to reflect, must not be null
149 * @param fieldName the field name to obtain
150 * @param forceAccess whether to break scope restrictions using the
151 * <code>setAccessible</code> method. False will only match public fields.
152 * @return the Field object
153 * @throws IllegalArgumentException if the class or field name is null
154 */
155 public static Field getDeclaredField(Class<?> cls, String fieldName, boolean forceAccess) {
156 if (cls == null) {
157 throw new IllegalArgumentException("The class must not be null");
158 }
159 if (fieldName == null) {
160 throw new IllegalArgumentException("The field name must not be null");
161 }
162 try {
163 // only consider the specified class by using getDeclaredField()
164 Field field = cls.getDeclaredField(fieldName);
165 if (!MemberUtils.isAccessible(field)) {
166 if (forceAccess) {
167 field.setAccessible(true);
168 } else {
169 return null;
170 }
171 }
172 return field;
173 } catch (NoSuchFieldException e) { // NOPMD
174 // ignore
175 }
176 return null;
177 }
178
179 /**
180 * Reads an accessible static Field.
181 * @param field to read
182 * @return the field value
183 * @throws IllegalArgumentException if the field is null or not static
184 * @throws IllegalAccessException if the field is not accessible
185 */
186 public static Object readStaticField(Field field) throws IllegalAccessException {
187 return readStaticField(field, false);
188 }
189
190 /**
191 * Reads a static Field.
192 * @param field to read
193 * @param forceAccess whether to break scope restrictions using the
194 * <code>setAccessible</code> method.
195 * @return the field value
196 * @throws IllegalArgumentException if the field is null or not static
197 * @throws IllegalAccessException if the field is not made accessible
198 */
199 public static Object readStaticField(Field field, boolean forceAccess) throws IllegalAccessException {
200 if (field == null) {
201 throw new IllegalArgumentException("The field must not be null");
202 }
203 if (!Modifier.isStatic(field.getModifiers())) {
204 throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
205 }
206 return readField(field, (Object) null, forceAccess);
207 }
208
209 /**
210 * Reads the named public static field. Superclasses will be considered.
211 * @param cls the class to reflect, must not be null
212 * @param fieldName the field name to obtain
213 * @return the value of the field
214 * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found
215 * @throws IllegalAccessException if the field is not accessible
216 */
217 public static Object readStaticField(Class<?> cls, String fieldName) throws IllegalAccessException {
218 return readStaticField(cls, fieldName, false);
219 }
220
221 /**
222 * Reads the named static field. Superclasses will be considered.
223 * @param cls the class to reflect, must not be null
224 * @param fieldName the field name to obtain
225 * @param forceAccess whether to break scope restrictions using the
226 * <code>setAccessible</code> method. <code>False</code> will only
227 * match public fields.
228 * @return the Field object
229 * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found
230 * @throws IllegalAccessException if the field is not made accessible
231 */
232 public static Object readStaticField(Class<?> cls, String fieldName, boolean forceAccess)
233 throws IllegalAccessException {
234 Field field = getField(cls, fieldName, forceAccess);
235 if (field == null) {
236 throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
237 }
238 //already forced access above, don't repeat it here:
239 return readStaticField(field, false);
240 }
241
242 /**
243 * Gets a static Field value by name. The field must be public.
244 * Only the specified class will be considered.
245 *
246 * @param cls the class to reflect, must not be null
247 * @param fieldName the field name to obtain
248 * @return the value of the field
249 * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found
250 * @throws IllegalAccessException if the field is not accessible
251 */
252 public static Object readDeclaredStaticField(Class<?> cls, String fieldName) throws IllegalAccessException {
253 return readDeclaredStaticField(cls, fieldName, false);
254 }
255
256 /**
257 * Gets a static Field value by name. Only the specified class will
258 * be considered.
259 *
260 * @param cls the class to reflect, must not be null
261 * @param fieldName the field name to obtain
262 * @param forceAccess whether to break scope restrictions using the
263 * <code>setAccessible</code> method. <code>False</code> will only
264 * match public fields.
265 * @return the Field object
266 * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found
267 * @throws IllegalAccessException if the field is not made accessible
268 */
269 public static Object readDeclaredStaticField(Class<?> cls, String fieldName, boolean forceAccess)
270 throws IllegalAccessException {
271 Field field = getDeclaredField(cls, fieldName, forceAccess);
272 if (field == null) {
273 throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
274 }
275 //already forced access above, don't repeat it here:
276 return readStaticField(field, false);
277 }
278
279 /**
280 * Reads an accessible Field.
281 * @param field the field to use
282 * @param target the object to call on, may be null for static fields
283 * @return the field value
284 * @throws IllegalArgumentException if the field is null
285 * @throws IllegalAccessException if the field is not accessible
286 */
287 public static Object readField(Field field, Object target) throws IllegalAccessException {
288 return readField(field, target, false);
289 }
290
291 /**
292 * Reads a Field.
293 * @param field the field to use
294 * @param target the object to call on, may be null for static fields
295 * @param forceAccess whether to break scope restrictions using the
296 * <code>setAccessible</code> method.
297 * @return the field value
298 * @throws IllegalArgumentException if the field is null
299 * @throws IllegalAccessException if the field is not made accessible
300 */
301 public static Object readField(Field field, Object target, boolean forceAccess) throws IllegalAccessException {
302 if (field == null) {
303 throw new IllegalArgumentException("The field must not be null");
304 }
305 if (forceAccess && !field.isAccessible()) {
306 field.setAccessible(true);
307 } else {
308 MemberUtils.setAccessibleWorkaround(field);
309 }
310 return field.get(target);
311 }
312
313 /**
314 * Reads the named public field. Superclasses will be considered.
315 * @param target the object to reflect, must not be null
316 * @param fieldName the field name to obtain
317 * @return the value of the field
318 * @throws IllegalArgumentException if the class or field name is null
319 * @throws IllegalAccessException if the named field is not public
320 */
321 public static Object readField(Object target, String fieldName) throws IllegalAccessException {
322 return readField(target, fieldName, false);
323 }
324
325 /**
326 * Reads the named field. Superclasses will be considered.
327 * @param target the object to reflect, must not be null
328 * @param fieldName the field name to obtain
329 * @param forceAccess whether to break scope restrictions using the
330 * <code>setAccessible</code> method. <code>False</code> will only
331 * match public fields.
332 * @return the field value
333 * @throws IllegalArgumentException if the class or field name is null
334 * @throws IllegalAccessException if the named field is not made accessible
335 */
336 public static Object readField(Object target, String fieldName, boolean forceAccess) throws IllegalAccessException {
337 if (target == null) {
338 throw new IllegalArgumentException("target object must not be null");
339 }
340 Class<?> cls = target.getClass();
341 Field field = getField(cls, fieldName, forceAccess);
342 if (field == null) {
343 throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
344 }
345 //already forced access above, don't repeat it here:
346 return readField(field, target);
347 }
348
349 /**
350 * Reads the named public field. Only the class of the specified object will be considered.
351 * @param target the object to reflect, must not be null
352 * @param fieldName the field name to obtain
353 * @return the value of the field
354 * @throws IllegalArgumentException if the class or field name is null
355 * @throws IllegalAccessException if the named field is not public
356 */
357 public static Object readDeclaredField(Object target, String fieldName) throws IllegalAccessException {
358 return readDeclaredField(target, fieldName, false);
359 }
360
361 /**
362 * <p<>Gets a Field value by name. Only the class of the specified
363 * object will be considered.
364 *
365 * @param target the object to reflect, must not be null
366 * @param fieldName the field name to obtain
367 * @param forceAccess whether to break scope restrictions using the
368 * <code>setAccessible</code> method. <code>False</code> will only
369 * match public fields.
370 * @return the Field object
371 * @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
372 * @throws IllegalAccessException if the field is not made accessible
373 */
374 public static Object readDeclaredField(Object target, String fieldName, boolean forceAccess)
375 throws IllegalAccessException {
376 if (target == null) {
377 throw new IllegalArgumentException("target object must not be null");
378 }
379 Class<?> cls = target.getClass();
380 Field field = getDeclaredField(cls, fieldName, forceAccess);
381 if (field == null) {
382 throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
383 }
384 //already forced access above, don't repeat it here:
385 return readField(field, target);
386 }
387
388 /**
389 * Writes a public static Field.
390 * @param field to write
391 * @param value to set
392 * @throws IllegalArgumentException if the field is null or not static
393 * @throws IllegalAccessException if the field is not public or is final
394 */
395 public static void writeStaticField(Field field, Object value) throws IllegalAccessException {
396 writeStaticField(field, value, false);
397 }
398
399 /**
400 * Writes a static Field.
401 * @param field to write
402 * @param value to set
403 * @param forceAccess whether to break scope restrictions using the
404 * <code>setAccessible</code> method. <code>False</code> will only
405 * match public fields.
406 * @throws IllegalArgumentException if the field is null or not static
407 * @throws IllegalAccessException if the field is not made accessible or is final
408 */
409 public static void writeStaticField(Field field, Object value, boolean forceAccess) throws IllegalAccessException {
410 if (field == null) {
411 throw new IllegalArgumentException("The field must not be null");
412 }
413 if (!Modifier.isStatic(field.getModifiers())) {
414 throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
415 }
416 writeField(field, (Object) null, value, forceAccess);
417 }
418
419 /**
420 * Writes a named public static Field. Superclasses will be considered.
421 * @param cls Class on which the Field is to be found
422 * @param fieldName to write
423 * @param value to set
424 * @throws IllegalArgumentException if the field cannot be located or is not static
425 * @throws IllegalAccessException if the field is not public or is final
426 */
427 public static void writeStaticField(Class<?> cls, String fieldName, Object value) throws IllegalAccessException {
428 writeStaticField(cls, fieldName, value, false);
429 }
430
431 /**
432 * Writes a named static Field. Superclasses will be considered.
433 * @param cls Class on which the Field is to be found
434 * @param fieldName to write
435 * @param value to set
436 * @param forceAccess whether to break scope restrictions using the
437 * <code>setAccessible</code> method. <code>False</code> will only
438 * match public fields.
439 * @throws IllegalArgumentException if the field cannot be located or is not static
440 * @throws IllegalAccessException if the field is not made accessible or is final
441 */
442 public static void writeStaticField(Class<?> cls, String fieldName, Object value, boolean forceAccess)
443 throws IllegalAccessException {
444 Field field = getField(cls, fieldName, forceAccess);
445 if (field == null) {
446 throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
447 }
448 //already forced access above, don't repeat it here:
449 writeStaticField(field, value);
450 }
451
452 /**
453 * Writes a named public static Field. Only the specified class will be considered.
454 * @param cls Class on which the Field is to be found
455 * @param fieldName to write
456 * @param value to set
457 * @throws IllegalArgumentException if the field cannot be located or is not static
458 * @throws IllegalAccessException if the field is not public or is final
459 */
460 public static void writeDeclaredStaticField(Class<?> cls, String fieldName, Object value)
461 throws IllegalAccessException {
462 writeDeclaredStaticField(cls, fieldName, value, false);
463 }
464
465 /**
466 * Writes a named static Field. Only the specified class will be considered.
467 * @param cls Class on which the Field is to be found
468 * @param fieldName to write
469 * @param value to set
470 * @param forceAccess whether to break scope restrictions using the
471 * <code>setAccessible</code> method. <code>False</code> will only
472 * match public fields.
473 * @throws IllegalArgumentException if the field cannot be located or is not static
474 * @throws IllegalAccessException if the field is not made accessible or is final
475 */
476 public static void writeDeclaredStaticField(Class<?> cls, String fieldName, Object value, boolean forceAccess)
477 throws IllegalAccessException {
478 Field field = getDeclaredField(cls, fieldName, forceAccess);
479 if (field == null) {
480 throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
481 }
482 //already forced access above, don't repeat it here:
483 writeField(field, (Object) null, value);
484 }
485
486 /**
487 * Writes an accessible field.
488 * @param field to write
489 * @param target the object to call on, may be null for static fields
490 * @param value to set
491 * @throws IllegalArgumentException if the field is null
492 * @throws IllegalAccessException if the field is not accessible or is final
493 */
494 public static void writeField(Field field, Object target, Object value) throws IllegalAccessException {
495 writeField(field, target, value, false);
496 }
497
498 /**
499 * Writes a field.
500 * @param field to write
501 * @param target the object to call on, may be null for static fields
502 * @param value to set
503 * @param forceAccess whether to break scope restrictions using the
504 * <code>setAccessible</code> method. <code>False</code> will only
505 * match public fields.
506 * @throws IllegalArgumentException if the field is null
507 * @throws IllegalAccessException if the field is not made accessible or is final
508 */
509 public static void writeField(Field field, Object target, Object value, boolean forceAccess)
510 throws IllegalAccessException {
511 if (field == null) {
512 throw new IllegalArgumentException("The field must not be null");
513 }
514 if (forceAccess && !field.isAccessible()) {
515 field.setAccessible(true);
516 } else {
517 MemberUtils.setAccessibleWorkaround(field);
518 }
519 field.set(target, value);
520 }
521
522 /**
523 * Writes a public field. Superclasses will be considered.
524 * @param target the object to reflect, must not be null
525 * @param fieldName the field name to obtain
526 * @param value to set
527 * @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
528 * @throws IllegalAccessException if the field is not accessible
529 */
530 public static void writeField(Object target, String fieldName, Object value) throws IllegalAccessException {
531 writeField(target, fieldName, value, false);
532 }
533
534 /**
535 * Writes a field. Superclasses will be considered.
536 * @param target the object to reflect, must not be null
537 * @param fieldName the field name to obtain
538 * @param value to set
539 * @param forceAccess whether to break scope restrictions using the
540 * <code>setAccessible</code> method. <code>False</code> will only
541 * match public fields.
542 * @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
543 * @throws IllegalAccessException if the field is not made accessible
544 */
545 public static void writeField(Object target, String fieldName, Object value, boolean forceAccess)
546 throws IllegalAccessException {
547 if (target == null) {
548 throw new IllegalArgumentException("target object must not be null");
549 }
550 Class<?> cls = target.getClass();
551 Field field = getField(cls, fieldName, forceAccess);
552 if (field == null) {
553 throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
554 }
555 //already forced access above, don't repeat it here:
556 writeField(field, target, value);
557 }
558
559 /**
560 * Writes a public field. Only the specified class will be considered.
561 * @param target the object to reflect, must not be null
562 * @param fieldName the field name to obtain
563 * @param value to set
564 * @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
565 * @throws IllegalAccessException if the field is not made accessible
566 */
567 public static void writeDeclaredField(Object target, String fieldName, Object value) throws IllegalAccessException {
568 writeDeclaredField(target, fieldName, value, false);
569 }
570
571 /**
572 * Writes a public field. Only the specified class will be considered.
573 * @param target the object to reflect, must not be null
574 * @param fieldName the field name to obtain
575 * @param value to set
576 * @param forceAccess whether to break scope restrictions using the
577 * <code>setAccessible</code> method. <code>False</code> will only
578 * match public fields.
579 * @throws IllegalArgumentException if <code>target</code> or <code>fieldName</code> is null
580 * @throws IllegalAccessException if the field is not made accessible
581 */
582 public static void writeDeclaredField(Object target, String fieldName, Object value, boolean forceAccess)
583 throws IllegalAccessException {
584 if (target == null) {
585 throw new IllegalArgumentException("target object must not be null");
586 }
587 Class<?> cls = target.getClass();
588 Field field = getDeclaredField(cls, fieldName, forceAccess);
589 if (field == null) {
590 throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName);
591 }
592 //already forced access above, don't repeat it here:
593 writeField(field, target, value);
594 }
595 }