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