Path: blob/master/src/java.desktop/share/classes/java/beans/EventSetDescriptor.java
41152 views
/*1* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package java.beans;2526import java.lang.ref.Reference;27import java.lang.reflect.Method;28import java.lang.reflect.Modifier;2930import com.sun.beans.introspect.EventSetInfo;3132/**33* An EventSetDescriptor describes a group of events that a given Java34* bean fires.35* <P>36* The given group of events are all delivered as method calls on a single37* event listener interface, and an event listener object can be registered38* via a call on a registration method supplied by the event source.39*40* @since 1.141*/42public class EventSetDescriptor extends FeatureDescriptor {4344private MethodDescriptor[] listenerMethodDescriptors;45private MethodDescriptor addMethodDescriptor;46private MethodDescriptor removeMethodDescriptor;47private MethodDescriptor getMethodDescriptor;4849private Reference<Method[]> listenerMethodsRef;50private Reference<? extends Class<?>> listenerTypeRef;5152private boolean unicast;53private boolean inDefaultEventSet = true;5455/**56* Creates an {@code EventSetDescriptor} assuming that you are57* following the most simple standard design pattern where a named58* event "fred" is (1) delivered as a call on the single method of59* interface FredListener, (2) has a single argument of type FredEvent,60* and (3) where the FredListener may be registered with a call on an61* addFredListener method of the source component and removed with a62* call on a removeFredListener method.63*64* @param sourceClass The class firing the event.65* @param eventSetName The programmatic name of the event. E.g. "fred".66* Note that this should normally start with a lower-case character.67* @param listenerType The target interface that events68* will get delivered to.69* @param listenerMethodName The method that will get called when the event gets70* delivered to its target listener interface.71* @exception IntrospectionException if an exception occurs during72* introspection.73*/74public EventSetDescriptor(Class<?> sourceClass, String eventSetName,75Class<?> listenerType, String listenerMethodName)76throws IntrospectionException {77this(sourceClass, eventSetName, listenerType,78new String[] { listenerMethodName },79Introspector.ADD_PREFIX + getListenerClassName(listenerType),80Introspector.REMOVE_PREFIX + getListenerClassName(listenerType),81Introspector.GET_PREFIX + getListenerClassName(listenerType) + "s");8283String eventName = NameGenerator.capitalize(eventSetName) + "Event";84Method[] listenerMethods = getListenerMethods();85if (listenerMethods.length > 0) {86Class<?>[] args = getParameterTypes(getClass0(), listenerMethods[0]);87// Check for EventSet compliance. Special case for vetoableChange. See 452999688if (!"vetoableChange".equals(eventSetName) && !args[0].getName().endsWith(eventName)) {89throw new IntrospectionException("Method \"" + listenerMethodName +90"\" should have argument \"" +91eventName + "\"");92}93}94}9596private static String getListenerClassName(Class<?> cls) {97String className = cls.getName();98return className.substring(className.lastIndexOf('.') + 1);99}100101/**102* Creates an {@code EventSetDescriptor} from scratch using103* string names.104*105* @param sourceClass The class firing the event.106* @param eventSetName The programmatic name of the event set.107* Note that this should normally start with a lower-case character.108* @param listenerType The Class of the target interface that events109* will get delivered to.110* @param listenerMethodNames The names of the methods that will get called111* when the event gets delivered to its target listener interface.112* @param addListenerMethodName The name of the method on the event source113* that can be used to register an event listener object.114* @param removeListenerMethodName The name of the method on the event source115* that can be used to de-register an event listener object.116* @exception IntrospectionException if an exception occurs during117* introspection.118*/119public EventSetDescriptor(Class<?> sourceClass,120String eventSetName,121Class<?> listenerType,122String[] listenerMethodNames,123String addListenerMethodName,124String removeListenerMethodName)125throws IntrospectionException {126this(sourceClass, eventSetName, listenerType,127listenerMethodNames, addListenerMethodName,128removeListenerMethodName, null);129}130131/**132* This constructor creates an EventSetDescriptor from scratch using133* string names.134*135* @param sourceClass The class firing the event.136* @param eventSetName The programmatic name of the event set.137* Note that this should normally start with a lower-case character.138* @param listenerType The Class of the target interface that events139* will get delivered to.140* @param listenerMethodNames The names of the methods that will get called141* when the event gets delivered to its target listener interface.142* @param addListenerMethodName The name of the method on the event source143* that can be used to register an event listener object.144* @param removeListenerMethodName The name of the method on the event source145* that can be used to de-register an event listener object.146* @param getListenerMethodName The method on the event source that147* can be used to access the array of event listener objects.148* @exception IntrospectionException if an exception occurs during149* introspection.150* @since 1.4151*/152public EventSetDescriptor(Class<?> sourceClass,153String eventSetName,154Class<?> listenerType,155String[] listenerMethodNames,156String addListenerMethodName,157String removeListenerMethodName,158String getListenerMethodName)159throws IntrospectionException {160if (sourceClass == null || eventSetName == null || listenerType == null) {161throw new NullPointerException();162}163setName(eventSetName);164setClass0(sourceClass);165setListenerType(listenerType);166167Method[] listenerMethods = new Method[listenerMethodNames.length];168for (int i = 0; i < listenerMethodNames.length; i++) {169// Check for null names170if (listenerMethodNames[i] == null) {171throw new NullPointerException();172}173listenerMethods[i] = getMethod(listenerType, listenerMethodNames[i], 1);174}175setListenerMethods(listenerMethods);176177setAddListenerMethod(getMethod(sourceClass, addListenerMethodName, 1));178setRemoveListenerMethod(getMethod(sourceClass, removeListenerMethodName, 1));179180// Be more forgiving of not finding the getListener method.181Method method = Introspector.findMethod(sourceClass, getListenerMethodName, 0);182if (method != null) {183setGetListenerMethod(method);184}185}186187private static Method getMethod(Class<?> cls, String name, int args)188throws IntrospectionException {189if (name == null) {190return null;191}192Method method = Introspector.findMethod(cls, name, args);193if ((method == null) || Modifier.isStatic(method.getModifiers())) {194throw new IntrospectionException("Method not found: " + name +195" on class " + cls.getName());196}197return method;198}199200/**201* Creates an {@code EventSetDescriptor} from scratch using202* {@code java.lang.reflect.Method} and {@code java.lang.Class} objects.203*204* @param eventSetName The programmatic name of the event set.205* @param listenerType The Class for the listener interface.206* @param listenerMethods An array of Method objects describing each207* of the event handling methods in the target listener.208* @param addListenerMethod The method on the event source209* that can be used to register an event listener object.210* @param removeListenerMethod The method on the event source211* that can be used to de-register an event listener object.212* @exception IntrospectionException if an exception occurs during213* introspection.214*/215public EventSetDescriptor(String eventSetName,216Class<?> listenerType,217Method[] listenerMethods,218Method addListenerMethod,219Method removeListenerMethod)220throws IntrospectionException {221this(eventSetName, listenerType, listenerMethods,222addListenerMethod, removeListenerMethod, null);223}224225/**226* This constructor creates an EventSetDescriptor from scratch using227* java.lang.reflect.Method and java.lang.Class objects.228*229* @param eventSetName The programmatic name of the event set.230* @param listenerType The Class for the listener interface.231* @param listenerMethods An array of Method objects describing each232* of the event handling methods in the target listener.233* @param addListenerMethod The method on the event source234* that can be used to register an event listener object.235* @param removeListenerMethod The method on the event source236* that can be used to de-register an event listener object.237* @param getListenerMethod The method on the event source238* that can be used to access the array of event listener objects.239* @exception IntrospectionException if an exception occurs during240* introspection.241* @since 1.4242*/243public EventSetDescriptor(String eventSetName,244Class<?> listenerType,245Method[] listenerMethods,246Method addListenerMethod,247Method removeListenerMethod,248Method getListenerMethod)249throws IntrospectionException {250setName(eventSetName);251setListenerMethods(listenerMethods);252setAddListenerMethod(addListenerMethod);253setRemoveListenerMethod( removeListenerMethod);254setGetListenerMethod(getListenerMethod);255setListenerType(listenerType);256}257258EventSetDescriptor(String base, EventSetInfo info, Method... methods) {259setName(Introspector.decapitalize(base));260setListenerMethods(methods);261setAddListenerMethod(info.getAddMethod());262setRemoveListenerMethod(info.getRemoveMethod());263setGetListenerMethod(info.getGetMethod());264setListenerType(info.getListenerType());265setUnicast(info.isUnicast());266}267268/**269* Creates an {@code EventSetDescriptor} from scratch using270* {@code java.lang.reflect.MethodDescriptor} and {@code java.lang.Class}271* objects.272*273* @param eventSetName The programmatic name of the event set.274* @param listenerType The Class for the listener interface.275* @param listenerMethodDescriptors An array of MethodDescriptor objects276* describing each of the event handling methods in the277* target listener.278* @param addListenerMethod The method on the event source279* that can be used to register an event listener object.280* @param removeListenerMethod The method on the event source281* that can be used to de-register an event listener object.282* @exception IntrospectionException if an exception occurs during283* introspection.284*/285public EventSetDescriptor(String eventSetName,286Class<?> listenerType,287MethodDescriptor[] listenerMethodDescriptors,288Method addListenerMethod,289Method removeListenerMethod)290throws IntrospectionException {291setName(eventSetName);292this.listenerMethodDescriptors = (listenerMethodDescriptors != null)293? listenerMethodDescriptors.clone()294: null;295setAddListenerMethod(addListenerMethod);296setRemoveListenerMethod(removeListenerMethod);297setListenerType(listenerType);298}299300/**301* Gets the {@code Class} object for the target interface.302*303* @return The Class object for the target interface that will304* get invoked when the event is fired.305*/306public Class<?> getListenerType() {307return (this.listenerTypeRef != null)308? this.listenerTypeRef.get()309: null;310}311312private void setListenerType(Class<?> cls) {313this.listenerTypeRef = getWeakReference(cls);314}315316/**317* Gets the methods of the target listener interface.318*319* @return An array of {@code Method} objects for the target methods320* within the target listener interface that will get called when321* events are fired.322*/323public synchronized Method[] getListenerMethods() {324Method[] methods = getListenerMethods0();325if (methods == null) {326if (listenerMethodDescriptors != null) {327methods = new Method[listenerMethodDescriptors.length];328for (int i = 0; i < methods.length; i++) {329methods[i] = listenerMethodDescriptors[i].getMethod();330}331}332setListenerMethods(methods);333}334return methods;335}336337private void setListenerMethods(Method[] methods) {338if (methods == null) {339return;340}341if (listenerMethodDescriptors == null) {342listenerMethodDescriptors = new MethodDescriptor[methods.length];343for (int i = 0; i < methods.length; i++) {344listenerMethodDescriptors[i] = new MethodDescriptor(methods[i]);345}346}347this.listenerMethodsRef = getSoftReference(methods);348}349350private Method[] getListenerMethods0() {351return (this.listenerMethodsRef != null)352? this.listenerMethodsRef.get()353: null;354}355356/**357* Gets the {@code MethodDescriptor}s of the target listener interface.358*359* @return An array of {@code MethodDescriptor} objects for the target methods360* within the target listener interface that will get called when361* events are fired.362*/363public synchronized MethodDescriptor[] getListenerMethodDescriptors() {364return (this.listenerMethodDescriptors != null)365? this.listenerMethodDescriptors.clone()366: null;367}368369/**370* Gets the method used to add event listeners.371*372* @return The method used to register a listener at the event source.373*/374public synchronized Method getAddListenerMethod() {375return getMethod(this.addMethodDescriptor);376}377378private synchronized void setAddListenerMethod(Method method) {379if (method == null) {380return;381}382if (getClass0() == null) {383setClass0(method.getDeclaringClass());384}385addMethodDescriptor = new MethodDescriptor(method);386setTransient(method.getAnnotation(Transient.class));387}388389/**390* Gets the method used to remove event listeners.391*392* @return The method used to remove a listener at the event source.393*/394public synchronized Method getRemoveListenerMethod() {395return getMethod(this.removeMethodDescriptor);396}397398private synchronized void setRemoveListenerMethod(Method method) {399if (method == null) {400return;401}402if (getClass0() == null) {403setClass0(method.getDeclaringClass());404}405removeMethodDescriptor = new MethodDescriptor(method);406setTransient(method.getAnnotation(Transient.class));407}408409/**410* Gets the method used to access the registered event listeners.411*412* @return The method used to access the array of listeners at the event413* source or null if it doesn't exist.414* @since 1.4415*/416public synchronized Method getGetListenerMethod() {417return getMethod(this.getMethodDescriptor);418}419420private synchronized void setGetListenerMethod(Method method) {421if (method == null) {422return;423}424if (getClass0() == null) {425setClass0(method.getDeclaringClass());426}427getMethodDescriptor = new MethodDescriptor(method);428setTransient(method.getAnnotation(Transient.class));429}430431/**432* Mark an event set as unicast (or not).433*434* @param unicast True if the event set is unicast.435*/436public void setUnicast(boolean unicast) {437this.unicast = unicast;438}439440/**441* Normally event sources are multicast. However there are some442* exceptions that are strictly unicast.443*444* @return {@code true} if the event set is unicast.445* Defaults to {@code false}.446*/447public boolean isUnicast() {448return unicast;449}450451/**452* Marks an event set as being in the "default" set (or not).453* By default this is {@code true}.454*455* @param inDefaultEventSet {@code true} if the event set is in456* the "default" set,457* {@code false} if not458*/459public void setInDefaultEventSet(boolean inDefaultEventSet) {460this.inDefaultEventSet = inDefaultEventSet;461}462463/**464* Reports if an event set is in the "default" set.465*466* @return {@code true} if the event set is in467* the "default" set. Defaults to {@code true}.468*/469public boolean isInDefaultEventSet() {470return inDefaultEventSet;471}472473/*474* Package-private constructor475* Merge two event set descriptors. Where they conflict, give the476* second argument (y) priority over the first argument (x).477*478* @param x The first (lower priority) EventSetDescriptor479* @param y The second (higher priority) EventSetDescriptor480*/481EventSetDescriptor(EventSetDescriptor x, EventSetDescriptor y) {482super(x,y);483listenerMethodDescriptors = x.listenerMethodDescriptors;484if (y.listenerMethodDescriptors != null) {485listenerMethodDescriptors = y.listenerMethodDescriptors;486}487488listenerTypeRef = x.listenerTypeRef;489if (y.listenerTypeRef != null) {490listenerTypeRef = y.listenerTypeRef;491}492493addMethodDescriptor = x.addMethodDescriptor;494if (y.addMethodDescriptor != null) {495addMethodDescriptor = y.addMethodDescriptor;496}497498removeMethodDescriptor = x.removeMethodDescriptor;499if (y.removeMethodDescriptor != null) {500removeMethodDescriptor = y.removeMethodDescriptor;501}502503getMethodDescriptor = x.getMethodDescriptor;504if (y.getMethodDescriptor != null) {505getMethodDescriptor = y.getMethodDescriptor;506}507508unicast = y.unicast;509if (!x.inDefaultEventSet || !y.inDefaultEventSet) {510inDefaultEventSet = false;511}512}513514/*515* Package-private dup constructor516* This must isolate the new object from any changes to the old object.517*/518EventSetDescriptor(EventSetDescriptor old) {519super(old);520if (old.listenerMethodDescriptors != null) {521int len = old.listenerMethodDescriptors.length;522listenerMethodDescriptors = new MethodDescriptor[len];523for (int i = 0; i < len; i++) {524listenerMethodDescriptors[i] = new MethodDescriptor(525old.listenerMethodDescriptors[i]);526}527}528listenerTypeRef = old.listenerTypeRef;529530addMethodDescriptor = old.addMethodDescriptor;531removeMethodDescriptor = old.removeMethodDescriptor;532getMethodDescriptor = old.getMethodDescriptor;533534unicast = old.unicast;535inDefaultEventSet = old.inDefaultEventSet;536}537538void appendTo(StringBuilder sb) {539appendTo(sb, "unicast", this.unicast);540appendTo(sb, "inDefaultEventSet", this.inDefaultEventSet);541appendTo(sb, "listenerType", this.listenerTypeRef);542appendTo(sb, "getListenerMethod", getMethod(this.getMethodDescriptor));543appendTo(sb, "addListenerMethod", getMethod(this.addMethodDescriptor));544appendTo(sb, "removeListenerMethod", getMethod(this.removeMethodDescriptor));545}546547private static Method getMethod(MethodDescriptor descriptor) {548return (descriptor != null)549? descriptor.getMethod()550: null;551}552}553554555