PropertyDescriptor.java revision 16136:a6eaacd1d678
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 */ 25package java.beans; 26 27import java.lang.ref.Reference; 28import java.lang.reflect.Method; 29import java.lang.reflect.Constructor; 30import java.util.Map.Entry; 31 32import com.sun.beans.introspect.PropertyInfo; 33import 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 */ 40public 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 @SuppressWarnings("deprecation") 465 public PropertyEditor createPropertyEditor(Object bean) { 466 Object editor = null; 467 468 final Class<?> cls = getPropertyEditorClass(); 469 if (cls != null && PropertyEditor.class.isAssignableFrom(cls) 470 && ReflectUtil.isPackageAccessible(cls)) { 471 Constructor<?> ctor = null; 472 if (bean != null) { 473 try { 474 ctor = cls.getConstructor(new Class<?>[] { Object.class }); 475 } catch (Exception ex) { 476 // Fall through 477 } 478 } 479 try { 480 if (ctor == null) { 481 editor = cls.newInstance(); 482 } else { 483 editor = ctor.newInstance(new Object[] { bean }); 484 } 485 } catch (Exception ex) { 486 // Fall through 487 } 488 } 489 return (PropertyEditor)editor; 490 } 491 492 493 /** 494 * Compares this {@code PropertyDescriptor} against the specified object. 495 * Returns true if the objects are the same. Two {@code PropertyDescriptor}s 496 * are the same if the read, write, property types, property editor and 497 * flags are equivalent. 498 * 499 * @since 1.4 500 */ 501 public boolean equals(Object obj) { 502 if (this == obj) { 503 return true; 504 } 505 if (obj != null && obj instanceof PropertyDescriptor) { 506 PropertyDescriptor other = (PropertyDescriptor)obj; 507 Method otherReadMethod = other.getReadMethod(); 508 Method otherWriteMethod = other.getWriteMethod(); 509 510 if (!compareMethods(getReadMethod(), otherReadMethod)) { 511 return false; 512 } 513 514 if (!compareMethods(getWriteMethod(), otherWriteMethod)) { 515 return false; 516 } 517 518 if (getPropertyType() == other.getPropertyType() && 519 getPropertyEditorClass() == other.getPropertyEditorClass() && 520 bound == other.isBound() && constrained == other.isConstrained() && 521 writeMethodName == other.writeMethodName && 522 readMethodName == other.readMethodName) { 523 return true; 524 } 525 } 526 return false; 527 } 528 529 /** 530 * Package private helper method for Descriptor .equals methods. 531 * 532 * @param a first method to compare 533 * @param b second method to compare 534 * @return boolean to indicate that the methods are equivalent 535 */ 536 boolean compareMethods(Method a, Method b) { 537 // Note: perhaps this should be a protected method in FeatureDescriptor 538 if ((a == null) != (b == null)) { 539 return false; 540 } 541 542 if (a != null && b != null) { 543 if (!a.equals(b)) { 544 return false; 545 } 546 } 547 return true; 548 } 549 550 /** 551 * Package-private constructor. 552 * Merge two property descriptors. Where they conflict, give the 553 * second argument (y) priority over the first argument (x). 554 * 555 * @param x The first (lower priority) PropertyDescriptor 556 * @param y The second (higher priority) PropertyDescriptor 557 */ 558 PropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) { 559 super(x,y); 560 561 if (y.baseName != null) { 562 baseName = y.baseName; 563 } else { 564 baseName = x.baseName; 565 } 566 567 if (y.readMethodName != null) { 568 readMethodName = y.readMethodName; 569 } else { 570 readMethodName = x.readMethodName; 571 } 572 573 if (y.writeMethodName != null) { 574 writeMethodName = y.writeMethodName; 575 } else { 576 writeMethodName = x.writeMethodName; 577 } 578 579 if (y.propertyTypeRef != null) { 580 propertyTypeRef = y.propertyTypeRef; 581 } else { 582 propertyTypeRef = x.propertyTypeRef; 583 } 584 585 // Figure out the merged read method. 586 Method xr = x.getReadMethod(); 587 Method yr = y.getReadMethod(); 588 589 // Normally give priority to y's readMethod. 590 try { 591 if (isAssignable(xr, yr)) { 592 setReadMethod(yr); 593 } else { 594 setReadMethod(xr); 595 } 596 } catch (IntrospectionException ex) { 597 // fall through 598 } 599 600 // However, if both x and y reference read methods in the same class, 601 // give priority to a boolean "is" method over a boolean "get" method. 602 if (xr != null && yr != null && 603 xr.getDeclaringClass() == yr.getDeclaringClass() && 604 getReturnType(getClass0(), xr) == boolean.class && 605 getReturnType(getClass0(), yr) == boolean.class && 606 xr.getName().indexOf(Introspector.IS_PREFIX) == 0 && 607 yr.getName().indexOf(Introspector.GET_PREFIX) == 0) { 608 try { 609 setReadMethod(xr); 610 } catch (IntrospectionException ex) { 611 // fall through 612 } 613 } 614 615 Method xw = x.getWriteMethod(); 616 Method yw = y.getWriteMethod(); 617 618 try { 619 if (yw != null) { 620 setWriteMethod(yw); 621 } else { 622 setWriteMethod(xw); 623 } 624 } catch (IntrospectionException ex) { 625 // Fall through 626 } 627 628 if (y.getPropertyEditorClass() != null) { 629 setPropertyEditorClass(y.getPropertyEditorClass()); 630 } else { 631 setPropertyEditorClass(x.getPropertyEditorClass()); 632 } 633 634 635 bound = x.bound | y.bound; 636 constrained = x.constrained | y.constrained; 637 } 638 639 /* 640 * Package-private dup constructor. 641 * This must isolate the new object from any changes to the old object. 642 */ 643 PropertyDescriptor(PropertyDescriptor old) { 644 super(old); 645 propertyTypeRef = old.propertyTypeRef; 646 this.readMethodRef.set(old.readMethodRef.get()); 647 this.writeMethodRef.set(old.writeMethodRef.get()); 648 propertyEditorClassRef = old.propertyEditorClassRef; 649 650 writeMethodName = old.writeMethodName; 651 readMethodName = old.readMethodName; 652 baseName = old.baseName; 653 654 bound = old.bound; 655 constrained = old.constrained; 656 } 657 658 void updateGenericsFor(Class<?> type) { 659 setClass0(type); 660 try { 661 setPropertyType(findPropertyType(this.readMethodRef.get(), this.writeMethodRef.get())); 662 } 663 catch (IntrospectionException exception) { 664 setPropertyType(null); 665 } 666 } 667 668 /** 669 * Returns the property type that corresponds to the read and write method. 670 * The type precedence is given to the readMethod. 671 * 672 * @return the type of the property descriptor or null if both 673 * read and write methods are null. 674 * @throws IntrospectionException if the read or write method is invalid 675 */ 676 private Class<?> findPropertyType(Method readMethod, Method writeMethod) 677 throws IntrospectionException { 678 Class<?> propertyType = null; 679 try { 680 if (readMethod != null) { 681 Class<?>[] params = getParameterTypes(getClass0(), readMethod); 682 if (params.length != 0) { 683 throw new IntrospectionException("bad read method arg count: " 684 + readMethod); 685 } 686 propertyType = getReturnType(getClass0(), readMethod); 687 if (propertyType == Void.TYPE) { 688 throw new IntrospectionException("read method " + 689 readMethod.getName() + " returns void"); 690 } 691 } 692 if (writeMethod != null) { 693 Class<?>[] params = getParameterTypes(getClass0(), writeMethod); 694 if (params.length != 1) { 695 throw new IntrospectionException("bad write method arg count: " 696 + writeMethod); 697 } 698 if (propertyType != null && !params[0].isAssignableFrom(propertyType)) { 699 throw new IntrospectionException("type mismatch between read and write methods"); 700 } 701 propertyType = params[0]; 702 } 703 } catch (IntrospectionException ex) { 704 throw ex; 705 } 706 return propertyType; 707 } 708 709 710 /** 711 * Returns a hash code value for the object. 712 * See {@link java.lang.Object#hashCode} for a complete description. 713 * 714 * @return a hash code value for this object. 715 * @since 1.5 716 */ 717 public int hashCode() { 718 int result = 7; 719 720 result = 37 * result + ((getPropertyType() == null) ? 0 : 721 getPropertyType().hashCode()); 722 result = 37 * result + ((getReadMethod() == null) ? 0 : 723 getReadMethod().hashCode()); 724 result = 37 * result + ((getWriteMethod() == null) ? 0 : 725 getWriteMethod().hashCode()); 726 result = 37 * result + ((getPropertyEditorClass() == null) ? 0 : 727 getPropertyEditorClass().hashCode()); 728 result = 37 * result + ((writeMethodName == null) ? 0 : 729 writeMethodName.hashCode()); 730 result = 37 * result + ((readMethodName == null) ? 0 : 731 readMethodName.hashCode()); 732 result = 37 * result + getName().hashCode(); 733 result = 37 * result + ((bound == false) ? 0 : 1); 734 result = 37 * result + ((constrained == false) ? 0 : 1); 735 736 return result; 737 } 738 739 // Calculate once since capitalize() is expensive. 740 String getBaseName() { 741 if (baseName == null) { 742 baseName = NameGenerator.capitalize(getName()); 743 } 744 return baseName; 745 } 746 747 void appendTo(StringBuilder sb) { 748 appendTo(sb, "bound", this.bound); 749 appendTo(sb, "constrained", this.constrained); 750 appendTo(sb, "propertyEditorClass", this.propertyEditorClassRef); 751 appendTo(sb, "propertyType", this.propertyTypeRef); 752 appendTo(sb, "readMethod", this.readMethodRef.get()); 753 appendTo(sb, "writeMethod", this.writeMethodRef.get()); 754 } 755 756 boolean isAssignable(Method m1, Method m2) { 757 if (m1 == null) { 758 return true; // choose second method 759 } 760 if (m2 == null) { 761 return false; // choose first method 762 } 763 if (!m1.getName().equals(m2.getName())) { 764 return true; // choose second method by default 765 } 766 Class<?> type1 = m1.getDeclaringClass(); 767 Class<?> type2 = m2.getDeclaringClass(); 768 if (!type1.isAssignableFrom(type2)) { 769 return false; // choose first method: it declared later 770 } 771 type1 = getReturnType(getClass0(), m1); 772 type2 = getReturnType(getClass0(), m2); 773 if (!type1.isAssignableFrom(type2)) { 774 return false; // choose first method: it overrides return type 775 } 776 Class<?>[] args1 = getParameterTypes(getClass0(), m1); 777 Class<?>[] args2 = getParameterTypes(getClass0(), m2); 778 if (args1.length != args2.length) { 779 return true; // choose second method by default 780 } 781 for (int i = 0; i < args1.length; i++) { 782 if (!args1[i].isAssignableFrom(args2[i])) { 783 return false; // choose first method: it overrides parameter 784 } 785 } 786 return true; // choose second method 787 } 788} 789