FileDocCategorySizeDatePackage
EventSetDescriptor.javaAPI DocJava SE 5 API20288Fri Aug 26 14:56:56 BST 2005java.beans

EventSetDescriptor.java

/*
 * @(#)EventSetDescriptor.java	1.64 04/05/05
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.beans;

import java.lang.ref.Reference;

import java.lang.reflect.Method;

/**
 * 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 Reference listenerMethodsRef;
    private Reference listenerTypeRef;
    private Reference addMethodRef;
    private Reference removeMethodRef;
    private Reference getMethodRef;

    private String[] listenerMethodNames;
    private String addMethodName;
    private String removeMethodName;
    private String getMethodName;

    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 },
	     "add" + getListenerClassName(listenerType),
	     "remove" + getListenerClassName(listenerType),
	     "get" + getListenerClassName(listenerType) + "s");

	String eventName = capitalize(eventSetName) + "Event";
	Method[] listenerMethods = getListenerMethods();
	if (listenerMethods.length > 0) {
	    Class[] args = listenerMethods[0].getParameterTypes();
	    // Check for EventSet compliance. Special case for vetoableChange. See 4529996
	    if (!"vetoableChange".equals(eventSetName) && !args[0].getName().endsWith(eventName)) {
		throw new IntrospectionException("Method \"" + listenerMethodNames[0] +
						 "\" 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) {
	    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;
	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 (Class)getObject(listenerTypeRef);
    }

    private void setListenerType(Class cls) {
	listenerTypeRef = createReference(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();
		}
	    } else if (listenerMethodNames != null) {
		methods = new Method[listenerMethodNames.length];
		
		for (int i = 0; i < methods.length; i++) {
		    methods[i] = Introspector.findMethod(getListenerType(),
							 listenerMethodNames[i], 1);
		}
	    }
	    setListenerMethods(methods);
	}
	return methods;
    }

    private void setListenerMethods(Method[] methods) {
	if (methods == null) {
	    return;
	}
	if (listenerMethodNames == null) {
	    listenerMethodNames = new String[methods.length];
	    for (int i = 0; i < methods.length; i++) {
		listenerMethodNames[i] = methods[i].getName();
	    }
	}
	listenerMethodsRef = createReference(methods, true);
    }

    private Method[] getListenerMethods0() {
	return (Method[])getObject(listenerMethodsRef);
    }

    /** 
     * 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() {
	Method[] listenerMethods = getListenerMethods();
	if (listenerMethodDescriptors == null && listenerMethods != null) { 
	    // Create MethodDescriptor array from Method array.
	    listenerMethodDescriptors = 
		                new MethodDescriptor[listenerMethods.length];
	    for (int i = 0; i < listenerMethods.length; i++) {
		listenerMethodDescriptors[i] = 
			        new MethodDescriptor(listenerMethods[i]);
	    }
	}
	return listenerMethodDescriptors;
    }

    /** 
     * Gets the method used to add event listeners.
     *
     * @return The method used to register a listener at the event source.
     */
    public synchronized Method getAddListenerMethod() {
	Method method = getAddListenerMethod0();
	if (method == null) {
	    Class cls = getClass0();
	    if (cls == null) {
		return null;
	    }
	    method = Introspector.findMethod(cls, addMethodName, 1);
	    setAddListenerMethod(method);
	}
	return method;
    }

    private Method getAddListenerMethod0() {
	return (Method)getObject(addMethodRef);
    }

    private synchronized void setAddListenerMethod(Method method) {
	if (method == null) {
	    return;
	}
	if (getClass0() == null) {
	    setClass0(method.getDeclaringClass());
	}
	addMethodName = method.getName();
	addMethodRef = createReference(method, true);
    }

    /** 
     * Gets the method used to remove event listeners.
     *
     * @return The method used to remove a listener at the event source.
     */
    public synchronized Method getRemoveListenerMethod() {
	Method method = getRemoveListenerMethod0();
	if (method == null) {
	    Class cls = getClass0();
	    if (cls == null) {
		return null;
	    }
	    method = Introspector.findMethod(cls, removeMethodName, 1);
	    setRemoveListenerMethod(method);
	}
	return method;
    }

    private Method getRemoveListenerMethod0() {
	return (Method)getObject(removeMethodRef);
    }

    private synchronized void setRemoveListenerMethod(Method method) {
	if (method == null) {
	    return;
	}
	if (getClass0() == null) {
	    setClass0(method.getDeclaringClass());
	}
	removeMethodName = method.getName();
	removeMethodRef = createReference(method, true);
    }

    /**
     * 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() {
	Method method = getGetListenerMethod0();
	if (method == null) {
	    Class cls = getClass0();
	    if (cls == null) {
		return null;
	    }
	    method = Introspector.findMethod(cls, getMethodName, 0);
	    setGetListenerMethod(method);
	}
	return method;
    }

    private Method getGetListenerMethod0() {
	return (Method)getObject(getMethodRef);
    }

    private synchronized void setGetListenerMethod(Method method) {
	if (method == null) {
	    return;
	}
	if (getClass0() == null) {
	    setClass0(method.getDeclaringClass());
	}
	getMethodName = method.getName();
	getMethodRef = createReference(method, true);
    }

    /**
     * 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;
	}
	listenerMethodNames = x.listenerMethodNames;
	if (y.listenerMethodNames != null) {
	    listenerMethodNames = y.listenerMethodNames;
	}
	listenerTypeRef = x.listenerTypeRef;
	if (y.listenerTypeRef != null) {
	    listenerTypeRef = y.listenerTypeRef;
	}

	addMethodRef = x.addMethodRef;
	if (y.addMethodRef != null) {
	    addMethodRef = y.addMethodRef;
	}
	addMethodName = x.addMethodName;
	if (y.addMethodName != null) {
	    addMethodName = y.addMethodName;
	}

	removeMethodRef = x.removeMethodRef;
	if (y.removeMethodRef != null) {
	    removeMethodRef = y.removeMethodRef;
	}
	removeMethodName = x.removeMethodName;
	if (y.removeMethodName != null) {
	    removeMethodName = y.removeMethodName;
	}

	getMethodRef = x.getMethodRef;
	if (y.getMethodRef != null) {
	    getMethodRef = y.getMethodRef;
	}
	getMethodName = x.getMethodName;
	if (y.getMethodName != null) {
	    getMethodName = y.getMethodName;
	}

	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]);
	    }
	}
	if (old.listenerMethodNames != null) {
	    int len = old.listenerMethodNames.length;
	    listenerMethodNames = new String[len];
	    for (int i = 0; i < len; i++) {
		listenerMethodNames[i] = old.listenerMethodNames[i];
	    }
	}
	listenerTypeRef = old.listenerTypeRef;

	addMethodRef = old.addMethodRef;
    addMethodName = old.addMethodName;

	removeMethodRef = old.removeMethodRef;
    removeMethodName = old.removeMethodName;

	getMethodRef = old.getMethodRef;
    getMethodName = old.getMethodName;

	unicast = old.unicast;
	inDefaultEventSet = old.inDefaultEventSet;
    }
}