blob: 0cab7d7cbd0651b94c1fafe106954c7a98f5f550 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.utility.model.listener;
import java.lang.reflect.Method;
import org.eclipse.jpt.utility.internal.ClassTools;
import org.eclipse.jpt.utility.model.event.ChangeEvent;
import org.eclipse.jpt.utility.model.event.CollectionChangeEvent;
import org.eclipse.jpt.utility.model.event.ListChangeEvent;
import org.eclipse.jpt.utility.model.event.PropertyChangeEvent;
import org.eclipse.jpt.utility.model.event.StateChangeEvent;
import org.eclipse.jpt.utility.model.event.TreeChangeEvent;
/**
* 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.
*
* Provisional API: This class is part of an interim API that is still
* under development and expected to change significantly before reaching
* stability. It is available at this early stage to solicit feedback from
* pioneering adopters on the understanding that any code that uses this API
* will almost certainly be broken (repeatedly) as the API evolves.
*/
public abstract class ReflectiveChangeListener {
/** the target object on which we will invoke the method */
protected final Object target;
protected static final Class<StateChangeEvent> STATE_CHANGE_EVENT_CLASS = StateChangeEvent.class;
@SuppressWarnings("unchecked")
protected static final Class<StateChangeEvent>[] STATE_CHANGE_EVENT_CLASS_ARRAY = new Class[] {STATE_CHANGE_EVENT_CLASS};
protected static final StateChangeEvent[] EMPTY_STATE_CHANGE_EVENT_ARRAY = new StateChangeEvent[0];
protected static final Class<PropertyChangeEvent> PROPERTY_CHANGE_EVENT_CLASS = PropertyChangeEvent.class;
@SuppressWarnings("unchecked")
protected static final Class<PropertyChangeEvent>[] PROPERTY_CHANGE_EVENT_CLASS_ARRAY = new Class[] {PROPERTY_CHANGE_EVENT_CLASS};
protected static final PropertyChangeEvent[] EMPTY_PROPERTY_CHANGE_EVENT_ARRAY = new PropertyChangeEvent[0];
protected static final Class<CollectionChangeEvent> COLLECTION_CHANGE_EVENT_CLASS = CollectionChangeEvent.class;
@SuppressWarnings("unchecked")
protected static final Class<CollectionChangeEvent>[] COLLECTION_CHANGE_EVENT_CLASS_ARRAY = new Class[] {COLLECTION_CHANGE_EVENT_CLASS};
protected static final CollectionChangeEvent[] EMPTY_COLLECTION_CHANGE_EVENT_ARRAY = new CollectionChangeEvent[0];
protected static final Class<ListChangeEvent> LIST_CHANGE_EVENT_CLASS = ListChangeEvent.class;
@SuppressWarnings("unchecked")
protected static final Class<ListChangeEvent>[] LIST_CHANGE_EVENT_CLASS_ARRAY = new Class[] {LIST_CHANGE_EVENT_CLASS};
protected static final ListChangeEvent[] EMPTY_LIST_CHANGE_EVENT_ARRAY = new ListChangeEvent[0];
protected static final Class<TreeChangeEvent> TREE_CHANGE_EVENT_CLASS = TreeChangeEvent.class;
@SuppressWarnings("unchecked")
protected static final Class<TreeChangeEvent>[] TREE_CHANGE_EVENT_CLASS_ARRAY = new Class[] {TREE_CHANGE_EVENT_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<? extends ChangeEvent>[] 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<? extends ChangeEvent> 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, STATE_CHANGE_EVENT_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, PROPERTY_CHANGE_EVENT_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 clearMethod, Method changeMethod) {
checkChangeListenerMethod(addMethod, COLLECTION_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(removeMethod, COLLECTION_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(clearMethod, COLLECTION_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(changeMethod, COLLECTION_CHANGE_EVENT_CLASS);
return new MultiMethodReflectiveChangeListener(target, addMethod, removeMethod, clearMethod, 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, 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 clearMethodName, String changeMethodName) {
return buildCollectionChangeListener(
target,
findChangeListenerMethod(target, addMethodName, COLLECTION_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, removeMethodName, COLLECTION_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, clearMethodName, 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 moveMethod, Method clearMethod, Method changeMethod) {
checkChangeListenerMethod(addMethod, LIST_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(removeMethod, LIST_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(replaceMethod, LIST_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(moveMethod, LIST_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(clearMethod, LIST_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(changeMethod, LIST_CHANGE_EVENT_CLASS);
return new MultiMethodReflectiveChangeListener(target, addMethod, removeMethod, replaceMethod, moveMethod, clearMethod, 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, 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 moveMethodName, String clearMethodName, 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, moveMethodName, LIST_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, clearMethodName, 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 clearMethod, Method changeMethod) {
checkChangeListenerMethod(addMethod, TREE_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(removeMethod, TREE_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(clearMethod, TREE_CHANGE_EVENT_CLASS);
checkChangeListenerMethod(changeMethod, TREE_CHANGE_EVENT_CLASS);
return new MultiMethodReflectiveChangeListener(target, addMethod, removeMethod, clearMethod, 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, 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 clearMethodName, String changeMethodName) {
return buildTreeChangeListener(
target,
findChangeListenerMethod(target, addMethodName, TREE_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, removeMethodName, TREE_CHANGE_EVENT_CLASS_ARRAY),
findChangeListenerMethod(target, clearMethodName, 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;
}
}