001 /*
002 * Copyright 1996-2004 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.beans;
027
028 import java.lang.ref.Reference;
029
030 import java.lang.reflect.Method;
031
032 /**
033 * An IndexedPropertyDescriptor describes a property that acts like an
034 * array and has an indexed read and/or indexed write method to access
035 * specific elements of the array.
036 * <p>
037 * An indexed property may also provide simple non-indexed read and write
038 * methods. If these are present, they read and write arrays of the type
039 * returned by the indexed read method.
040 */
041
042 public class IndexedPropertyDescriptor extends PropertyDescriptor {
043
044 private Reference<Class> indexedPropertyTypeRef;
045 private Reference<Method> indexedReadMethodRef;
046 private Reference<Method> indexedWriteMethodRef;
047
048 private String indexedReadMethodName;
049 private String indexedWriteMethodName;
050
051 /**
052 * This constructor constructs an IndexedPropertyDescriptor for a property
053 * that follows the standard Java conventions by having getFoo and setFoo
054 * accessor methods, for both indexed access and array access.
055 * <p>
056 * Thus if the argument name is "fred", it will assume that there
057 * is an indexed reader method "getFred", a non-indexed (array) reader
058 * method also called "getFred", an indexed writer method "setFred",
059 * and finally a non-indexed writer method "setFred".
060 *
061 * @param propertyName The programmatic name of the property.
062 * @param beanClass The Class object for the target bean.
063 * @exception IntrospectionException if an exception occurs during
064 * introspection.
065 */
066 public IndexedPropertyDescriptor(String propertyName,
067 Class<?> beanClass) throws IntrospectionException {
068 this (propertyName, beanClass, Introspector.GET_PREFIX
069 + NameGenerator.capitalize(propertyName),
070 Introspector.SET_PREFIX
071 + NameGenerator.capitalize(propertyName),
072 Introspector.GET_PREFIX
073 + NameGenerator.capitalize(propertyName),
074 Introspector.SET_PREFIX
075 + NameGenerator.capitalize(propertyName));
076 }
077
078 /**
079 * This constructor takes the name of a simple property, and method
080 * names for reading and writing the property, both indexed
081 * and non-indexed.
082 *
083 * @param propertyName The programmatic name of the property.
084 * @param beanClass The Class object for the target bean.
085 * @param readMethodName The name of the method used for reading the property
086 * values as an array. May be null if the property is write-only
087 * or must be indexed.
088 * @param writeMethodName The name of the method used for writing the property
089 * values as an array. May be null if the property is read-only
090 * or must be indexed.
091 * @param indexedReadMethodName The name of the method used for reading
092 * an indexed property value.
093 * May be null if the property is write-only.
094 * @param indexedWriteMethodName The name of the method used for writing
095 * an indexed property value.
096 * May be null if the property is read-only.
097 * @exception IntrospectionException if an exception occurs during
098 * introspection.
099 */
100 public IndexedPropertyDescriptor(String propertyName,
101 Class<?> beanClass, String readMethodName,
102 String writeMethodName, String indexedReadMethodName,
103 String indexedWriteMethodName)
104 throws IntrospectionException {
105 super (propertyName, beanClass, readMethodName, writeMethodName);
106
107 this .indexedReadMethodName = indexedReadMethodName;
108 if (indexedReadMethodName != null
109 && getIndexedReadMethod() == null) {
110 throw new IntrospectionException("Method not found: "
111 + indexedReadMethodName);
112 }
113
114 this .indexedWriteMethodName = indexedWriteMethodName;
115 if (indexedWriteMethodName != null
116 && getIndexedWriteMethod() == null) {
117 throw new IntrospectionException("Method not found: "
118 + indexedWriteMethodName);
119 }
120 // Implemented only for type checking.
121 findIndexedPropertyType(getIndexedReadMethod(),
122 getIndexedWriteMethod());
123 }
124
125 /**
126 * This constructor takes the name of a simple property, and Method
127 * objects for reading and writing the property.
128 *
129 * @param propertyName The programmatic name of the pro
130 perty.
131 * @param readMethod The method used for reading the property values as an array.
132 * May be null if the property is write-only or must be indexed.
133 * @param writeMethod The method used for writing the property values as an array.
134 * May be null if the property is read-only or must be indexed.
135 * @param indexedReadMethod The method used for reading an indexed property value.
136 * May be null if the property is write-only.
137 * @param indexedWriteMethod The method used for writing an indexed property value.
138 * May be null if the property is read-only.
139 * @exception IntrospectionException if an exception occurs during
140 * introspection.
141 */
142 public IndexedPropertyDescriptor(String propertyName,
143 Method readMethod, Method writeMethod,
144 Method indexedReadMethod, Method indexedWriteMethod)
145 throws IntrospectionException {
146 super (propertyName, readMethod, writeMethod);
147
148 setIndexedReadMethod0(indexedReadMethod);
149 setIndexedWriteMethod0(indexedWriteMethod);
150
151 // Type checking
152 setIndexedPropertyType(findIndexedPropertyType(
153 indexedReadMethod, indexedWriteMethod));
154 }
155
156 /**
157 * Creates <code>PropertyDescriptor</code> for the specified bean
158 * with the specified name and methods to read/write the property value.
159 *
160 * @param bean the type of the target bean
161 * @param base the base name of the property (the rest of the method name)
162 * @param read the method used for reading the property value
163 * @param write the method used for writing the property value
164 * @param readIndexed the method used for reading an indexed property value
165 * @param writeIndexed the method used for writing an indexed property value
166 * @exception IntrospectionException if an exception occurs during introspection
167 *
168 * @since 1.7
169 */
170 IndexedPropertyDescriptor(Class<?> bean, String base, Method read,
171 Method write, Method readIndexed, Method writeIndexed)
172 throws IntrospectionException {
173 super (bean, base, read, write);
174
175 setIndexedReadMethod0(readIndexed);
176 setIndexedWriteMethod0(writeIndexed);
177
178 // Type checking
179 setIndexedPropertyType(findIndexedPropertyType(readIndexed,
180 writeIndexed));
181 }
182
183 /**
184 * Gets the method that should be used to read an indexed
185 * property value.
186 *
187 * @return The method that should be used to read an indexed
188 * property value.
189 * May return null if the property isn't indexed or is write-only.
190 */
191 public synchronized Method getIndexedReadMethod() {
192 Method indexedReadMethod = getIndexedReadMethod0();
193 if (indexedReadMethod == null) {
194 Class cls = getClass0();
195 if (cls == null
196 || (indexedReadMethodName == null && indexedReadMethodRef == null)) {
197 // the Indexed readMethod was explicitly set to null.
198 return null;
199 }
200 if (indexedReadMethodName == null) {
201 Class type = getIndexedPropertyType0();
202 if (type == boolean.class || type == null) {
203 indexedReadMethodName = Introspector.IS_PREFIX
204 + getBaseName();
205 } else {
206 indexedReadMethodName = Introspector.GET_PREFIX
207 + getBaseName();
208 }
209 }
210
211 Class[] args = { int.class };
212
213 indexedReadMethod = Introspector.findMethod(cls,
214 indexedReadMethodName, 1, args);
215 if (indexedReadMethod == null) {
216 // no "is" method, so look for a "get" method.
217 indexedReadMethodName = Introspector.GET_PREFIX
218 + getBaseName();
219 indexedReadMethod = Introspector.findMethod(cls,
220 indexedReadMethodName, 1, args);
221 }
222 setIndexedReadMethod0(indexedReadMethod);
223 }
224 return indexedReadMethod;
225 }
226
227 /**
228 * Sets the method that should be used to read an indexed property value.
229 *
230 * @param readMethod The new indexed read method.
231 */
232 public synchronized void setIndexedReadMethod(Method readMethod)
233 throws IntrospectionException {
234
235 // the indexed property type is set by the reader.
236 setIndexedPropertyType(findIndexedPropertyType(readMethod,
237 getIndexedWriteMethod0()));
238 setIndexedReadMethod0(readMethod);
239 }
240
241 private void setIndexedReadMethod0(Method readMethod) {
242 if (readMethod == null) {
243 indexedReadMethodName = null;
244 indexedReadMethodRef = null;
245 return;
246 }
247 setClass0(readMethod.getDeclaringClass());
248
249 indexedReadMethodName = readMethod.getName();
250 this .indexedReadMethodRef = getSoftReference(readMethod);
251 }
252
253 /**
254 * Gets the method that should be used to write an indexed property value.
255 *
256 * @return The method that should be used to write an indexed
257 * property value.
258 * May return null if the property isn't indexed or is read-only.
259 */
260 public synchronized Method getIndexedWriteMethod() {
261 Method indexedWriteMethod = getIndexedWriteMethod0();
262 if (indexedWriteMethod == null) {
263 Class cls = getClass0();
264 if (cls == null
265 || (indexedWriteMethodName == null && indexedWriteMethodRef == null)) {
266 // the Indexed writeMethod was explicitly set to null.
267 return null;
268 }
269
270 // We need the indexed type to ensure that we get the correct method.
271 // Cannot use the getIndexedPropertyType method since that could
272 // result in an infinite loop.
273 Class type = getIndexedPropertyType0();
274 if (type == null) {
275 try {
276 type = findIndexedPropertyType(
277 getIndexedReadMethod(), null);
278 setIndexedPropertyType(type);
279 } catch (IntrospectionException ex) {
280 // Set iprop type to be the classic type
281 Class propType = getPropertyType();
282 if (propType.isArray()) {
283 type = propType.getComponentType();
284 }
285 }
286 }
287
288 if (indexedWriteMethodName == null) {
289 indexedWriteMethodName = Introspector.SET_PREFIX
290 + getBaseName();
291 }
292 indexedWriteMethod = Introspector.findMethod(cls,
293 indexedWriteMethodName, 2, (type == null) ? null
294 : new Class[] { int.class, type });
295 setIndexedWriteMethod0(indexedWriteMethod);
296 }
297 return indexedWriteMethod;
298 }
299
300 /**
301 * Sets the method that should be used to write an indexed property value.
302 *
303 * @param writeMethod The new indexed write method.
304 */
305 public synchronized void setIndexedWriteMethod(Method writeMethod)
306 throws IntrospectionException {
307
308 // If the indexed property type has not been set, then set it.
309 Class type = findIndexedPropertyType(getIndexedReadMethod(),
310 writeMethod);
311 setIndexedPropertyType(type);
312 setIndexedWriteMethod0(writeMethod);
313 }
314
315 private void setIndexedWriteMethod0(Method writeMethod) {
316 if (writeMethod == null) {
317 indexedWriteMethodName = null;
318 indexedWriteMethodRef = null;
319 return;
320 }
321 setClass0(writeMethod.getDeclaringClass());
322
323 indexedWriteMethodName = writeMethod.getName();
324 this .indexedWriteMethodRef = getSoftReference(writeMethod);
325 }
326
327 /**
328 * Gets the <code>Class</code> object of the indexed properties' type.
329 * The returned <code>Class</code> may describe a primitive type such as <code>int</code>.
330 *
331 * @return The <code>Class</code> for the indexed properties' type; may return <code>null</code>
332 * if the type cannot be determined.
333 */
334 public synchronized Class<?> getIndexedPropertyType() {
335 Class type = getIndexedPropertyType0();
336 if (type == null) {
337 try {
338 type = findIndexedPropertyType(getIndexedReadMethod(),
339 getIndexedWriteMethod());
340 setIndexedPropertyType(type);
341 } catch (IntrospectionException ex) {
342 // fall
343 }
344 }
345 return type;
346 }
347
348 // Private methods which set get/set the Reference objects
349
350 private void setIndexedPropertyType(Class type) {
351 this .indexedPropertyTypeRef = getWeakReference(type);
352 }
353
354 private Class getIndexedPropertyType0() {
355 return (this .indexedPropertyTypeRef != null) ? this .indexedPropertyTypeRef
356 .get()
357 : null;
358 }
359
360 private Method getIndexedReadMethod0() {
361 return (this .indexedReadMethodRef != null) ? this .indexedReadMethodRef
362 .get()
363 : null;
364 }
365
366 private Method getIndexedWriteMethod0() {
367 return (this .indexedWriteMethodRef != null) ? this .indexedWriteMethodRef
368 .get()
369 : null;
370 }
371
372 private Class findIndexedPropertyType(Method indexedReadMethod,
373 Method indexedWriteMethod) throws IntrospectionException {
374 Class indexedPropertyType = null;
375
376 if (indexedReadMethod != null) {
377 Class params[] = getParameterTypes(getClass0(),
378 indexedReadMethod);
379 if (params.length != 1) {
380 throw new IntrospectionException(
381 "bad indexed read method arg count");
382 }
383 if (params[0] != Integer.TYPE) {
384 throw new IntrospectionException(
385 "non int index to indexed read method");
386 }
387 indexedPropertyType = getReturnType(getClass0(),
388 indexedReadMethod);
389 if (indexedPropertyType == Void.TYPE) {
390 throw new IntrospectionException(
391 "indexed read method returns void");
392 }
393 }
394 if (indexedWriteMethod != null) {
395 Class params[] = getParameterTypes(getClass0(),
396 indexedWriteMethod);
397 if (params.length != 2) {
398 throw new IntrospectionException(
399 "bad indexed write method arg count");
400 }
401 if (params[0] != Integer.TYPE) {
402 throw new IntrospectionException(
403 "non int index to indexed write method");
404 }
405 if (indexedPropertyType != null
406 && indexedPropertyType != params[1]) {
407 throw new IntrospectionException(
408 "type mismatch between indexed read and indexed write methods: "
409 + getName());
410 }
411 indexedPropertyType = params[1];
412 }
413 Class propertyType = getPropertyType();
414 if (propertyType != null
415 && (!propertyType.isArray() || propertyType
416 .getComponentType() != indexedPropertyType)) {
417 throw new IntrospectionException(
418 "type mismatch between indexed and non-indexed methods: "
419 + getName());
420 }
421 return indexedPropertyType;
422 }
423
424 /**
425 * Compares this <code>PropertyDescriptor</code> against the specified object.
426 * Returns true if the objects are the same. Two <code>PropertyDescriptor</code>s
427 * are the same if the read, write, property types, property editor and
428 * flags are equivalent.
429 *
430 * @since 1.4
431 */
432 public boolean equals(Object obj) {
433 // Note: This would be identical to PropertyDescriptor but they don't
434 // share the same fields.
435 if (this == obj) {
436 return true;
437 }
438
439 if (obj != null && obj instanceof IndexedPropertyDescriptor) {
440 IndexedPropertyDescriptor other = (IndexedPropertyDescriptor) obj;
441 Method otherIndexedReadMethod = other
442 .getIndexedReadMethod();
443 Method otherIndexedWriteMethod = other
444 .getIndexedWriteMethod();
445
446 if (!compareMethods(getIndexedReadMethod(),
447 otherIndexedReadMethod)) {
448 return false;
449 }
450
451 if (!compareMethods(getIndexedWriteMethod(),
452 otherIndexedWriteMethod)) {
453 return false;
454 }
455
456 if (getIndexedPropertyType() != other
457 .getIndexedPropertyType()) {
458 return false;
459 }
460 return super .equals(obj);
461 }
462 return false;
463 }
464
465 /**
466 * Package-private constructor.
467 * Merge two property descriptors. Where they conflict, give the
468 * second argument (y) priority over the first argumnnt (x).
469 *
470 * @param x The first (lower priority) PropertyDescriptor
471 * @param y The second (higher priority) PropertyDescriptor
472 */
473
474 IndexedPropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) {
475 super (x, y);
476 if (x instanceof IndexedPropertyDescriptor) {
477 IndexedPropertyDescriptor ix = (IndexedPropertyDescriptor) x;
478 try {
479 Method xr = ix.getIndexedReadMethod();
480 if (xr != null) {
481 setIndexedReadMethod(xr);
482 }
483
484 Method xw = ix.getIndexedWriteMethod();
485 if (xw != null) {
486 setIndexedWriteMethod(xw);
487 }
488 } catch (IntrospectionException ex) {
489 // Should not happen
490 throw new AssertionError(ex);
491 }
492 }
493 if (y instanceof IndexedPropertyDescriptor) {
494 IndexedPropertyDescriptor iy = (IndexedPropertyDescriptor) y;
495 try {
496 Method yr = iy.getIndexedReadMethod();
497 if (yr != null && yr.getDeclaringClass() == getClass0()) {
498 setIndexedReadMethod(yr);
499 }
500
501 Method yw = iy.getIndexedWriteMethod();
502 if (yw != null && yw.getDeclaringClass() == getClass0()) {
503 setIndexedWriteMethod(yw);
504 }
505 } catch (IntrospectionException ex) {
506 // Should not happen
507 throw new AssertionError(ex);
508 }
509 }
510 }
511
512 /*
513 * Package-private dup constructor
514 * This must isolate the new object from any changes to the old object.
515 */
516 IndexedPropertyDescriptor(IndexedPropertyDescriptor old) {
517 super (old);
518 indexedReadMethodRef = old.indexedReadMethodRef;
519 indexedWriteMethodRef = old.indexedWriteMethodRef;
520 indexedPropertyTypeRef = old.indexedPropertyTypeRef;
521 indexedWriteMethodName = old.indexedWriteMethodName;
522 indexedReadMethodName = old.indexedReadMethodName;
523 }
524
525 /**
526 * Returns a hash code value for the object.
527 * See {@link java.lang.Object#hashCode} for a complete description.
528 *
529 * @return a hash code value for this object.
530 * @since 1.5
531 */
532 public int hashCode() {
533 int result = super .hashCode();
534
535 result = 37
536 * result
537 + ((indexedWriteMethodName == null) ? 0
538 : indexedWriteMethodName.hashCode());
539 result = 37
540 * result
541 + ((indexedReadMethodName == null) ? 0
542 : indexedReadMethodName.hashCode());
543 result = 37
544 * result
545 + ((getIndexedPropertyType() == null) ? 0
546 : getIndexedPropertyType().hashCode());
547
548 return result;
549 }
550
551 /*
552 public String toString() {
553 String message = super.toString();
554
555 message += ", indexedType=";
556 message += getIndexedPropertyType();
557
558 message += ", indexedWriteMethod=";
559 message += indexedWriteMethodName;
560
561 message += ", indexedReadMethod=";
562 message += indexedReadMethodName;
563
564 return message;
565 }
566 */
567 }
|