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