1 /* 2 * Copyright (c) 1996, 2015, 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 java.beans; 27 28 import java.lang.ref.Reference; 29 import java.lang.reflect.Method; 30 import java.lang.reflect.Constructor; 31 import sun.reflect.misc.ReflectUtil; 32 33 /** 34 * A PropertyDescriptor describes one property that a Java Bean 35 * exports via a pair of accessor methods. 36 */ 37 public class PropertyDescriptor extends FeatureDescriptor { 38 39 private Reference<? extends Class<?>> propertyTypeRef; 40 private final MethodRef readMethodRef = new MethodRef(); 41 private final MethodRef writeMethodRef = new MethodRef(); 42 private Reference<? extends Class<?>> propertyEditorClassRef; 43 44 private boolean bound; 45 private boolean constrained; 46 47 // The base name of the method name which will be prefixed with the 48 // read and write method. If name == "foo" then the baseName is "Foo" 49 private String baseName; 50 51 private String writeMethodName; 52 private String readMethodName; 53 54 /** 55 * Constructs a PropertyDescriptor for a property that follows 56 * the standard Java convention by having getFoo and setFoo 57 * accessor methods. Thus if the argument name is "fred", it will 58 * assume that the writer method is "setFred" and the reader method 59 * is "getFred" (or "isFred" for a boolean property). Note that the 60 * property name should start with a lower case character, which will 61 * be capitalized in the method names. 62 * 63 * @param propertyName The programmatic name of the property. 64 * @param beanClass The Class object for the target bean. For 65 * example sun.beans.OurButton.class. 66 * @exception IntrospectionException if an exception occurs during 67 * introspection. 68 */ PropertyDescriptor(String propertyName, Class<?> beanClass)69 public PropertyDescriptor(String propertyName, Class<?> beanClass) 70 throws IntrospectionException { 71 this(propertyName, beanClass, 72 Introspector.IS_PREFIX + NameGenerator.capitalize(propertyName), 73 Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName)); 74 } 75 76 /** 77 * This constructor takes the name of a simple property, and method 78 * names for reading and writing the property. 79 * 80 * @param propertyName The programmatic name of the property. 81 * @param beanClass The Class object for the target bean. For 82 * example sun.beans.OurButton.class. 83 * @param readMethodName The name of the method used for reading the property 84 * value. May be null if the property is write-only. 85 * @param writeMethodName The name of the method used for writing the property 86 * value. May be null if the property is read-only. 87 * @exception IntrospectionException if an exception occurs during 88 * introspection. 89 */ PropertyDescriptor(String propertyName, Class<?> beanClass, String readMethodName, String writeMethodName)90 public PropertyDescriptor(String propertyName, Class<?> beanClass, 91 String readMethodName, String writeMethodName) 92 throws IntrospectionException { 93 if (beanClass == null) { 94 throw new IntrospectionException("Target Bean class is null"); 95 } 96 if (propertyName == null || propertyName.length() == 0) { 97 throw new IntrospectionException("bad property name"); 98 } 99 if ("".equals(readMethodName) || "".equals(writeMethodName)) { 100 throw new IntrospectionException("read or write method name should not be the empty string"); 101 } 102 setName(propertyName); 103 setClass0(beanClass); 104 105 this.readMethodName = readMethodName; 106 if (readMethodName != null && getReadMethod() == null) { 107 throw new IntrospectionException("Method not found: " + readMethodName); 108 } 109 this.writeMethodName = writeMethodName; 110 if (writeMethodName != null && getWriteMethod() == null) { 111 throw new IntrospectionException("Method not found: " + writeMethodName); 112 } 113 // If this class or one of its base classes allow PropertyChangeListener, 114 // then we assume that any properties we discover are "bound". 115 // See Introspector.getTargetPropertyInfo() method. 116 Class[] args = { PropertyChangeListener.class }; 117 this.bound = null != Introspector.findMethod(beanClass, "addPropertyChangeListener", args.length, args); 118 } 119 120 /** 121 * This constructor takes the name of a simple property, and Method 122 * objects for reading and writing the property. 123 * 124 * @param propertyName The programmatic name of the property. 125 * @param readMethod The method used for reading the property value. 126 * May be null if the property is write-only. 127 * @param writeMethod The method used for writing the property value. 128 * May be null if the property is read-only. 129 * @exception IntrospectionException if an exception occurs during 130 * introspection. 131 */ PropertyDescriptor(String propertyName, Method readMethod, Method writeMethod)132 public PropertyDescriptor(String propertyName, Method readMethod, Method writeMethod) 133 throws IntrospectionException { 134 if (propertyName == null || propertyName.length() == 0) { 135 throw new IntrospectionException("bad property name"); 136 } 137 setName(propertyName); 138 setReadMethod(readMethod); 139 setWriteMethod(writeMethod); 140 } 141 142 /** 143 * Creates <code>PropertyDescriptor</code> for the specified bean 144 * with the specified name and methods to read/write the property value. 145 * 146 * @param bean the type of the target bean 147 * @param base the base name of the property (the rest of the method name) 148 * @param read the method used for reading the property value 149 * @param write the method used for writing the property value 150 * @exception IntrospectionException if an exception occurs during introspection 151 * 152 * @since 1.7 153 */ PropertyDescriptor(Class<?> bean, String base, Method read, Method write)154 PropertyDescriptor(Class<?> bean, String base, Method read, Method write) throws IntrospectionException { 155 if (bean == null) { 156 throw new IntrospectionException("Target Bean class is null"); 157 } 158 setClass0(bean); 159 setName(Introspector.decapitalize(base)); 160 setReadMethod(read); 161 setWriteMethod(write); 162 this.baseName = base; 163 } 164 165 /** 166 * Returns the Java type info for the property. 167 * Note that the {@code Class} object may describe 168 * primitive Java types such as {@code int}. 169 * This type is returned by the read method 170 * or is used as the parameter type of the write method. 171 * Returns {@code null} if the type is an indexed property 172 * that does not support non-indexed access. 173 * 174 * @return the {@code Class} object that represents the Java type info, 175 * or {@code null} if the type cannot be determined 176 */ getPropertyType()177 public synchronized Class<?> getPropertyType() { 178 Class<?> type = getPropertyType0(); 179 if (type == null) { 180 try { 181 type = findPropertyType(getReadMethod(), getWriteMethod()); 182 setPropertyType(type); 183 } catch (IntrospectionException ex) { 184 // Fall 185 } 186 } 187 return type; 188 } 189 setPropertyType(Class<?> type)190 private void setPropertyType(Class<?> type) { 191 this.propertyTypeRef = getWeakReference(type); 192 } 193 getPropertyType0()194 private Class<?> getPropertyType0() { 195 return (this.propertyTypeRef != null) 196 ? this.propertyTypeRef.get() 197 : null; 198 } 199 200 /** 201 * Gets the method that should be used to read the property value. 202 * 203 * @return The method that should be used to read the property value. 204 * May return null if the property can't be read. 205 */ getReadMethod()206 public synchronized Method getReadMethod() { 207 Method readMethod = this.readMethodRef.get(); 208 if (readMethod == null) { 209 Class<?> cls = getClass0(); 210 if (cls == null || (readMethodName == null && !this.readMethodRef.isSet())) { 211 // The read method was explicitly set to null. 212 return null; 213 } 214 String nextMethodName = Introspector.GET_PREFIX + getBaseName(); 215 if (readMethodName == null) { 216 Class<?> type = getPropertyType0(); 217 if (type == boolean.class || type == null) { 218 readMethodName = Introspector.IS_PREFIX + getBaseName(); 219 } else { 220 readMethodName = nextMethodName; 221 } 222 } 223 224 // Since there can be multiple write methods but only one getter 225 // method, find the getter method first so that you know what the 226 // property type is. For booleans, there can be "is" and "get" 227 // methods. If an "is" method exists, this is the official 228 // reader method so look for this one first. 229 readMethod = Introspector.findMethod(cls, readMethodName, 0); 230 if ((readMethod == null) && !readMethodName.equals(nextMethodName)) { 231 readMethodName = nextMethodName; 232 readMethod = Introspector.findMethod(cls, readMethodName, 0); 233 } 234 try { 235 setReadMethod(readMethod); 236 } catch (IntrospectionException ex) { 237 // fall 238 } 239 } 240 return readMethod; 241 } 242 243 /** 244 * Sets the method that should be used to read the property value. 245 * 246 * @param readMethod The new read method. 247 * @throws IntrospectionException if the read method is invalid 248 */ setReadMethod(Method readMethod)249 public synchronized void setReadMethod(Method readMethod) 250 throws IntrospectionException { 251 this.readMethodRef.set(readMethod); 252 if (readMethod == null) { 253 readMethodName = null; 254 return; 255 } 256 // The property type is determined by the read method. 257 setPropertyType(findPropertyType(readMethod, this.writeMethodRef.get())); 258 setClass0(readMethod.getDeclaringClass()); 259 260 readMethodName = readMethod.getName(); 261 setTransient(readMethod.getAnnotation(Transient.class)); 262 } 263 264 /** 265 * Gets the method that should be used to write the property value. 266 * 267 * @return The method that should be used to write the property value. 268 * May return null if the property can't be written. 269 */ getWriteMethod()270 public synchronized Method getWriteMethod() { 271 Method writeMethod = this.writeMethodRef.get(); 272 if (writeMethod == null) { 273 Class<?> cls = getClass0(); 274 if (cls == null || (writeMethodName == null && !this.writeMethodRef.isSet())) { 275 // The write method was explicitly set to null. 276 return null; 277 } 278 279 // We need the type to fetch the correct method. 280 Class<?> type = getPropertyType0(); 281 if (type == null) { 282 try { 283 // Can't use getPropertyType since it will lead to recursive loop. 284 type = findPropertyType(getReadMethod(), null); 285 setPropertyType(type); 286 } catch (IntrospectionException ex) { 287 // Without the correct property type we can't be guaranteed 288 // to find the correct method. 289 return null; 290 } 291 } 292 293 if (writeMethodName == null) { 294 writeMethodName = Introspector.SET_PREFIX + getBaseName(); 295 } 296 297 Class<?>[] args = (type == null) ? null : new Class<?>[] { type }; 298 writeMethod = Introspector.findMethod(cls, writeMethodName, 1, args); 299 if (writeMethod != null) { 300 if (!writeMethod.getReturnType().equals(void.class)) { 301 writeMethod = null; 302 } 303 } 304 try { 305 setWriteMethod(writeMethod); 306 } catch (IntrospectionException ex) { 307 // fall through 308 } 309 } 310 return writeMethod; 311 } 312 313 /** 314 * Sets the method that should be used to write the property value. 315 * 316 * @param writeMethod The new write method. 317 * @throws IntrospectionException if the write method is invalid 318 */ setWriteMethod(Method writeMethod)319 public synchronized void setWriteMethod(Method writeMethod) 320 throws IntrospectionException { 321 this.writeMethodRef.set(writeMethod); 322 if (writeMethod == null) { 323 writeMethodName = null; 324 return; 325 } 326 // Set the property type - which validates the method 327 setPropertyType(findPropertyType(getReadMethod(), writeMethod)); 328 setClass0(writeMethod.getDeclaringClass()); 329 330 writeMethodName = writeMethod.getName(); 331 setTransient(writeMethod.getAnnotation(Transient.class)); 332 } 333 334 /** 335 * Overridden to ensure that a super class doesn't take precedent 336 */ setClass0(Class<?> clz)337 void setClass0(Class<?> clz) { 338 if (getClass0() != null && clz.isAssignableFrom(getClass0())) { 339 // don't replace a subclass with a superclass 340 return; 341 } 342 super.setClass0(clz); 343 } 344 345 /** 346 * Updates to "bound" properties will cause a "PropertyChange" event to 347 * get fired when the property is changed. 348 * 349 * @return True if this is a bound property. 350 */ isBound()351 public boolean isBound() { 352 return bound; 353 } 354 355 /** 356 * Updates to "bound" properties will cause a "PropertyChange" event to 357 * get fired when the property is changed. 358 * 359 * @param bound True if this is a bound property. 360 */ setBound(boolean bound)361 public void setBound(boolean bound) { 362 this.bound = bound; 363 } 364 365 /** 366 * Attempted updates to "Constrained" properties will cause a "VetoableChange" 367 * event to get fired when the property is changed. 368 * 369 * @return True if this is a constrained property. 370 */ isConstrained()371 public boolean isConstrained() { 372 return constrained; 373 } 374 375 /** 376 * Attempted updates to "Constrained" properties will cause a "VetoableChange" 377 * event to get fired when the property is changed. 378 * 379 * @param constrained True if this is a constrained property. 380 */ setConstrained(boolean constrained)381 public void setConstrained(boolean constrained) { 382 this.constrained = constrained; 383 } 384 385 386 /** 387 * Normally PropertyEditors will be found using the PropertyEditorManager. 388 * However if for some reason you want to associate a particular 389 * PropertyEditor with a given property, then you can do it with 390 * this method. 391 * 392 * @param propertyEditorClass The Class for the desired PropertyEditor. 393 */ setPropertyEditorClass(Class<?> propertyEditorClass)394 public void setPropertyEditorClass(Class<?> propertyEditorClass) { 395 this.propertyEditorClassRef = getWeakReference(propertyEditorClass); 396 } 397 398 /** 399 * Gets any explicit PropertyEditor Class that has been registered 400 * for this property. 401 * 402 * @return Any explicit PropertyEditor Class that has been registered 403 * for this property. Normally this will return "null", 404 * indicating that no special editor has been registered, 405 * so the PropertyEditorManager should be used to locate 406 * a suitable PropertyEditor. 407 */ getPropertyEditorClass()408 public Class<?> getPropertyEditorClass() { 409 return (this.propertyEditorClassRef != null) 410 ? this.propertyEditorClassRef.get() 411 : null; 412 } 413 414 /** 415 * Constructs an instance of a property editor using the current 416 * property editor class. 417 * <p> 418 * If the property editor class has a public constructor that takes an 419 * Object argument then it will be invoked using the bean parameter 420 * as the argument. Otherwise, the default constructor will be invoked. 421 * 422 * @param bean the source object 423 * @return a property editor instance or null if a property editor has 424 * not been defined or cannot be created 425 * @since 1.5 426 */ createPropertyEditor(Object bean)427 public PropertyEditor createPropertyEditor(Object bean) { 428 Object editor = null; 429 430 final Class<?> cls = getPropertyEditorClass(); 431 if (cls != null && PropertyEditor.class.isAssignableFrom(cls) 432 && ReflectUtil.isPackageAccessible(cls)) { 433 Constructor<?> ctor = null; 434 if (bean != null) { 435 try { 436 ctor = cls.getConstructor(new Class<?>[] { Object.class }); 437 } catch (Exception ex) { 438 // Fall through 439 } 440 } 441 try { 442 if (ctor == null) { 443 editor = cls.newInstance(); 444 } else { 445 editor = ctor.newInstance(new Object[] { bean }); 446 } 447 } catch (Exception ex) { 448 // Fall through 449 } 450 } 451 return (PropertyEditor)editor; 452 } 453 454 455 /** 456 * Compares this <code>PropertyDescriptor</code> against the specified object. 457 * Returns true if the objects are the same. Two <code>PropertyDescriptor</code>s 458 * are the same if the read, write, property types, property editor and 459 * flags are equivalent. 460 * 461 * @since 1.4 462 */ equals(Object obj)463 public boolean equals(Object obj) { 464 if (this == obj) { 465 return true; 466 } 467 if (obj != null && obj instanceof PropertyDescriptor) { 468 PropertyDescriptor other = (PropertyDescriptor)obj; 469 Method otherReadMethod = other.getReadMethod(); 470 Method otherWriteMethod = other.getWriteMethod(); 471 472 if (!compareMethods(getReadMethod(), otherReadMethod)) { 473 return false; 474 } 475 476 if (!compareMethods(getWriteMethod(), otherWriteMethod)) { 477 return false; 478 } 479 480 if (getPropertyType() == other.getPropertyType() && 481 getPropertyEditorClass() == other.getPropertyEditorClass() && 482 bound == other.isBound() && constrained == other.isConstrained() && 483 writeMethodName == other.writeMethodName && 484 readMethodName == other.readMethodName) { 485 return true; 486 } 487 } 488 return false; 489 } 490 491 /** 492 * Package private helper method for Descriptor .equals methods. 493 * 494 * @param a first method to compare 495 * @param b second method to compare 496 * @return boolean to indicate that the methods are equivalent 497 */ compareMethods(Method a, Method b)498 boolean compareMethods(Method a, Method b) { 499 // Note: perhaps this should be a protected method in FeatureDescriptor 500 if ((a == null) != (b == null)) { 501 return false; 502 } 503 504 if (a != null && b != null) { 505 if (!a.equals(b)) { 506 return false; 507 } 508 } 509 return true; 510 } 511 512 /** 513 * Package-private constructor. 514 * Merge two property descriptors. Where they conflict, give the 515 * second argument (y) priority over the first argument (x). 516 * 517 * @param x The first (lower priority) PropertyDescriptor 518 * @param y The second (higher priority) PropertyDescriptor 519 */ PropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y)520 PropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) { 521 super(x,y); 522 523 if (y.baseName != null) { 524 baseName = y.baseName; 525 } else { 526 baseName = x.baseName; 527 } 528 529 if (y.readMethodName != null) { 530 readMethodName = y.readMethodName; 531 } else { 532 readMethodName = x.readMethodName; 533 } 534 535 if (y.writeMethodName != null) { 536 writeMethodName = y.writeMethodName; 537 } else { 538 writeMethodName = x.writeMethodName; 539 } 540 541 if (y.propertyTypeRef != null) { 542 propertyTypeRef = y.propertyTypeRef; 543 } else { 544 propertyTypeRef = x.propertyTypeRef; 545 } 546 547 // Figure out the merged read method. 548 Method xr = x.getReadMethod(); 549 Method yr = y.getReadMethod(); 550 551 // Normally give priority to y's readMethod. 552 try { 553 if (isAssignable(xr, yr)) { 554 setReadMethod(yr); 555 } else { 556 setReadMethod(xr); 557 } 558 } catch (IntrospectionException ex) { 559 // fall through 560 } 561 562 // However, if both x and y reference read methods in the same class, 563 // give priority to a boolean "is" method over a boolean "get" method. 564 if (xr != null && yr != null && 565 xr.getDeclaringClass() == yr.getDeclaringClass() && 566 getReturnType(getClass0(), xr) == boolean.class && 567 getReturnType(getClass0(), yr) == boolean.class && 568 xr.getName().indexOf(Introspector.IS_PREFIX) == 0 && 569 yr.getName().indexOf(Introspector.GET_PREFIX) == 0) { 570 try { 571 setReadMethod(xr); 572 } catch (IntrospectionException ex) { 573 // fall through 574 } 575 } 576 577 Method xw = x.getWriteMethod(); 578 Method yw = y.getWriteMethod(); 579 580 try { 581 if (yw != null) { 582 setWriteMethod(yw); 583 } else { 584 setWriteMethod(xw); 585 } 586 } catch (IntrospectionException ex) { 587 // Fall through 588 } 589 590 if (y.getPropertyEditorClass() != null) { 591 setPropertyEditorClass(y.getPropertyEditorClass()); 592 } else { 593 setPropertyEditorClass(x.getPropertyEditorClass()); 594 } 595 596 597 bound = x.bound | y.bound; 598 constrained = x.constrained | y.constrained; 599 } 600 601 /* 602 * Package-private dup constructor. 603 * This must isolate the new object from any changes to the old object. 604 */ PropertyDescriptor(PropertyDescriptor old)605 PropertyDescriptor(PropertyDescriptor old) { 606 super(old); 607 propertyTypeRef = old.propertyTypeRef; 608 this.readMethodRef.set(old.readMethodRef.get()); 609 this.writeMethodRef.set(old.writeMethodRef.get()); 610 propertyEditorClassRef = old.propertyEditorClassRef; 611 612 writeMethodName = old.writeMethodName; 613 readMethodName = old.readMethodName; 614 baseName = old.baseName; 615 616 bound = old.bound; 617 constrained = old.constrained; 618 } 619 updateGenericsFor(Class<?> type)620 void updateGenericsFor(Class<?> type) { 621 setClass0(type); 622 try { 623 setPropertyType(findPropertyType(this.readMethodRef.get(), this.writeMethodRef.get())); 624 } 625 catch (IntrospectionException exception) { 626 setPropertyType(null); 627 } 628 } 629 630 /** 631 * Returns the property type that corresponds to the read and write method. 632 * The type precedence is given to the readMethod. 633 * 634 * @return the type of the property descriptor or null if both 635 * read and write methods are null. 636 * @throws IntrospectionException if the read or write method is invalid 637 */ findPropertyType(Method readMethod, Method writeMethod)638 private Class<?> findPropertyType(Method readMethod, Method writeMethod) 639 throws IntrospectionException { 640 Class<?> propertyType = null; 641 try { 642 if (readMethod != null) { 643 Class<?>[] params = getParameterTypes(getClass0(), readMethod); 644 if (params.length != 0) { 645 throw new IntrospectionException("bad read method arg count: " 646 + readMethod); 647 } 648 propertyType = getReturnType(getClass0(), readMethod); 649 if (propertyType == Void.TYPE) { 650 throw new IntrospectionException("read method " + 651 readMethod.getName() + " returns void"); 652 } 653 } 654 if (writeMethod != null) { 655 Class<?>[] params = getParameterTypes(getClass0(), writeMethod); 656 if (params.length != 1) { 657 throw new IntrospectionException("bad write method arg count: " 658 + writeMethod); 659 } 660 if (propertyType != null && !params[0].isAssignableFrom(propertyType)) { 661 throw new IntrospectionException("type mismatch between read and write methods"); 662 } 663 propertyType = params[0]; 664 } 665 } catch (IntrospectionException ex) { 666 throw ex; 667 } 668 return propertyType; 669 } 670 671 672 /** 673 * Returns a hash code value for the object. 674 * See {@link java.lang.Object#hashCode} for a complete description. 675 * 676 * @return a hash code value for this object. 677 * @since 1.5 678 */ hashCode()679 public int hashCode() { 680 int result = 7; 681 682 result = 37 * result + ((getPropertyType() == null) ? 0 : 683 getPropertyType().hashCode()); 684 result = 37 * result + ((getReadMethod() == null) ? 0 : 685 getReadMethod().hashCode()); 686 result = 37 * result + ((getWriteMethod() == null) ? 0 : 687 getWriteMethod().hashCode()); 688 result = 37 * result + ((getPropertyEditorClass() == null) ? 0 : 689 getPropertyEditorClass().hashCode()); 690 result = 37 * result + ((writeMethodName == null) ? 0 : 691 writeMethodName.hashCode()); 692 result = 37 * result + ((readMethodName == null) ? 0 : 693 readMethodName.hashCode()); 694 result = 37 * result + getName().hashCode(); 695 result = 37 * result + ((bound == false) ? 0 : 1); 696 result = 37 * result + ((constrained == false) ? 0 : 1); 697 698 return result; 699 } 700 701 // Calculate once since capitalize() is expensive. getBaseName()702 String getBaseName() { 703 if (baseName == null) { 704 baseName = NameGenerator.capitalize(getName()); 705 } 706 return baseName; 707 } 708 appendTo(StringBuilder sb)709 void appendTo(StringBuilder sb) { 710 appendTo(sb, "bound", this.bound); 711 appendTo(sb, "constrained", this.constrained); 712 appendTo(sb, "propertyEditorClass", this.propertyEditorClassRef); 713 appendTo(sb, "propertyType", this.propertyTypeRef); 714 appendTo(sb, "readMethod", this.readMethodRef.get()); 715 appendTo(sb, "writeMethod", this.writeMethodRef.get()); 716 } 717 isAssignable(Method m1, Method m2)718 boolean isAssignable(Method m1, Method m2) { 719 if (m1 == null) { 720 return true; // choose second method 721 } 722 if (m2 == null) { 723 return false; // choose first method 724 } 725 if (!m1.getName().equals(m2.getName())) { 726 return true; // choose second method by default 727 } 728 Class<?> type1 = m1.getDeclaringClass(); 729 Class<?> type2 = m2.getDeclaringClass(); 730 if (!type1.isAssignableFrom(type2)) { 731 return false; // choose first method: it declared later 732 } 733 type1 = getReturnType(getClass0(), m1); 734 type2 = getReturnType(getClass0(), m2); 735 if (!type1.isAssignableFrom(type2)) { 736 return false; // choose first method: it overrides return type 737 } 738 Class<?>[] args1 = getParameterTypes(getClass0(), m1); 739 Class<?>[] args2 = getParameterTypes(getClass0(), m2); 740 if (args1.length != args2.length) { 741 return true; // choose second method by default 742 } 743 for (int i = 0; i < args1.length; i++) { 744 if (!args1[i].isAssignableFrom(args2[i])) { 745 return false; // choose first method: it overrides parameter 746 } 747 } 748 return true; // choose second method 749 } 750 } 751