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 }