/*
* 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 java.beans;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.TooManyListenersException;
public class EventSetDescriptor extends FeatureDescriptor {
private Class<?> listenerType;
private ArrayList<MethodDescriptor> listenerMethodDescriptors;
private Method[] listenerMethods;
private Method getListenerMethod;
private Method addListenerMethod;
private Method removeListenerMethod;
private boolean unicast;
private boolean inDefaultEventSet = true;
public EventSetDescriptor(Class<?> sourceClass, String eventSetName,
Class<?> listenerType, String listenerMethodName)
throws IntrospectionException {
checkNotNull(sourceClass, eventSetName, listenerType,
listenerMethodName);
setName(eventSetName);
this.listenerType = listenerType;
Method method = findListenerMethodByName(listenerMethodName);
checkEventType(eventSetName, method);
listenerMethodDescriptors = new ArrayList<MethodDescriptor>();
listenerMethodDescriptors.add(new MethodDescriptor(method));
addListenerMethod = findMethodByPrefix(sourceClass, "add", "");
removeListenerMethod = findMethodByPrefix(sourceClass, "remove", "");
if (addListenerMethod == null || removeListenerMethod == null) {
throw new IntrospectionException("Add and remove methods are not available");
}
getListenerMethod = findMethodByPrefix(sourceClass, "get", "s");
unicast = isUnicastByDefault(addListenerMethod);
}
public EventSetDescriptor(Class<?> sourceClass, String eventSetName,
Class<?> listenerType, String[] listenerMethodNames,
String addListenerMethodName, String removeListenerMethodName)
throws IntrospectionException {
this(sourceClass, eventSetName, listenerType, listenerMethodNames,
addListenerMethodName, removeListenerMethodName, null);
}
public EventSetDescriptor(Class<?> sourceClass, String eventSetName,
Class<?> listenerType, String[] listenerMethodNames,
String addListenerMethodName, String removeListenerMethodName,
String getListenerMethodName) throws IntrospectionException {
checkNotNull(sourceClass, eventSetName, listenerType,
listenerMethodNames);
setName(eventSetName);
this.listenerType = listenerType;
listenerMethodDescriptors = new ArrayList<MethodDescriptor>();
for (String element : listenerMethodNames) {
Method m = findListenerMethodByName(element);
// checkEventType(eventSetName, m);
listenerMethodDescriptors.add(new MethodDescriptor(m));
}
if (addListenerMethodName != null) {
this.addListenerMethod = findAddRemoveListenerMethod(sourceClass,
addListenerMethodName);
}
if (removeListenerMethodName != null) {
this.removeListenerMethod = findAddRemoveListenerMethod(
sourceClass, removeListenerMethodName);
}
if (getListenerMethodName != null) {
this.getListenerMethod = findGetListenerMethod(sourceClass,
getListenerMethodName);
}
this.unicast = isUnicastByDefault(addListenerMethod);
}
private Method findListenerMethodByName(String listenerMethodName)
throws IntrospectionException {
Method result = null;
Method[] methods = listenerType.getMethods();
for (Method method : methods) {
if (listenerMethodName.equals(method.getName())) {
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length == 1
&& paramTypes[0].getName().endsWith("Event")) {
result = method;
break;
}
}
}
if (null == result) {
throw new IntrospectionException("No valid method " +
listenerMethodName + " from " + listenerType.getName() + " listener.");
}
return result;
}
public EventSetDescriptor(String eventSetName, Class<?> listenerType,
Method[] listenerMethods, Method addListenerMethod,
Method removeListenerMethod) throws IntrospectionException {
this(eventSetName, listenerType, listenerMethods, addListenerMethod,
removeListenerMethod, null);
}
public EventSetDescriptor(String eventSetName, Class<?> listenerType,
Method[] listenerMethods, Method addListenerMethod,
Method removeListenerMethod, Method getListenerMethod)
throws IntrospectionException {
setName(eventSetName);
this.listenerType = listenerType;
this.listenerMethods = listenerMethods;
if (listenerMethods != null) {
listenerMethodDescriptors = new ArrayList<MethodDescriptor>();
for (Method element : listenerMethods) {
// XXX do we need this check?
// checkEventType(eventSetName, element);
// if (checkMethod(listenerType, element)) {
this.listenerMethodDescriptors
.add(new MethodDescriptor(element));
// }
}
}
this.addListenerMethod = addListenerMethod;
this.removeListenerMethod = removeListenerMethod;
this.getListenerMethod = getListenerMethod;
this.unicast = isUnicastByDefault(addListenerMethod);
}
public EventSetDescriptor(String eventSetName, Class<?> listenerType,
MethodDescriptor[] listenerMethodDescriptors,
Method addListenerMethod, Method removeListenerMethod)
throws IntrospectionException {
this(eventSetName, listenerType, null, addListenerMethod,
removeListenerMethod, null);
if (listenerMethodDescriptors != null) {
this.listenerMethodDescriptors = new ArrayList<MethodDescriptor>();
for (MethodDescriptor element : listenerMethodDescriptors) {
this.listenerMethodDescriptors.add(element);
}
}
}
// ensures that there is no nulls
@SuppressWarnings("nls")
private void checkNotNull(Object sourceClass, Object eventSetName,
Object alistenerType, Object listenerMethodName) {
if (sourceClass == null) {
throw new NullPointerException("source is null");
}
if (eventSetName == null) {
throw new NullPointerException("eventSetName is null");
}
if (alistenerType == null) {
throw new NullPointerException("listenerType is null");
}
if (listenerMethodName == null) {
throw new NullPointerException("listenerMethodName(s) is null");
}
}
/**
* Checks that given listener method has an argument of the valid type.
*
* @param eventSetName
* event set name
* @param listenerMethod
* listener method
* @throws IntrospectionException
* if check fails
*/
private static void checkEventType(String eventSetName,
Method listenerMethod) throws IntrospectionException {
Class<?>[] params = listenerMethod.getParameterTypes();
String firstParamTypeName = null;
String eventTypeName = prepareEventTypeName(eventSetName);
if (params.length > 0) {
firstParamTypeName = extractShortClassName(params[0]
.getName());
}
if (firstParamTypeName == null
|| !firstParamTypeName.equals(eventTypeName)) {
throw new IntrospectionException("Listener method " +
listenerMethod.getName() + " should have parameter of type " + eventTypeName);
}
}
/**
* @param fullClassName full name of the class
* @return name with package and encapsulating class info omitted
*/
private static String extractShortClassName(String fullClassName) {
int k = fullClassName.lastIndexOf('$');
k = (k == -1 ? fullClassName.lastIndexOf('.') : k);
return fullClassName.substring(k + 1);
}
private static String prepareEventTypeName(String eventSetName) {
StringBuilder sb = new StringBuilder();
if (eventSetName != null && eventSetName.length() > 0) {
sb.append(Character.toUpperCase(eventSetName.charAt(0)));
if (eventSetName.length() > 1) {
sb.append(eventSetName.substring(1));
}
}
sb.append("Event"); //$NON-NLS-1$
return sb.toString();
}
public Method[] getListenerMethods() {
if (listenerMethods != null) {
return listenerMethods;
}
if (listenerMethodDescriptors != null) {
listenerMethods = new Method[listenerMethodDescriptors.size()];
int index = 0;
for (MethodDescriptor md : listenerMethodDescriptors) {
listenerMethods[index++] = md.getMethod();
}
return listenerMethods;
}
return null;
}
public MethodDescriptor[] getListenerMethodDescriptors() {
return listenerMethodDescriptors == null ? null
: listenerMethodDescriptors.toArray(new MethodDescriptor[0]);
}
public Method getRemoveListenerMethod() {
return removeListenerMethod;
}
public Method getGetListenerMethod() {
return getListenerMethod;
}
public Method getAddListenerMethod() {
return addListenerMethod;
}
public Class<?> getListenerType() {
return listenerType;
}
public void setUnicast(boolean unicast) {
this.unicast = unicast;
}
public void setInDefaultEventSet(boolean inDefaultEventSet) {
this.inDefaultEventSet = inDefaultEventSet;
}
public boolean isUnicast() {
return unicast;
}
public boolean isInDefaultEventSet() {
return inDefaultEventSet;
}
/**
* Searches for {add|remove}Listener methods in the event source. Parameter
* check is also performed.
*
* @param sourceClass
* event source class
* @param methodName
* method name to search
* @return found method
* @throws IntrospectionException
* if no valid method found
*/
private Method findAddRemoveListenerMethod(Class<?> sourceClass,
String methodName) throws IntrospectionException {
try {
return sourceClass.getMethod(methodName, listenerType);
} catch (NoSuchMethodException e) {
return findAddRemoveListnerMethodWithLessCheck(sourceClass,
methodName);
} catch (Exception e) {
throw new IntrospectionException("No valid method " +
methodName + " for " + listenerType.getName() + " found.");
}
}
private Method findAddRemoveListnerMethodWithLessCheck(
Class<?> sourceClass, String methodName)
throws IntrospectionException {
Method[] methods = sourceClass.getMethods();
Method result = null;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length == 1) {
result = method;
break;
}
}
}
if (null == result) {
throw new IntrospectionException("No valid method " +
methodName + " for " + listenerType.getName() + " found.");
}
return result;
}
/**
* @param sourceClass
* class of event source
* @param methodName
* name of the custom getListeners() method
* @return found Method object for custom getListener or null if nothing is
* found
*/
private Method findGetListenerMethod(Class<?> sourceClass, String methodName) {
try {
return sourceClass.getMethod(methodName);
} catch (Exception e) {
// RI keeps silence here and just returns null
return null;
}
}
private Method findMethodByPrefix(Class<?> sourceClass, String prefix,
String postfix) {
String shortName = listenerType.getName();
if (listenerType.getPackage() != null) {
shortName = shortName.substring(listenerType.getPackage().getName()
.length() + 1);
}
String methodName = prefix + shortName + postfix;
try {
if ("get".equals(prefix)) { //$NON-NLS-1$
return sourceClass.getMethod(methodName);
}
} catch (NoSuchMethodException nsme) {
return null;
}
Method[] methods = sourceClass.getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName)) {
Class<?>[] paramTypes = methods[i].getParameterTypes();
if (paramTypes.length == 1) {
return methods[i];
}
}
}
return null;
}
private static boolean isUnicastByDefault(Method addMethod) {
if (addMethod != null) {
Class<?>[] exceptionTypes = addMethod.getExceptionTypes();
for (Class<?> element : exceptionTypes) {
if (element.equals(TooManyListenersException.class)) {
return true;
}
}
}
return false;
}
void merge(EventSetDescriptor event) {
super.merge(event);
if (addListenerMethod == null) {
addListenerMethod = event.addListenerMethod;
}
if (getListenerMethod == null) {
getListenerMethod = event.getListenerMethod;
}
if (listenerMethodDescriptors == null) {
listenerMethodDescriptors = event.listenerMethodDescriptors;
}
if (listenerMethods == null) {
listenerMethods = event.listenerMethods;
}
if (listenerType == null) {
listenerType = event.listenerType;
}
if (removeListenerMethod == null) {
removeListenerMethod = event.removeListenerMethod;
}
inDefaultEventSet &= event.inDefaultEventSet;
}
}