1 /*
2  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4  *
5  *
6  *
7  *
8  *
9  *
10  *
11  *
12  *
13  *
14  *
15  *
16  *
17  *
18  *
19  *
20  *
21  *
22  *
23  *
24  */

25
26 package java.beans;
27
28 import java.lang.ref.Reference;
29 import java.lang.reflect.Method;
30
31 /**
32  * An IndexedPropertyDescriptor describes a property that acts like an
33  * array and has an indexed read and/or indexed write method to access
34  * specific elements of the array.
35  * <p>
36  * An indexed property may also provide simple non-indexed read and write
37  * methods.  If these are present, they read and write arrays of the type
38  * returned by the indexed read method.
39  */

40
41 public class IndexedPropertyDescriptor extends PropertyDescriptor {
42
43     private Reference<? extends Class<?>> indexedPropertyTypeRef;
44     private final MethodRef indexedReadMethodRef = new MethodRef();
45     private final MethodRef indexedWriteMethodRef = new MethodRef();
46
47     private String indexedReadMethodName;
48     private String indexedWriteMethodName;
49
50     /**
51      * This constructor constructs an IndexedPropertyDescriptor for a property
52      * that follows the standard Java conventions by having getFoo and setFoo
53      * accessor methods, for both indexed access and array access.
54      * <p>
55      * Thus if the argument name is "fred", it will assume that there
56      * is an indexed reader method "getFred", a non-indexed (array) reader
57      * method also called "getFred", an indexed writer method "setFred",
58      * and finally a non-indexed writer method "setFred".
59      *
60      * @param propertyName The programmatic name of the property.
61      * @param beanClass The Class object for the target bean.
62      * @exception IntrospectionException if an exception occurs during
63      *              introspection.
64      */

65     public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass)
66                 throws IntrospectionException {
67         this(propertyName, beanClass,
68              Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName),
69              Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName),
70              Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName),
71              Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName));
72     }
73
74     /**
75      * This constructor takes the name of a simple property, and method
76      * names for reading and writing the property, both indexed
77      * and non-indexed.
78      *
79      * @param propertyName The programmatic name of the property.
80      * @param beanClass  The Class object for the target bean.
81      * @param readMethodName The name of the method used for reading the property
82      *           values as an array.  May be null if the property is write-only
83      *           or must be indexed.
84      * @param writeMethodName The name of the method used for writing the property
85      *           values as an array.  May be null if the property is read-only
86      *           or must be indexed.
87      * @param indexedReadMethodName The name of the method used for reading
88      *          an indexed property value.
89      *          May be null if the property is write-only.
90      * @param indexedWriteMethodName The name of the method used for writing
91      *          an indexed property value.
92      *          May be null if the property is read-only.
93      * @exception IntrospectionException if an exception occurs during
94      *              introspection.
95      */

96     public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass,
97                 String readMethodName, String writeMethodName,
98                 String indexedReadMethodName, String indexedWriteMethodName)
99                 throws IntrospectionException {
100         super(propertyName, beanClass, readMethodName, writeMethodName);
101
102         this.indexedReadMethodName = indexedReadMethodName;
103         if (indexedReadMethodName != null && getIndexedReadMethod() == null) {
104             throw new IntrospectionException("Method not found: " + indexedReadMethodName);
105         }
106
107         this.indexedWriteMethodName = indexedWriteMethodName;
108         if (indexedWriteMethodName != null && getIndexedWriteMethod() == null) {
109             throw new IntrospectionException("Method not found: " + indexedWriteMethodName);
110         }
111         // Implemented only for type checking.
112         findIndexedPropertyType(getIndexedReadMethod(), getIndexedWriteMethod());
113     }
114
115     /**
116      * This constructor takes the name of a simple property, and Method
117      * objects for reading and writing the property.
118      *
119      * @param propertyName The programmatic name of the property.
120      * @param readMethod The method used for reading the property values as an array.
121      *          May be null if the property is write-only or must be indexed.
122      * @param writeMethod The method used for writing the property values as an array.
123      *          May be null if the property is read-only or must be indexed.
124      * @param indexedReadMethod The method used for reading an indexed property value.
125      *          May be null if the property is write-only.
126      * @param indexedWriteMethod The method used for writing an indexed property value.
127      *          May be null if the property is read-only.
128      * @exception IntrospectionException if an exception occurs during
129      *              introspection.
130      */

