0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package java.beans;
0019:
0020: import static java.beans.Introspector.decapitalize;
0021:
0022: import java.awt.Image;
0023: import java.lang.reflect.Method;
0024: import java.lang.reflect.Modifier;
0025: import java.util.ArrayList;
0026: import java.util.Arrays;
0027: import java.util.Comparator;
0028: import java.util.EventListener;
0029: import java.util.EventObject;
0030: import java.util.HashMap;
0031: import java.util.Iterator;
0032: import java.util.TooManyListenersException;
0033:
0034: class StandardBeanInfo extends SimpleBeanInfo {
0035:
0036: // Prefixes for methods that set or get a Property
0037: private static final String PREFIX_IS = "is"; //$NON-NLS-1$
0038:
0039: private static final String PREFIX_GET = "get"; //$NON-NLS-1$
0040:
0041: private static final String PREFIX_SET = "set"; //$NON-NLS-1$
0042:
0043: // Prefix and suffix for Event related methods
0044: private static final String PREFIX_ADD = "add"; //$NON-NLS-1$
0045:
0046: private static final String PREFIX_REMOVE = "remove"; //$NON-NLS-1$
0047:
0048: private static final String SUFFIX_LISTEN = "Listener"; //$NON-NLS-1$
0049:
0050: private boolean explicitMethods = false;
0051:
0052: private boolean explicitProperties = false;
0053:
0054: private boolean explicitEvents = false;
0055:
0056: private BeanInfo explicitBeanInfo = null;
0057:
0058: private EventSetDescriptor[] events = null;
0059:
0060: private MethodDescriptor[] methods = null;
0061:
0062: private PropertyDescriptor[] properties = null;
0063:
0064: BeanInfo[] additionalBeanInfo = null;
0065:
0066: private Class<?> beanClass;
0067:
0068: private int defaultEventIndex = -1;
0069:
0070: private int defaultPropertyIndex = -1;
0071:
0072: private static PropertyComparator comparator = new PropertyComparator();
0073:
0074: private Image[] icon = new Image[4];
0075:
0076: private boolean canAddPropertyChangeListener;
0077:
0078: private boolean canRemovePropertyChangeListener;
0079:
0080: StandardBeanInfo(Class<?> beanClass, BeanInfo explicitBeanInfo,
0081: Class<?> stopClass) throws IntrospectionException {
0082: assert (beanClass != null);
0083: this .beanClass = beanClass;
0084: /*--------------------------------------------------------------------------------------
0085: * There are 3 aspects of BeanInfo that must be supplied:
0086: * a) PropertyDescriptors
0087: * b) MethodDescriptors
0088: * c) EventSetDescriptors
0089: * Each of these may be optionally provided in the explicitBeanInfo object relating to
0090: * this bean. Where the explicitBeanInfo provides one of these aspects, it is used
0091: * without question and no introspection of the beanClass is performed for that aspect.
0092: * There are also 3 optional items of BeanInfo that may be provided by the
0093: * explicitBeanInfo object:
0094: * 1) BeanDescriptor
0095: * 2) DefaultEventIndex
0096: * 3) DefaultPropertyIndex
0097: * These aspects of the beanClass cannot be derived through introspection of the class.
0098: * If they are not provided by the explicitBeanInfo, then they must be left null in the
0099: * returned BeanInfo, otherwise they will be copied from the explicitBeanInfo
0100: --------------------------------------------------------------------------------------*/
0101: if (explicitBeanInfo != null) {
0102: this .explicitBeanInfo = explicitBeanInfo;
0103: events = explicitBeanInfo.getEventSetDescriptors();
0104: methods = explicitBeanInfo.getMethodDescriptors();
0105: properties = explicitBeanInfo.getPropertyDescriptors();
0106: defaultEventIndex = explicitBeanInfo.getDefaultEventIndex();
0107: if (defaultEventIndex < 0
0108: || defaultEventIndex >= events.length) {
0109: defaultEventIndex = -1;
0110: }
0111: defaultPropertyIndex = explicitBeanInfo
0112: .getDefaultPropertyIndex();
0113: if (defaultPropertyIndex < 0
0114: || defaultPropertyIndex >= properties.length) {
0115: defaultPropertyIndex = -1;
0116: }
0117: additionalBeanInfo = explicitBeanInfo
0118: .getAdditionalBeanInfo();
0119: for (int i = 0; i < 4; i++) {
0120: icon[i] = explicitBeanInfo.getIcon(i + 1);
0121: }
0122:
0123: if (events != null)
0124: explicitEvents = true;
0125: if (methods != null)
0126: explicitMethods = true;
0127: if (properties != null)
0128: explicitProperties = true;
0129: }
0130:
0131: if (methods == null) {
0132: methods = introspectMethods();
0133: }
0134:
0135: if (properties == null) {
0136: properties = introspectProperties(stopClass);
0137: }
0138:
0139: if (events == null) {
0140: events = introspectEvents();
0141: }
0142: }
0143:
0144: @Override
0145: public BeanInfo[] getAdditionalBeanInfo() {
0146: return null;
0147: }
0148:
0149: @Override
0150: public EventSetDescriptor[] getEventSetDescriptors() {
0151: return events;
0152: }
0153:
0154: @Override
0155: public MethodDescriptor[] getMethodDescriptors() {
0156: return methods;
0157: }
0158:
0159: @Override
0160: public PropertyDescriptor[] getPropertyDescriptors() {
0161: return properties;
0162: }
0163:
0164: @Override
0165: public BeanDescriptor getBeanDescriptor() {
0166: if (explicitBeanInfo != null) {
0167: BeanDescriptor beanDesc = explicitBeanInfo
0168: .getBeanDescriptor();
0169: if (beanDesc != null) {
0170: return beanDesc;
0171: }
0172: }
0173: return new BeanDescriptor(beanClass);
0174: }
0175:
0176: @Override
0177: public int getDefaultEventIndex() {
0178: return this .defaultEventIndex;
0179: }
0180:
0181: @Override
0182: public int getDefaultPropertyIndex() {
0183: return this .defaultPropertyIndex;
0184: }
0185:
0186: @Override
0187: public Image getIcon(int iconKind) {
0188: return icon[iconKind - 1];
0189: }
0190:
0191: void mergeBeanInfo(BeanInfo beanInfo, boolean force)
0192: throws IntrospectionException {
0193: if (force || !explicitProperties) {
0194: PropertyDescriptor[] super Descs = beanInfo
0195: .getPropertyDescriptors();
0196: if (super Descs != null) {
0197: if (getPropertyDescriptors() != null) {
0198: properties = mergeProps(super Descs, beanInfo
0199: .getDefaultPropertyIndex());
0200: } else {
0201: properties = super Descs;
0202: defaultPropertyIndex = beanInfo
0203: .getDefaultPropertyIndex();
0204: }
0205: }
0206: }
0207:
0208: if (force || !explicitMethods) {
0209: MethodDescriptor[] super Methods = beanInfo
0210: .getMethodDescriptors();
0211: if (super Methods != null) {
0212: if (methods != null) {
0213: methods = mergeMethods(super Methods);
0214: } else {
0215: methods = super Methods;
0216: }
0217: }
0218: }
0219:
0220: if (force || !explicitEvents) {
0221: EventSetDescriptor[] super Events = beanInfo
0222: .getEventSetDescriptors();
0223: if (super Events != null) {
0224: if (events != null) {
0225: events = mergeEvents(super Events, beanInfo
0226: .getDefaultEventIndex());
0227: } else {
0228: events = super Events;
0229: defaultEventIndex = beanInfo.getDefaultEventIndex();
0230: }
0231: }
0232: }
0233: }
0234:
0235: /*
0236: * merge the PropertyDescriptor with superclass
0237: */
0238: private PropertyDescriptor[] mergeProps(
0239: PropertyDescriptor[] super Descs, int super DefaultIndex)
0240: throws IntrospectionException {
0241: // FIXME:change to OO way as EventSetD and MethodD
0242: HashMap<String, PropertyDescriptor> subMap = internalAsMap(properties);
0243: String defaultPropertyName = null;
0244: if (defaultPropertyIndex >= 0
0245: && defaultPropertyIndex < properties.length) {
0246: defaultPropertyName = properties[defaultPropertyIndex]
0247: .getName();
0248: } else if (super DefaultIndex >= 0
0249: && super DefaultIndex < super Descs.length) {
0250: defaultPropertyName = super Descs[super DefaultIndex]
0251: .getName();
0252: }
0253:
0254: for (int i = 0; i < super Descs.length; i++) {
0255: PropertyDescriptor super Desc = super Descs[i];
0256: String propertyName = super Desc.getName();
0257: if (!subMap.containsKey(propertyName)) {
0258: subMap.put(propertyName, super Desc);
0259: continue;
0260: }
0261:
0262: Object value = subMap.get(propertyName);
0263: // if sub and super are both PropertyDescriptor
0264: Method subGet = ((PropertyDescriptor) value)
0265: .getReadMethod();
0266: Method subSet = ((PropertyDescriptor) value)
0267: .getWriteMethod();
0268: Method super Get = super Desc.getReadMethod();
0269: Method super Set = super Desc.getWriteMethod();
0270:
0271: Class<?> super Type = super Desc.getPropertyType();
0272: Class<?> super IndexedType = null;
0273: Class<?> subType = ((PropertyDescriptor) value)
0274: .getPropertyType();
0275: Class<?> subIndexedType = null;
0276:
0277: if (value instanceof IndexedPropertyDescriptor) {
0278: subIndexedType = ((IndexedPropertyDescriptor) value)
0279: .getIndexedPropertyType();
0280: }
0281: if (super Desc instanceof IndexedPropertyDescriptor) {
0282: super IndexedType = ((IndexedPropertyDescriptor) super Desc)
0283: .getIndexedPropertyType();
0284: }
0285:
0286: // if superDesc is PropertyDescriptor
0287: if (super IndexedType == null) {
0288: PropertyDescriptor subDesc = (PropertyDescriptor) value;
0289: // Sub is PropertyDescriptor
0290: if (subIndexedType == null) {
0291: // Same property type
0292: if (subType.getName().equals(super Type.getName())) {
0293: if ((subGet == null) && (super Get != null)) {
0294: subDesc.setReadMethod(super Get);
0295: }
0296: if ((subSet == null) && (super Set != null)) {
0297: subDesc.setWriteMethod(super Set);
0298: }
0299: } else { // Different type: type = getMethod
0300: if ((subGet == null) && (super Get != null)) {
0301: subDesc.setWriteMethod(null);
0302: subDesc.setReadMethod(super Get);
0303: }
0304: }
0305: } else { // Sub is IndexedPropertyDescriptor
0306: if ((super Type.isArray())
0307: && (super Type.getComponentType().getName()
0308: .equals(subIndexedType.getName()))) {
0309: // same type
0310: if ((subGet == null) && (super Get != null)) {
0311: subDesc.setReadMethod(super Get);
0312: }
0313: if ((subSet == null) && (super Set != null)) {
0314: subDesc.setWriteMethod(super Set);
0315: }
0316: } // different type do nothing
0317: }
0318: subMap.put(propertyName, subDesc);
0319: } else { // Super is IndexedPropertyDescriptor
0320: if (subIndexedType == null) { // Sub is PropertyDescriptor
0321: if (subType.isArray()
0322: && (subType.getComponentType().getName()
0323: .equals(super IndexedType.getName()))) {
0324: // Same type
0325: if (subGet != null) {
0326: super Desc.setReadMethod(subGet);
0327: }
0328: if (subSet != null) {
0329: super Desc.setWriteMethod(subSet);
0330: }
0331: subMap.put(propertyName, super Desc);
0332: } else { // Different type do nothing
0333: subMap.put(propertyName,
0334: (PropertyDescriptor) value);
0335: }
0336:
0337: } else if (subIndexedType.getName().equals(
0338: super IndexedType.getName())) {
0339: // Sub is IndexedPropertyDescriptor and Same type
0340: IndexedPropertyDescriptor subDesc = (IndexedPropertyDescriptor) value;
0341: if ((subGet == null) && (super Get != null)) {
0342: subDesc.setReadMethod(super Get);
0343: }
0344: if ((subSet == null) && (super Set != null)) {
0345: subDesc.setWriteMethod(super Set);
0346: }
0347: IndexedPropertyDescriptor super IndexedDesc = (IndexedPropertyDescriptor) super Desc;
0348:
0349: if ((subDesc.getIndexedReadMethod() == null)
0350: && (super IndexedDesc.getIndexedReadMethod() != null)) {
0351: subDesc.setIndexedReadMethod(super IndexedDesc
0352: .getIndexedReadMethod());
0353: }
0354:
0355: if ((subDesc.getIndexedWriteMethod() == null)
0356: && (super IndexedDesc
0357: .getIndexedWriteMethod() != null)) {
0358: subDesc.setIndexedWriteMethod(super IndexedDesc
0359: .getIndexedWriteMethod());
0360: }
0361:
0362: subMap.put(propertyName, subDesc);
0363: } // Different indexed type, do nothing
0364: }
0365: mergeAttributes((PropertyDescriptor) value, super Desc);
0366: }
0367:
0368: PropertyDescriptor[] theDescs = new PropertyDescriptor[subMap
0369: .size()];
0370: subMap.values().toArray(theDescs);
0371:
0372: if (defaultPropertyName != null && !explicitProperties) {
0373: for (int i = 0; i < theDescs.length; i++) {
0374: if (defaultPropertyName.equals(theDescs[i].getName())) {
0375: defaultPropertyIndex = i;
0376: break;
0377: }
0378: }
0379: }
0380: return theDescs;
0381: }
0382:
0383: private static void mergeAttributes(PropertyDescriptor subDesc,
0384: PropertyDescriptor super Desc) {
0385: // FIXME: this is just temp workaround, need more elegant solution to
0386: // handle this
0387: subDesc.hidden |= super Desc.hidden;
0388: subDesc.expert |= super Desc.expert;
0389: subDesc.preferred |= super Desc.preferred;
0390: subDesc.bound |= super Desc.bound;
0391: subDesc.constrained |= super Desc.constrained;
0392: subDesc.name = super Desc.name;
0393: if (subDesc.shortDescription == null
0394: && super Desc.shortDescription != null) {
0395: subDesc.shortDescription = super Desc.shortDescription;
0396: }
0397: if (subDesc.displayName == null
0398: && super Desc.displayName != null) {
0399: subDesc.displayName = super Desc.displayName;
0400: }
0401: }
0402:
0403: /*
0404: * merge the MethodDescriptor
0405: */
0406: private MethodDescriptor[] mergeMethods(
0407: MethodDescriptor[] super Descs) {
0408: HashMap<String, MethodDescriptor> subMap = internalAsMap(methods);
0409:
0410: for (MethodDescriptor super Method : super Descs) {
0411: String methodName = getQualifiedName(super Method
0412: .getMethod());
0413: MethodDescriptor method = subMap.get(methodName);
0414: if (method == null) {
0415: subMap.put(methodName, super Method);
0416: } else {
0417: method.merge(super Method);
0418: }
0419: }
0420: MethodDescriptor[] theMethods = new MethodDescriptor[subMap
0421: .size()];
0422: subMap.values().toArray(theMethods);
0423: return theMethods;
0424: }
0425:
0426: private EventSetDescriptor[] mergeEvents(
0427: EventSetDescriptor[] otherEvents, int otherDefaultIndex) {
0428: HashMap<String, EventSetDescriptor> subMap = internalAsMap(events);
0429: String defaultEventName = null;
0430: if (defaultEventIndex >= 0 && defaultEventIndex < events.length) {
0431: defaultEventName = events[defaultEventIndex].getName();
0432: } else if (otherDefaultIndex >= 0
0433: && otherDefaultIndex < otherEvents.length) {
0434: defaultEventName = otherEvents[otherDefaultIndex].getName();
0435: }
0436:
0437: for (EventSetDescriptor event : otherEvents) {
0438: String eventName = event.getName();
0439: EventSetDescriptor subEvent = subMap.get(eventName);
0440: if (subEvent == null) {
0441: subMap.put(eventName, event);
0442: } else {
0443: subEvent.merge(event);
0444: }
0445: }
0446:
0447: EventSetDescriptor[] theEvents = new EventSetDescriptor[subMap
0448: .size()];
0449: subMap.values().toArray(theEvents);
0450:
0451: if (defaultEventName != null && !explicitEvents) {
0452: for (int i = 0; i < theEvents.length; i++) {
0453: if (defaultEventName.equals(theEvents[i].getName())) {
0454: defaultEventIndex = i;
0455: break;
0456: }
0457: }
0458: }
0459: return theEvents;
0460: }
0461:
0462: private static HashMap<String, PropertyDescriptor> internalAsMap(
0463: PropertyDescriptor[] propertyDescs) {
0464: HashMap<String, PropertyDescriptor> map = new HashMap<String, PropertyDescriptor>();
0465: for (int i = 0; i < propertyDescs.length; i++) {
0466: map.put(propertyDescs[i].getName(), propertyDescs[i]);
0467: }
0468: return map;
0469: }
0470:
0471: private static HashMap<String, MethodDescriptor> internalAsMap(
0472: MethodDescriptor[] theDescs) {
0473: HashMap<String, MethodDescriptor> map = new HashMap<String, MethodDescriptor>();
0474: for (int i = 0; i < theDescs.length; i++) {
0475: String qualifiedName = getQualifiedName(theDescs[i]
0476: .getMethod());
0477: map.put(qualifiedName, theDescs[i]);
0478: }
0479: return map;
0480: }
0481:
0482: private static HashMap<String, EventSetDescriptor> internalAsMap(
0483: EventSetDescriptor[] theDescs) {
0484: HashMap<String, EventSetDescriptor> map = new HashMap<String, EventSetDescriptor>();
0485: for (int i = 0; i < theDescs.length; i++) {
0486: map.put(theDescs[i].getName(), theDescs[i]);
0487: }
0488: return map;
0489: }
0490:
0491: private static String getQualifiedName(Method method) {
0492: String qualifiedName = method.getName();
0493: Class[] paramTypes = method.getParameterTypes();
0494: if (paramTypes != null) {
0495: for (int i = 0; i < paramTypes.length; i++) {
0496: qualifiedName += "_" + paramTypes[i].getName(); //$NON-NLS-1$
0497: }
0498: }
0499: return qualifiedName;
0500: }
0501:
0502: /**
0503: * Introspects the supplied class and returns a list of the public methods
0504: * of the class
0505: *
0506: * @param beanClass -
0507: * the class
0508: * @return An array of MethodDescriptors with the public methods. null if
0509: * there are no public methods
0510: */
0511: private MethodDescriptor[] introspectMethods() {
0512: return introspectMethods(false, beanClass);
0513: }
0514:
0515: private MethodDescriptor[] introspectMethods(boolean includeSuper) {
0516: return introspectMethods(includeSuper, beanClass);
0517: }
0518:
0519: private MethodDescriptor[] introspectMethods(boolean includeSuper,
0520: Class<?> introspectorClass) {
0521:
0522: // Get the list of methods belonging to this class
0523: Method[] basicMethods = includeSuper ? introspectorClass
0524: .getMethods() : introspectorClass.getDeclaredMethods();
0525:
0526: if (basicMethods == null || basicMethods.length == 0)
0527: return null;
0528:
0529: ArrayList<MethodDescriptor> methodList = new ArrayList<MethodDescriptor>(
0530: basicMethods.length);
0531:
0532: // Loop over the methods found, looking for public non-static methods
0533: for (int i = 0; i < basicMethods.length; i++) {
0534: int modifiers = basicMethods[i].getModifiers();
0535: if (Modifier.isPublic(modifiers)
0536: && !Modifier.isStatic(modifiers)) {
0537: // Allocate a MethodDescriptor for this method
0538: MethodDescriptor theDescriptor = new MethodDescriptor(
0539: basicMethods[i]);
0540: methodList.add(theDescriptor);
0541: }
0542: }
0543:
0544: // Get the list of public methods into the returned array
0545: int methodCount = methodList.size();
0546: MethodDescriptor[] theMethods = null;
0547: if (methodCount > 0) {
0548: theMethods = new MethodDescriptor[methodCount];
0549: theMethods = methodList.toArray(theMethods);
0550: }
0551:
0552: return theMethods;
0553: }
0554:
0555: /**
0556: * Introspects the supplied class and returns a list of the Properties of
0557: * the class
0558: *
0559: * @param beanClass -
0560: * the Class
0561: * @return The list of Properties as an array of PropertyDescriptors
0562: * @throws IntrospectionException
0563: */
0564: @SuppressWarnings("unchecked")
0565: private PropertyDescriptor[] introspectProperties(Class<?> stopClass)
0566: throws IntrospectionException {
0567:
0568: // Get descriptors for the public methods
0569: MethodDescriptor[] theMethods = introspectMethods();
0570:
0571: if (theMethods == null)
0572: return null;
0573:
0574: HashMap<String, HashMap> propertyTable = new HashMap<String, HashMap>(
0575: theMethods.length);
0576:
0577: // Search for methods that either get or set a Property
0578: for (int i = 0; i < theMethods.length; i++) {
0579: introspectGet(theMethods[i].getMethod(), propertyTable);
0580: introspectSet(theMethods[i].getMethod(), propertyTable);
0581: }
0582:
0583: // If there are listener methods, should be bound.
0584: MethodDescriptor[] allMethods = introspectMethods(true);
0585: if (stopClass != null) {
0586: MethodDescriptor[] excludeMethods = introspectMethods(true,
0587: stopClass);
0588: if (excludeMethods != null) {
0589: ArrayList<MethodDescriptor> tempMethods = new ArrayList<MethodDescriptor>();
0590: for (MethodDescriptor method : allMethods) {
0591: if (!isInSuper(method, excludeMethods)) {
0592: tempMethods.add(method);
0593: }
0594: }
0595: allMethods = tempMethods
0596: .toArray(new MethodDescriptor[0]);
0597: }
0598: }
0599: for (int i = 0; i < allMethods.length; i++) {
0600: introspectPropertyListener(allMethods[i].getMethod());
0601: }
0602: // Put the properties found into the PropertyDescriptor array
0603: ArrayList<PropertyDescriptor> propertyList = new ArrayList<PropertyDescriptor>();
0604:
0605: Iterator<String> keys = propertyTable.keySet().iterator();
0606: while (keys.hasNext()) {
0607: String propertyName = keys.next();
0608: HashMap table = propertyTable.get(propertyName);
0609: if (table == null) {
0610: continue;
0611: }
0612: String normalTag = (String) table.get("normal"); //$NON-NLS-1$
0613: String indexedTag = (String) table.get("indexed"); //$NON-NLS-1$
0614:
0615: if ((normalTag == null) && (indexedTag == null)) {
0616: continue;
0617: }
0618:
0619: Method get = (Method) table.get("normalget"); //$NON-NLS-1$
0620: Method set = (Method) table.get("normalset"); //$NON-NLS-1$
0621: Method indexedGet = (Method) table.get("indexedget"); //$NON-NLS-1$
0622: Method indexedSet = (Method) table.get("indexedset"); //$NON-NLS-1$
0623:
0624: PropertyDescriptor propertyDesc = null;
0625: if (indexedTag == null) {
0626: propertyDesc = new PropertyDescriptor(propertyName,
0627: get, set);
0628: } else {
0629: try {
0630: propertyDesc = new IndexedPropertyDescriptor(
0631: propertyName, get, set, indexedGet,
0632: indexedSet);
0633: } catch (IntrospectionException e) {
0634: // If the getter and the indexGetter is not compatible, try
0635: // getter/setter is null;
0636: propertyDesc = new IndexedPropertyDescriptor(
0637: propertyName, null, null, indexedGet,
0638: indexedSet);
0639: }
0640: }
0641: // RI set propretyDescriptor as bound. FIXME
0642: // propertyDesc.setBound(true);
0643: if (canAddPropertyChangeListener
0644: && canRemovePropertyChangeListener) {
0645: propertyDesc.setBound(true);
0646: } else {
0647: propertyDesc.setBound(false);
0648: }
0649: if (table.get("isConstrained") == Boolean.TRUE) { //$NON-NLS-1$
0650: propertyDesc.setConstrained(true);
0651: }
0652: propertyList.add(propertyDesc);
0653: }
0654:
0655: PropertyDescriptor[] theProperties = new PropertyDescriptor[propertyList
0656: .size()];
0657: propertyList.toArray(theProperties);
0658: return theProperties;
0659: }
0660:
0661: private boolean isInSuper(MethodDescriptor method,
0662: MethodDescriptor[] excludeMethods) {
0663: for (MethodDescriptor m : excludeMethods) {
0664: if (method.getMethod().equals(m.getMethod())) {
0665: return true;
0666: }
0667: }
0668: return false;
0669: }
0670:
0671: @SuppressWarnings("nls")
0672: private void introspectPropertyListener(Method theMethod) {
0673: String methodName = theMethod.getName();
0674: Class[] param = theMethod.getParameterTypes();
0675: if (param.length != 1) {
0676: return;
0677: }
0678: if (methodName.equals("addPropertyChangeListener")
0679: && param[0].equals(PropertyChangeListener.class))
0680: canAddPropertyChangeListener = true;
0681: if (methodName.equals("removePropertyChangeListener")
0682: && param[0].equals(PropertyChangeListener.class))
0683: canRemovePropertyChangeListener = true;
0684: }
0685:
0686: @SuppressWarnings("unchecked")
0687: private static void introspectGet(Method theMethod,
0688: HashMap<String, HashMap> propertyTable) {
0689: String methodName = theMethod.getName();
0690: if (methodName == null) {
0691: return;
0692: }
0693:
0694: int prefixLength = 0;
0695: if (methodName.startsWith(PREFIX_GET)) {
0696: prefixLength = PREFIX_GET.length();
0697: }
0698:
0699: if (methodName.startsWith(PREFIX_IS)) {
0700: prefixLength = PREFIX_IS.length();
0701: }
0702:
0703: if (prefixLength == 0) {
0704: return;
0705: }
0706:
0707: String propertyName = decapitalize(methodName
0708: .substring(prefixLength));
0709: // validate property name
0710: if (!isValidProperty(propertyName)) {
0711: return;
0712: }
0713:
0714: Class propertyType = theMethod.getReturnType();
0715:
0716: // check return type getMethod
0717: if (propertyType.getName().equals(Void.TYPE.getName())) {
0718: return;
0719: }
0720:
0721: // isXXX return boolean
0722: if (prefixLength == 2) {
0723: if (!propertyType.getName().equals(Boolean.TYPE.getName())) {
0724: return;
0725: }
0726: }
0727:
0728: // indexed get method
0729: Class[] paramTypes = theMethod.getParameterTypes();
0730:
0731: if (paramTypes.length > 1) {
0732: return;
0733: }
0734:
0735: String tag = "normal"; //$NON-NLS-1$
0736:
0737: if (paramTypes.length == 1) {
0738: if (paramTypes[0].getName().equals(Integer.TYPE.getName())) {
0739: tag = "indexed"; //$NON-NLS-1$
0740: } else {
0741: return;
0742: }
0743:
0744: }
0745:
0746: HashMap table = propertyTable.get(propertyName);
0747: if (table == null) {
0748: table = new HashMap();
0749: propertyTable.put(propertyName, table);
0750: }
0751:
0752: // the "get" propertyType is conflict with "set" propertyType
0753: Class oldPropertyType = (Class) table.get(tag + "PropertyType"); //$NON-NLS-1$
0754: if ((oldPropertyType != null)
0755: && (!oldPropertyType.getName().equals(
0756: propertyType.getName()))) {
0757: table.put(tag, "invalid"); //$NON-NLS-1$
0758: table.remove(tag + "set"); //$NON-NLS-1$
0759: } else {
0760: table.put(tag, "valid"); //$NON-NLS-1$
0761: }
0762:
0763: table.put(tag + "PropertyType", propertyType); //$NON-NLS-1$
0764:
0765: // According to the spec "is" method should be used prior to "get"
0766: if (prefixLength == 3) {
0767: if (!table.containsKey(tag + "get")) { //$NON-NLS-1$
0768: table.put(tag + "get", theMethod); //$NON-NLS-1$
0769: }
0770: } else {
0771: table.put(tag + "get", theMethod); //$NON-NLS-1$
0772: }
0773: }
0774:
0775: @SuppressWarnings("unchecked")
0776: private static void introspectSet(Method theMethod,
0777: HashMap<String, HashMap> propertyTable) {
0778: String methodName = theMethod.getName();
0779: if (methodName == null) {
0780: return;
0781: }
0782:
0783: int prefixLength = 0;
0784: if (methodName.startsWith(PREFIX_SET)) {
0785: prefixLength = PREFIX_GET.length();
0786: }
0787:
0788: if (prefixLength == 0) {
0789: return;
0790: }
0791:
0792: String propertyName = decapitalize(methodName
0793: .substring(prefixLength));
0794:
0795: // validate property name
0796: if (!isValidProperty(propertyName)) {
0797: return;
0798: }
0799:
0800: Class returnType = theMethod.getReturnType();
0801:
0802: if (!returnType.getName().equals(Void.TYPE.getName())) {
0803: return;
0804: }
0805:
0806: // indexed get method
0807: Class[] paramTypes = theMethod.getParameterTypes();
0808:
0809: if ((paramTypes.length == 0) || (paramTypes.length > 2)) {
0810: return;
0811: }
0812:
0813: String tag = "normal"; //$NON-NLS-1$
0814:
0815: Class propertyType = paramTypes[0];
0816:
0817: if (paramTypes.length == 2) {
0818: if (paramTypes[0].getName().equals(Integer.TYPE.getName())) {
0819: tag = "indexed"; //$NON-NLS-1$
0820: propertyType = paramTypes[1];
0821: } else {
0822: return;
0823: }
0824: }
0825:
0826: HashMap table = propertyTable.get(propertyName);
0827: if (table == null) {
0828: table = new HashMap();
0829: }
0830:
0831: Class oldPropertyType = (Class) table.get(tag + "PropertyType"); //$NON-NLS-1$
0832: if ((oldPropertyType != null)
0833: && (!oldPropertyType.getName().equals(
0834: propertyType.getName()))) {
0835: table.put(tag, "invalid"); //$NON-NLS-1$
0836: return;
0837: }
0838:
0839: table.put(tag, "valid"); //$NON-NLS-1$
0840: table.put(tag + "set", theMethod); //$NON-NLS-1$
0841: table.put(tag + "PropertyType", propertyType); //$NON-NLS-1$
0842:
0843: // handle constrained
0844: Class[] exceptions = theMethod.getExceptionTypes();
0845: for (Class e : exceptions) {
0846: if (e.equals(PropertyVetoException.class)) {
0847: table.put("isConstrained", Boolean.TRUE); //$NON-NLS-1$
0848: }
0849: }
0850: propertyTable.put(propertyName, table);
0851: }
0852:
0853: /**
0854: * Introspects the supplied Bean class and returns a list of the Events of
0855: * the class
0856: *
0857: * @param beanClass
0858: * @return the events
0859: * @throws IntrospectionException
0860: */
0861: @SuppressWarnings("unchecked")
0862: private EventSetDescriptor[] introspectEvents()
0863: throws IntrospectionException {
0864: // Get descriptors for the public methods
0865: // FIXME: performance
0866: MethodDescriptor[] theMethods = introspectMethods();
0867:
0868: if (theMethods == null)
0869: return null;
0870:
0871: HashMap<String, HashMap> eventTable = new HashMap<String, HashMap>(
0872: theMethods.length);
0873:
0874: // Search for methods that add an Event Listener
0875: for (int i = 0; i < theMethods.length; i++) {
0876: introspectListenerMethods(PREFIX_ADD, theMethods[i]
0877: .getMethod(), eventTable);
0878: introspectListenerMethods(PREFIX_REMOVE, theMethods[i]
0879: .getMethod(), eventTable);
0880: introspectGetListenerMethods(theMethods[i].getMethod(),
0881: eventTable);
0882:
0883: }
0884:
0885: ArrayList<EventSetDescriptor> eventList = new ArrayList<EventSetDescriptor>();
0886: Iterator<String> keys = eventTable.keySet().iterator();
0887: while (keys.hasNext()) {
0888: String key = keys.next();
0889: HashMap table = eventTable.get(key);
0890: Method add = (Method) table.get(PREFIX_ADD);
0891: Method remove = (Method) table.get(PREFIX_REMOVE);
0892:
0893: if ((add == null) || (remove == null)) {
0894: continue;
0895: }
0896:
0897: Method get = (Method) table.get(PREFIX_GET);
0898: Class<?> listenerType = (Class) table.get("listenerType"); //$NON-NLS-1$
0899: Method[] listenerMethods = (Method[]) table
0900: .get("listenerMethods"); //$NON-NLS-1$
0901: EventSetDescriptor eventSetDescriptor = new EventSetDescriptor(
0902: decapitalize(key), listenerType, listenerMethods,
0903: add, remove, get);
0904:
0905: eventSetDescriptor
0906: .setUnicast(table.get("isUnicast") != null); //$NON-NLS-1$
0907: eventList.add(eventSetDescriptor);
0908: }
0909:
0910: EventSetDescriptor[] theEvents = new EventSetDescriptor[eventList
0911: .size()];
0912: eventList.toArray(theEvents);
0913:
0914: return theEvents;
0915: }
0916:
0917: /*
0918: * find the add, remove listener method
0919: */
0920: @SuppressWarnings("unchecked")
0921: private static void introspectListenerMethods(String type,
0922: Method theMethod, HashMap<String, HashMap> methodsTable) {
0923: String methodName = theMethod.getName();
0924: if (methodName == null) {
0925: return;
0926: }
0927:
0928: if (!((methodName.startsWith(type)) && (methodName
0929: .endsWith(SUFFIX_LISTEN)))) {
0930: return;
0931: }
0932:
0933: String listenerName = methodName.substring(type.length());
0934: String eventName = listenerName.substring(0, listenerName
0935: .lastIndexOf(SUFFIX_LISTEN));
0936: if ((eventName == null) || (eventName.length() == 0)) {
0937: return;
0938: }
0939:
0940: Class[] paramTypes = theMethod.getParameterTypes();
0941: if ((paramTypes == null) || (paramTypes.length != 1)) {
0942: return;
0943: }
0944:
0945: Class<?> listenerType = paramTypes[0];
0946:
0947: if (!EventListener.class.isAssignableFrom(listenerType)) {
0948: return;
0949: }
0950:
0951: if (!listenerType.getName().endsWith(listenerName)) {
0952: return;
0953: }
0954:
0955: HashMap table = methodsTable.get(eventName);
0956: if (table == null) {
0957: table = new HashMap();
0958: }
0959: // put listener type
0960: if (table.get("listenerType") == null) { //$NON-NLS-1$
0961: table.put("listenerType", listenerType); //$NON-NLS-1$
0962: table.put("listenerMethods", //$NON-NLS-1$
0963: introspectListenerMethods(listenerType));
0964: }
0965: // put add / remove
0966: table.put(type, theMethod);
0967:
0968: // determine isUnicast()
0969: if (type.equals(PREFIX_ADD)) {
0970: Class[] exceptionTypes = theMethod.getExceptionTypes();
0971: if (exceptionTypes != null) {
0972: for (int i = 0; i < exceptionTypes.length; i++) {
0973: if (exceptionTypes[i].getName().equals(
0974: TooManyListenersException.class.getName())) {
0975: table.put("isUnicast", "true"); //$NON-NLS-1$//$NON-NLS-2$
0976: break;
0977: }
0978: }
0979: }
0980: }
0981:
0982: methodsTable.put(eventName, table);
0983: }
0984:
0985: private static Method[] introspectListenerMethods(
0986: Class<?> listenerType) {
0987: Method[] methods = listenerType.getDeclaredMethods();
0988: ArrayList<Method> list = new ArrayList<Method>();
0989: for (int i = 0; i < methods.length; i++) {
0990: Class[] paramTypes = methods[i].getParameterTypes();
0991: if (paramTypes.length != 1) {
0992: continue;
0993: }
0994:
0995: if (EventObject.class.isAssignableFrom(paramTypes[0])) {
0996: list.add(methods[i]);
0997: }
0998: }
0999: Method[] matchedMethods = new Method[list.size()];
1000: list.toArray(matchedMethods);
1001: return matchedMethods;
1002: }
1003:
1004: @SuppressWarnings("unchecked")
1005: private static void introspectGetListenerMethods(Method theMethod,
1006: HashMap<String, HashMap> methodsTable) {
1007: String type = PREFIX_GET;
1008:
1009: String methodName = theMethod.getName();
1010: if (methodName == null) {
1011: return;
1012: }
1013:
1014: if (!((methodName.startsWith(type)) && (methodName
1015: .endsWith(SUFFIX_LISTEN + "s")))) { //$NON-NLS-1$
1016: return;
1017: }
1018:
1019: String listenerName = methodName.substring(type.length(),
1020: methodName.length() - 1);
1021: String eventName = listenerName.substring(0, listenerName
1022: .lastIndexOf(SUFFIX_LISTEN));
1023: if ((eventName == null) || (eventName.length() == 0)) {
1024: return;
1025: }
1026:
1027: Class[] paramTypes = theMethod.getParameterTypes();
1028: if ((paramTypes == null) || (paramTypes.length != 0)) {
1029: return;
1030: }
1031:
1032: Class returnType = theMethod.getReturnType();
1033: if ((returnType.getComponentType() == null)
1034: || (!returnType.getComponentType().getName().endsWith(
1035: listenerName))) {
1036: return;
1037: }
1038:
1039: HashMap table = methodsTable.get(eventName);
1040: if (table == null) {
1041: table = new HashMap();
1042: }
1043: // put add / remove
1044: table.put(type, theMethod);
1045: methodsTable.put(eventName, table);
1046: }
1047:
1048: private static boolean isValidProperty(String propertyName) {
1049: return (propertyName != null) && (propertyName.length() != 0);
1050: }
1051:
1052: private static class PropertyComparator implements
1053: Comparator<PropertyDescriptor> {
1054: public int compare(PropertyDescriptor object1,
1055: PropertyDescriptor object2) {
1056: return object1.getName().compareTo(object2.getName());
1057: }
1058:
1059: }
1060:
1061: // TODO
1062: void init() {
1063: if (this .events == null) {
1064: events = new EventSetDescriptor[0];
1065: }
1066: if (this .properties == null) {
1067: this .properties = new PropertyDescriptor[0];
1068: }
1069:
1070: if (properties != null) {
1071: String defaultPropertyName = (defaultPropertyIndex != -1 ? properties[defaultPropertyIndex]
1072: .getName()
1073: : null);
1074: Arrays.sort(properties, comparator);
1075: if (null != defaultPropertyName) {
1076: for (int i = 0; i < properties.length; i++) {
1077: if (defaultPropertyName.equals(properties[i]
1078: .getName())) {
1079: defaultPropertyIndex = i;
1080: break;
1081: }
1082: }
1083: }
1084: }
1085: }
1086: }
|