jdk/src/share/classes/java/beans/IndexedPropertyDescriptor.java
changeset 2 90ce3da70b43
child 466 6acd5ec503a8
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 1996-2004 Sun Microsystems, Inc.  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.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package java.beans;
       
    27 
       
    28 import java.lang.ref.Reference;
       
    29 
       
    30 import java.lang.reflect.Method;
       
    31 
       
    32 /**
       
    33  * An IndexedPropertyDescriptor describes a property that acts like an
       
    34  * array and has an indexed read and/or indexed write method to access
       
    35  * specific elements of the array.
       
    36  * <p>
       
    37  * An indexed property may also provide simple non-indexed read and write
       
    38  * methods.  If these are present, they read and write arrays of the type
       
    39  * returned by the indexed read method.
       
    40  */
       
    41 
       
    42 public class IndexedPropertyDescriptor extends PropertyDescriptor {
       
    43 
       
    44     private Reference<Class> indexedPropertyTypeRef;
       
    45     private Reference<Method> indexedReadMethodRef;
       
    46     private Reference<Method> indexedWriteMethodRef;
       
    47 
       
    48     private String indexedReadMethodName;
       
    49     private String indexedWriteMethodName;
       
    50 
       
    51     /**
       
    52      * This constructor constructs an IndexedPropertyDescriptor for a property
       
    53      * that follows the standard Java conventions by having getFoo and setFoo
       
    54      * accessor methods, for both indexed access and array access.
       
    55      * <p>
       
    56      * Thus if the argument name is "fred", it will assume that there
       
    57      * is an indexed reader method "getFred", a non-indexed (array) reader
       
    58      * method also called "getFred", an indexed writer method "setFred",
       
    59      * and finally a non-indexed writer method "setFred".
       
    60      *
       
    61      * @param propertyName The programmatic name of the property.
       
    62      * @param beanClass The Class object for the target bean.
       
    63      * @exception IntrospectionException if an exception occurs during
       
    64      *              introspection.
       
    65      */
       
    66     public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass)
       
    67                 throws IntrospectionException {
       
    68         this(propertyName, beanClass,
       
    69              Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName),
       
    70              Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName),
       
    71              Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName),
       
    72              Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName));
       
    73     }
       
    74 
       
    75     /**
       
    76      * This constructor takes the name of a simple property, and method
       
    77      * names for reading and writing the property, both indexed
       
    78      * and non-indexed.
       
    79      *
       
    80      * @param propertyName The programmatic name of the property.
       
    81      * @param beanClass  The Class object for the target bean.
       
    82      * @param readMethodName The name of the method used for reading the property
       
    83      *           values as an array.  May be null if the property is write-only
       
    84      *           or must be indexed.
       
    85      * @param writeMethodName The name of the method used for writing the property
       
    86      *           values as an array.  May be null if the property is read-only
       
    87      *           or must be indexed.
       
    88      * @param indexedReadMethodName The name of the method used for reading
       
    89      *          an indexed property value.
       
    90      *          May be null if the property is write-only.
       
    91      * @param indexedWriteMethodName The name of the method used for writing
       
    92      *          an indexed property value.
       
    93      *          May be null if the property is read-only.
       
    94      * @exception IntrospectionException if an exception occurs during
       
    95      *              introspection.
       
    96      */
       
    97     public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass,
       
    98                 String readMethodName, String writeMethodName,
       
    99                 String indexedReadMethodName, String indexedWriteMethodName)
       
   100                 throws IntrospectionException {
       
   101         super(propertyName, beanClass, readMethodName, writeMethodName);
       
   102 
       
   103         this.indexedReadMethodName = indexedReadMethodName;
       
   104         if (indexedReadMethodName != null && getIndexedReadMethod() == null) {
       
   105             throw new IntrospectionException("Method not found: " + indexedReadMethodName);
       
   106         }
       
   107 
       
   108         this.indexedWriteMethodName = indexedWriteMethodName;
       
   109         if (indexedWriteMethodName != null && getIndexedWriteMethod() == null) {
       
   110             throw new IntrospectionException("Method not found: " + indexedWriteMethodName);
       
   111         }
       
   112         // Implemented only for type checking.
       
   113         findIndexedPropertyType(getIndexedReadMethod(), getIndexedWriteMethod());
       
   114     }
       
   115 
       
   116     /**
       
   117      * This constructor takes the name of a simple property, and Method
       
   118      * objects for reading and writing the property.
       
   119      *
       
   120      * @param propertyName The programmatic name of the pro
       
   121 perty.
       
   122      * @param readMethod The method used for reading the property values as an array.
       
   123      *          May be null if the property is write-only or must be indexed.
       
   124      * @param writeMethod The method used for writing the property values as an array.
       
   125      *          May be null if the property is read-only or must be indexed.
       
   126      * @param indexedReadMethod The method used for reading an indexed property value.
       
   127      *          May be null if the property is write-only.
       
   128      * @param indexedWriteMethod The method used for writing an indexed property value.
       
   129      *          May be null if the property is read-only.
       
   130      * @exception IntrospectionException if an exception occurs during
       
   131      *              introspection.
       
   132      */
       
   133     public IndexedPropertyDescriptor(String propertyName, Method readMethod, Method writeMethod,
       
   134                                             Method indexedReadMethod, Method indexedWriteMethod)
       
   135                 throws IntrospectionException {
       
   136         super(propertyName, readMethod, writeMethod);
       
   137 
       
   138         setIndexedReadMethod0(indexedReadMethod);
       
   139         setIndexedWriteMethod0(indexedWriteMethod);
       
   140 
       
   141         // Type checking
       
   142         setIndexedPropertyType(findIndexedPropertyType(indexedReadMethod, indexedWriteMethod));
       
   143     }
       
   144 
       
   145     /**
       
   146      * Creates <code>PropertyDescriptor</code> for the specified bean
       
   147      * with the specified name and methods to read/write the property value.
       
   148      *
       
   149      * @param bean          the type of the target bean
       
   150      * @param base          the base name of the property (the rest of the method name)
       
   151      * @param read          the method used for reading the property value
       
   152      * @param write         the method used for writing the property value
       
   153      * @param readIndexed   the method used for reading an indexed property value
       
   154      * @param writeIndexed  the method used for writing an indexed property value
       
   155      * @exception IntrospectionException if an exception occurs during introspection
       
   156      *
       
   157      * @since 1.7
       
   158      */
       
   159     IndexedPropertyDescriptor(Class<?> bean, String base, Method read, Method write, Method readIndexed, Method writeIndexed) throws IntrospectionException {
       
   160         super(bean, base, read, write);
       
   161 
       
   162         setIndexedReadMethod0(readIndexed);
       
   163         setIndexedWriteMethod0(writeIndexed);
       
   164 
       
   165         // Type checking
       
   166         setIndexedPropertyType(findIndexedPropertyType(readIndexed, writeIndexed));
       
   167     }
       
   168 
       
   169     /**
       
   170      * Gets the method that should be used to read an indexed
       
   171      * property value.
       
   172      *
       
   173      * @return The method that should be used to read an indexed
       
   174      * property value.
       
   175      * May return null if the property isn't indexed or is write-only.
       
   176      */
       
   177     public synchronized Method getIndexedReadMethod() {
       
   178         Method indexedReadMethod = getIndexedReadMethod0();
       
   179         if (indexedReadMethod == null) {
       
   180             Class cls = getClass0();
       
   181             if (cls == null ||
       
   182                 (indexedReadMethodName == null && indexedReadMethodRef == null)) {
       
   183                 // the Indexed readMethod was explicitly set to null.
       
   184                 return null;
       
   185             }
       
   186             if (indexedReadMethodName == null) {
       
   187                 Class type = getIndexedPropertyType0();
       
   188                 if (type == boolean.class || type == null) {
       
   189                     indexedReadMethodName = Introspector.IS_PREFIX + getBaseName();
       
   190                 } else {
       
   191                     indexedReadMethodName = Introspector.GET_PREFIX + getBaseName();
       
   192                 }
       
   193             }
       
   194 
       
   195             Class[] args = { int.class };
       
   196 
       
   197             indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName,
       
   198                                                         1, args);
       
   199             if (indexedReadMethod == null) {
       
   200                 // no "is" method, so look for a "get" method.
       
   201                 indexedReadMethodName = Introspector.GET_PREFIX + getBaseName();
       
   202                 indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName,
       
   203                                                             1, args);
       
   204             }
       
   205             setIndexedReadMethod0(indexedReadMethod);
       
   206         }
       
   207         return indexedReadMethod;
       
   208     }
       
   209 
       
   210     /**
       
   211      * Sets the method that should be used to read an indexed property value.
       
   212      *
       
   213      * @param readMethod The new indexed read method.
       
   214      */
       
   215     public synchronized void setIndexedReadMethod(Method readMethod)
       
   216         throws IntrospectionException {
       
   217 
       
   218         // the indexed property type is set by the reader.
       
   219         setIndexedPropertyType(findIndexedPropertyType(readMethod,
       
   220                                                        getIndexedWriteMethod0()));
       
   221         setIndexedReadMethod0(readMethod);
       
   222     }
       
   223 
       
   224     private void setIndexedReadMethod0(Method readMethod) {
       
   225         if (readMethod == null) {
       
   226             indexedReadMethodName = null;
       
   227             indexedReadMethodRef = null;
       
   228             return;
       
   229         }
       
   230         setClass0(readMethod.getDeclaringClass());
       
   231 
       
   232         indexedReadMethodName = readMethod.getName();
       
   233         this.indexedReadMethodRef = getSoftReference(readMethod);
       
   234     }
       
   235 
       
   236 
       
   237     /**
       
   238      * Gets the method that should be used to write an indexed property value.
       
   239      *
       
   240      * @return The method that should be used to write an indexed
       
   241      * property value.
       
   242      * May return null if the property isn't indexed or is read-only.
       
   243      */
       
   244     public synchronized Method getIndexedWriteMethod() {
       
   245         Method indexedWriteMethod = getIndexedWriteMethod0();
       
   246         if (indexedWriteMethod == null) {
       
   247             Class cls = getClass0();
       
   248             if (cls == null ||
       
   249                 (indexedWriteMethodName == null && indexedWriteMethodRef == null)) {
       
   250                 // the Indexed writeMethod was explicitly set to null.
       
   251                 return null;
       
   252             }
       
   253 
       
   254             // We need the indexed type to ensure that we get the correct method.
       
   255             // Cannot use the getIndexedPropertyType method since that could
       
   256             // result in an infinite loop.
       
   257             Class type = getIndexedPropertyType0();
       
   258             if (type == null) {
       
   259                 try {
       
   260                     type = findIndexedPropertyType(getIndexedReadMethod(), null);
       
   261                     setIndexedPropertyType(type);
       
   262                 } catch (IntrospectionException ex) {
       
   263                     // Set iprop type to be the classic type
       
   264                     Class propType = getPropertyType();
       
   265                     if (propType.isArray()) {
       
   266                         type = propType.getComponentType();
       
   267                     }
       
   268                 }
       
   269             }
       
   270 
       
   271             if (indexedWriteMethodName == null) {
       
   272                 indexedWriteMethodName = Introspector.SET_PREFIX + getBaseName();
       
   273             }
       
   274             indexedWriteMethod = Introspector.findMethod(cls, indexedWriteMethodName,
       
   275                          2, (type == null) ? null : new Class[] { int.class, type });
       
   276             setIndexedWriteMethod0(indexedWriteMethod);
       
   277         }
       
   278         return indexedWriteMethod;
       
   279     }
       
   280 
       
   281     /**
       
   282      * Sets the method that should be used to write an indexed property value.
       
   283      *
       
   284      * @param writeMethod The new indexed write method.
       
   285      */
       
   286     public synchronized void setIndexedWriteMethod(Method writeMethod)
       
   287         throws IntrospectionException {
       
   288 
       
   289         // If the indexed property type has not been set, then set it.
       
   290         Class type = findIndexedPropertyType(getIndexedReadMethod(),
       
   291                                              writeMethod);
       
   292         setIndexedPropertyType(type);
       
   293         setIndexedWriteMethod0(writeMethod);
       
   294     }
       
   295 
       
   296     private void setIndexedWriteMethod0(Method writeMethod) {
       
   297         if (writeMethod == null) {
       
   298             indexedWriteMethodName = null;
       
   299             indexedWriteMethodRef = null;
       
   300             return;
       
   301         }
       
   302         setClass0(writeMethod.getDeclaringClass());
       
   303 
       
   304         indexedWriteMethodName = writeMethod.getName();
       
   305         this.indexedWriteMethodRef = getSoftReference(writeMethod);
       
   306     }
       
   307 
       
   308     /**
       
   309      * Gets the <code>Class</code> object of the indexed properties' type.
       
   310      * The returned <code>Class</code> may describe a primitive type such as <code>int</code>.
       
   311      *
       
   312      * @return The <code>Class</code> for the indexed properties' type; may return <code>null</code>
       
   313      *         if the type cannot be determined.
       
   314      */
       
   315     public synchronized Class<?> getIndexedPropertyType() {
       
   316         Class type = getIndexedPropertyType0();
       
   317         if (type == null) {
       
   318             try {
       
   319                 type = findIndexedPropertyType(getIndexedReadMethod(),
       
   320                                                getIndexedWriteMethod());
       
   321                 setIndexedPropertyType(type);
       
   322             } catch (IntrospectionException ex) {
       
   323                 // fall
       
   324             }
       
   325         }
       
   326         return type;
       
   327     }
       
   328 
       
   329     // Private methods which set get/set the Reference objects
       
   330 
       
   331     private void setIndexedPropertyType(Class type) {
       
   332         this.indexedPropertyTypeRef = getWeakReference(type);
       
   333     }
       
   334 
       
   335     private Class getIndexedPropertyType0() {
       
   336         return (this.indexedPropertyTypeRef != null)
       
   337                 ? this.indexedPropertyTypeRef.get()
       
   338                 : null;
       
   339     }
       
   340 
       
   341     private Method getIndexedReadMethod0() {
       
   342         return (this.indexedReadMethodRef != null)
       
   343                 ? this.indexedReadMethodRef.get()
       
   344                 : null;
       
   345     }
       
   346 
       
   347     private Method getIndexedWriteMethod0() {
       
   348         return (this.indexedWriteMethodRef != null)
       
   349                 ? this.indexedWriteMethodRef.get()
       
   350                 : null;
       
   351     }
       
   352 
       
   353     private Class findIndexedPropertyType(Method indexedReadMethod,
       
   354                                           Method indexedWriteMethod)
       
   355         throws IntrospectionException {
       
   356         Class indexedPropertyType = null;
       
   357 
       
   358         if (indexedReadMethod != null) {
       
   359             Class params[] = getParameterTypes(getClass0(), indexedReadMethod);
       
   360             if (params.length != 1) {
       
   361                 throw new IntrospectionException("bad indexed read method arg count");
       
   362             }
       
   363             if (params[0] != Integer.TYPE) {
       
   364                 throw new IntrospectionException("non int index to indexed read method");
       
   365             }
       
   366             indexedPropertyType = getReturnType(getClass0(), indexedReadMethod);
       
   367             if (indexedPropertyType == Void.TYPE) {
       
   368                 throw new IntrospectionException("indexed read method returns void");
       
   369             }
       
   370         }
       
   371         if (indexedWriteMethod != null) {
       
   372             Class params[] = getParameterTypes(getClass0(), indexedWriteMethod);
       
   373             if (params.length != 2) {
       
   374                 throw new IntrospectionException("bad indexed write method arg count");
       
   375             }
       
   376             if (params[0] != Integer.TYPE) {
       
   377                 throw new IntrospectionException("non int index to indexed write method");
       
   378             }
       
   379             if (indexedPropertyType != null && indexedPropertyType != params[1]) {
       
   380                 throw new IntrospectionException(
       
   381                                                  "type mismatch between indexed read and indexed write methods: "
       
   382                                                  + getName());
       
   383             }
       
   384             indexedPropertyType = params[1];
       
   385         }
       
   386         Class propertyType = getPropertyType();
       
   387         if (propertyType != null && (!propertyType.isArray() ||
       
   388                                      propertyType.getComponentType() != indexedPropertyType)) {
       
   389             throw new IntrospectionException("type mismatch between indexed and non-indexed methods: "
       
   390                                              + getName());
       
   391         }
       
   392         return indexedPropertyType;
       
   393     }
       
   394 
       
   395     /**
       
   396      * Compares this <code>PropertyDescriptor</code> against the specified object.
       
   397      * Returns true if the objects are the same. Two <code>PropertyDescriptor</code>s
       
   398      * are the same if the read, write, property types, property editor and
       
   399      * flags  are equivalent.
       
   400      *
       
   401      * @since 1.4
       
   402      */
       
   403     public boolean equals(Object obj) {
       
   404         // Note: This would be identical to PropertyDescriptor but they don't
       
   405         // share the same fields.
       
   406         if (this == obj) {
       
   407             return true;
       
   408         }
       
   409 
       
   410         if (obj != null && obj instanceof IndexedPropertyDescriptor) {
       
   411             IndexedPropertyDescriptor other = (IndexedPropertyDescriptor)obj;
       
   412             Method otherIndexedReadMethod = other.getIndexedReadMethod();
       
   413             Method otherIndexedWriteMethod = other.getIndexedWriteMethod();
       
   414 
       
   415             if (!compareMethods(getIndexedReadMethod(), otherIndexedReadMethod)) {
       
   416                 return false;
       
   417             }
       
   418 
       
   419             if (!compareMethods(getIndexedWriteMethod(), otherIndexedWriteMethod)) {
       
   420                 return false;
       
   421             }
       
   422 
       
   423             if (getIndexedPropertyType() != other.getIndexedPropertyType()) {
       
   424                 return false;
       
   425             }
       
   426             return super.equals(obj);
       
   427         }
       
   428         return false;
       
   429     }
       
   430 
       
   431     /**
       
   432      * Package-private constructor.
       
   433      * Merge two property descriptors.  Where they conflict, give the
       
   434      * second argument (y) priority over the first argumnnt (x).
       
   435      *
       
   436      * @param x  The first (lower priority) PropertyDescriptor
       
   437      * @param y  The second (higher priority) PropertyDescriptor
       
   438      */
       
   439 
       
   440     IndexedPropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) {
       
   441         super(x,y);
       
   442         if (x instanceof IndexedPropertyDescriptor) {
       
   443             IndexedPropertyDescriptor ix = (IndexedPropertyDescriptor)x;
       
   444             try {
       
   445                 Method xr = ix.getIndexedReadMethod();
       
   446                 if (xr != null) {
       
   447                     setIndexedReadMethod(xr);
       
   448                 }
       
   449 
       
   450                 Method xw = ix.getIndexedWriteMethod();
       
   451                 if (xw != null) {
       
   452                     setIndexedWriteMethod(xw);
       
   453                 }
       
   454             } catch (IntrospectionException ex) {
       
   455                 // Should not happen
       
   456                 throw new AssertionError(ex);
       
   457             }
       
   458         }
       
   459         if (y instanceof IndexedPropertyDescriptor) {
       
   460             IndexedPropertyDescriptor iy = (IndexedPropertyDescriptor)y;
       
   461             try {
       
   462                 Method yr = iy.getIndexedReadMethod();
       
   463                 if (yr != null && yr.getDeclaringClass() == getClass0()) {
       
   464                     setIndexedReadMethod(yr);
       
   465                 }
       
   466 
       
   467                 Method yw = iy.getIndexedWriteMethod();
       
   468                 if (yw != null && yw.getDeclaringClass() == getClass0()) {
       
   469                     setIndexedWriteMethod(yw);
       
   470                 }
       
   471             } catch (IntrospectionException ex) {
       
   472                 // Should not happen
       
   473                 throw new AssertionError(ex);
       
   474             }
       
   475         }
       
   476     }
       
   477 
       
   478     /*
       
   479      * Package-private dup constructor
       
   480      * This must isolate the new object from any changes to the old object.
       
   481      */
       
   482     IndexedPropertyDescriptor(IndexedPropertyDescriptor old) {
       
   483         super(old);
       
   484         indexedReadMethodRef = old.indexedReadMethodRef;
       
   485         indexedWriteMethodRef = old.indexedWriteMethodRef;
       
   486         indexedPropertyTypeRef = old.indexedPropertyTypeRef;
       
   487         indexedWriteMethodName = old.indexedWriteMethodName;
       
   488         indexedReadMethodName = old.indexedReadMethodName;
       
   489     }
       
   490 
       
   491     /**
       
   492      * Returns a hash code value for the object.
       
   493      * See {@link java.lang.Object#hashCode} for a complete description.
       
   494      *
       
   495      * @return a hash code value for this object.
       
   496      * @since 1.5
       
   497      */
       
   498     public int hashCode() {
       
   499         int result = super.hashCode();
       
   500 
       
   501         result = 37 * result + ((indexedWriteMethodName == null) ? 0 :
       
   502                                 indexedWriteMethodName.hashCode());
       
   503         result = 37 * result + ((indexedReadMethodName == null) ? 0 :
       
   504                                 indexedReadMethodName.hashCode());
       
   505         result = 37 * result + ((getIndexedPropertyType() == null) ? 0 :
       
   506                                 getIndexedPropertyType().hashCode());
       
   507 
       
   508         return result;
       
   509     }
       
   510 
       
   511     /*
       
   512     public String toString() {
       
   513         String message = super.toString();
       
   514 
       
   515         message += ", indexedType=";
       
   516         message += getIndexedPropertyType();
       
   517 
       
   518         message += ", indexedWriteMethod=";
       
   519         message += indexedWriteMethodName;
       
   520 
       
   521         message += ", indexedReadMethod=";
       
   522         message += indexedReadMethodName;
       
   523 
       
   524         return message;
       
   525     }
       
   526     */
       
   527 }