131     public IndexedPropertyDescriptor(String propertyName, Method readMethod, Method writeMethod,
132                                             Method indexedReadMethod, Method indexedWriteMethod)
133                 throws IntrospectionException {
134         super(propertyName, readMethod, writeMethod);
135
136         setIndexedReadMethod0(indexedReadMethod);
137         setIndexedWriteMethod0(indexedWriteMethod);
138
139         // Type checking
140         setIndexedPropertyType(findIndexedPropertyType(indexedReadMethod, indexedWriteMethod));
141     }
142
143     /**
144      * Creates <code>PropertyDescriptor</code> for the specified bean
145      * with the specified name and methods to read/write the property value.
146      *
147      * @param bean          the type of the target bean
148      * @param base          the base name of the property (the rest of the method name)
149      * @param read          the method used for reading the property value
150      * @param write         the method used for writing the property value
151      * @param readIndexed   the method used for reading an indexed property value
152      * @param writeIndexed  the method used for writing an indexed property value
153      * @exception IntrospectionException if an exception occurs during introspection
154      *
155      * @since 1.7
156      */

157     IndexedPropertyDescriptor(Class<?> bean, String base, Method read, Method write, Method readIndexed, Method writeIndexed) throws IntrospectionException {
158         super(bean, base, read, write);
159
160         setIndexedReadMethod0(readIndexed);
161         setIndexedWriteMethod0(writeIndexed);
162
163         // Type checking
164         setIndexedPropertyType(findIndexedPropertyType(readIndexed, writeIndexed));
165     }
166
167     /**
168      * Gets the method that should be used to read an indexed
169      * property value.
170      *
171      * @return The method that should be used to read an indexed
172      * property value.
173      * May return null if the property isn't indexed or is write-only.
174      */

175     public synchronized Method getIndexedReadMethod() {
176         Method indexedReadMethod = this.indexedReadMethodRef.get();
177         if (indexedReadMethod == null) {
178             Class<?> cls = getClass0();
179             if (cls == null ||
180                 (indexedReadMethodName == null && !this.indexedReadMethodRef.isSet())) {
181                 // the Indexed readMethod was explicitly set to null.
182                 return null;
183             }
184             String nextMethodName = Introspector.GET_PREFIX + getBaseName();
185             if (indexedReadMethodName == null) {
186                 Class<?> type = getIndexedPropertyType0();
187                 if (type == boolean.class || type == null) {
188                     indexedReadMethodName = Introspector.IS_PREFIX + getBaseName();
189                 } else {
190                     indexedReadMethodName = nextMethodName;
191                 }
192             }
193
194             Class<?>[] args = { int.class };
195             indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args);
196             if ((indexedReadMethod == null) && !indexedReadMethodName.equals(nextMethodName)) {
197                 // no "is" method, so look for a "get" method.
198                 indexedReadMethodName = nextMethodName;
199                 indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args);
200             }
201             setIndexedReadMethod0(indexedReadMethod);
202         }
203         return indexedReadMethod;
204     }
205
206     /**
207      * Sets the method that should be used to read an indexed property value.
208      *
209      * @param readMethod The new indexed read method.
210      * @throws IntrospectionException if an exception occurs during
211      * introspection.
212      */

