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