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