/* * 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. *

* 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 listenerMethodsRef; private Reference> listenerTypeRef; private boolean unicast; private boolean inDefaultEventSet = true; /** * Creates an EventSetDescriptor 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 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. * @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 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. * @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 EventSetDescriptor from scratch using * java.lang.reflect.MethodDescriptor and java.lang.Class * 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 Class 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 Method 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 MethodDescriptors of the target listener interface. * * @return An array of MethodDescriptor 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 true if the event set is unicast. * Defaults to false. */ public boolean isUnicast() { return unicast; } /** * Marks an event set as being in the "default" set (or not). * By default this is true. * * @param inDefaultEventSet true if the event set is in * the "default" set, * false if not */ public void setInDefaultEventSet(boolean inDefaultEventSet) { this.inDefaultEventSet = inDefaultEventSet; } /** * Reports if an event set is in the "default" set. * * @return true if the event set is in * the "default" set. Defaults to true. */ 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; } }