You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ListenerList.java 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. * Copyright (c) 2006-2017 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  5. * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
  6. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
  7. * permit persons to whom the Software is furnished to do so, subject to the following conditions:
  8. *
  9. * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
  10. * Software.
  11. *
  12. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  13. * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
  14. * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  15. * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  16. */
  17. package com.dmdirc.util.collections;
  18. import java.lang.reflect.InvocationHandler;
  19. import java.lang.reflect.InvocationTargetException;
  20. import java.lang.reflect.Method;
  21. import java.lang.reflect.Proxy;
  22. import java.util.Collection;
  23. import java.util.HashMap;
  24. import java.util.Map;
  25. import java.util.concurrent.CopyOnWriteArrayList;
  26. /**
  27. * Represents a list of event listeners, similar to EventListenerList, but
  28. * not swing specific.
  29. */
  30. public class ListenerList {
  31. /** The map of class->listener or string->listener that we're using. */
  32. private final Map<Object, Collection<Object>> listeners = new HashMap<>();
  33. /**
  34. * Adds a new listener of the specified type to this listener list.
  35. *
  36. * @param <T> The type of listener to be added
  37. * @param listenerType The type of listener to be added
  38. * @param listener The listener to be added
  39. */
  40. public <T> void add(final Class<T> listenerType, final T listener) {
  41. if (listener == null) {
  42. throw new IllegalArgumentException("Listener cannot be null");
  43. }
  44. if (!listeners.containsKey(listenerType)) {
  45. listeners.put(listenerType, new CopyOnWriteArrayList<>());
  46. }
  47. listeners.get(listenerType).add(listener);
  48. }
  49. /**
  50. * Adds a new listener of the specified type to this listener list.
  51. *
  52. * @param listenerType The name of the type of listener that's being added
  53. * @param listener The listener to be added
  54. */
  55. public void add(final String listenerType, final Object listener) {
  56. if (listener == null) {
  57. throw new IllegalArgumentException("Listener cannot be null");
  58. }
  59. if (!listeners.containsKey(listenerType)) {
  60. listeners.put(listenerType, new CopyOnWriteArrayList<>());
  61. }
  62. listeners.get(listenerType).add(listener);
  63. }
  64. /**
  65. * Removes the specified listener from the list of listeners for the
  66. * specified type.
  67. *
  68. * @param <T> The type of listener to be removed
  69. * @param listenerType The type that the listener should be removed from
  70. * @param listener The listener to be removed
  71. */
  72. public <T> void remove(final Class<T> listenerType, final T listener) {
  73. get(listenerType).remove(listener);
  74. }
  75. /**
  76. * Removes the specified listener from the list of listeners for the
  77. * specified type.
  78. *
  79. * @param listenerType The name of the type that the listener should be
  80. * removed from
  81. * @param listener The listener to be removed
  82. */
  83. public void remove(final String listenerType, final Object listener) {
  84. get(listenerType).remove(listener);
  85. }
  86. /**
  87. * Retrieves the list of listeners for the specified type.
  88. *
  89. * @param <T> The type of listener to be retrieved
  90. * @param listenerType The type of listener that's being retrieved
  91. * @return A list of listeners for the specified type
  92. */
  93. @SuppressWarnings("unchecked")
  94. public <T> Collection<T> get(final Class<T> listenerType) {
  95. if (listeners.containsKey(listenerType)) {
  96. return (Collection<T>) listeners.get(listenerType);
  97. } else {
  98. return new CopyOnWriteArrayList<>();
  99. }
  100. }
  101. /**
  102. * Retrieves the list of listeners for the specified type.
  103. *
  104. * @param listenerType The type of listener to be retrieved
  105. * @return A list of listeners for the specified type
  106. */
  107. public Collection<Object> get(final String listenerType) {
  108. if (listeners.containsKey(listenerType)) {
  109. return listeners.get(listenerType);
  110. } else {
  111. return new CopyOnWriteArrayList<>();
  112. }
  113. }
  114. /**
  115. * Returns a callable proxy of the specified type. Methods called on the
  116. * returned proxy will be proxied to all of the registered listeners.
  117. *
  118. * @param <T> The type of listener to be called
  119. * @param listenerType The type of listener to be called
  120. * @return A proxy instance that can be used to call methods
  121. */
  122. @SuppressWarnings("unchecked")
  123. public <T> T getCallable(final Class<T> listenerType) {
  124. return (T) Proxy.newProxyInstance(listenerType.getClassLoader(),
  125. new Class<?>[] { listenerType }, new CallHandler<>(listenerType));
  126. }
  127. /**
  128. * Utility class to handle calls to a "callable" interface as returned by
  129. * {@link #getCallable(Class)}.
  130. *
  131. * @param <T> The type of listener being called
  132. */
  133. private class CallHandler<T> implements InvocationHandler {
  134. /** The type of listener this handler is handling. */
  135. private final Class<T> listenerType;
  136. /**
  137. * Creates a new call handler for the specified type of listener.
  138. *
  139. * @param listenerType The type of listener to handle
  140. */
  141. CallHandler(final Class<T> listenerType) {
  142. this.listenerType = listenerType;
  143. }
  144. @Override
  145. public Object invoke(final Object proxy, final Method method,
  146. final Object[] args) throws Throwable {
  147. for (T target : get(listenerType)) {
  148. try {
  149. method.invoke(target, args);
  150. } catch (IllegalAccessException | IllegalArgumentException ex) {
  151. // Ignore, not possible
  152. } catch (InvocationTargetException ex) {
  153. throw ex.getCause();
  154. }
  155. }
  156. return null;
  157. }
  158. }
  159. }