213     public synchronized void setIndexedReadMethod(Method readMethod)
214         throws IntrospectionException {
215
216         // the indexed property type is set by the reader.
217         setIndexedPropertyType(findIndexedPropertyType(readMethod,
218                                                        this.indexedWriteMethodRef.get()));
219         setIndexedReadMethod0(readMethod);
220     }
221
222     private void setIndexedReadMethod0(Method readMethod) {
223         this.indexedReadMethodRef.set(readMethod);
224         if (readMethod == null) {
225             indexedReadMethodName = null;
226             return;
227         }
228         setClass0(readMethod.getDeclaringClass());
229
230         indexedReadMethodName = readMethod.getName();
231         setTransient(readMethod.getAnnotation(Transient.class));
232     }
233
234
235     /**
236      * Gets the method that should be used to write an indexed property value.
237      *
238      * @return The method that should be used to write an indexed
239      * property value.
240      * May return null if the property isn't indexed or is read-only.
241      */

242     public synchronized Method getIndexedWriteMethod() {
243         Method indexedWriteMethod = this.indexedWriteMethodRef.get();
244         if (indexedWriteMethod == null) {
245             Class<?> cls = getClass0();
246             if (cls == null ||
247                 (indexedWriteMethodName == null && !this.indexedWriteMethodRef.isSet())) {
248                 // the Indexed writeMethod was explicitly set to null.
249                 return null;
250             }
251
252             // We need the indexed type to ensure that we get the correct method.
253             // Cannot use the getIndexedPropertyType method since that could
254             // result in an infinite loop.
255             Class<?> type = getIndexedPropertyType0();
256             if (type == null) {
257                 try {
258                     type = findIndexedPropertyType(getIndexedReadMethod(), null);
259                     setIndexedPropertyType(type);
260                 } catch (IntrospectionException ex) {
261                     // Set iprop type to be the classic type
262                     Class<?> propType = getPropertyType();
263                     if (propType.isArray()) {
264                         type = propType.getComponentType();
265                     }
266                 }
267             }
268
269             if (indexedWriteMethodName == null) {
270                 indexedWriteMethodName = Introspector.SET_PREFIX + getBaseName();
271             }
272
273             Class<?>[] args = (type == null) ? null : new Class<?>[] { int.class, type };
274             indexedWriteMethod = Introspector.findMethod(cls, indexedWriteMethodName, 2, args);
275             if (indexedWriteMethod != null) {
276                 if (!indexedWriteMethod.getReturnType().equals(void.class)) {
277                     indexedWriteMethod = null;
278                 }
279             }
280             setIndexedWriteMethod0(indexedWriteMethod);
281         }
282         return indexedWriteMethod;
283     }
284
285     /**
286      * Sets the method that should be used to write an indexed property value.
287      *
288      * @param writeMethod The new indexed write method.
289      * @throws IntrospectionException if an exception occurs during
290      * introspection.
291      */

292     public synchronized void setIndexedWriteMethod(Method writeMethod)
293         throws IntrospectionException {
294
295         // If the indexed property type has not been set, then set it.
296         Class<?> type = findIndexedPropertyType(getIndexedReadMethod(),
297                                              writeMethod);
298         setIndexedPropertyType(type);
299         setIndexedWriteMethod0(writeMethod);
300     }
301
302     private void setIndexedWriteMethod0(Method writeMethod) {
303         this.indexedWriteMethodRef.set(writeMethod);
304         if (writeMethod == null) {
305             indexedWriteMethodName = null;
306             return;
307         }
308         setClass0(writeMethod.getDeclaringClass());
309
310         indexedWriteMethodName = writeMethod.getName();
311         setTransient(writeMethod.getAnnotation(Transient.class));
312     }
313
314     /**
315      * Returns the Java type info for the indexed property.
316      * Note that the {@code Class} object may describe
317      * primitive Java types such as {@code int}.
318      * This type is returned by the indexed read method
319      * or is used as the parameter type of the indexed write method.
320      *
321      * @return the {@code Class} object that represents the Java type info,
322      *         or {@code nullif the type cannot be determined
323      */

