1. /*
  2. * @(#)EventSetDescriptor.java 1.64 04/05/05
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.beans;
  8. import java.lang.ref.Reference;
  9. import java.lang.reflect.Method;
  10. /**
  11. * An EventSetDescriptor describes a group of events that a given Java
  12. * bean fires.
  13. * <P>
  14. * The given group of events are all delivered as method calls on a single
  15. * event listener interface, and an event listener object can be registered
  16. * via a call on a registration method supplied by the event source.
  17. */
  18. public class EventSetDescriptor extends FeatureDescriptor {
  19. private MethodDescriptor[] listenerMethodDescriptors;
  20. private Reference listenerMethodsRef;
  21. private Reference listenerTypeRef;
  22. private Reference addMethodRef;
  23. private Reference removeMethodRef;
  24. private Reference getMethodRef;
  25. private String[] listenerMethodNames;
  26. private String addMethodName;
  27. private String removeMethodName;
  28. private String getMethodName;
  29. private boolean unicast;
  30. private boolean inDefaultEventSet = true;
  31. /**
  32. * Creates an <TT>EventSetDescriptor</TT> assuming that you are
  33. * following the most simple standard design pattern where a named
  34. * event "fred" is (1) delivered as a call on the single method of
  35. * interface FredListener, (2) has a single argument of type FredEvent,
  36. * and (3) where the FredListener may be registered with a call on an
  37. * addFredListener method of the source component and removed with a
  38. * call on a removeFredListener method.
  39. *
  40. * @param sourceClass The class firing the event.
  41. * @param eventSetName The programmatic name of the event. E.g. "fred".
  42. * Note that this should normally start with a lower-case character.
  43. * @param listenerType The target interface that events
  44. * will get delivered to.
  45. * @param listenerMethodName The method that will get called when the event gets
  46. * delivered to its target listener interface.
  47. * @exception IntrospectionException if an exception occurs during
  48. * introspection.
  49. */
  50. public EventSetDescriptor(Class<?> sourceClass, String eventSetName,
  51. Class<?> listenerType, String listenerMethodName)
  52. throws IntrospectionException {
  53. this(sourceClass, eventSetName, listenerType,
  54. new String[] { listenerMethodName },
  55. "add" + getListenerClassName(listenerType),
  56. "remove" + getListenerClassName(listenerType),
  57. "get" + getListenerClassName(listenerType) + "s");
  58. String eventName = capitalize(eventSetName) + "Event";
  59. Method[] listenerMethods = getListenerMethods();
  60. if (listenerMethods.length > 0) {
  61. Class[] args = listenerMethods[0].getParameterTypes();
  62. // Check for EventSet compliance. Special case for vetoableChange. See 4529996
  63. if (!"vetoableChange".equals(eventSetName) && !args[0].getName().endsWith(eventName)) {
  64. throw new IntrospectionException("Method \"" + listenerMethodNames[0] +
  65. "\" should have argument \"" +
  66. eventName + "\"");
  67. }
  68. }
  69. }
  70. private static String getListenerClassName(Class cls) {
  71. String className = cls.getName();
  72. return className.substring(className.lastIndexOf('.') + 1);
  73. }
  74. /**
  75. * Creates an <TT>EventSetDescriptor</TT> from scratch using
  76. * string names.
  77. *
  78. * @param sourceClass The class firing the event.
  79. * @param eventSetName The programmatic name of the event set.
  80. * Note that this should normally start with a lower-case character.
  81. * @param listenerType The Class of the target interface that events
  82. * will get delivered to.
  83. * @param listenerMethodNames The names of the methods that will get called
  84. * when the event gets delivered to its target listener interface.
  85. * @param addListenerMethodName The name of the method on the event source
  86. * that can be used to register an event listener object.
  87. * @param removeListenerMethodName The name of the method on the event source
  88. * that can be used to de-register an event listener object.
  89. * @exception IntrospectionException if an exception occurs during
  90. * introspection.
  91. */
  92. public EventSetDescriptor(Class<?> sourceClass,
  93. String eventSetName,
  94. Class<?> listenerType,
  95. String listenerMethodNames[],
  96. String addListenerMethodName,
  97. String removeListenerMethodName)
  98. throws IntrospectionException {
  99. this(sourceClass, eventSetName, listenerType,
  100. listenerMethodNames, addListenerMethodName,
  101. removeListenerMethodName, null);
  102. }
  103. /**
  104. * This constructor creates an EventSetDescriptor from scratch using
  105. * string names.
  106. *
  107. * @param sourceClass The class firing the event.
  108. * @param eventSetName The programmatic name of the event set.
  109. * Note that this should normally start with a lower-case character.
  110. * @param listenerType The Class of the target interface that events
  111. * will get delivered to.
  112. * @param listenerMethodNames The names of the methods that will get called
  113. * when the event gets delivered to its target listener interface.
  114. * @param addListenerMethodName The name of the method on the event source
  115. * that can be used to register an event listener object.
  116. * @param removeListenerMethodName The name of the method on the event source
  117. * that can be used to de-register an event listener object.
  118. * @param getListenerMethodName The method on the event source that
  119. * can be used to access the array of event listener objects.
  120. * @exception IntrospectionException if an exception occurs during
  121. * introspection.
  122. * @since 1.4
  123. */
  124. public EventSetDescriptor(Class<?> sourceClass,
  125. String eventSetName,
  126. Class<?> listenerType,
  127. String listenerMethodNames[],
  128. String addListenerMethodName,
  129. String removeListenerMethodName,
  130. String getListenerMethodName)
  131. throws IntrospectionException {
  132. if (sourceClass == null || eventSetName == null || listenerType == null) {
  133. throw new NullPointerException();
  134. }
  135. setName(eventSetName);
  136. setClass0(sourceClass);
  137. setListenerType(listenerType);
  138. Method[] listenerMethods = new Method[listenerMethodNames.length];
  139. for (int i = 0; i < listenerMethodNames.length; i++) {
  140. // Check for null names
  141. if (listenerMethodNames[i] == null) {
  142. throw new NullPointerException();
  143. }
  144. listenerMethods[i] = getMethod(listenerType, listenerMethodNames[i], 1);
  145. }
  146. setListenerMethods(listenerMethods);
  147. setAddListenerMethod(getMethod(sourceClass, addListenerMethodName, 1));
  148. setRemoveListenerMethod(getMethod(sourceClass, removeListenerMethodName, 1));
  149. // Be more forgiving of not finding the getListener method.
  150. Method method = Introspector.findMethod(sourceClass,
  151. getListenerMethodName, 0);
  152. if (method != null) {
  153. setGetListenerMethod(method);
  154. }
  155. }
  156. private static Method getMethod(Class cls, String name, int args)
  157. throws IntrospectionException {
  158. if (name == null) {
  159. return null;
  160. }
  161. Method method = Introspector.findMethod(cls, name, args);
  162. if (method == null) {
  163. throw new IntrospectionException("Method not found: " + name +
  164. " on class " + cls.getName());
  165. }
  166. return method;
  167. }
  168. /**
  169. * Creates an <TT>EventSetDescriptor</TT> from scratch using
  170. * <TT>java.lang.reflect.Method</TT> and <TT>java.lang.Class</TT> objects.
  171. *
  172. * @param eventSetName The programmatic name of the event set.
  173. * @param listenerType The Class for the listener interface.
  174. * @param listenerMethods An array of Method objects describing each
  175. * of the event handling methods in the target listener.
  176. * @param addListenerMethod The method on the event source
  177. * that can be used to register an event listener object.
  178. * @param removeListenerMethod The method on the event source
  179. * that can be used to de-register an event listener object.
  180. * @exception IntrospectionException if an exception occurs during
  181. * introspection.
  182. */
  183. public EventSetDescriptor(String eventSetName,
  184. Class<?> listenerType,
  185. Method listenerMethods[],
  186. Method addListenerMethod,
  187. Method removeListenerMethod)
  188. throws IntrospectionException {
  189. this(eventSetName, listenerType, listenerMethods,
  190. addListenerMethod, removeListenerMethod, null);
  191. }
  192. /**
  193. * This constructor creates an EventSetDescriptor from scratch using
  194. * java.lang.reflect.Method and java.lang.Class objects.
  195. *
  196. * @param eventSetName The programmatic name of the event set.
  197. * @param listenerType The Class for the listener interface.
  198. * @param listenerMethods An array of Method objects describing each
  199. * of the event handling methods in the target listener.
  200. * @param addListenerMethod The method on the event source
  201. * that can be used to register an event listener object.
  202. * @param removeListenerMethod The method on the event source
  203. * that can be used to de-register an event listener object.
  204. * @param getListenerMethod The method on the event source
  205. * that can be used to access the array of event listener objects.
  206. * @exception IntrospectionException if an exception occurs during
  207. * introspection.
  208. * @since 1.4
  209. */
  210. public EventSetDescriptor(String eventSetName,
  211. Class<?> listenerType,
  212. Method listenerMethods[],
  213. Method addListenerMethod,
  214. Method removeListenerMethod,
  215. Method getListenerMethod)
  216. throws IntrospectionException {
  217. setName(eventSetName);
  218. setListenerMethods(listenerMethods);
  219. setAddListenerMethod(addListenerMethod);
  220. setRemoveListenerMethod( removeListenerMethod);
  221. setGetListenerMethod(getListenerMethod);
  222. setListenerType(listenerType);
  223. }
  224. /**
  225. * Creates an <TT>EventSetDescriptor</TT> from scratch using
  226. * <TT>java.lang.reflect.MethodDescriptor</TT> and <TT>java.lang.Class</TT>
  227. * objects.
  228. *
  229. * @param eventSetName The programmatic name of the event set.
  230. * @param listenerType The Class for the listener interface.
  231. * @param listenerMethodDescriptors An array of MethodDescriptor objects
  232. * describing each of the event handling methods in the
  233. * target listener.
  234. * @param addListenerMethod The method on the event source
  235. * that can be used to register an event listener object.
  236. * @param removeListenerMethod The method on the event source
  237. * that can be used to de-register an event listener object.
  238. * @exception IntrospectionException if an exception occurs during
  239. * introspection.
  240. */
  241. public EventSetDescriptor(String eventSetName,
  242. Class<?> listenerType,
  243. MethodDescriptor listenerMethodDescriptors[],
  244. Method addListenerMethod,
  245. Method removeListenerMethod)
  246. throws IntrospectionException {
  247. setName(eventSetName);
  248. this.listenerMethodDescriptors = listenerMethodDescriptors;
  249. setAddListenerMethod(addListenerMethod);
  250. setRemoveListenerMethod(removeListenerMethod);
  251. setListenerType(listenerType);
  252. }
  253. /**
  254. * Gets the <TT>Class</TT> object for the target interface.
  255. *
  256. * @return The Class object for the target interface that will
  257. * get invoked when the event is fired.
  258. */
  259. public Class<?> getListenerType() {
  260. return (Class)getObject(listenerTypeRef);
  261. }
  262. private void setListenerType(Class cls) {
  263. listenerTypeRef = createReference(cls);
  264. }
  265. /**
  266. * Gets the methods of the target listener interface.
  267. *
  268. * @return An array of <TT>Method</TT> objects for the target methods
  269. * within the target listener interface that will get called when
  270. * events are fired.
  271. */
  272. public synchronized Method[] getListenerMethods() {
  273. Method[] methods = getListenerMethods0();
  274. if (methods == null) {
  275. if (listenerMethodDescriptors != null) {
  276. methods = new Method[listenerMethodDescriptors.length];
  277. for (int i = 0; i < methods.length; i++) {
  278. methods[i] = listenerMethodDescriptors[i].getMethod();
  279. }
  280. } else if (listenerMethodNames != null) {
  281. methods = new Method[listenerMethodNames.length];
  282. for (int i = 0; i < methods.length; i++) {
  283. methods[i] = Introspector.findMethod(getListenerType(),
  284. listenerMethodNames[i], 1);
  285. }
  286. }
  287. setListenerMethods(methods);
  288. }
  289. return methods;
  290. }
  291. private void setListenerMethods(Method[] methods) {
  292. if (methods == null) {
  293. return;
  294. }
  295. if (listenerMethodNames == null) {
  296. listenerMethodNames = new String[methods.length];
  297. for (int i = 0; i < methods.length; i++) {
  298. listenerMethodNames[i] = methods[i].getName();
  299. }
  300. }
  301. listenerMethodsRef = createReference(methods, true);
  302. }
  303. private Method[] getListenerMethods0() {
  304. return (Method[])getObject(listenerMethodsRef);
  305. }
  306. /**
  307. * Gets the <code>MethodDescriptor</code>s of the target listener interface.
  308. *
  309. * @return An array of <code>MethodDescriptor</code> objects for the target methods
  310. * within the target listener interface that will get called when
  311. * events are fired.
  312. */
  313. public synchronized MethodDescriptor[] getListenerMethodDescriptors() {
  314. Method[] listenerMethods = getListenerMethods();
  315. if (listenerMethodDescriptors == null && listenerMethods != null) {
  316. // Create MethodDescriptor array from Method array.
  317. listenerMethodDescriptors =
  318. new MethodDescriptor[listenerMethods.length];
  319. for (int i = 0; i < listenerMethods.length; i++) {
  320. listenerMethodDescriptors[i] =
  321. new MethodDescriptor(listenerMethods[i]);
  322. }
  323. }
  324. return listenerMethodDescriptors;
  325. }
  326. /**
  327. * Gets the method used to add event listeners.
  328. *
  329. * @return The method used to register a listener at the event source.
  330. */
  331. public synchronized Method getAddListenerMethod() {
  332. Method method = getAddListenerMethod0();
  333. if (method == null) {
  334. Class cls = getClass0();
  335. if (cls == null) {
  336. return null;
  337. }
  338. method = Introspector.findMethod(cls, addMethodName, 1);
  339. setAddListenerMethod(method);
  340. }
  341. return method;
  342. }
  343. private Method getAddListenerMethod0() {
  344. return (Method)getObject(addMethodRef);
  345. }
  346. private synchronized void setAddListenerMethod(Method method) {
  347. if (method == null) {
  348. return;
  349. }
  350. if (getClass0() == null) {
  351. setClass0(method.getDeclaringClass());
  352. }
  353. addMethodName = method.getName();
  354. addMethodRef = createReference(method, true);
  355. }
  356. /**
  357. * Gets the method used to remove event listeners.
  358. *
  359. * @return The method used to remove a listener at the event source.
  360. */
  361. public synchronized Method getRemoveListenerMethod() {
  362. Method method = getRemoveListenerMethod0();
  363. if (method == null) {
  364. Class cls = getClass0();
  365. if (cls == null) {
  366. return null;
  367. }
  368. method = Introspector.findMethod(cls, removeMethodName, 1);
  369. setRemoveListenerMethod(method);
  370. }
  371. return method;
  372. }
  373. private Method getRemoveListenerMethod0() {
  374. return (Method)getObject(removeMethodRef);
  375. }
  376. private synchronized void setRemoveListenerMethod(Method method) {
  377. if (method == null) {
  378. return;
  379. }
  380. if (getClass0() == null) {
  381. setClass0(method.getDeclaringClass());
  382. }
  383. removeMethodName = method.getName();
  384. removeMethodRef = createReference(method, true);
  385. }
  386. /**
  387. * Gets the method used to access the registered event listeners.
  388. *
  389. * @return The method used to access the array of listeners at the event
  390. * source or null if it doesn't exist.
  391. * @since 1.4
  392. */
  393. public synchronized Method getGetListenerMethod() {
  394. Method method = getGetListenerMethod0();
  395. if (method == null) {
  396. Class cls = getClass0();
  397. if (cls == null) {
  398. return null;
  399. }
  400. method = Introspector.findMethod(cls, getMethodName, 0);
  401. setGetListenerMethod(method);
  402. }
  403. return method;
  404. }
  405. private Method getGetListenerMethod0() {
  406. return (Method)getObject(getMethodRef);
  407. }
  408. private synchronized void setGetListenerMethod(Method method) {
  409. if (method == null) {
  410. return;
  411. }
  412. if (getClass0() == null) {
  413. setClass0(method.getDeclaringClass());
  414. }
  415. getMethodName = method.getName();
  416. getMethodRef = createReference(method, true);
  417. }
  418. /**
  419. * Mark an event set as unicast (or not).
  420. *
  421. * @param unicast True if the event set is unicast.
  422. */
  423. public void setUnicast(boolean unicast) {
  424. this.unicast = unicast;
  425. }
  426. /**
  427. * Normally event sources are multicast. However there are some
  428. * exceptions that are strictly unicast.
  429. *
  430. * @return <TT>true</TT> if the event set is unicast.
  431. * Defaults to <TT>false</TT>.
  432. */
  433. public boolean isUnicast() {
  434. return unicast;
  435. }
  436. /**
  437. * Marks an event set as being in the "default" set (or not).
  438. * By default this is <TT>true</TT>.
  439. *
  440. * @param inDefaultEventSet <code>true</code> if the event set is in
  441. * the "default" set,
  442. * <code>false</code> if not
  443. */
  444. public void setInDefaultEventSet(boolean inDefaultEventSet) {
  445. this.inDefaultEventSet = inDefaultEventSet;
  446. }
  447. /**
  448. * Reports if an event set is in the "default" set.
  449. *
  450. * @return <TT>true</TT> if the event set is in
  451. * the "default" set. Defaults to <TT>true</TT>.
  452. */
  453. public boolean isInDefaultEventSet() {
  454. return inDefaultEventSet;
  455. }
  456. /*
  457. * Package-private constructor
  458. * Merge two event set descriptors. Where they conflict, give the
  459. * second argument (y) priority over the first argument (x).
  460. *
  461. * @param x The first (lower priority) EventSetDescriptor
  462. * @param y The second (higher priority) EventSetDescriptor
  463. */
  464. EventSetDescriptor(EventSetDescriptor x, EventSetDescriptor y) {
  465. super(x,y);
  466. listenerMethodDescriptors = x.listenerMethodDescriptors;
  467. if (y.listenerMethodDescriptors != null) {
  468. listenerMethodDescriptors = y.listenerMethodDescriptors;
  469. }
  470. listenerMethodNames = x.listenerMethodNames;
  471. if (y.listenerMethodNames != null) {
  472. listenerMethodNames = y.listenerMethodNames;
  473. }
  474. listenerTypeRef = x.listenerTypeRef;
  475. if (y.listenerTypeRef != null) {
  476. listenerTypeRef = y.listenerTypeRef;
  477. }
  478. addMethodRef = x.addMethodRef;
  479. if (y.addMethodRef != null) {
  480. addMethodRef = y.addMethodRef;
  481. }
  482. addMethodName = x.addMethodName;
  483. if (y.addMethodName != null) {
  484. addMethodName = y.addMethodName;
  485. }
  486. removeMethodRef = x.removeMethodRef;
  487. if (y.removeMethodRef != null) {
  488. removeMethodRef = y.removeMethodRef;
  489. }
  490. removeMethodName = x.removeMethodName;
  491. if (y.removeMethodName != null) {
  492. removeMethodName = y.removeMethodName;
  493. }
  494. getMethodRef = x.getMethodRef;
  495. if (y.getMethodRef != null) {
  496. getMethodRef = y.getMethodRef;
  497. }
  498. getMethodName = x.getMethodName;
  499. if (y.getMethodName != null) {
  500. getMethodName = y.getMethodName;
  501. }
  502. unicast = y.unicast;
  503. if (!x.inDefaultEventSet || !y.inDefaultEventSet) {
  504. inDefaultEventSet = false;
  505. }
  506. }
  507. /*
  508. * Package-private dup constructor
  509. * This must isolate the new object from any changes to the old object.
  510. */
  511. EventSetDescriptor(EventSetDescriptor old) {
  512. super(old);
  513. if (old.listenerMethodDescriptors != null) {
  514. int len = old.listenerMethodDescriptors.length;
  515. listenerMethodDescriptors = new MethodDescriptor[len];
  516. for (int i = 0; i < len; i++) {
  517. listenerMethodDescriptors[i] = new MethodDescriptor(
  518. old.listenerMethodDescriptors[i]);
  519. }
  520. }
  521. if (old.listenerMethodNames != null) {
  522. int len = old.listenerMethodNames.length;
  523. listenerMethodNames = new String[len];
  524. for (int i = 0; i < len; i++) {
  525. listenerMethodNames[i] = old.listenerMethodNames[i];
  526. }
  527. }
  528. listenerTypeRef = old.listenerTypeRef;
  529. addMethodRef = old.addMethodRef;
  530. addMethodName = old.addMethodName;
  531. removeMethodRef = old.removeMethodRef;
  532. removeMethodName = old.removeMethodName;
  533. getMethodRef = old.getMethodRef;
  534. getMethodName = old.getMethodName;
  535. unicast = old.unicast;
  536. inDefaultEventSet = old.inDefaultEventSet;
  537. }
  538. }