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