|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package java.beans; |
|
|
|
import java.lang.ref.Reference; |
|
import java.lang.reflect.Method; |
|
|
|
/** |
|
* An IndexedPropertyDescriptor describes a property that acts like an |
|
* array and has an indexed read and/or indexed write method to access |
|
* specific elements of the array. |
|
* <p> |
|
* An indexed property may also provide simple non-indexed read and write |
|
* methods. If these are present, they read and write arrays of the type |
|
* returned by the indexed read method. |
|
*/ |
|
|
|
public class IndexedPropertyDescriptor extends PropertyDescriptor { |
|
|
|
private Reference<? extends Class<?>> indexedPropertyTypeRef; |
|
private final MethodRef indexedReadMethodRef = new MethodRef(); |
|
private final MethodRef indexedWriteMethodRef = new MethodRef(); |
|
|
|
private String indexedReadMethodName; |
|
private String indexedWriteMethodName; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass) |
|
throws IntrospectionException { |
|
this(propertyName, beanClass, |
|
Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName), |
|
Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName), |
|
Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName), |
|
Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass, |
|
String readMethodName, String writeMethodName, |
|
String indexedReadMethodName, String indexedWriteMethodName) |
|
throws IntrospectionException { |
|
super(propertyName, beanClass, readMethodName, writeMethodName); |
|
|
|
this.indexedReadMethodName = indexedReadMethodName; |
|
if (indexedReadMethodName != null && getIndexedReadMethod() == null) { |
|
throw new IntrospectionException("Method not found: " + indexedReadMethodName); |
|
} |
|
|
|
this.indexedWriteMethodName = indexedWriteMethodName; |
|
if (indexedWriteMethodName != null && getIndexedWriteMethod() == null) { |
|
throw new IntrospectionException("Method not found: " + indexedWriteMethodName); |
|
} |
|
|
|
findIndexedPropertyType(getIndexedReadMethod(), getIndexedWriteMethod()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public IndexedPropertyDescriptor(String propertyName, Method readMethod, Method writeMethod, |
|
Method indexedReadMethod, Method indexedWriteMethod) |
|
throws IntrospectionException { |
|
super(propertyName, readMethod, writeMethod); |
|
|
|
setIndexedReadMethod0(indexedReadMethod); |
|
setIndexedWriteMethod0(indexedWriteMethod); |
|
|
|
|
|
setIndexedPropertyType(findIndexedPropertyType(indexedReadMethod, indexedWriteMethod)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
IndexedPropertyDescriptor(Class<?> bean, String base, Method read, Method write, Method readIndexed, Method writeIndexed) throws IntrospectionException { |
|
super(bean, base, read, write); |
|
|
|
setIndexedReadMethod0(readIndexed); |
|
setIndexedWriteMethod0(writeIndexed); |
|
|
|
|
|
setIndexedPropertyType(findIndexedPropertyType(readIndexed, writeIndexed)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized Method getIndexedReadMethod() { |
|
Method indexedReadMethod = this.indexedReadMethodRef.get(); |
|
if (indexedReadMethod == null) { |
|
Class<?> cls = getClass0(); |
|
if (cls == null || |
|
(indexedReadMethodName == null && !this.indexedReadMethodRef.isSet())) { |
|
|
|
return null; |
|
} |
|
String nextMethodName = Introspector.GET_PREFIX + getBaseName(); |
|
if (indexedReadMethodName == null) { |
|
Class<?> type = getIndexedPropertyType0(); |
|
if (type == boolean.class || type == null) { |
|
indexedReadMethodName = Introspector.IS_PREFIX + getBaseName(); |
|
} else { |
|
indexedReadMethodName = nextMethodName; |
|
} |
|
} |
|
|
|
Class<?>[] args = { int.class }; |
|
indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args); |
|
if ((indexedReadMethod == null) && !indexedReadMethodName.equals(nextMethodName)) { |
|
|
|
indexedReadMethodName = nextMethodName; |
|
indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args); |
|
} |
|
setIndexedReadMethod0(indexedReadMethod); |
|
} |
|
return indexedReadMethod; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized void setIndexedReadMethod(Method readMethod) |
|
throws IntrospectionException { |
|
|
|
|
|
setIndexedPropertyType(findIndexedPropertyType(readMethod, |
|
this.indexedWriteMethodRef.get())); |
|
setIndexedReadMethod0(readMethod); |
|
} |
|
|
|
private void setIndexedReadMethod0(Method readMethod) { |
|
this.indexedReadMethodRef.set(readMethod); |
|
if (readMethod == null) { |
|
indexedReadMethodName = null; |
|
return; |
|
} |
|
setClass0(readMethod.getDeclaringClass()); |
|
|
|
indexedReadMethodName = readMethod.getName(); |
|
setTransient(readMethod.getAnnotation(Transient.class)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized Method getIndexedWriteMethod() { |
|
Method indexedWriteMethod = this.indexedWriteMethodRef.get(); |
|
if (indexedWriteMethod == null) { |
|
Class<?> cls = getClass0(); |
|
if (cls == null || |
|
(indexedWriteMethodName == null && !this.indexedWriteMethodRef.isSet())) { |
|
|
|
return null; |
|
} |
|
|
|
// We need the indexed type to ensure that we get the correct method. |
|
// Cannot use the getIndexedPropertyType method since that could |
|
|
|
Class<?> type = getIndexedPropertyType0(); |
|
if (type == null) { |
|
try { |
|
type = findIndexedPropertyType(getIndexedReadMethod(), null); |
|
setIndexedPropertyType(type); |
|
} catch (IntrospectionException ex) { |
|
|
|
Class<?> propType = getPropertyType(); |
|
if (propType.isArray()) { |
|
type = propType.getComponentType(); |
|
} |
|
} |
|
} |
|
|
|
if (indexedWriteMethodName == null) { |
|
indexedWriteMethodName = Introspector.SET_PREFIX + getBaseName(); |
|
} |
|
|
|
Class<?>[] args = (type == null) ? null : new Class<?>[] { int.class, type }; |
|
indexedWriteMethod = Introspector.findMethod(cls, indexedWriteMethodName, 2, args); |
|
if (indexedWriteMethod != null) { |
|
if (!indexedWriteMethod.getReturnType().equals(void.class)) { |
|
indexedWriteMethod = null; |
|
} |
|
} |
|
setIndexedWriteMethod0(indexedWriteMethod); |
|
} |
|
return indexedWriteMethod; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized void setIndexedWriteMethod(Method writeMethod) |
|
throws IntrospectionException { |
|
|
|
|
|
Class<?> type = findIndexedPropertyType(getIndexedReadMethod(), |
|
writeMethod); |
|
setIndexedPropertyType(type); |
|
setIndexedWriteMethod0(writeMethod); |
|
} |
|
|
|
private void setIndexedWriteMethod0(Method writeMethod) { |
|
this.indexedWriteMethodRef.set(writeMethod); |
|
if (writeMethod == null) { |
|
indexedWriteMethodName = null; |
|
return; |
|
} |
|
setClass0(writeMethod.getDeclaringClass()); |
|
|
|
indexedWriteMethodName = writeMethod.getName(); |
|
setTransient(writeMethod.getAnnotation(Transient.class)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized Class<?> getIndexedPropertyType() { |
|
Class<?> type = getIndexedPropertyType0(); |
|
if (type == null) { |
|
try { |
|
type = findIndexedPropertyType(getIndexedReadMethod(), |
|
getIndexedWriteMethod()); |
|
setIndexedPropertyType(type); |
|
} catch (IntrospectionException ex) { |
|
// fall |
|
} |
|
} |
|
return type; |
|
} |
|
|
|
// Private methods which set get/set the Reference objects |
|
|
|
private void setIndexedPropertyType(Class<?> type) { |
|
this.indexedPropertyTypeRef = getWeakReference(type); |
|
} |
|
|
|
private Class<?> getIndexedPropertyType0() { |
|
return (this.indexedPropertyTypeRef != null) |
|
? this.indexedPropertyTypeRef.get() |
|
: null; |
|
} |
|
|
|
private Class<?> findIndexedPropertyType(Method indexedReadMethod, |
|
Method indexedWriteMethod) |
|
throws IntrospectionException { |
|
Class<?> indexedPropertyType = null; |
|
|
|
if (indexedReadMethod != null) { |
|
Class params[] = getParameterTypes(getClass0(), indexedReadMethod); |
|
if (params.length != 1) { |
|
throw new IntrospectionException("bad indexed read method arg count"); |
|
} |
|
if (params[0] != Integer.TYPE) { |
|
throw new IntrospectionException("non int index to indexed read method"); |
|
} |
|
indexedPropertyType = getReturnType(getClass0(), indexedReadMethod); |
|
if (indexedPropertyType == Void.TYPE) { |
|
throw new IntrospectionException("indexed read method returns void"); |
|
} |
|
} |
|
if (indexedWriteMethod != null) { |
|
Class params[] = getParameterTypes(getClass0(), indexedWriteMethod); |
|
if (params.length != 2) { |
|
throw new IntrospectionException("bad indexed write method arg count"); |
|
} |
|
if (params[0] != Integer.TYPE) { |
|
throw new IntrospectionException("non int index to indexed write method"); |
|
} |
|
if (indexedPropertyType == null || params[1].isAssignableFrom(indexedPropertyType)) { |
|
indexedPropertyType = params[1]; |
|
} else if (!indexedPropertyType.isAssignableFrom(params[1])) { |
|
throw new IntrospectionException( |
|
"type mismatch between indexed read and indexed write methods: " |
|
+ getName()); |
|
} |
|
} |
|
Class<?> propertyType = getPropertyType(); |
|
if (propertyType != null && (!propertyType.isArray() || |
|
propertyType.getComponentType() != indexedPropertyType)) { |
|
throw new IntrospectionException("type mismatch between indexed and non-indexed methods: " |
|
+ getName()); |
|
} |
|
return indexedPropertyType; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean equals(Object obj) { |
|
// Note: This would be identical to PropertyDescriptor but they don't |
|
|
|
if (this == obj) { |
|
return true; |
|
} |
|
|
|
if (obj != null && obj instanceof IndexedPropertyDescriptor) { |
|
IndexedPropertyDescriptor other = (IndexedPropertyDescriptor)obj; |
|
Method otherIndexedReadMethod = other.getIndexedReadMethod(); |
|
Method otherIndexedWriteMethod = other.getIndexedWriteMethod(); |
|
|
|
if (!compareMethods(getIndexedReadMethod(), otherIndexedReadMethod)) { |
|
return false; |
|
} |
|
|
|
if (!compareMethods(getIndexedWriteMethod(), otherIndexedWriteMethod)) { |
|
return false; |
|
} |
|
|
|
if (getIndexedPropertyType() != other.getIndexedPropertyType()) { |
|
return false; |
|
} |
|
return super.equals(obj); |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Package-private constructor. |
|
* Merge two property descriptors. Where they conflict, give the |
|
* second argument (y) priority over the first argument (x). |
|
* |
|
* @param x The first (lower priority) PropertyDescriptor |
|
* @param y The second (higher priority) PropertyDescriptor |
|
*/ |
|
|
|
IndexedPropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) { |
|
super(x,y); |
|
Method tr = null; |
|
Method tw = null; |
|
|
|
if (x instanceof IndexedPropertyDescriptor) { |
|
IndexedPropertyDescriptor ix = (IndexedPropertyDescriptor) x; |
|
tr = ix.getIndexedReadMethod(); |
|
tw = ix.getIndexedWriteMethod(); |
|
} |
|
if (y instanceof IndexedPropertyDescriptor) { |
|
IndexedPropertyDescriptor iy = (IndexedPropertyDescriptor) y; |
|
Method yr = iy.getIndexedReadMethod(); |
|
if (isAssignable(tr, yr)) { |
|
tr = yr; |
|
} |
|
|
|
Method yw = iy.getIndexedWriteMethod(); |
|
if (isAssignable(tw, yw)) { |
|
tw = yw; |
|
} |
|
} |
|
|
|
try { |
|
if(tr != null) { |
|
setIndexedReadMethod(tr); |
|
} |
|
if(tw != null) { |
|
setIndexedWriteMethod(tw); |
|
} |
|
} catch(IntrospectionException ex) { |
|
|
|
throw new AssertionError(ex); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
IndexedPropertyDescriptor(IndexedPropertyDescriptor old) { |
|
super(old); |
|
this.indexedReadMethodRef.set(old.indexedReadMethodRef.get()); |
|
this.indexedWriteMethodRef.set(old.indexedWriteMethodRef.get()); |
|
indexedPropertyTypeRef = old.indexedPropertyTypeRef; |
|
indexedWriteMethodName = old.indexedWriteMethodName; |
|
indexedReadMethodName = old.indexedReadMethodName; |
|
} |
|
|
|
void updateGenericsFor(Class<?> type) { |
|
super.updateGenericsFor(type); |
|
try { |
|
setIndexedPropertyType(findIndexedPropertyType(this.indexedReadMethodRef.get(), this.indexedWriteMethodRef.get())); |
|
} |
|
catch (IntrospectionException exception) { |
|
setIndexedPropertyType(null); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int hashCode() { |
|
int result = super.hashCode(); |
|
|
|
result = 37 * result + ((indexedWriteMethodName == null) ? 0 : |
|
indexedWriteMethodName.hashCode()); |
|
result = 37 * result + ((indexedReadMethodName == null) ? 0 : |
|
indexedReadMethodName.hashCode()); |
|
result = 37 * result + ((getIndexedPropertyType() == null) ? 0 : |
|
getIndexedPropertyType().hashCode()); |
|
|
|
return result; |
|
} |
|
|
|
void appendTo(StringBuilder sb) { |
|
super.appendTo(sb); |
|
appendTo(sb, "indexedPropertyType", this.indexedPropertyTypeRef); |
|
appendTo(sb, "indexedReadMethod", this.indexedReadMethodRef.get()); |
|
appendTo(sb, "indexedWriteMethod", this.indexedWriteMethodRef.get()); |
|
} |
|
} |