324     public synchronized Class<?> getIndexedPropertyType() {
325         Class<?> type = getIndexedPropertyType0();
326         if (type == null) {
327             try {
328                 type = findIndexedPropertyType(getIndexedReadMethod(),
329                                                getIndexedWriteMethod());
330                 setIndexedPropertyType(type);
331             } catch (IntrospectionException ex) {
332                 // fall
333             }
334         }
335         return type;
336     }
337
338     // Private methods which set get/set the Reference objects
339
340     private void setIndexedPropertyType(Class<?> type) {
341         this.indexedPropertyTypeRef = getWeakReference(type);
342     }
343
344     private Class<?> getIndexedPropertyType0() {
345         return (this.indexedPropertyTypeRef != null)
346                 ? this.indexedPropertyTypeRef.get()
347                 : null;
348     }
349
350     private Class<?> findIndexedPropertyType(Method indexedReadMethod,
351                                           Method indexedWriteMethod)
352         throws IntrospectionException {
353         Class<?> indexedPropertyType = null;
354
355         if (indexedReadMethod != null) {
356             Class params[] = getParameterTypes(getClass0(), indexedReadMethod);
357             if (params.length != 1) {
358                 throw new IntrospectionException("bad indexed read method arg count");
359             }
360             if (params[0] != Integer.TYPE) {
361                 throw new IntrospectionException("non int index to indexed read method");
362             }
363             indexedPropertyType = getReturnType(getClass0(), indexedReadMethod);
364             if (indexedPropertyType == Void.TYPE) {
365                 throw new IntrospectionException("indexed read method returns void");
366             }
367         }
368         if (indexedWriteMethod != null) {
369             Class params[] = getParameterTypes(getClass0(), indexedWriteMethod);
370             if (params.length != 2) {
371                 throw new IntrospectionException("bad indexed write method arg count");
372             }
373             if (params[0] != Integer.TYPE) {
374                 throw new IntrospectionException("non int index to indexed write method");
375             }
376             if (indexedPropertyType == null || params[1].isAssignableFrom(indexedPropertyType)) {
377                 indexedPropertyType = params[1];
378             } else if (!indexedPropertyType.isAssignableFrom(params[1])) {
379                 throw new IntrospectionException(
380                                                  "type mismatch between indexed read and indexed write methods: "
381                                                  + getName());
382             }
383         }
384         Class<?> propertyType = getPropertyType();
385         if (propertyType != null && (!propertyType.isArray() ||
386                                      propertyType.getComponentType() != indexedPropertyType)) {
387             throw new IntrospectionException("type mismatch between indexed and non-indexed methods: "
388                                              + getName());
389         }
390         return indexedPropertyType;
391     }
392
393     /**
394      * Compares this <code>PropertyDescriptor</code> against the specified object.
395      * Returns true if the objects are the same. Two <code>PropertyDescriptor</code>s
396      * are the same if the read, write, property types, property editor and
397      * flags  are equivalent.
398      *
399      * @since 1.4
400      */

401     public boolean equals(Object obj) {
402         // Note: This would be identical to PropertyDescriptor but they don't
403         // share the same fields.
404         if (this == obj) {
405             return true;
406         }
407
408         if (obj != null && obj instanceof IndexedPropertyDescriptor) {
409             IndexedPropertyDescriptor other = (IndexedPropertyDescriptor)obj;
410             Method otherIndexedReadMethod = other.getIndexedReadMethod();
411             Method otherIndexedWriteMethod = other.getIndexedWriteMethod();
412
413             if (!compareMethods(getIndexedReadMethod(), otherIndexedReadMethod)) {
414                 return false;
415             }
416
417             if (!compareMethods(getIndexedWriteMethod(), otherIndexedWriteMethod)) {
418                 return false;
419             }
420
421             if (getIndexedPropertyType() != other.getIndexedPropertyType()) {
422                 return false;
423             }
424             return super.equals(obj);
425         }
426         return false;
427     }
428
429     /**
430      * Package-private constructor.
431      * Merge two property descriptors.  Where they conflict, give the
432      * second argument (y) priority over the first argumnnt (x).
433      *
434      * @param x  The first (lower priority) PropertyDescriptor
435      * @param y  The second (higher priority) PropertyDescriptor
436      */

