/* | 
|
 * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. | 
|
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|
 * | 
|
 * This code is free software; you can redistribute it and/or modify it | 
|
 * under the terms of the GNU General Public License version 2 only, as | 
|
 * published by the Free Software Foundation.  Oracle designates this | 
|
 * particular file as subject to the "Classpath" exception as provided | 
|
 * by Oracle in the LICENSE file that accompanied this code. | 
|
 * | 
|
 * This code is distributed in the hope that it will be useful, but WITHOUT | 
|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|
 * version 2 for more details (a copy is included in the LICENSE file that | 
|
 * accompanied this code). | 
|
 * | 
|
 * You should have received a copy of the GNU General Public License version | 
|
 * 2 along with this work; if not, write to the Free Software Foundation, | 
|
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|
 * | 
|
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|
 * or visit www.oracle.com if you need additional information or have any | 
|
 * questions. | 
|
*/  | 
|
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;  | 
|
}  | 
|
}  |