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