437
438     IndexedPropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) {
439         super(x,y);
440         if (x instanceof IndexedPropertyDescriptor) {
441             IndexedPropertyDescriptor ix = (IndexedPropertyDescriptor)x;
442             try {
443                 Method xr = ix.getIndexedReadMethod();
444                 if (xr != null) {
445                     setIndexedReadMethod(xr);
446                 }
447
448                 Method xw = ix.getIndexedWriteMethod();
449                 if (xw != null) {
450                     setIndexedWriteMethod(xw);
451                 }
452             } catch (IntrospectionException ex) {
453                 // Should not happen
454                 throw new AssertionError(ex);
455             }
456         }
457         if (y instanceof IndexedPropertyDescriptor) {
458             IndexedPropertyDescriptor iy = (IndexedPropertyDescriptor)y;
459             try {
460                 Method yr = iy.getIndexedReadMethod();
461                 if (yr != null && yr.getDeclaringClass() == getClass0()) {
462                     setIndexedReadMethod(yr);
463                 }
464
465                 Method yw = iy.getIndexedWriteMethod();
466                 if (yw != null && yw.getDeclaringClass() == getClass0()) {
467                     setIndexedWriteMethod(yw);
468                 }
469             } catch (IntrospectionException ex) {
470                 // Should not happen
471                 throw new AssertionError(ex);
472             }
473         }
474     }
475
476     /*
477      * Package-private dup constructor
478      * This must isolate the new object from any changes to the old object.
479      */

480     IndexedPropertyDescriptor(IndexedPropertyDescriptor old) {
481         super(old);
482         this.indexedReadMethodRef.set(old.indexedReadMethodRef.get());
483         this.indexedWriteMethodRef.set(old.indexedWriteMethodRef.get());
484         indexedPropertyTypeRef = old.indexedPropertyTypeRef;
485         indexedWriteMethodName = old.indexedWriteMethodName;
486         indexedReadMethodName = old.indexedReadMethodName;
487     }
488
489     void updateGenericsFor(Class<?> type) {
490         super.updateGenericsFor(type);
491         try {
492             setIndexedPropertyType(findIndexedPropertyType(this.indexedReadMethodRef.get(), this.indexedWriteMethodRef.get()));
493         }
494         catch (IntrospectionException exception) {
495             setIndexedPropertyType(null);
496         }
497     }
498
499     /**
500      * Returns a hash code value for the object.
501      * See {@link java.lang.Object#hashCode} for a complete description.
502      *
503      * @return a hash code value for this object.
504      * @since 1.5
505      */

506     public int hashCode() {
507         int result = super.hashCode();
508
509         result = 37 * result + ((indexedWriteMethodName == null) ? 0 :
510                                 indexedWriteMethodName.hashCode());
511         result = 37 * result + ((indexedReadMethodName == null) ? 0 :
512                                 indexedReadMethodName.hashCode());
513         result = 37 * result + ((getIndexedPropertyType() == null) ? 0 :
514                                 getIndexedPropertyType().hashCode());
515
516         return result;
517     }
518
519     void appendTo(StringBuilder sb) {
520         super.appendTo(sb);
521         appendTo(sb, "indexedPropertyType"this.indexedPropertyTypeRef);
522         appendTo(sb, "indexedReadMethod"this.indexedReadMethodRef.get());
523         appendTo(sb, "indexedWriteMethod"this.indexedWriteMethodRef.get());
524     }
525 }
526
Powered by JavaMelody