/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.utility.events;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
import org.eclipse.persistence.tools.workbench.utility.ClassTools;
/**
* This factory builds listeners that reflectively forward ChangeEvents.
* If you are worried about having too many little classes that have to be
* loaded and maintained by the class loader, you can use one of these.
* Of course, this comes with the additional overhead of reflection....
* Also note that the validity of the method name is not checked at compile
* time, but at runtime; although we *do* check the method as soon as the
* listener is instantiated.
*/
public abstract class ReflectiveChangeListener {
/** the target object on which we will invoke the method */
protected Object target;
protected static final Class StateChangeEvent_class = StateChangeEvent.class;
protected static final Class[] STATE_CHANGE_EVENT_CLASS_ARRAY = new Class[] {StateChangeEvent_class};
protected static final StateChangeEvent[] EMPTY_STATE_CHANGE_EVENT_ARRAY = new StateChangeEvent[0];
protected static final Class PropertyChangeEvent_class = PropertyChangeEvent.class;
protected static final Class[] PROPERTY_CHANGE_EVENT_CLASS_ARRAY = new Class[] {PropertyChangeEvent_class};
protected static final PropertyChangeEvent[] EMPTY_PROPERTY_CHANGE_EVENT_ARRAY = new PropertyChangeEvent[0];
protected static final Class CollectionChangeEvent_class = CollectionChangeEvent.class;
protected static final Class[] COLLECTION_CHANGE_EVENT_CLASS_ARRAY = new Class[] {CollectionChangeEvent_class};
protected static final CollectionChangeEvent[] EMPTY_COLLECTION_CHANGE_EVENT_ARRAY = new CollectionChangeEvent[0];
protected static final Class ListChangeEvent_class = ListChangeEvent.class;
protected static final Class[] LIST_CHANGE_EVENT_CLASS_ARRAY = new Class[] {ListChangeEvent_class};
protected static final ListChangeEvent[] EMPTY_LIST_CHANGE_EVENT_ARRAY = new ListChangeEvent[0];
protected static final Class TreeChangeEvent_class = TreeChangeEvent.class;
protected static final Class[] TREE_CHANGE_EVENT_CLASS_ARRAY = new Class[] {TreeChangeEvent_class};
protected static final TreeChangeEvent[] EMPTY_TREE_CHANGE_EVENT_ARRAY = new TreeChangeEvent[0];
// ********** helper methods **********
/**
* Find and return a method implemented by the target that can be invoked
* reflectively when a change event occurs.
*/
private static Method findChangeListenerMethod(Object target, String methodName, Class[] eventClassArray) {
Method method;
try {
method = ClassTools.method(target, methodName, eventClassArray);
} catch (NoSuchMethodException ex1) {
try {
method = ClassTools.method(target, methodName);
} catch (NoSuchMethodException ex2) {
throw new RuntimeException(ex2); // "checked" exceptions bite
}
}
return method;
}
/**
* Check whether the specified method is suitable for being invoked when a
* change event has occurred. Throw an exception if it is not suitable.
*/
private static void checkChangeListenerMethod(Method method, Class eventClass) {
Class[] parmTypes = method.getParameterTypes();
int parmTypesLength = parmTypes.length;
if (parmTypesLength == 0) {
return;
}
if ((parmTypesLength == 1) && parmTypes[0].isAssignableFrom(eventClass)) {
return;
}
throw new IllegalArgumentException(method.toString());
}
// ********** factory methods: StateChangeListener **********
/**
* Construct a state change listener that will invoke the specified method
* on the specified target.
*/
public static StateChangeListener buildStateChangeListener(Object target, Method method) {
checkChangeListenerMethod(method, StateChangeEvent_class);
return new SingleMethodReflectiveChangeListener(target, method);
}
/**
* Construct a state change listener that will invoke the specified method
* on the specified target. If a single-argument method with the specified
* name and appropriate argument is found, it will be invoked; otherwise,
* a zero-argument method with the specified name will be invoked.
*/
public static StateChangeListener buildStateChangeListener(Object target, String methodName) {
return buildStateChangeListener(target, findChangeListenerMethod(target, methodName, STATE_CHANGE_EVENT_CLASS_ARRAY));
}
// ********** factory methods: PropertyChangeListener **********
/**
* Construct a property change listener that will invoke the specified method
* on the specified target.
*/
public static PropertyChangeListener buildPropertyChangeListener(Object target, Method method) {
checkChangeListenerMethod(method, PropertyChangeEvent_class);
return new SingleMethodReflectiveChangeListener(target, method);
}
/**
* Construct a property change listener that will invoke the specified method
* on the specified target. If a single-argument method with the specified
* name and appropriate argument is found, it will be invoked; otherwise,
* a zero-argument method with the specified name will be invoked.
*/
public static PropertyChangeListener buildPropertyChangeListener(Object target, String methodName) {
return buildPropertyChangeListener(target, findChangeListenerMethod(target, methodName, PROPERTY_CHANGE_EVENT_CLASS_ARRAY));
}
// ********** factory methods: CollectionChangeListener **********
/**
* Construct a collection change listener that will invoke the specified methods
* on the specified target.
*/
public static CollectionChangeListener buildCollectionChangeListener(Object target, Method addMethod, Method removeMethod, Method changeMethod) {
checkChangeListenerMethod(addMethod, CollectionChangeEvent_class);
checkChangeListenerMethod(removeMethod, CollectionChangeEvent_class);
checkChangeListenerMethod(changeMethod, CollectionChangeEvent_class);
return new MultiMethodReflectiveChangeListener(target, addMethod, removeMethod, changeMethod);
}
/**
* Construct a collection change listener that will invoke the specified method
* on the specified target for any change event.
*/
public static CollectionChangeListener buildCollectionChangeListener(Object target, Method method) {
return buildCollectionChangeListener(target, method, method, method);
}
/**
* Construct a collection change listener that will invoke the specified methods
* on the specified target for change events. If a single-argument method
* with the specified name and appropriate argument is found, it will be invoked;
* otherwise, a zero-argument method with the specified name will be invoked.
*/
public static CollectionChangeListener buildCollectionChangeListener(Object target, String addMethodName, String removeMethodName, String changeMethodName) {
return buildCollectionChangeListener(
target,
findChangeListenerMethod(target, addMethodName, COLLECTION_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, removeMethodName, COLLECTION_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, changeMethodName, COLLECTION_CHANGE_EVENT_CLASS_ARRAY)
);
}
/**
* Construct a collection change listener that will invoke the specified method
* on the specified target for any change event. If a single-argument method
* with the specified name and appropriate argument is found, it will be invoked;
* otherwise, a zero-argument method with the specified name will be invoked.
*/
public static CollectionChangeListener buildCollectionChangeListener(Object target, String methodName) {
return buildCollectionChangeListener(target, findChangeListenerMethod(target, methodName, COLLECTION_CHANGE_EVENT_CLASS_ARRAY));
}
// ********** factory methods: ListChangeListener **********
/**
* Construct a list change listener that will invoke the specified methods
* on the specified target.
*/
public static ListChangeListener buildListChangeListener(Object target, Method addMethod, Method removeMethod, Method replaceMethod, Method changeMethod) {
checkChangeListenerMethod(addMethod, ListChangeEvent_class);
checkChangeListenerMethod(removeMethod, ListChangeEvent_class);
checkChangeListenerMethod(replaceMethod, ListChangeEvent_class);
checkChangeListenerMethod(changeMethod, ListChangeEvent_class);
return new MultiMethodReflectiveChangeListener(target, addMethod, removeMethod, replaceMethod, changeMethod);
}
/**
* Construct a list change listener that will invoke the specified method
* on the specified target for any change event.
*/
public static ListChangeListener buildListChangeListener(Object target, Method method) {
return buildListChangeListener(target, method, method, method, method);
}
/**
* Construct a list change listener that will invoke the specified methods
* on the specified target for change events. If a single-argument method
* with the specified name and appropriate argument is found, it will be invoked;
* otherwise, a zero-argument method with the specified name will be invoked.
*/
public static ListChangeListener buildListChangeListener(Object target, String addMethodName, String removeMethodName, String replaceMethodName, String changeMethodName) {
return buildListChangeListener(
target,
findChangeListenerMethod(target, addMethodName, LIST_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, removeMethodName, LIST_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, replaceMethodName, LIST_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, changeMethodName, LIST_CHANGE_EVENT_CLASS_ARRAY)
);
}
/**
* Construct a list change listener that will invoke the specified method
* on the specified target for any change event. If a single-argument method
* with the specified name and appropriate argument is found, it will be invoked;
* otherwise, a zero-argument method with the specified name will be invoked.
*/
public static ListChangeListener buildListChangeListener(Object target, String methodName) {
return buildListChangeListener(target, findChangeListenerMethod(target, methodName, LIST_CHANGE_EVENT_CLASS_ARRAY));
}
// ********** factory methods: TreeChangeListener **********
/**
* Construct a tree change listener that will invoke the specified methods
* on the specified target.
*/
public static TreeChangeListener buildTreeChangeListener(Object target, Method addMethod, Method removeMethod, Method changeMethod) {
checkChangeListenerMethod(addMethod, TreeChangeEvent_class);
checkChangeListenerMethod(removeMethod, TreeChangeEvent_class);
checkChangeListenerMethod(changeMethod, TreeChangeEvent_class);
return new MultiMethodReflectiveChangeListener(target, addMethod, removeMethod, changeMethod);
}
/**
* Construct a tree change listener that will invoke the specified method
* on the specified target for any change event.
*/
public static TreeChangeListener buildTreeChangeListener(Object target, Method method) {
return buildTreeChangeListener(target, method, method, method);
}
/**
* Construct a tree change listener that will invoke the specified methods
* on the specified target for change events. If a single-argument method
* with the specified name and appropriate argument is found, it will be invoked;
* otherwise, a zero-argument method with the specified name will be invoked.
*/
public static TreeChangeListener buildTreeChangeListener(Object target, String addMethodName, String removeMethodName, String changeMethodName) {
return buildTreeChangeListener(
target,
findChangeListenerMethod(target, addMethodName, TREE_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, removeMethodName, TREE_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, changeMethodName, TREE_CHANGE_EVENT_CLASS_ARRAY)
);
}
/**
* Construct a tree change listener that will invoke the specified method
* on the specified target for any change event. If a single-argument method
* with the specified name and appropriate argument is found, it will be invoked;
* otherwise, a zero-argument method with the specified name will be invoked.
*/
public static TreeChangeListener buildTreeChangeListener(Object target, String methodName) {
return buildTreeChangeListener(target, findChangeListenerMethod(target, methodName, TREE_CHANGE_EVENT_CLASS_ARRAY));
}
// ********** constructor **********
/**
* Construct a listener that will invoke the specified method
* on the specified target.
*/
protected ReflectiveChangeListener(Object target) {
super();
this.target = target;
}
}