com.googlecode.openbeans.StandardBeanInfo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of app55-java Show documentation
Show all versions of app55-java Show documentation
App55 client library for the Java platform.
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.openbeans;
import static com.googlecode.openbeans.Introspector.decapitalize;
import java.awt.Image;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.EventListener;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Map;
import java.util.TooManyListenersException;
@SuppressWarnings({ "rawtypes", "unchecked" })
class StandardBeanInfo extends SimpleBeanInfo
{
// Prefixes for methods that set or get a Property
private static final String PREFIX_IS = "is"; //$NON-NLS-1$
private static final String PREFIX_GET = "get"; //$NON-NLS-1$
private static final String PREFIX_SET = "set"; //$NON-NLS-1$
// Prefix and suffix for Event related methods
private static final String PREFIX_ADD = "add"; //$NON-NLS-1$
private static final String PREFIX_REMOVE = "remove"; //$NON-NLS-1$
private static final String SUFFIX_LISTEN = "Listener"; //$NON-NLS-1$
private static final String STR_NORMAL = "normal"; //$NON-NLS-1$
private static final String STR_INDEXED = "indexed"; //$NON-NLS-1$
private static final String STR_VALID = "valid"; //$NON-NLS-1$
private static final String STR_INVALID = "invalid"; //$NON-NLS-1$
private static final String STR_PROPERTY_TYPE = "PropertyType"; //$NON-NLS-1$
private static final String STR_IS_CONSTRAINED = "isConstrained"; //$NON-NLS-1$
private static final String STR_SETTERS = "setters"; //$NON-NLS-1$
private static final String STR_GETTERS = "getters"; //$NON-NLS-1$
private boolean explicitMethods = false;
private boolean explicitProperties = false;
private boolean explicitEvents = false;
private BeanInfo explicitBeanInfo = null;
private EventSetDescriptor[] events = null;
private MethodDescriptor[] methods = null;
private PropertyDescriptor[] properties = null;
private BeanDescriptor beanDescriptor = null;
BeanInfo[] additionalBeanInfo = null;
private Class> beanClass;
private int defaultEventIndex = -1;
private int defaultPropertyIndex = -1;
private static PropertyComparator comparator = new PropertyComparator();
private Object[] icon = new Object[4];
private boolean canAddPropertyChangeListener;
private boolean canRemovePropertyChangeListener;
StandardBeanInfo(Class> beanClass, BeanInfo explicitBeanInfo, Class> stopClass) throws IntrospectionException
{
this.beanClass = beanClass;
/*--------------------------------------------------------------------------------------
* There are 3 aspects of BeanInfo that must be supplied:
* a) PropertyDescriptors
* b) MethodDescriptors
* c) EventSetDescriptors
* Each of these may be optionally provided in the explicitBeanInfo object relating to
* this bean. Where the explicitBeanInfo provides one of these aspects, it is used
* without question and no introspection of the beanClass is performed for that aspect.
* There are also 3 optional items of BeanInfo that may be provided by the
* explicitBeanInfo object:
* 1) BeanDescriptor
* 2) DefaultEventIndex
* 3) DefaultPropertyIndex
* These aspects of the beanClass cannot be derived through introspection of the class.
* If they are not provided by the explicitBeanInfo, then they must be left null in the
* returned BeanInfo, otherwise they will be copied from the explicitBeanInfo
--------------------------------------------------------------------------------------*/
if (explicitBeanInfo != null)
{
this.explicitBeanInfo = explicitBeanInfo;
events = explicitBeanInfo.getEventSetDescriptors();
methods = explicitBeanInfo.getMethodDescriptors();
properties = explicitBeanInfo.getPropertyDescriptors();
defaultEventIndex = explicitBeanInfo.getDefaultEventIndex();
if (defaultEventIndex < 0 || defaultEventIndex >= events.length)
{
defaultEventIndex = -1;
}
defaultPropertyIndex = explicitBeanInfo.getDefaultPropertyIndex();
if (defaultPropertyIndex < 0 || defaultPropertyIndex >= properties.length)
{
defaultPropertyIndex = -1;
}
additionalBeanInfo = explicitBeanInfo.getAdditionalBeanInfo();
for (int i = 0; i < 4; i++)
{
icon[i] = explicitBeanInfo.getIcon(i + 1);
}
if (events != null)
explicitEvents = true;
if (methods != null)
explicitMethods = true;
if (properties != null)
explicitProperties = true;
}
if (methods == null)
{
methods = introspectMethods();
}
if (properties == null)
{
properties = introspectProperties(stopClass);
}
if (events == null)
{
events = introspectEvents();
}
}
@Override
public BeanInfo[] getAdditionalBeanInfo()
{
return null;
}
@Override
public EventSetDescriptor[] getEventSetDescriptors()
{
return events;
}
@Override
public MethodDescriptor[] getMethodDescriptors()
{
return methods;
}
@Override
public PropertyDescriptor[] getPropertyDescriptors()
{
return properties;
}
@Override
public BeanDescriptor getBeanDescriptor()
{
if (beanDescriptor == null)
{
if (explicitBeanInfo != null)
{
beanDescriptor = explicitBeanInfo.getBeanDescriptor();
}
if (beanDescriptor == null)
{
beanDescriptor = new BeanDescriptor(beanClass);
}
}
return beanDescriptor;
}
@Override
public int getDefaultEventIndex()
{
return this.defaultEventIndex;
}
@Override
public int getDefaultPropertyIndex()
{
return this.defaultPropertyIndex;
}
@Override
public Image getIcon(int iconKind)
{
return (Image) icon[iconKind - 1];
}
void mergeBeanInfo(BeanInfo beanInfo, boolean force) throws IntrospectionException
{
if (force || !explicitProperties)
{
PropertyDescriptor[] superDescs = beanInfo.getPropertyDescriptors();
if (superDescs != null)
{
if (getPropertyDescriptors() != null)
{
properties = mergeProps(superDescs, beanInfo.getDefaultPropertyIndex());
}
else
{
properties = superDescs;
defaultPropertyIndex = beanInfo.getDefaultPropertyIndex();
}
}
}
if (force || !explicitMethods)
{
MethodDescriptor[] superMethods = beanInfo.getMethodDescriptors();
if (superMethods != null)
{
if (methods != null)
{
methods = mergeMethods(superMethods);
}
else
{
methods = superMethods;
}
}
}
if (force || !explicitEvents)
{
EventSetDescriptor[] superEvents = beanInfo.getEventSetDescriptors();
if (superEvents != null)
{
if (events != null)
{
events = mergeEvents(superEvents, beanInfo.getDefaultEventIndex());
}
else
{
events = superEvents;
defaultEventIndex = beanInfo.getDefaultEventIndex();
}
}
}
}
/*
* merge the PropertyDescriptor with superclass
*/
private PropertyDescriptor[] mergeProps(PropertyDescriptor[] superDescs, int superDefaultIndex) throws IntrospectionException
{
// FIXME:change to OO way as EventSetD and MethodD
HashMap subMap = internalAsMap(properties);
String defaultPropertyName = null;
if (defaultPropertyIndex >= 0 && defaultPropertyIndex < properties.length)
{
defaultPropertyName = properties[defaultPropertyIndex].getName();
}
else if (superDefaultIndex >= 0 && superDefaultIndex < superDescs.length)
{
defaultPropertyName = superDescs[superDefaultIndex].getName();
}
for (int i = 0; i < superDescs.length; i++)
{
PropertyDescriptor superDesc = superDescs[i];
String propertyName = superDesc.getName();
if (!subMap.containsKey(propertyName))
{
subMap.put(propertyName, superDesc);
continue;
}
Object value = subMap.get(propertyName);
// if sub and super are both PropertyDescriptor
Method subGet = ((PropertyDescriptor) value).getReadMethod();
Method subSet = ((PropertyDescriptor) value).getWriteMethod();
Method superGet = superDesc.getReadMethod();
Method superSet = superDesc.getWriteMethod();
Class> superType = superDesc.getPropertyType();
Class> superIndexedType = null;
Class> subType = ((PropertyDescriptor) value).getPropertyType();
Class> subIndexedType = null;
if (value instanceof IndexedPropertyDescriptor)
{
subIndexedType = ((IndexedPropertyDescriptor) value).getIndexedPropertyType();
}
if (superDesc instanceof IndexedPropertyDescriptor)
{
superIndexedType = ((IndexedPropertyDescriptor) superDesc).getIndexedPropertyType();
}
// if superDesc is PropertyDescriptor
if (superIndexedType == null)
{
PropertyDescriptor subDesc = (PropertyDescriptor) value;
// Sub is PropertyDescriptor
if (subIndexedType == null)
{
// Same property type
if (subType != null && superType != null && subType.getName() != null && subType.getName().equals(superType.getName()))
{
if (superGet != null && (subGet == null || superGet.equals(subGet)))
{
subDesc.setReadMethod(superGet);
}
if (superSet != null && (subSet == null || superSet.equals(subSet)))
{
subDesc.setWriteMethod(superSet);
}
if (subType == boolean.class && subGet != null && superGet != null)
{
if (superGet.getName().startsWith(PREFIX_IS))
{
subDesc.setReadMethod(superGet);
}
}
}
else
{ // Different type
if ((subGet == null || subSet == null) && (superGet != null))
{
subDesc = new PropertyDescriptor(propertyName, superGet, superSet);
if (subGet != null)
{
String subGetName = subGet.getName();
Method method = null;
MethodDescriptor[] introspectMethods = introspectMethods();
for (MethodDescriptor methodDesc : introspectMethods)
{
method = methodDesc.getMethod();
if (method != subGet && subGetName.equals(method.getName()) && method.getParameterTypes().length == 0
&& method.getReturnType() == superType)
{
subDesc.setReadMethod(method);
break;
}
}
}
}
}
}
else
{ // Sub is IndexedPropertyDescriptor and super is PropertyDescriptor
if (superType != null && (superType.isArray()) && (superType.getComponentType().getName().equals(subIndexedType.getName())))
{
if ((subGet == null) && (superGet != null))
{
subDesc.setReadMethod(superGet);
}
if ((subSet == null) && (superSet != null))
{
subDesc.setWriteMethod(superSet);
}
} // different type do nothing
// sub is indexed pd and super is normal pd
if (subIndexedType == boolean.class && superType == boolean.class)
{
Method subIndexedSet = ((IndexedPropertyDescriptor) subDesc).getIndexedWriteMethod();
if (subGet == null && subSet == null && subIndexedSet != null && superGet != null)
{
try
{
subSet = beanClass.getDeclaredMethod(subIndexedSet.getName(), boolean.class);
}
catch (Exception e)
{
// ignored
}
if (subSet != null)
{
// Cast sub into PropertyDescriptor
subDesc = new PropertyDescriptor(propertyName, superGet, subSet);
}
}
}
}
subMap.put(propertyName, subDesc);
}
else
{ // Super is IndexedPropertyDescriptor
if (subIndexedType == null)
{ // Sub is PropertyDescriptor
if (subType != null && subType.isArray() && (subType.getComponentType().getName().equals(superIndexedType.getName())))
{
// Same type
if (subGet != null)
{
superDesc.setReadMethod(subGet);
}
if (subSet != null)
{
superDesc.setWriteMethod(subSet);
}
subMap.put(propertyName, superDesc);
}
else
{
// subDesc is PropertyDescriptor
// superDesc is IndexedPropertyDescriptor
// fill null subGet or subSet method with superClass's
if (subGet == null || subSet == null)
{
Class> beanSuperClass = beanClass.getSuperclass();
String methodSuffix = capitalize(propertyName);
Method method = null;
if (subGet == null)
{
// subGet is null
if (subType == boolean.class)
{
try
{
method = beanSuperClass.getDeclaredMethod(PREFIX_IS + methodSuffix);
}
catch (Exception e)
{
// ignored
}
}
else
{
try
{
method = beanSuperClass.getDeclaredMethod(PREFIX_GET + methodSuffix);
}
catch (Exception e)
{
// ignored
}
}
if (method != null && !Modifier.isStatic(method.getModifiers()) && method.getReturnType() == subType)
{
((PropertyDescriptor) value).setReadMethod(method);
}
}
else
{
// subSet is null
try
{
method = beanSuperClass.getDeclaredMethod(PREFIX_SET + methodSuffix, subType);
}
catch (Exception e)
{
// ignored
}
if (method != null && !Modifier.isStatic(method.getModifiers()) && method.getReturnType() == void.class)
{
((PropertyDescriptor) value).setWriteMethod(method);
}
}
}
subMap.put(propertyName, (PropertyDescriptor) value);
}
}
else if (subIndexedType.getName().equals(superIndexedType.getName()))
{
// Sub is IndexedPropertyDescriptor and Same type
IndexedPropertyDescriptor subDesc = (IndexedPropertyDescriptor) value;
if ((subGet == null) && (superGet != null))
{
subDesc.setReadMethod(superGet);
}
if ((subSet == null) && (superSet != null))
{
subDesc.setWriteMethod(superSet);
}
IndexedPropertyDescriptor superIndexedDesc = (IndexedPropertyDescriptor) superDesc;
if ((subDesc.getIndexedReadMethod() == null) && (superIndexedDesc.getIndexedReadMethod() != null))
{
subDesc.setIndexedReadMethod(superIndexedDesc.getIndexedReadMethod());
}
if ((subDesc.getIndexedWriteMethod() == null) && (superIndexedDesc.getIndexedWriteMethod() != null))
{
subDesc.setIndexedWriteMethod(superIndexedDesc.getIndexedWriteMethod());
}
subMap.put(propertyName, subDesc);
} // Different indexed type, do nothing
}
mergeAttributes((PropertyDescriptor) value, superDesc);
}
PropertyDescriptor[] theDescs = new PropertyDescriptor[subMap.size()];
subMap.values().toArray(theDescs);
if (defaultPropertyName != null && !explicitProperties)
{
for (int i = 0; i < theDescs.length; i++)
{
if (defaultPropertyName.equals(theDescs[i].getName()))
{
defaultPropertyIndex = i;
break;
}
}
}
return theDescs;
}
private String capitalize(String name)
{
if (name == null)
{
return null;
}
// The rule for decapitalize is that:
// If the first letter of the string is Upper Case, make it lower case
// UNLESS the second letter of the string is also Upper Case, in which case no
// changes are made.
if (name.length() == 0 || (name.length() > 1 && Character.isUpperCase(name.charAt(1))))
{
return name;
}
char[] chars = name.toCharArray();
chars[0] = Character.toUpperCase(chars[0]);
return new String(chars);
}
private static void mergeAttributes(PropertyDescriptor subDesc, PropertyDescriptor superDesc)
{
// FIXME: this is just temp workaround, need more elegant solution to
// handle this
subDesc.hidden |= superDesc.hidden;
subDesc.expert |= superDesc.expert;
subDesc.preferred |= superDesc.preferred;
subDesc.bound |= superDesc.bound;
subDesc.constrained |= superDesc.constrained;
subDesc.name = superDesc.name;
if (subDesc.shortDescription == null && superDesc.shortDescription != null)
{
subDesc.shortDescription = superDesc.shortDescription;
}
if (subDesc.displayName == null && superDesc.displayName != null)
{
subDesc.displayName = superDesc.displayName;
}
}
/*
* merge the MethodDescriptor
*/
private MethodDescriptor[] mergeMethods(MethodDescriptor[] superDescs)
{
HashMap subMap = internalAsMap(methods);
for (MethodDescriptor superMethod : superDescs)
{
String methodName = getQualifiedName(superMethod.getMethod());
MethodDescriptor method = subMap.get(methodName);
if (method == null)
{
subMap.put(methodName, superMethod);
}
else
{
method.merge(superMethod);
}
}
MethodDescriptor[] theMethods = new MethodDescriptor[subMap.size()];
subMap.values().toArray(theMethods);
return theMethods;
}
private EventSetDescriptor[] mergeEvents(EventSetDescriptor[] otherEvents, int otherDefaultIndex)
{
HashMap subMap = internalAsMap(events);
String defaultEventName = null;
if (defaultEventIndex >= 0 && defaultEventIndex < events.length)
{
defaultEventName = events[defaultEventIndex].getName();
}
else if (otherDefaultIndex >= 0 && otherDefaultIndex < otherEvents.length)
{
defaultEventName = otherEvents[otherDefaultIndex].getName();
}
for (EventSetDescriptor event : otherEvents)
{
String eventName = event.getName();
EventSetDescriptor subEvent = subMap.get(eventName);
if (subEvent == null)
{
subMap.put(eventName, event);
}
else
{
subEvent.merge(event);
}
}
EventSetDescriptor[] theEvents = new EventSetDescriptor[subMap.size()];
subMap.values().toArray(theEvents);
if (defaultEventName != null && !explicitEvents)
{
for (int i = 0; i < theEvents.length; i++)
{
if (defaultEventName.equals(theEvents[i].getName()))
{
defaultEventIndex = i;
break;
}
}
}
return theEvents;
}
private static HashMap internalAsMap(PropertyDescriptor[] propertyDescs)
{
HashMap map = new HashMap();
for (int i = 0; i < propertyDescs.length; i++)
{
map.put(propertyDescs[i].getName(), propertyDescs[i]);
}
return map;
}
private static HashMap internalAsMap(MethodDescriptor[] theDescs)
{
HashMap map = new HashMap();
for (int i = 0; i < theDescs.length; i++)
{
String qualifiedName = getQualifiedName(theDescs[i].getMethod());
map.put(qualifiedName, theDescs[i]);
}
return map;
}
private static HashMap internalAsMap(EventSetDescriptor[] theDescs)
{
HashMap map = new HashMap();
for (int i = 0; i < theDescs.length; i++)
{
map.put(theDescs[i].getName(), theDescs[i]);
}
return map;
}
private static String getQualifiedName(Method method)
{
String qualifiedName = method.getName();
Class>[] paramTypes = method.getParameterTypes();
if (paramTypes != null)
{
for (int i = 0; i < paramTypes.length; i++)
{
qualifiedName += "_" + paramTypes[i].getName(); //$NON-NLS-1$
}
}
return qualifiedName;
}
/**
* Introspects the supplied class and returns a list of the public methods of the class
*
* @return An array of MethodDescriptors with the public methods. null if there are no public methods
*/
private MethodDescriptor[] introspectMethods()
{
return introspectMethods(false, beanClass);
}
private MethodDescriptor[] introspectMethods(boolean includeSuper)
{
return introspectMethods(includeSuper, beanClass);
}
private MethodDescriptor[] introspectMethods(boolean includeSuper, Class> introspectorClass)
{
// Get the list of methods belonging to this class
Method[] basicMethods = includeSuper ? introspectorClass.getMethods() : introspectorClass.getDeclaredMethods();
if (basicMethods == null || basicMethods.length == 0)
return null;
ArrayList methodList = new ArrayList(basicMethods.length);
// Loop over the methods found, looking for public non-static methods
for (int i = 0; i < basicMethods.length; i++)
{
int modifiers = basicMethods[i].getModifiers();
if (Modifier.isPublic(modifiers))
{
// Allocate a MethodDescriptor for this method
MethodDescriptor theDescriptor = new MethodDescriptor(basicMethods[i]);
methodList.add(theDescriptor);
}
}
// Get the list of public methods into the returned array
int methodCount = methodList.size();
MethodDescriptor[] theMethods = null;
if (methodCount > 0)
{
theMethods = new MethodDescriptor[methodCount];
theMethods = methodList.toArray(theMethods);
}
return theMethods;
}
/**
* Introspects the supplied class and returns a list of the Properties of the class
*
* @param stopClass
* - the to introspecting at
* @return The list of Properties as an array of PropertyDescriptors
* @throws IntrospectionException
*/
private PropertyDescriptor[] introspectProperties(Class> stopClass) throws IntrospectionException
{
// Get descriptors for the public methods
MethodDescriptor[] methodDescriptors = introspectMethods();
if (methodDescriptors == null)
{
return null;
}
ArrayList methodList = new ArrayList();
// Loop over the methods found, looking for public non-static methods
for (int index = 0; index < methodDescriptors.length; index++)
{
int modifiers = methodDescriptors[index].getMethod().getModifiers();
if (!Modifier.isStatic(modifiers))
{
methodList.add(methodDescriptors[index]);
}
}
// Get the list of public non-static methods into an array
int methodCount = methodList.size();
MethodDescriptor[] theMethods = null;
if (methodCount > 0)
{
theMethods = new MethodDescriptor[methodCount];
theMethods = methodList.toArray(theMethods);
}
if (theMethods == null)
{
return null;
}
HashMap propertyTable = new HashMap(theMethods.length);
// Search for methods that either get or set a Property
for (int i = 0; i < theMethods.length; i++)
{
introspectGet(theMethods[i].getMethod(), propertyTable);
introspectSet(theMethods[i].getMethod(), propertyTable);
}
// fix possible getter & setter collisions
fixGetSet(propertyTable);
// If there are listener methods, should be bound.
MethodDescriptor[] allMethods = introspectMethods(true);
if (stopClass != null)
{
MethodDescriptor[] excludeMethods = introspectMethods(true, stopClass);
if (excludeMethods != null)
{
ArrayList tempMethods = new ArrayList();
for (MethodDescriptor method : allMethods)
{
if (!isInSuper(method, excludeMethods))
{
tempMethods.add(method);
}
}
allMethods = tempMethods.toArray(new MethodDescriptor[0]);
}
}
for (int i = 0; i < allMethods.length; i++)
{
introspectPropertyListener(allMethods[i].getMethod());
}
// Put the properties found into the PropertyDescriptor array
ArrayList propertyList = new ArrayList();
for (Map.Entry entry : propertyTable.entrySet())
{
String propertyName = entry.getKey();
HashMap table = entry.getValue();
if (table == null)
{
continue;
}
String normalTag = (String) table.get(STR_NORMAL);
String indexedTag = (String) table.get(STR_INDEXED);
if ((normalTag == null) && (indexedTag == null))
{
continue;
}
Method get = (Method) table.get(STR_NORMAL + PREFIX_GET);
Method set = (Method) table.get(STR_NORMAL + PREFIX_SET);
Method indexedGet = (Method) table.get(STR_INDEXED + PREFIX_GET);
Method indexedSet = (Method) table.get(STR_INDEXED + PREFIX_SET);
PropertyDescriptor propertyDesc = null;
if (indexedTag == null)
{
propertyDesc = new PropertyDescriptor(propertyName, get, set);
}
else
{
try
{
propertyDesc = new IndexedPropertyDescriptor(propertyName, get, set, indexedGet, indexedSet);
}
catch (IntrospectionException e)
{
// If the getter and the indexGetter is not compatible, try
// getter/setter is null;
propertyDesc = new IndexedPropertyDescriptor(propertyName, null, null, indexedGet, indexedSet);
}
}
// RI set propretyDescriptor as bound. FIXME
// propertyDesc.setBound(true);
if (canAddPropertyChangeListener && canRemovePropertyChangeListener)
{
propertyDesc.setBound(true);
}
else
{
propertyDesc.setBound(false);
}
if (table.get(STR_IS_CONSTRAINED) == Boolean.TRUE)
{ //$NON-NLS-1$
propertyDesc.setConstrained(true);
}
propertyList.add(propertyDesc);
}
PropertyDescriptor[] theProperties = new PropertyDescriptor[propertyList.size()];
propertyList.toArray(theProperties);
return theProperties;
}
private boolean isInSuper(MethodDescriptor method, MethodDescriptor[] excludeMethods)
{
for (MethodDescriptor m : excludeMethods)
{
if (method.getMethod().equals(m.getMethod()))
{
return true;
}
}
return false;
}
@SuppressWarnings("nls")
private void introspectPropertyListener(Method theMethod)
{
String methodName = theMethod.getName();
Class>[] param = theMethod.getParameterTypes();
if (param.length != 1)
{
return;
}
if (methodName.equals("addPropertyChangeListener") && param[0].equals(PropertyChangeListener.class))
canAddPropertyChangeListener = true;
if (methodName.equals("removePropertyChangeListener") && param[0].equals(PropertyChangeListener.class))
canRemovePropertyChangeListener = true;
}
private static void introspectGet(Method theMethod, HashMap propertyTable)
{
String methodName = theMethod.getName();
int prefixLength = 0;
String propertyName;
Class propertyType;
Class[] paramTypes;
HashMap table;
ArrayList getters;
if (methodName == null)
{
return;
}
if (methodName.startsWith(PREFIX_GET))
{
prefixLength = PREFIX_GET.length();
}
if (methodName.startsWith(PREFIX_IS))
{
prefixLength = PREFIX_IS.length();
}
if (prefixLength == 0)
{
return;
}
propertyName = decapitalize(methodName.substring(prefixLength));
// validate property name
if (!isValidProperty(propertyName))
{
return;
}
// validate return type
propertyType = theMethod.getReturnType();
if (propertyType == null || propertyType == void.class)
{
return;
}
// isXXX return boolean
if (prefixLength == 2)
{
if (!(propertyType == boolean.class))
{
return;
}
}
// validate parameter types
paramTypes = theMethod.getParameterTypes();
if (paramTypes.length > 1 || (paramTypes.length == 1 && paramTypes[0] != int.class))
{
return;
}
table = propertyTable.get(propertyName);
if (table == null)
{
table = new HashMap();
propertyTable.put(propertyName, table);
}
getters = (ArrayList) table.get(STR_GETTERS);
if (getters == null)
{
getters = new ArrayList();
table.put(STR_GETTERS, getters);
}
// add current method as a valid getter
getters.add(theMethod);
}
private static void introspectSet(Method theMethod, HashMap propertyTable)
{
String methodName = theMethod.getName();
if (methodName == null)
{
return;
}
String propertyName;
Class returnType;
Class[] paramTypes;
// setter method should never return type other than void
returnType = theMethod.getReturnType();
if (returnType != void.class)
{
return;
}
if (methodName == null || !methodName.startsWith(PREFIX_SET))
{
return;
}
propertyName = decapitalize(methodName.substring(PREFIX_SET.length()));
// validate property name
if (!isValidProperty(propertyName))
{
return;
}
// It seems we do not need to validate return type
// validate param types
paramTypes = theMethod.getParameterTypes();
if (paramTypes.length == 0 || paramTypes.length > 2 || (paramTypes.length == 2 && paramTypes[0] != int.class))
{
return;
}
HashMap table = propertyTable.get(propertyName);
if (table == null)
{
table = new HashMap();
propertyTable.put(propertyName, table);
}
ArrayList setters = (ArrayList) table.get(STR_SETTERS);
if (setters == null)
{
setters = new ArrayList();
table.put(STR_SETTERS, setters);
}
// handle constrained
Class[] exceptions = theMethod.getExceptionTypes();
for (Class e : exceptions)
{
if (e.equals(PropertyVetoException.class))
{
table.put(STR_IS_CONSTRAINED, Boolean.TRUE); //$NON-NLS-1$
}
}
// add new setter
setters.add(theMethod);
}
/**
* Checks and fixs all cases when several incompatible checkers / getters were specified for single property.
*
* @param propertyTable
* @throws IntrospectionException
*/
private void fixGetSet(HashMap propertyTable) throws IntrospectionException
{
if (propertyTable == null)
{
return;
}
for (Map.Entry entry : propertyTable.entrySet())
{
HashMap table = entry.getValue();
ArrayList getters = (ArrayList) table.get(STR_GETTERS);
ArrayList setters = (ArrayList) table.get(STR_SETTERS);
Method normalGetter = null;
Method indexedGetter = null;
Method normalSetter = null;
Method indexedSetter = null;
Class> normalPropType = null;
Class> indexedPropType = null;
if (getters == null)
{
getters = new ArrayList();
}
if (setters == null)
{
setters = new ArrayList();
}
// retrieve getters
Class>[] paramTypes = null;
String methodName = null;
for (Method getter : getters)
{
paramTypes = getter.getParameterTypes();
methodName = getter.getName();
// checks if it's a normal getter
if (paramTypes == null || paramTypes.length == 0)
{
// normal getter found
if (normalGetter == null || methodName.startsWith(PREFIX_IS))
{
normalGetter = getter;
}
}
// checks if it's an indexed getter
if (paramTypes != null && paramTypes.length == 1 && paramTypes[0] == int.class)
{
// indexed getter found
if (indexedGetter == null || methodName.startsWith(PREFIX_GET)
|| (methodName.startsWith(PREFIX_IS) && !indexedGetter.getName().startsWith(PREFIX_GET)))
{
indexedGetter = getter;
}
}
}
// retrieve normal setter
if (normalGetter != null)
{
// Now we will try to look for normal setter of the same type.
Class> propertyType = normalGetter.getReturnType();
for (Method setter : setters)
{
if (setter.getParameterTypes().length == 1 && propertyType.equals(setter.getParameterTypes()[0]))
{
normalSetter = setter;
break;
}
}
}
else
{
// Normal getter wasn't defined. Let's look for the last
// defined setter
for (Method setter : setters)
{
if (setter.getParameterTypes().length == 1)
{
normalSetter = setter;
}
}
}
// retrieve indexed setter
if (indexedGetter != null)
{
// Now we will try to look for indexed setter of the same type.
Class> propertyType = indexedGetter.getReturnType();
for (Method setter : setters)
{
if (setter.getParameterTypes().length == 2 && setter.getParameterTypes()[0] == int.class
&& propertyType.equals(setter.getParameterTypes()[1]))
{
indexedSetter = setter;
break;
}
}
}
else
{
// Indexed getter wasn't defined. Let's look for the last
// defined indexed setter
for (Method setter : setters)
{
if (setter.getParameterTypes().length == 2 && setter.getParameterTypes()[0] == int.class)
{
indexedSetter = setter;
}
}
}
// determine property type
if (normalGetter != null)
{
normalPropType = normalGetter.getReturnType();
}
else if (normalSetter != null)
{
normalPropType = normalSetter.getParameterTypes()[0];
}
// determine indexed getter/setter type
if (indexedGetter != null)
{
indexedPropType = indexedGetter.getReturnType();
}
else if (indexedSetter != null)
{
indexedPropType = indexedSetter.getParameterTypes()[1];
}
// convert array-typed normal getters to indexed getters
if (normalGetter != null && normalGetter.getReturnType().isArray())
{
}
// RULES
// These rules were created after performing extensive black-box
// testing of RI
// RULE1
// Both normal getter and setter of the same type were defined;
// no indexed getter/setter *PAIR* of the other type defined
if (normalGetter != null && normalSetter != null && (indexedGetter == null || indexedSetter == null))
{
table.put(STR_NORMAL, STR_VALID);
table.put(STR_NORMAL + PREFIX_GET, normalGetter);
table.put(STR_NORMAL + PREFIX_SET, normalSetter);
table.put(STR_NORMAL + STR_PROPERTY_TYPE, normalPropType);
continue;
}
// RULE2
// normal getter and/or setter was defined; no indexed
// getters & setters defined
if ((normalGetter != null || normalSetter != null) && indexedGetter == null && indexedSetter == null)
{
table.put(STR_NORMAL, STR_VALID);
table.put(STR_NORMAL + PREFIX_GET, normalGetter);
table.put(STR_NORMAL + PREFIX_SET, normalSetter);
table.put(STR_NORMAL + STR_PROPERTY_TYPE, normalPropType);
continue;
}
// RULE3
// mix of normal / indexed getters and setters are defined. Types
// are compatible
if ((normalGetter != null || normalSetter != null) && (indexedGetter != null || indexedSetter != null))
{
// (1)!A!B!C!D
if (normalGetter != null && normalSetter != null && indexedGetter != null && indexedSetter != null)
{
if (indexedGetter.getName().startsWith(PREFIX_GET))
{
table.put(STR_NORMAL, STR_VALID);
table.put(STR_NORMAL + PREFIX_GET, normalGetter);
table.put(STR_NORMAL + PREFIX_SET, normalSetter);
table.put(STR_NORMAL + STR_PROPERTY_TYPE, normalPropType);
table.put(STR_INDEXED, STR_VALID);
table.put(STR_INDEXED + PREFIX_GET, indexedGetter);
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
}
else
{
if (normalPropType != boolean.class && normalGetter.getName().startsWith(PREFIX_IS))
{
table.put(STR_INDEXED, STR_VALID);
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
}
else
{
table.put(STR_NORMAL, STR_VALID);
table.put(STR_NORMAL + PREFIX_GET, normalGetter);
table.put(STR_NORMAL + PREFIX_SET, normalSetter);
table.put(STR_NORMAL + STR_PROPERTY_TYPE, normalPropType);
}
}
continue;
}
// (2)!AB!C!D
if (normalGetter != null && normalSetter == null && indexedGetter != null && indexedSetter != null)
{
table.put(STR_NORMAL, STR_VALID);
table.put(STR_NORMAL + PREFIX_GET, normalGetter);
table.put(STR_NORMAL + PREFIX_SET, normalSetter);
table.put(STR_NORMAL + STR_PROPERTY_TYPE, normalPropType);
table.put(STR_INDEXED, STR_VALID);
if (indexedGetter.getName().startsWith(PREFIX_GET))
{
table.put(STR_INDEXED + PREFIX_GET, indexedGetter);
}
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
continue;
}
// (3)A!B!C!D
if (normalGetter == null && normalSetter != null && indexedGetter != null && indexedSetter != null)
{
table.put(STR_INDEXED, STR_VALID);
if (indexedGetter.getName().startsWith(PREFIX_GET))
{
table.put(STR_INDEXED + PREFIX_GET, indexedGetter);
}
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
continue;
}
// (4)!AB!CD
if (normalGetter != null && normalSetter == null && indexedGetter != null && indexedSetter == null)
{
if (indexedGetter.getName().startsWith(PREFIX_GET))
{
table.put(STR_NORMAL, STR_VALID);
table.put(STR_NORMAL + PREFIX_GET, normalGetter);
table.put(STR_NORMAL + PREFIX_SET, normalSetter);
table.put(STR_NORMAL + STR_PROPERTY_TYPE, normalPropType);
table.put(STR_INDEXED, STR_VALID);
table.put(STR_INDEXED + PREFIX_GET, indexedGetter);
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
}
else
{
table.put(STR_NORMAL, STR_VALID);
table.put(STR_NORMAL + PREFIX_GET, normalGetter);
table.put(STR_NORMAL + PREFIX_SET, normalSetter);
table.put(STR_NORMAL + STR_PROPERTY_TYPE, normalPropType);
}
continue;
}
// (5)A!B!CD
if (normalGetter == null && normalSetter != null && indexedGetter != null && indexedSetter == null)
{
if (indexedGetter.getName().startsWith(PREFIX_GET))
{
table.put(STR_NORMAL, STR_VALID);
table.put(STR_NORMAL + PREFIX_GET, normalGetter);
table.put(STR_NORMAL + PREFIX_SET, normalSetter);
table.put(STR_NORMAL + STR_PROPERTY_TYPE, normalPropType);
table.put(STR_INDEXED, STR_VALID);
table.put(STR_INDEXED + PREFIX_GET, indexedGetter);
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
}
else
{
table.put(STR_NORMAL, STR_VALID);
table.put(STR_NORMAL + PREFIX_GET, normalGetter);
table.put(STR_NORMAL + PREFIX_SET, normalSetter);
table.put(STR_NORMAL + STR_PROPERTY_TYPE, normalPropType);
}
continue;
}
// (6)!ABC!D
if (normalGetter != null && normalSetter == null && indexedGetter == null && indexedSetter != null)
{
table.put(STR_INDEXED, STR_VALID);
table.put(STR_INDEXED + PREFIX_GET, indexedGetter);
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
continue;
}
// (7)A!BC!D
if (normalGetter == null && normalSetter != null && indexedGetter == null && indexedSetter != null)
{
table.put(STR_INDEXED, STR_VALID);
table.put(STR_INDEXED + PREFIX_GET, indexedGetter);
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
continue;
}
}
// RULE4
// no normal normal getter / setter.
// Only indexed getter and/or setter is given
// no normal setters / getters defined
if (normalSetter == null && normalGetter == null && (indexedGetter != null || indexedSetter != null))
{
if (indexedGetter != null && indexedGetter.getName().startsWith(PREFIX_IS))
{
if (indexedSetter != null)
{
table.put(STR_INDEXED, STR_VALID);
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
}
continue;
}
table.put(STR_INDEXED, STR_VALID);
table.put(STR_INDEXED + PREFIX_GET, indexedGetter);
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
continue;
}
// RULE5
// Both indexed getter and setter methods are defined
// no normal getter/setter *PAIR* of the other type defined
if ((normalSetter != null || normalGetter != null) && indexedGetter != null && indexedSetter != null)
{
table.put(STR_INDEXED, STR_VALID);
table.put(STR_INDEXED + PREFIX_GET, indexedGetter);
table.put(STR_INDEXED + PREFIX_SET, indexedSetter);
table.put(STR_INDEXED + STR_PROPERTY_TYPE, indexedPropType);
continue;
}
// default rule - invalid property
table.put(STR_NORMAL, STR_INVALID);
table.put(STR_INDEXED, STR_INVALID);
}
}
/**
* Introspects the supplied Bean class and returns a list of the Events of the class
*
* @return the events
* @throws IntrospectionException
*/
private EventSetDescriptor[] introspectEvents() throws IntrospectionException
{
// Get descriptors for the public methods
// FIXME: performance
MethodDescriptor[] theMethods = introspectMethods();
if (theMethods == null)
return null;
HashMap eventTable = new HashMap(theMethods.length);
// Search for methods that add an Event Listener
for (int i = 0; i < theMethods.length; i++)
{
introspectListenerMethods(PREFIX_ADD, theMethods[i].getMethod(), eventTable);
introspectListenerMethods(PREFIX_REMOVE, theMethods[i].getMethod(), eventTable);
introspectGetListenerMethods(theMethods[i].getMethod(), eventTable);
}
ArrayList eventList = new ArrayList();
for (Map.Entry entry : eventTable.entrySet())
{
HashMap table = entry.getValue();
Method add = (Method) table.get(PREFIX_ADD);
Method remove = (Method) table.get(PREFIX_REMOVE);
if ((add == null) || (remove == null))
{
continue;
}
Method get = (Method) table.get(PREFIX_GET);
Class> listenerType = (Class) table.get("listenerType"); //$NON-NLS-1$
Method[] listenerMethods = (Method[]) table.get("listenerMethods"); //$NON-NLS-1$
EventSetDescriptor eventSetDescriptor = new EventSetDescriptor(decapitalize(entry.getKey()), listenerType, listenerMethods, add, remove, get);
eventSetDescriptor.setUnicast(table.get("isUnicast") != null); //$NON-NLS-1$
eventList.add(eventSetDescriptor);
}
EventSetDescriptor[] theEvents = new EventSetDescriptor[eventList.size()];
eventList.toArray(theEvents);
return theEvents;
}
/*
* find the add, remove listener method
*/
private static void introspectListenerMethods(String type, Method theMethod, HashMap methodsTable)
{
String methodName = theMethod.getName();
if (methodName == null)
{
return;
}
if (!((methodName.startsWith(type)) && (methodName.endsWith(SUFFIX_LISTEN))))
{
return;
}
String listenerName = methodName.substring(type.length());
String eventName = listenerName.substring(0, listenerName.lastIndexOf(SUFFIX_LISTEN));
if ((eventName == null) || (eventName.length() == 0))
{
return;
}
Class[] paramTypes = theMethod.getParameterTypes();
if ((paramTypes == null) || (paramTypes.length != 1))
{
return;
}
Class> listenerType = paramTypes[0];
if (!EventListener.class.isAssignableFrom(listenerType))
{
return;
}
if (!listenerType.getName().endsWith(listenerName))
{
return;
}
HashMap table = methodsTable.get(eventName);
if (table == null)
{
table = new HashMap();
}
// put listener type
if (table.get("listenerType") == null) { //$NON-NLS-1$
table.put("listenerType", listenerType); //$NON-NLS-1$
table.put("listenerMethods", //$NON-NLS-1$
introspectListenerMethods(listenerType));
}
// put add / remove
table.put(type, theMethod);
// determine isUnicast()
if (type.equals(PREFIX_ADD))
{
Class[] exceptionTypes = theMethod.getExceptionTypes();
if (exceptionTypes != null)
{
for (int i = 0; i < exceptionTypes.length; i++)
{
if (exceptionTypes[i].getName().equals(TooManyListenersException.class.getName()))
{
table.put("isUnicast", "true"); //$NON-NLS-1$//$NON-NLS-2$
break;
}
}
}
}
methodsTable.put(eventName, table);
}
private static Method[] introspectListenerMethods(Class> listenerType)
{
Method[] methods = listenerType.getDeclaredMethods();
ArrayList list = new ArrayList();
for (int i = 0; i < methods.length; i++)
{
Class>[] paramTypes = methods[i].getParameterTypes();
if (paramTypes.length != 1)
{
continue;
}
if (EventObject.class.isAssignableFrom(paramTypes[0]))
{
list.add(methods[i]);
}
}
Method[] matchedMethods = new Method[list.size()];
list.toArray(matchedMethods);
return matchedMethods;
}
private static void introspectGetListenerMethods(Method theMethod, HashMap methodsTable)
{
String type = PREFIX_GET;
String methodName = theMethod.getName();
if (methodName == null)
{
return;
}
if (!((methodName.startsWith(type)) && (methodName.endsWith(SUFFIX_LISTEN + "s")))) { //$NON-NLS-1$
return;
}
String listenerName = methodName.substring(type.length(), methodName.length() - 1);
String eventName = listenerName.substring(0, listenerName.lastIndexOf(SUFFIX_LISTEN));
if ((eventName == null) || (eventName.length() == 0))
{
return;
}
Class[] paramTypes = theMethod.getParameterTypes();
if ((paramTypes == null) || (paramTypes.length != 0))
{
return;
}
Class returnType = theMethod.getReturnType();
if ((returnType.getComponentType() == null) || (!returnType.getComponentType().getName().endsWith(listenerName)))
{
return;
}
HashMap table = methodsTable.get(eventName);
if (table == null)
{
table = new HashMap();
}
// put add / remove
table.put(type, theMethod);
methodsTable.put(eventName, table);
}
private static boolean isValidProperty(String propertyName)
{
return (propertyName != null) && (propertyName.length() != 0);
}
private static class PropertyComparator implements Comparator
{
public int compare(PropertyDescriptor object1, PropertyDescriptor object2)
{
return object1.getName().compareTo(object2.getName());
}
}
// TODO
void init()
{
if (this.events == null)
{
events = new EventSetDescriptor[0];
}
if (this.properties == null)
{
this.properties = new PropertyDescriptor[0];
}
if (properties != null)
{
String defaultPropertyName = (defaultPropertyIndex != -1 ? properties[defaultPropertyIndex].getName() : null);
Arrays.sort(properties, comparator);
if (null != defaultPropertyName)
{
for (int i = 0; i < properties.length; i++)
{
if (defaultPropertyName.equals(properties[i].getName()))
{
defaultPropertyIndex = i;
break;
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy