/*
* Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.beans;
import java.lang.ref.
Reference;
import java.lang.reflect.
Method;
import java.lang.reflect.
Modifier;
/**
* An EventSetDescriptor describes a group of events that a given Java
* bean fires.
* <P>
* The given group of events are all delivered as method calls on a single
* event listener interface, and an event listener object can be registered
* via a call on a registration method supplied by the event source.
*/
public class
EventSetDescriptor extends
FeatureDescriptor {
private
MethodDescriptor[]
listenerMethodDescriptors;
private
MethodDescriptor addMethodDescriptor;
private
MethodDescriptor removeMethodDescriptor;
private
MethodDescriptor getMethodDescriptor;
private
Reference<
Method[]>
listenerMethodsRef;
private
Reference<? extends
Class<?>>
listenerTypeRef;
private boolean
unicast;
private boolean
inDefaultEventSet = true;
/**
* Creates an <TT>EventSetDescriptor</TT> assuming that you are
* following the most simple standard design pattern where a named
* event "fred" is (1) delivered as a call on the single method of
* interface FredListener, (2) has a single argument of type FredEvent,
* and (3) where the FredListener may be registered with a call on an
* addFredListener method of the source component and removed with a
* call on a removeFredListener method.
*
* @param sourceClass The class firing the event.
* @param eventSetName The programmatic name of the event. E.g. "fred".
* Note that this should normally start with a lower-case character.
* @param listenerType The target interface that events
* will get delivered to.
* @param listenerMethodName The method that will get called when the event gets
* delivered to its target listener interface.
* @exception IntrospectionException if an exception occurs during
* introspection.
*/
public
EventSetDescriptor(
Class<?>
sourceClass,
String eventSetName,
Class<?>
listenerType,
String listenerMethodName)
throws
IntrospectionException {
this(
sourceClass,
eventSetName,
listenerType,
new
String[] {
listenerMethodName },
Introspector.
ADD_PREFIX +
getListenerClassName(
listenerType),
Introspector.
REMOVE_PREFIX +
getListenerClassName(
listenerType),
Introspector.
GET_PREFIX +
getListenerClassName(
listenerType) + "s");
String eventName =
NameGenerator.
capitalize(
eventSetName) + "Event";
Method[]
listenerMethods =
getListenerMethods();
if (
listenerMethods.length > 0) {
Class[]
args =
getParameterTypes(
getClass0(),
listenerMethods[0]);
// Check for EventSet compliance. Special case for vetoableChange. See 4529996
if (!"vetoableChange".
equals(
eventSetName) && !
args[0].
getName().
endsWith(
eventName)) {
throw new
IntrospectionException("Method \"" +
listenerMethodName +
"\" should have argument \"" +
eventName + "\"");
}
}
}
private static
String getListenerClassName(
Class<?>
cls) {
String className =
cls.
getName();
return
className.
substring(
className.
lastIndexOf('.') + 1);
}
/**
* Creates an <TT>EventSetDescriptor</TT> from scratch using
* string names.
*
* @param sourceClass The class firing the event.
* @param eventSetName The programmatic name of the event set.
* Note that this should normally start with a lower-case character.
* @param listenerType The Class of the target interface that events
* will get delivered to.
* @param listenerMethodNames The names of the methods that will get called
* when the event gets delivered to its target listener interface.
* @param addListenerMethodName The name of the method on the event source
* that can be used to register an event listener object.
* @param removeListenerMethodName The name of the method on the event source
* that can be used to de-register an event listener object.
* @exception IntrospectionException if an exception occurs during
* introspection.
*/
public
EventSetDescriptor(
Class<?>
sourceClass,
String eventSetName,
Class<?>
listenerType,
String listenerMethodNames[],
String addListenerMethodName,
String removeListenerMethodName)
throws
IntrospectionException {
this(
sourceClass,
eventSetName,
listenerType,
listenerMethodNames,
addListenerMethodName,
removeListenerMethodName, null);
}
/**
* This constructor creates an EventSetDescriptor from scratch using
* string names.
*
* @param sourceClass The class firing the event.
* @param eventSetName The programmatic name of the event set.
* Note that this should normally start with a lower-case character.
* @param listenerType The Class of the target interface that events
* will get delivered to.
* @param listenerMethodNames The names of the methods that will get called
* when the event gets delivered to its target listener interface.
* @param addListenerMethodName The name of the method on the event source
* that can be used to register an event listener object.
* @param removeListenerMethodName The name of the method on the event source
* that can be used to de-register an event listener object.
* @param getListenerMethodName The method on the event source that
* can be used to access the array of event listener objects.
* @exception IntrospectionException if an exception occurs during
* introspection.
* @since 1.4
*/
public
EventSetDescriptor(
Class<?>
sourceClass,
String eventSetName,
Class<?>
listenerType,
String listenerMethodNames[],
String addListenerMethodName,
String removeListenerMethodName,
String getListenerMethodName)
throws
IntrospectionException {
if (
sourceClass == null ||
eventSetName == null ||
listenerType == null) {
throw new
NullPointerException();
}
setName(
eventSetName);
setClass0(
sourceClass);
setListenerType(
listenerType);
Method[]
listenerMethods = new
Method[
listenerMethodNames.length];
for (int
i = 0;
i <
listenerMethodNames.length;
i++) {
// Check for null names
if (
listenerMethodNames[
i] == null) {
throw new
NullPointerException();
}
listenerMethods[
i] =
getMethod(
listenerType,
listenerMethodNames[
i], 1);
}
setListenerMethods(
listenerMethods);
setAddListenerMethod(
getMethod(
sourceClass,
addListenerMethodName, 1));
setRemoveListenerMethod(
getMethod(
sourceClass,
removeListenerMethodName, 1));
// Be more forgiving of not finding the getListener method.
Method method =
Introspector.
findMethod(
sourceClass,
getListenerMethodName, 0);
if (
method != null) {
setGetListenerMethod(
method);
}
}
private static
Method getMethod(
Class<?>
cls,
String name, int
args)
throws
IntrospectionException {
if (
name == null) {
return null;
}
Method method =
Introspector.
findMethod(
cls,
name,
args);
if ((
method == null) ||
Modifier.
isStatic(
method.
getModifiers())) {
throw new
IntrospectionException("Method not found: " +
name +
" on class " +
cls.
getName());
}
return
method;
}
/**
* Creates an <TT>EventSetDescriptor</TT> from scratch using
* <TT>java.lang.reflect.Method</TT> and <TT>java.lang.Class</TT> objects.
*
* @param eventSetName The programmatic name of the event set.
* @param listenerType The Class for the listener interface.
* @param listenerMethods An array of Method objects describing each
* of the event handling methods in the target listener.
* @param addListenerMethod The method on the event source
* that can be used to register an event listener object.
* @param removeListenerMethod The method on the event source
* that can be used to de-register an event listener object.
* @exception IntrospectionException if an exception occurs during
* introspection.
*/
public
EventSetDescriptor(
String eventSetName,
Class<?>
listenerType,
Method listenerMethods[],
Method addListenerMethod,
Method removeListenerMethod)
throws
IntrospectionException {
this(
eventSetName,
listenerType,
listenerMethods,
addListenerMethod,
removeListenerMethod, null);
}
/**
* This constructor creates an EventSetDescriptor from scratch using
* java.lang.reflect.Method and java.lang.Class objects.
*
* @param eventSetName The programmatic name of the event set.
* @param listenerType The Class for the listener interface.
* @param listenerMethods An array of Method objects describing each
* of the event handling methods in the target listener.
* @param addListenerMethod The method on the event source
* that can be used to register an event listener object.
* @param removeListenerMethod The method on the event source
* that can be used to de-register an event listener object.
* @param getListenerMethod The method on the event source
* that can be used to access the array of event listener objects.
* @exception IntrospectionException if an exception occurs during
* introspection.
* @since 1.4
*/
public
EventSetDescriptor(
String eventSetName,
Class<?>
listenerType,
Method listenerMethods[],
Method addListenerMethod,
Method removeListenerMethod,
Method getListenerMethod)
throws
IntrospectionException {
setName(
eventSetName);
setListenerMethods(
listenerMethods);
setAddListenerMethod(
addListenerMethod);
setRemoveListenerMethod(
removeListenerMethod);
setGetListenerMethod(
getListenerMethod);
setListenerType(
listenerType);
}
/**
* Creates an <TT>EventSetDescriptor</TT> from scratch using
* <TT>java.lang.reflect.MethodDescriptor</TT> and <TT>java.lang.Class</TT>
* objects.
*
* @param eventSetName The programmatic name of the event set.
* @param listenerType The Class for the listener interface.
* @param listenerMethodDescriptors An array of MethodDescriptor objects
* describing each of the event handling methods in the
* target listener.
* @param addListenerMethod The method on the event source
* that can be used to register an event listener object.
* @param removeListenerMethod The method on the event source
* that can be used to de-register an event listener object.
* @exception IntrospectionException if an exception occurs during
* introspection.
*/
public
EventSetDescriptor(
String eventSetName,
Class<?>
listenerType,
MethodDescriptor listenerMethodDescriptors[],
Method addListenerMethod,
Method removeListenerMethod)
throws
IntrospectionException {
setName(
eventSetName);
this.
listenerMethodDescriptors = (
listenerMethodDescriptors != null)
?
listenerMethodDescriptors.
clone()
: null;
setAddListenerMethod(
addListenerMethod);
setRemoveListenerMethod(
removeListenerMethod);
setListenerType(
listenerType);
}
/**
* Gets the <TT>Class</TT> object for the target interface.
*
* @return The Class object for the target interface that will
* get invoked when the event is fired.
*/
public
Class<?>
getListenerType() {
return (this.
listenerTypeRef != null)
? this.
listenerTypeRef.
get()
: null;
}
private void
setListenerType(
Class<?>
cls) {
this.
listenerTypeRef =
getWeakReference(
cls);
}
/**
* Gets the methods of the target listener interface.
*
* @return An array of <TT>Method</TT> objects for the target methods
* within the target listener interface that will get called when
* events are fired.
*/
public synchronized
Method[]
getListenerMethods() {
Method[]
methods =
getListenerMethods0();
if (
methods == null) {
if (
listenerMethodDescriptors != null) {
methods = new
Method[
listenerMethodDescriptors.length];
for (int
i = 0;
i <
methods.length;
i++) {
methods[
i] =
listenerMethodDescriptors[
i].
getMethod();
}
}
setListenerMethods(
methods);
}
return
methods;
}
private void
setListenerMethods(
Method[]
methods) {
if (
methods == null) {
return;
}
if (
listenerMethodDescriptors == null) {
listenerMethodDescriptors = new
MethodDescriptor[
methods.length];
for (int
i = 0;
i <
methods.length;
i++) {
listenerMethodDescriptors[
i] = new
MethodDescriptor(
methods[
i]);
}
}
this.
listenerMethodsRef =
getSoftReference(
methods);
}
private
Method[]
getListenerMethods0() {
return (this.
listenerMethodsRef != null)
? this.
listenerMethodsRef.
get()
: null;
}
/**
* Gets the <code>MethodDescriptor</code>s of the target listener interface.
*
* @return An array of <code>MethodDescriptor</code> objects for the target methods
* within the target listener interface that will get called when
* events are fired.
*/
public synchronized
MethodDescriptor[]
getListenerMethodDescriptors() {
return (this.
listenerMethodDescriptors != null)
? this.
listenerMethodDescriptors.
clone()
: null;
}
/**
* Gets the method used to add event listeners.
*
* @return The method used to register a listener at the event source.
*/
public synchronized
Method getAddListenerMethod() {
return
getMethod(this.
addMethodDescriptor);
}
private synchronized void
setAddListenerMethod(
Method method) {
if (
method == null) {
return;
}
if (
getClass0() == null) {
setClass0(
method.
getDeclaringClass());
}
addMethodDescriptor = new
MethodDescriptor(
method);
setTransient(
method.
getAnnotation(
Transient.class));
}
/**
* Gets the method used to remove event listeners.
*
* @return The method used to remove a listener at the event source.
*/
public synchronized
Method getRemoveListenerMethod() {
return
getMethod(this.
removeMethodDescriptor);
}
private synchronized void
setRemoveListenerMethod(
Method method) {
if (
method == null) {
return;
}
if (
getClass0() == null) {
setClass0(
method.
getDeclaringClass());
}
removeMethodDescriptor = new
MethodDescriptor(
method);
setTransient(
method.
getAnnotation(
Transient.class));
}
/**
* Gets the method used to access the registered event listeners.
*
* @return The method used to access the array of listeners at the event
* source or null if it doesn't exist.
* @since 1.4
*/
public synchronized
Method getGetListenerMethod() {
return
getMethod(this.
getMethodDescriptor);
}
private synchronized void
setGetListenerMethod(
Method method) {
if (
method == null) {
return;
}
if (
getClass0() == null) {
setClass0(
method.
getDeclaringClass());
}
getMethodDescriptor = new
MethodDescriptor(
method);
setTransient(
method.
getAnnotation(
Transient.class));
}
/**
* Mark an event set as unicast (or not).
*
* @param unicast True if the event set is unicast.
*/
public void
setUnicast(boolean
unicast) {
this.
unicast =
unicast;
}
/**
* Normally event sources are multicast. However there are some
* exceptions that are strictly unicast.
*
* @return <TT>true</TT> if the event set is unicast.
* Defaults to <TT>false</TT>.
*/
public boolean
isUnicast() {
return
unicast;
}
/**
* Marks an event set as being in the "default" set (or not).
* By default this is <TT>true</TT>.
*
* @param inDefaultEventSet <code>true</code> if the event set is in
* the "default" set,
* <code>false</code> if not
*/
public void
setInDefaultEventSet(boolean
inDefaultEventSet) {
this.
inDefaultEventSet =
inDefaultEventSet;
}
/**
* Reports if an event set is in the "default" set.
*
* @return <TT>true</TT> if the event set is in
* the "default" set. Defaults to <TT>true</TT>.
*/
public boolean
isInDefaultEventSet() {
return
inDefaultEventSet;
}
/*
* Package-private constructor
* Merge two event set descriptors. Where they conflict, give the
* second argument (y) priority over the first argument (x).
*
* @param x The first (lower priority) EventSetDescriptor
* @param y The second (higher priority) EventSetDescriptor
*/
EventSetDescriptor(
EventSetDescriptor x,
EventSetDescriptor y) {
super(
x,
y);
listenerMethodDescriptors =
x.
listenerMethodDescriptors;
if (
y.
listenerMethodDescriptors != null) {
listenerMethodDescriptors =
y.
listenerMethodDescriptors;
}
listenerTypeRef =
x.
listenerTypeRef;
if (
y.
listenerTypeRef != null) {
listenerTypeRef =
y.
listenerTypeRef;
}
addMethodDescriptor =
x.
addMethodDescriptor;
if (
y.
addMethodDescriptor != null) {
addMethodDescriptor =
y.
addMethodDescriptor;
}
removeMethodDescriptor =
x.
removeMethodDescriptor;
if (
y.
removeMethodDescriptor != null) {
removeMethodDescriptor =
y.
removeMethodDescriptor;
}
getMethodDescriptor =
x.
getMethodDescriptor;
if (
y.
getMethodDescriptor != null) {
getMethodDescriptor =
y.
getMethodDescriptor;
}
unicast =
y.
unicast;
if (!
x.
inDefaultEventSet || !
y.
inDefaultEventSet) {
inDefaultEventSet = false;
}
}
/*
* Package-private dup constructor
* This must isolate the new object from any changes to the old object.
*/
EventSetDescriptor(
EventSetDescriptor old) {
super(
old);
if (
old.
listenerMethodDescriptors != null) {
int
len =
old.
listenerMethodDescriptors.length;
listenerMethodDescriptors = new
MethodDescriptor[
len];
for (int
i = 0;
i <
len;
i++) {
listenerMethodDescriptors[
i] = new
MethodDescriptor(
old.
listenerMethodDescriptors[
i]);
}
}
listenerTypeRef =
old.
listenerTypeRef;
addMethodDescriptor =
old.
addMethodDescriptor;
removeMethodDescriptor =
old.
removeMethodDescriptor;
getMethodDescriptor =
old.
getMethodDescriptor;
unicast =
old.
unicast;
inDefaultEventSet =
old.
inDefaultEventSet;
}
void
appendTo(
StringBuilder sb) {
appendTo(
sb, "unicast", this.
unicast);
appendTo(
sb, "inDefaultEventSet", this.
inDefaultEventSet);
appendTo(
sb, "listenerType", this.
listenerTypeRef);
appendTo(
sb, "getListenerMethod",
getMethod(this.
getMethodDescriptor));
appendTo(
sb, "addListenerMethod",
getMethod(this.
addMethodDescriptor));
appendTo(
sb, "removeListenerMethod",
getMethod(this.
removeMethodDescriptor));
}
private static
Method getMethod(
MethodDescriptor descriptor) {
return (
descriptor != null)
?
descriptor.
getMethod()
: null;
}
}