1 /* 2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.fxml; 27 28 import java.lang.reflect.InvocationTargetException; 29 import java.lang.reflect.Method; 30 import java.lang.reflect.Modifier; 31 import java.lang.reflect.ParameterizedType; 32 import java.lang.reflect.Type; 33 import java.lang.reflect.TypeVariable; 34 import java.math.BigDecimal; 35 import java.math.BigInteger; 36 import java.util.*; 37 38 import java.lang.reflect.*; 39 import java.security.AccessController; 40 import java.security.PrivilegedAction; 41 42 import javafx.beans.value.ObservableValue; 43 import sun.reflect.misc.FieldUtil; 44 import sun.reflect.misc.MethodUtil; 45 import sun.reflect.misc.ReflectUtil; 46 47 /** 48 * Exposes Java Bean properties of an object via the {@link Map} interface. 49 * A call to {@link Map#get(Object)} invokes the getter for the corresponding 50 * property, and a call to {@link Map#put(Object, Object)} invokes the 51 * property's setter. Appending a "Property" suffix to the key returns the 52 * corresponding property model. 53 */ 54 public class BeanAdapter extends AbstractMap<String, Object> { 55 private final Object bean; 56 57 private static class MethodCache { 58 private final Map<String, List<Method>> methods; 59 private final MethodCache nextClassCache; 60 61 private MethodCache(Map<String, List<Method>> methods, MethodCache nextClassCache) { 62 this.methods = methods; 63 this.nextClassCache = nextClassCache; 64 } 65 66 private Method getMethod(String name, Class<?>... parameterTypes) { 67 List<Method> namedMethods = methods.get(name); 68 if (namedMethods != null) { 69 for (int i = 0; i < namedMethods.size(); i++) { 70 Method namedMethod = namedMethods.get(i); 71 if (namedMethod.getName().equals(name) 72 && Arrays.equals(namedMethod.getParameterTypes(), parameterTypes)) { 73 return namedMethod; 74 } 75 } 76 } 77 78 return nextClassCache != null ? nextClassCache.getMethod(name, parameterTypes) : null; 79 } 80 81 } 82 83 private static final HashMap<Class<?>, MethodCache> globalMethodCache = 84 new HashMap<>(); 85 86 private final MethodCache localCache; 87 88 public static final String GET_PREFIX = "get"; 89 public static final String IS_PREFIX = "is"; 90 public static final String SET_PREFIX = "set"; 91 public static final String PROPERTY_SUFFIX = "Property"; 92 93 public static final String VALUE_OF_METHOD_NAME = "valueOf"; 94 95 /** 96 * Creates a new Bean adapter. 97 * 98 * @param bean 99 * The Bean object to wrap. 100 */ 101 public BeanAdapter(Object bean) { 102 this.bean = bean; 103 104 localCache = getClassMethodCache(bean.getClass()); 105 } 106 107 private static MethodCache getClassMethodCache(final Class<?> type) { 108 if (type == Object.class) { 109 return null; 110 } 111 MethodCache classMethodCache; 112 synchronized (globalMethodCache) { 113 if ((classMethodCache = globalMethodCache.get(type)) != null) { 114 return classMethodCache; 115 } 116 Map<String, List<Method>> classMethods = new HashMap<>(); 117 118 ReflectUtil.checkPackageAccess(type); 119 if (Modifier.isPublic(type.getModifiers())) { 120 // only interested in public methods in public classes in 121 // non-restricted packages 122 final Method[] declaredMethods = 123 AccessController.doPrivileged( 124 new PrivilegedAction<Method[]>() { 125 @Override 126 public Method[] run() { 127 return type.getDeclaredMethods(); 128 } 129 }); 130 for (int i = 0; i < declaredMethods.length; i++) { 131 Method method = declaredMethods[i]; 132 int modifiers = method.getModifiers(); 133 134 if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { 135 String name = method.getName(); 136 List<Method> namedMethods = classMethods.get(name); 137 138 if (namedMethods == null) { 139 namedMethods = new ArrayList<>(); 140 classMethods.put(name, namedMethods); 141 } 142 143 namedMethods.add(method); 144 } 145 } 146 } 147 MethodCache cache = new MethodCache(classMethods, getClassMethodCache(type.getSuperclass())); 148 globalMethodCache.put(type, cache); 149 return cache; 150 } 151 } 152 153 /** 154 * Returns the Bean object this adapter wraps. 155 * 156 * @return 157 * The Bean object, or <tt>null</tt> if no Bean has been set. 158 */ 159 public Object getBean() { 160 return bean; 161 } 162 163 private Method getGetterMethod(String key) { 164 Method getterMethod = localCache.getMethod(getMethodName(GET_PREFIX, key)); 165 166 if (getterMethod == null) { 167 getterMethod = localCache.getMethod(getMethodName(IS_PREFIX, key)); 168 } 169 170 return getterMethod; 171 } 172 173 private Method getSetterMethod(String key) { 174 Class<?> type = getType(key); 175 176 if (type == null) { 177 throw new UnsupportedOperationException("Cannot determine type for property."); 178 } 179 180 return localCache.getMethod(getMethodName(SET_PREFIX, key), type); 181 } 182 183 private static String getMethodName(String prefix, String key) { 184 return prefix + Character.toUpperCase(key.charAt(0)) + key.substring(1); 185 } 186 187 /** 188 * Invokes the getter method for the given property. 189 * 190 * @param key 191 * The property name. 192 * 193 * @return 194 * The value returned by the method, or <tt>null</tt> if no such method 195 * exists. 196 */ 197 @Override 198 public Object get(Object key) { 199 if (key == null) { 200 throw new NullPointerException(); 201 } 202 203 return get(key.toString()); 204 } 205 206 private Object get(String key) { 207 Method getterMethod = key.endsWith(PROPERTY_SUFFIX) ? localCache.getMethod(key) : getGetterMethod(key); 208 209 Object value; 210 if (getterMethod != null) { 211 try { 212 value = ModuleHelper.invoke(getterMethod, bean, (Object[]) null); 213 } catch (IllegalAccessException exception) { 214 throw new RuntimeException(exception); 215 } catch (InvocationTargetException exception) { 216 throw new RuntimeException(exception); 217 } 218 } else { 219 value = null; 220 } 221 222 return value; 223 } 224 225 /** 226 * Invokes a setter method for the given property. The 227 * {@link #coerce(Object, Class)} method is used as needed to attempt to 228 * convert a given value to the property type, as defined by the return 229 * value of the getter method. 230 * 231 * @param key 232 * The property name. 233 * 234 * @param value 235 * The new property value. 236 * 237 * @return 238 * Returns <tt>null</tt>, since returning the previous value would require 239 * an unnecessary call to the getter method. 240 * 241 * @throws PropertyNotFoundException 242 * If the given property does not exist or is read-only. 243 */ 244 @Override 245 public Object put(String key, Object value) { 246 if (key == null) { 247 throw new NullPointerException(); 248 } 249 250 Method setterMethod = getSetterMethod(key); 251 252 if (setterMethod == null) { 253 throw new PropertyNotFoundException("Property \"" + key + "\" does not exist" 254 + " or is read-only."); 255 } 256 257 try { 258 ModuleHelper.invoke(setterMethod, bean, new Object[] { coerce(value, getType(key)) }); 259 } catch (IllegalAccessException exception) { 260 throw new RuntimeException(exception); 261 } catch (InvocationTargetException exception) { 262 throw new RuntimeException(exception); 263 } 264 265 return null; 266 } 267 268 /** 269 * Verifies the existence of a property. 270 * 271 * @param key 272 * The property name. 273 * 274 * @return 275 * <tt>true</tt> if the property exists; <tt>false</tt>, otherwise. 276 */ 277 @Override 278 public boolean containsKey(Object key) { 279 if (key == null) { 280 throw new NullPointerException(); 281 } 282 283 return getType(key.toString()) != null; 284 } 285 286 @Override 287 public Set<Entry<String, Object>> entrySet() { 288 throw new UnsupportedOperationException(); 289 } 290 291 /** 292 * Tests the mutability of a property. 293 * 294 * @param key 295 * The property name. 296 * 297 * @return 298 * <tt>true</tt> if the property is read-only; <tt>false</tt>, otherwise. 299 */ 300 public boolean isReadOnly(String key) { 301 if (key == null) { 302 throw new NullPointerException(); 303 } 304 305 return getSetterMethod(key) == null; 306 } 307 308 /** 309 * Returns the property model for the given property. 310 * 311 * @param key 312 * The property name. 313 * 314 * @return 315 * The named property model, or <tt>null</tt> if no such property exists. 316 */ 317 @SuppressWarnings("unchecked") 318 public <T> ObservableValue<T> getPropertyModel(String key) { 319 if (key == null) { 320 throw new NullPointerException(); 321 } 322 323 return (ObservableValue<T>)get(key + BeanAdapter.PROPERTY_SUFFIX); 324 } 325 326 /** 327 * Returns the type of a property. 328 * 329 * @param key 330 * The property name. 331 */ 332 public Class<?> getType(String key) { 333 if (key == null) { 334 throw new NullPointerException(); 335 } 336 337 Method getterMethod = getGetterMethod(key); 338 339 return (getterMethod == null) ? null : getterMethod.getReturnType(); 340 } 341 342 /** 343 * Returns the generic type of a property. 344 * 345 * @param key 346 * The property name. 347 */ 348 public Type getGenericType(String key) { 349 if (key == null) { 350 throw new NullPointerException(); 351 } 352 353 Method getterMethod = getGetterMethod(key); 354 355 return (getterMethod == null) ? null : getterMethod.getGenericReturnType(); 356 } 357 358 @Override 359 public boolean equals(Object object) { 360 boolean equals = false; 361 362 if (object instanceof BeanAdapter) { 363 BeanAdapter beanAdapter = (BeanAdapter)object; 364 equals = (bean == beanAdapter.bean); 365 } 366 367 return equals; 368 } 369 370 @Override 371 public int hashCode() { 372 return (bean == null) ? -1 : bean.hashCode(); 373 } 374 375 /** 376 * Coerces a value to a given type. 377 * 378 * @param value 379 * @param type 380 * 381 * @return 382 * The coerced value. 383 */ 384 @SuppressWarnings("unchecked") 385 public static <T> T coerce(Object value, Class<? extends T> type) { 386 if (type == null) { 387 throw new NullPointerException(); 388 } 389 390 Object coercedValue = null; 391 392 if (value == null) { 393 // Null values can only be coerced to null 394 coercedValue = null; 395 } else if (type.isAssignableFrom(value.getClass())) { 396 // Value doesn't require coercion 397 coercedValue = value; 398 } else if (type == Boolean.class 399 || type == Boolean.TYPE) { 400 coercedValue = Boolean.valueOf(value.toString()); 401 } else if (type == Character.class 402 || type == Character.TYPE) { 403 coercedValue = value.toString().charAt(0); 404 } else if (type == Byte.class 405 || type == Byte.TYPE) { 406 if (value instanceof Number) { 407 coercedValue = ((Number)value).byteValue(); 408 } else { 409 coercedValue = Byte.valueOf(value.toString()); 410 } 411 } else if (type == Short.class 412 || type == Short.TYPE) { 413 if (value instanceof Number) { 414 coercedValue = ((Number)value).shortValue(); 415 } else { 416 coercedValue = Short.valueOf(value.toString()); 417 } 418 } else if (type == Integer.class 419 || type == Integer.TYPE) { 420 if (value instanceof Number) { 421 coercedValue = ((Number)value).intValue(); 422 } else { 423 coercedValue = Integer.valueOf(value.toString()); 424 } 425 } else if (type == Long.class 426 || type == Long.TYPE) { 427 if (value instanceof Number) { 428 coercedValue = ((Number)value).longValue(); 429 } else { 430 coercedValue = Long.valueOf(value.toString()); 431 } 432 } else if (type == BigInteger.class) { 433 if (value instanceof Number) { 434 coercedValue = BigInteger.valueOf(((Number)value).longValue()); 435 } else { 436 coercedValue = new BigInteger(value.toString()); 437 } 438 } else if (type == Float.class 439 || type == Float.TYPE) { 440 if (value instanceof Number) { 441 coercedValue = ((Number)value).floatValue(); 442 } else { 443 coercedValue = Float.valueOf(value.toString()); 444 } 445 } else if (type == Double.class 446 || type == Double.TYPE) { 447 if (value instanceof Number) { 448 coercedValue = ((Number)value).doubleValue(); 449 } else { 450 coercedValue = Double.valueOf(value.toString()); 451 } 452 } else if (type == Number.class) { 453 String number = value.toString(); 454 if (number.contains(".")) { 455 coercedValue = Double.valueOf(number); 456 } else { 457 coercedValue = Long.valueOf(number); 458 } 459 } else if (type == BigDecimal.class) { 460 if (value instanceof Number) { 461 coercedValue = BigDecimal.valueOf(((Number)value).doubleValue()); 462 } else { 463 coercedValue = new BigDecimal(value.toString()); 464 } 465 } else if (type == Class.class) { 466 try { 467 final String className = value.toString(); 468 ReflectUtil.checkPackageAccess(className); 469 final ClassLoader cl = Thread.currentThread().getContextClassLoader(); 470 coercedValue = Class.forName( 471 className, 472 false, 473 cl); 474 } catch (ClassNotFoundException exception) { 475 throw new IllegalArgumentException(exception); 476 } 477 } else { 478 Class<?> valueType = value.getClass(); 479 Method valueOfMethod = null; 480 481 while (valueOfMethod == null 482 && valueType != null) { 483 try { 484 ReflectUtil.checkPackageAccess(type); 485 valueOfMethod = type.getDeclaredMethod(VALUE_OF_METHOD_NAME, valueType); 486 } catch (NoSuchMethodException exception) { 487 // No-op 488 } 489 490 if (valueOfMethod == null) { 491 valueType = valueType.getSuperclass(); 492 } 493 } 494 495 if (valueOfMethod == null) { 496 throw new IllegalArgumentException("Unable to coerce " + value + " to " + type + "."); 497 } 498 499 if (type.isEnum() 500 && value instanceof String 501 && Character.isLowerCase(((String)value).charAt(0))) { 502 value = toAllCaps((String)value); 503 } 504 505 try { 506 coercedValue = ModuleHelper.invoke(valueOfMethod, null, new Object[] { value }); 507 } catch (IllegalAccessException exception) { 508 throw new RuntimeException(exception); 509 } catch (InvocationTargetException exception) { 510 throw new RuntimeException(exception); 511 } catch (SecurityException exception) { 512 throw new RuntimeException(exception); 513 } 514 } 515 516 return (T)coercedValue; 517 } 518 519 /** 520 * Invokes the static getter method for the given property. 521 * 522 * @param target 523 * The object to which the property is attached. 524 * 525 * @param sourceType 526 * The class that defines the property. 527 * 528 * @param key 529 * The property name. 530 * 531 * @return 532 * The value returned by the method, or <tt>null</tt> if no such method 533 * exists. 534 */ 535 @SuppressWarnings("unchecked") 536 public static <T> T get(Object target, Class<?> sourceType, String key) { 537 T value = null; 538 539 Class<?> targetType = target.getClass(); 540 Method getterMethod = getStaticGetterMethod(sourceType, key, targetType); 541 542 if (getterMethod != null) { 543 try { 544 value = (T) ModuleHelper.invoke(getterMethod, null, new Object[] { target } ); 545 } catch (InvocationTargetException exception) { 546 throw new RuntimeException(exception); 547 } catch (IllegalAccessException exception) { 548 throw new RuntimeException(exception); 549 } 550 } 551 552 return value; 553 } 554 555 /** 556 * Invokes a static setter method for the given property. If the value is 557 * <tt>null</tt> or there is no explicit setter for a given type, the 558 * {@link #coerce(Object, Class)} method is used to attempt to convert the 559 * value to the actual property type (defined by the return value of the 560 * getter method). 561 * 562 * @param target 563 * The object to which the property is or will be attached. 564 * 565 * @param sourceType 566 * The class that defines the property. 567 * 568 * @param key 569 * The property name. 570 * 571 * @param value 572 * The new property value. 573 * 574 * @throws PropertyNotFoundException 575 * If the given static property does not exist or is read-only. 576 */ 577 public static void put(Object target, Class<?> sourceType, String key, Object value) { 578 Class<?> targetType = target.getClass(); 579 580 Method setterMethod = null; 581 if (value != null) { 582 setterMethod = getStaticSetterMethod(sourceType, key, value.getClass(), targetType); 583 } 584 585 if (setterMethod == null) { 586 // Get the property type and attempt to coerce the value to it 587 Class<?> propertyType = getType(sourceType, key, targetType); 588 589 if (propertyType != null) { 590 setterMethod = getStaticSetterMethod(sourceType, key, propertyType, targetType); 591 value = coerce(value, propertyType); 592 } 593 } 594 595 if (setterMethod == null) { 596 throw new PropertyNotFoundException("Static property \"" + key + "\" does not exist" 597 + " or is read-only."); 598 } 599 600 // Invoke the setter 601 try { 602 ModuleHelper.invoke(setterMethod, null, new Object[] { target, value }); 603 } catch (InvocationTargetException exception) { 604 throw new RuntimeException(exception); 605 } catch (IllegalAccessException exception) { 606 throw new RuntimeException(exception); 607 } 608 } 609 610 /** 611 * Tests the existence of a static property. 612 * 613 * @param sourceType 614 * The class that defines the property. 615 * 616 * @param key 617 * The property name. 618 * 619 * @param targetType 620 * The type of the object to which the property applies. 621 * 622 * @return 623 * <tt>true</tt> if the property exists; <tt>false</tt>, otherwise. 624 */ 625 public static boolean isDefined(Class<?> sourceType, String key, Class<?> targetType) { 626 return (getStaticGetterMethod(sourceType, key, targetType) != null); 627 } 628 629 /** 630 * Returns the type of a static property. 631 * 632 * @param sourceType 633 * The class that defines the property. 634 * 635 * @param key 636 * The property name. 637 * 638 * @param targetType 639 * The type of the object to which the property applies. 640 */ 641 public static Class<?> getType(Class<?> sourceType, String key, Class<?> targetType) { 642 Method getterMethod = getStaticGetterMethod(sourceType, key, targetType); 643 return (getterMethod == null) ? null : getterMethod.getReturnType(); 644 } 645 646 /** 647 * Returns the generic type of a static property. 648 * 649 * @param sourceType 650 * The class that defines the property. 651 * 652 * @param key 653 * The property name. 654 * 655 * @param targetType 656 * The type of the object to which the property applies. 657 */ 658 public static Type getGenericType(Class<?> sourceType, String key, Class<?> targetType) { 659 Method getterMethod = getStaticGetterMethod(sourceType, key, targetType); 660 return (getterMethod == null) ? null : getterMethod.getGenericReturnType(); 661 } 662 663 /** 664 * Determines the type of a list item. 665 * 666 * @param listType 667 */ 668 public static Class<?> getListItemType(Type listType) { 669 Type itemType = getGenericListItemType(listType); 670 671 if (itemType instanceof ParameterizedType) { 672 itemType = ((ParameterizedType)itemType).getRawType(); 673 } 674 675 return (Class<?>)itemType; 676 } 677 678 /** 679 * Determines the type of a map value. 680 * 681 * @param mapType 682 */ 683 public static Class<?> getMapValueType(Type mapType) { 684 Type valueType = getGenericMapValueType(mapType); 685 686 if (valueType instanceof ParameterizedType) { 687 valueType = ((ParameterizedType)valueType).getRawType(); 688 } 689 690 return (Class<?>)valueType; 691 } 692 693 /** 694 * Determines the type of a list item. 695 * 696 * @param listType 697 */ 698 public static Type getGenericListItemType(Type listType) { 699 Type itemType = null; 700 701 Type parentType = listType; 702 while (parentType != null) { 703 if (parentType instanceof ParameterizedType) { 704 ParameterizedType parameterizedType = (ParameterizedType)parentType; 705 Class<?> rawType = (Class<?>)parameterizedType.getRawType(); 706 707 if (List.class.isAssignableFrom(rawType)) { 708 itemType = parameterizedType.getActualTypeArguments()[0]; 709 } 710 711 break; 712 } 713 714 Class<?> classType = (Class<?>)parentType; 715 Type[] genericInterfaces = classType.getGenericInterfaces(); 716 717 for (int i = 0; i < genericInterfaces.length; i++) { 718 Type genericInterface = genericInterfaces[i]; 719 720 if (genericInterface instanceof ParameterizedType) { 721 ParameterizedType parameterizedType = (ParameterizedType)genericInterface; 722 Class<?> interfaceType = (Class<?>)parameterizedType.getRawType(); 723 724 if (List.class.isAssignableFrom(interfaceType)) { 725 itemType = parameterizedType.getActualTypeArguments()[0]; 726 break; 727 } 728 } 729 } 730 731 if (itemType != null) { 732 break; 733 } 734 735 parentType = classType.getGenericSuperclass(); 736 } 737 738 if (itemType != null && itemType instanceof TypeVariable<?>) { 739 itemType = Object.class; 740 } 741 742 return itemType; 743 } 744 745 /** 746 * Determines the type of a map value. 747 * 748 * @param mapType 749 */ 750 public static Type getGenericMapValueType(Type mapType) { 751 Type valueType = null; 752 753 Type parentType = mapType; 754 while (parentType != null) { 755 if (parentType instanceof ParameterizedType) { 756 ParameterizedType parameterizedType = (ParameterizedType)parentType; 757 Class<?> rawType = (Class<?>)parameterizedType.getRawType(); 758 759 if (Map.class.isAssignableFrom(rawType)) { 760 valueType = parameterizedType.getActualTypeArguments()[1]; 761 } 762 763 break; 764 } 765 766 Class<?> classType = (Class<?>)parentType; 767 Type[] genericInterfaces = classType.getGenericInterfaces(); 768 769 for (int i = 0; i < genericInterfaces.length; i++) { 770 Type genericInterface = genericInterfaces[i]; 771 772 if (genericInterface instanceof ParameterizedType) { 773 ParameterizedType parameterizedType = (ParameterizedType)genericInterface; 774 Class<?> interfaceType = (Class<?>)parameterizedType.getRawType(); 775 776 if (Map.class.isAssignableFrom(interfaceType)) { 777 valueType = parameterizedType.getActualTypeArguments()[1]; 778 break; 779 } 780 } 781 } 782 783 if (valueType != null) { 784 break; 785 } 786 787 parentType = classType.getGenericSuperclass(); 788 } 789 790 if (valueType != null && valueType instanceof TypeVariable<?>) { 791 valueType = Object.class; 792 } 793 794 return valueType; 795 } 796 797 /** 798 * Returns the value of a named constant. 799 * 800 * @param type 801 * The type that defines the constant. 802 * 803 * @param name 804 * The name of the constant. 805 */ 806 public static Object getConstantValue(Class<?> type, String name) { 807 if (type == null) { 808 throw new IllegalArgumentException(); 809 } 810 811 if (name == null) { 812 throw new IllegalArgumentException(); 813 } 814 815 Field field; 816 try { 817 field = FieldUtil.getField(type, name); 818 } catch (NoSuchFieldException exception) { 819 throw new IllegalArgumentException(exception); 820 } 821 822 int fieldModifiers = field.getModifiers(); 823 if ((fieldModifiers & Modifier.STATIC) == 0 824 || (fieldModifiers & Modifier.FINAL) == 0) { 825 throw new IllegalArgumentException("Field is not a constant."); 826 } 827 828 Object value; 829 try { 830 value = field.get(null); 831 } catch (IllegalAccessException exception) { 832 throw new IllegalArgumentException(exception); 833 } 834 835 return value; 836 } 837 838 private static Method getStaticGetterMethod(Class<?> sourceType, String key, 839 Class<?> targetType) { 840 if (sourceType == null) { 841 throw new NullPointerException(); 842 } 843 844 if (key == null) { 845 throw new NullPointerException(); 846 } 847 848 Method method = null; 849 850 if (targetType != null) { 851 key = Character.toUpperCase(key.charAt(0)) + key.substring(1); 852 853 String getMethodName = GET_PREFIX + key; 854 String isMethodName = IS_PREFIX + key; 855 856 try { 857 method = MethodUtil.getMethod(sourceType, getMethodName, new Class[] { targetType }); 858 } catch (NoSuchMethodException exception) { 859 // No-op 860 } 861 862 if (method == null) { 863 try { 864 method = MethodUtil.getMethod(sourceType, isMethodName, new Class[] { targetType }); 865 } catch (NoSuchMethodException exception) { 866 // No-op 867 } 868 } 869 870 // Check for interfaces 871 if (method == null) { 872 Class<?>[] interfaces = targetType.getInterfaces(); 873 for (int i = 0; i < interfaces.length; i++) { 874 try { 875 method = MethodUtil.getMethod(sourceType, getMethodName, new Class[] { interfaces[i] }); 876 } catch (NoSuchMethodException exception) { 877 // No-op 878 } 879 880 if (method == null) { 881 try { 882 method = MethodUtil.getMethod(sourceType, isMethodName, new Class[] { interfaces[i] }); 883 } catch (NoSuchMethodException exception) { 884 // No-op 885 } 886 } 887 888 if (method != null) { 889 break; 890 } 891 } 892 } 893 894 if (method == null) { 895 method = getStaticGetterMethod(sourceType, key, targetType.getSuperclass()); 896 } 897 } 898 899 return method; 900 } 901 902 private static Method getStaticSetterMethod(Class<?> sourceType, String key, 903 Class<?> valueType, Class<?> targetType) { 904 if (sourceType == null) { 905 throw new NullPointerException(); 906 } 907 908 if (key == null) { 909 throw new NullPointerException(); 910 } 911 912 if (valueType == null) { 913 throw new NullPointerException(); 914 } 915 916 Method method = null; 917 918 if (targetType != null) { 919 key = Character.toUpperCase(key.charAt(0)) + key.substring(1); 920 921 String setMethodName = SET_PREFIX + key; 922 try { 923 method = MethodUtil.getMethod(sourceType, setMethodName, new Class[] { targetType, valueType }); 924 } catch (NoSuchMethodException exception) { 925 // No-op 926 } 927 928 // Check for interfaces 929 if (method == null) { 930 Class<?>[] interfaces = targetType.getInterfaces(); 931 for (int i = 0; i < interfaces.length; i++) { 932 try { 933 method = MethodUtil.getMethod(sourceType, setMethodName, new Class[] { interfaces[i], valueType }); 934 } catch (NoSuchMethodException exception) { 935 // No-op 936 } 937 938 if (method != null) { 939 break; 940 } 941 } 942 } 943 944 if (method == null) { 945 method = getStaticSetterMethod(sourceType, key, valueType, targetType.getSuperclass()); 946 } 947 } 948 949 return method; 950 } 951 952 private static String toAllCaps(String value) { 953 if (value == null) { 954 throw new NullPointerException(); 955 } 956 957 StringBuilder allCapsBuilder = new StringBuilder(); 958 959 for (int i = 0, n = value.length(); i < n; i++) { 960 char c = value.charAt(i); 961 962 if (Character.isUpperCase(c)) { 963 allCapsBuilder.append('_'); 964 } 965 966 allCapsBuilder.append(Character.toUpperCase(c)); 967 } 968 969 return allCapsBuilder.toString(); 970 } 971 }