Path: blob/master/src/java.desktop/share/classes/java/beans/IndexedPropertyDescriptor.java
41152 views
/*1* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package java.beans;2526import java.lang.ref.Reference;27import java.lang.reflect.Method;28import java.util.Map.Entry;2930import com.sun.beans.introspect.PropertyInfo;3132/**33* An IndexedPropertyDescriptor describes a property that acts like an34* array and has an indexed read and/or indexed write method to access35* specific elements of the array.36* <p>37* An indexed property may also provide simple non-indexed read and write38* methods. If these are present, they read and write arrays of the type39* returned by the indexed read method.40*41* @since 1.142*/4344public class IndexedPropertyDescriptor extends PropertyDescriptor {4546private Reference<? extends Class<?>> indexedPropertyTypeRef;47private final MethodRef indexedReadMethodRef = new MethodRef();48private final MethodRef indexedWriteMethodRef = new MethodRef();4950private String indexedReadMethodName;51private String indexedWriteMethodName;5253/**54* This constructor constructs an IndexedPropertyDescriptor for a property55* that follows the standard Java conventions by having getFoo and setFoo56* accessor methods, for both indexed access and array access.57* <p>58* Thus if the argument name is "fred", it will assume that there59* is an indexed reader method "getFred", a non-indexed (array) reader60* method also called "getFred", an indexed writer method "setFred",61* and finally a non-indexed writer method "setFred".62*63* @param propertyName The programmatic name of the property.64* @param beanClass The Class object for the target bean.65* @exception IntrospectionException if an exception occurs during66* introspection.67*/68public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass)69throws IntrospectionException {70this(propertyName, beanClass,71Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName),72Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName),73Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName),74Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName));75}7677/**78* This constructor takes the name of a simple property, and method79* names for reading and writing the property, both indexed80* and non-indexed.81*82* @param propertyName The programmatic name of the property.83* @param beanClass The Class object for the target bean.84* @param readMethodName The name of the method used for reading the property85* values as an array. May be null if the property is write-only86* or must be indexed.87* @param writeMethodName The name of the method used for writing the property88* values as an array. May be null if the property is read-only89* or must be indexed.90* @param indexedReadMethodName The name of the method used for reading91* an indexed property value.92* May be null if the property is write-only.93* @param indexedWriteMethodName The name of the method used for writing94* an indexed property value.95* May be null if the property is read-only.96* @exception IntrospectionException if an exception occurs during97* introspection.98*/99public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass,100String readMethodName, String writeMethodName,101String indexedReadMethodName, String indexedWriteMethodName)102throws IntrospectionException {103super(propertyName, beanClass, readMethodName, writeMethodName);104105this.indexedReadMethodName = indexedReadMethodName;106if (indexedReadMethodName != null && getIndexedReadMethod() == null) {107throw new IntrospectionException("Method not found: " + indexedReadMethodName);108}109110this.indexedWriteMethodName = indexedWriteMethodName;111if (indexedWriteMethodName != null && getIndexedWriteMethod() == null) {112throw new IntrospectionException("Method not found: " + indexedWriteMethodName);113}114// Implemented only for type checking.115findIndexedPropertyType(getIndexedReadMethod(), getIndexedWriteMethod());116}117118/**119* This constructor takes the name of a simple property, and Method120* objects for reading and writing the property.121*122* @param propertyName The programmatic name of the property.123* @param readMethod The method used for reading the property values as an array.124* May be null if the property is write-only or must be indexed.125* @param writeMethod The method used for writing the property values as an array.126* May be null if the property is read-only or must be indexed.127* @param indexedReadMethod The method used for reading an indexed property value.128* May be null if the property is write-only.129* @param indexedWriteMethod The method used for writing an indexed property value.130* May be null if the property is read-only.131* @exception IntrospectionException if an exception occurs during132* introspection.133*/134public IndexedPropertyDescriptor(String propertyName, Method readMethod, Method writeMethod,135Method indexedReadMethod, Method indexedWriteMethod)136throws IntrospectionException {137super(propertyName, readMethod, writeMethod);138139setIndexedReadMethod0(indexedReadMethod);140setIndexedWriteMethod0(indexedWriteMethod);141142// Type checking143setIndexedPropertyType(findIndexedPropertyType(indexedReadMethod, indexedWriteMethod));144}145146/**147* Creates {@code IndexedPropertyDescriptor} from the specified property info.148*149* @param entry the key-value pair,150* where the {@code key} is the base name of the property (the rest of the method name)151* and the {@code value} is the automatically generated property info152* @param bound the flag indicating whether it is possible to treat this property as a bound property153*154* @since 9155*/156IndexedPropertyDescriptor(Entry<String,PropertyInfo> entry, boolean bound) {157super(entry, bound);158PropertyInfo info = entry.getValue().getIndexed();159setIndexedReadMethod0(info.getReadMethod());160setIndexedWriteMethod0(info.getWriteMethod());161setIndexedPropertyType(info.getPropertyType());162}163164/**165* Gets the method that should be used to read an indexed166* property value.167*168* @return The method that should be used to read an indexed169* property value.170* May return null if the property isn't indexed or is write-only.171*/172public synchronized Method getIndexedReadMethod() {173Method indexedReadMethod = this.indexedReadMethodRef.get();174if (indexedReadMethod == null) {175Class<?> cls = getClass0();176if (cls == null ||177(indexedReadMethodName == null && !this.indexedReadMethodRef.isSet())) {178// the Indexed readMethod was explicitly set to null.179return null;180}181String nextMethodName = Introspector.GET_PREFIX + getBaseName();182if (indexedReadMethodName == null) {183Class<?> type = getIndexedPropertyType0();184if (type == boolean.class || type == null) {185indexedReadMethodName = Introspector.IS_PREFIX + getBaseName();186} else {187indexedReadMethodName = nextMethodName;188}189}190191Class<?>[] args = { int.class };192indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args);193if ((indexedReadMethod == null) && !indexedReadMethodName.equals(nextMethodName)) {194// no "is" method, so look for a "get" method.195indexedReadMethodName = nextMethodName;196indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args);197}198setIndexedReadMethod0(indexedReadMethod);199}200return indexedReadMethod;201}202203/**204* Sets the method that should be used to read an indexed property value.205*206* @param readMethod The new indexed read method.207* @throws IntrospectionException if an exception occurs during208* introspection.209*210* @since 1.2211*/212public synchronized void setIndexedReadMethod(Method readMethod)213throws IntrospectionException {214215// the indexed property type is set by the reader.216setIndexedPropertyType(findIndexedPropertyType(readMethod,217this.indexedWriteMethodRef.get()));218setIndexedReadMethod0(readMethod);219}220221private void setIndexedReadMethod0(Method readMethod) {222this.indexedReadMethodRef.set(readMethod);223if (readMethod == null) {224indexedReadMethodName = null;225return;226}227setClass0(readMethod.getDeclaringClass());228229indexedReadMethodName = readMethod.getName();230setTransient(readMethod.getAnnotation(Transient.class));231}232233234/**235* Gets the method that should be used to write an indexed property value.236*237* @return The method that should be used to write an indexed238* property value.239* May return null if the property isn't indexed or is read-only.240*/241public synchronized Method getIndexedWriteMethod() {242Method indexedWriteMethod = this.indexedWriteMethodRef.get();243if (indexedWriteMethod == null) {244Class<?> cls = getClass0();245if (cls == null ||246(indexedWriteMethodName == null && !this.indexedWriteMethodRef.isSet())) {247// the Indexed writeMethod was explicitly set to null.248return null;249}250251// We need the indexed type to ensure that we get the correct method.252// Cannot use the getIndexedPropertyType method since that could253// result in an infinite loop.254Class<?> type = getIndexedPropertyType0();255if (type == null) {256try {257type = findIndexedPropertyType(getIndexedReadMethod(), null);258setIndexedPropertyType(type);259} catch (IntrospectionException ex) {260// Set iprop type to be the classic type261Class<?> propType = getPropertyType();262if (propType.isArray()) {263type = propType.getComponentType();264}265}266}267268if (indexedWriteMethodName == null) {269indexedWriteMethodName = Introspector.SET_PREFIX + getBaseName();270}271272Class<?>[] args = (type == null) ? null : new Class<?>[] { int.class, type };273indexedWriteMethod = Introspector.findMethod(cls, indexedWriteMethodName, 2, args);274if (indexedWriteMethod != null) {275if (!indexedWriteMethod.getReturnType().equals(void.class)) {276indexedWriteMethod = null;277}278}279setIndexedWriteMethod0(indexedWriteMethod);280}281return indexedWriteMethod;282}283284/**285* Sets the method that should be used to write an indexed property value.286*287* @param writeMethod The new indexed write method.288* @throws IntrospectionException if an exception occurs during289* introspection.290*291* @since 1.2292*/293public synchronized void setIndexedWriteMethod(Method writeMethod)294throws IntrospectionException {295296// If the indexed property type has not been set, then set it.297Class<?> type = findIndexedPropertyType(getIndexedReadMethod(),298writeMethod);299setIndexedPropertyType(type);300setIndexedWriteMethod0(writeMethod);301}302303private void setIndexedWriteMethod0(Method writeMethod) {304this.indexedWriteMethodRef.set(writeMethod);305if (writeMethod == null) {306indexedWriteMethodName = null;307return;308}309setClass0(writeMethod.getDeclaringClass());310311indexedWriteMethodName = writeMethod.getName();312setTransient(writeMethod.getAnnotation(Transient.class));313}314315/**316* Returns the Java type info for the indexed property.317* Note that the {@code Class} object may describe318* primitive Java types such as {@code int}.319* This type is returned by the indexed read method320* or is used as the parameter type of the indexed write method.321*322* @return the {@code Class} object that represents the Java type info,323* or {@code null} if the type cannot be determined324*/325public synchronized Class<?> getIndexedPropertyType() {326Class<?> type = getIndexedPropertyType0();327if (type == null) {328try {329type = findIndexedPropertyType(getIndexedReadMethod(),330getIndexedWriteMethod());331setIndexedPropertyType(type);332} catch (IntrospectionException ex) {333// fall334}335}336return type;337}338339// Private methods which set get/set the Reference objects340341private void setIndexedPropertyType(Class<?> type) {342this.indexedPropertyTypeRef = getWeakReference(type);343}344345private Class<?> getIndexedPropertyType0() {346return (this.indexedPropertyTypeRef != null)347? this.indexedPropertyTypeRef.get()348: null;349}350351private Class<?> findIndexedPropertyType(Method indexedReadMethod,352Method indexedWriteMethod)353throws IntrospectionException {354Class<?> indexedPropertyType = null;355356if (indexedReadMethod != null) {357Class<?>[] params = getParameterTypes(getClass0(), indexedReadMethod);358if (params.length != 1) {359throw new IntrospectionException("bad indexed read method arg count");360}361if (params[0] != Integer.TYPE) {362throw new IntrospectionException("non int index to indexed read method");363}364indexedPropertyType = getReturnType(getClass0(), indexedReadMethod);365if (indexedPropertyType == Void.TYPE) {366throw new IntrospectionException("indexed read method returns void");367}368}369if (indexedWriteMethod != null) {370Class<?>[] params = getParameterTypes(getClass0(), indexedWriteMethod);371if (params.length != 2) {372throw new IntrospectionException("bad indexed write method arg count");373}374if (params[0] != Integer.TYPE) {375throw new IntrospectionException("non int index to indexed write method");376}377if (indexedPropertyType == null || params[1].isAssignableFrom(indexedPropertyType)) {378indexedPropertyType = params[1];379} else if (!indexedPropertyType.isAssignableFrom(params[1])) {380throw new IntrospectionException(381"type mismatch between indexed read and indexed write methods: "382+ getName());383}384}385Class<?> propertyType = getPropertyType();386if (propertyType != null && (!propertyType.isArray() ||387propertyType.getComponentType() != indexedPropertyType)) {388throw new IntrospectionException("type mismatch between indexed and non-indexed methods: "389+ getName());390}391return indexedPropertyType;392}393394/**395* Compares this {@code PropertyDescriptor} against the specified object.396* Returns true if the objects are the same. Two {@code PropertyDescriptor}s397* are the same if the read, write, property types, property editor and398* flags are equivalent.399*400* @since 1.4401*/402public boolean equals(Object obj) {403// Note: This would be identical to PropertyDescriptor but they don't404// share the same fields.405if (this == obj) {406return true;407}408409if (obj != null && obj instanceof IndexedPropertyDescriptor) {410IndexedPropertyDescriptor other = (IndexedPropertyDescriptor)obj;411Method otherIndexedReadMethod = other.getIndexedReadMethod();412Method otherIndexedWriteMethod = other.getIndexedWriteMethod();413414if (!compareMethods(getIndexedReadMethod(), otherIndexedReadMethod)) {415return false;416}417418if (!compareMethods(getIndexedWriteMethod(), otherIndexedWriteMethod)) {419return false;420}421422if (getIndexedPropertyType() != other.getIndexedPropertyType()) {423return false;424}425return super.equals(obj);426}427return false;428}429430/**431* Package-private constructor.432* Merge two property descriptors. Where they conflict, give the433* second argument (y) priority over the first argument (x).434*435* @param x The first (lower priority) PropertyDescriptor436* @param y The second (higher priority) PropertyDescriptor437*/438439IndexedPropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) {440super(x,y);441Method tr = null;442Method tw = null;443444if (x instanceof IndexedPropertyDescriptor) {445IndexedPropertyDescriptor ix = (IndexedPropertyDescriptor) x;446tr = ix.getIndexedReadMethod();447tw = ix.getIndexedWriteMethod();448}449if (y instanceof IndexedPropertyDescriptor) {450IndexedPropertyDescriptor iy = (IndexedPropertyDescriptor) y;451Method yr = iy.getIndexedReadMethod();452if (isAssignable(tr, yr)) {453tr = yr;454}455456Method yw = iy.getIndexedWriteMethod();457if (isAssignable(tw, yw)) {458tw = yw;459}460}461462try {463if(tr != null) {464setIndexedReadMethod(tr);465}466if(tw != null) {467setIndexedWriteMethod(tw);468}469} catch(IntrospectionException ex) {470// Should not happen471throw new AssertionError(ex);472}473}474475/*476* Package-private dup constructor477* This must isolate the new object from any changes to the old object.478*/479IndexedPropertyDescriptor(IndexedPropertyDescriptor old) {480super(old);481this.indexedReadMethodRef.set(old.indexedReadMethodRef.get());482this.indexedWriteMethodRef.set(old.indexedWriteMethodRef.get());483indexedPropertyTypeRef = old.indexedPropertyTypeRef;484indexedWriteMethodName = old.indexedWriteMethodName;485indexedReadMethodName = old.indexedReadMethodName;486}487488void updateGenericsFor(Class<?> type) {489super.updateGenericsFor(type);490try {491setIndexedPropertyType(findIndexedPropertyType(this.indexedReadMethodRef.get(), this.indexedWriteMethodRef.get()));492}493catch (IntrospectionException exception) {494setIndexedPropertyType(null);495}496}497498/**499* Returns a hash code value for the object.500* See {@link java.lang.Object#hashCode} for a complete description.501*502* @return a hash code value for this object.503* @since 1.5504*/505public int hashCode() {506int result = super.hashCode();507508result = 37 * result + ((indexedWriteMethodName == null) ? 0 :509indexedWriteMethodName.hashCode());510result = 37 * result + ((indexedReadMethodName == null) ? 0 :511indexedReadMethodName.hashCode());512result = 37 * result + ((getIndexedPropertyType() == null) ? 0 :513getIndexedPropertyType().hashCode());514515return result;516}517518void appendTo(StringBuilder sb) {519super.appendTo(sb);520appendTo(sb, "indexedPropertyType", this.indexedPropertyTypeRef);521appendTo(sb, "indexedReadMethod", this.indexedReadMethodRef.get());522appendTo(sb, "indexedWriteMethod", this.indexedWriteMethodRef.get());523}524}525526527