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.

CallbackManager.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /*
  2. * Copyright (c) 2006-2015 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.parser.common;
  23. import com.dmdirc.parser.interfaces.Parser;
  24. import com.dmdirc.parser.interfaces.SpecificCallback;
  25. import com.dmdirc.parser.interfaces.callbacks.AuthNoticeListener;
  26. import com.dmdirc.parser.interfaces.callbacks.AwayStateListener;
  27. import com.dmdirc.parser.interfaces.callbacks.CallbackInterface;
  28. import com.dmdirc.parser.interfaces.callbacks.ChannelActionListener;
  29. import com.dmdirc.parser.interfaces.callbacks.ChannelCtcpListener;
  30. import com.dmdirc.parser.interfaces.callbacks.ChannelCtcpReplyListener;
  31. import com.dmdirc.parser.interfaces.callbacks.ChannelJoinListener;
  32. import com.dmdirc.parser.interfaces.callbacks.ChannelKickListener;
  33. import com.dmdirc.parser.interfaces.callbacks.ChannelListModeListener;
  34. import com.dmdirc.parser.interfaces.callbacks.ChannelMessageListener;
  35. import com.dmdirc.parser.interfaces.callbacks.ChannelModeChangeListener;
  36. import com.dmdirc.parser.interfaces.callbacks.ChannelModeMessageListener;
  37. import com.dmdirc.parser.interfaces.callbacks.ChannelModeNoticeListener;
  38. import com.dmdirc.parser.interfaces.callbacks.ChannelNamesListener;
  39. import com.dmdirc.parser.interfaces.callbacks.ChannelNickChangeListener;
  40. import com.dmdirc.parser.interfaces.callbacks.ChannelNonUserModeChangeListener;
  41. import com.dmdirc.parser.interfaces.callbacks.ChannelNoticeListener;
  42. import com.dmdirc.parser.interfaces.callbacks.ChannelOtherAwayStateListener;
  43. import com.dmdirc.parser.interfaces.callbacks.ChannelPartListener;
  44. import com.dmdirc.parser.interfaces.callbacks.ChannelQuitListener;
  45. import com.dmdirc.parser.interfaces.callbacks.ChannelSelfJoinListener;
  46. import com.dmdirc.parser.interfaces.callbacks.ChannelSingleModeChangeListener;
  47. import com.dmdirc.parser.interfaces.callbacks.ChannelTopicListener;
  48. import com.dmdirc.parser.interfaces.callbacks.ChannelUserModeChangeListener;
  49. import com.dmdirc.parser.interfaces.callbacks.CompositionStateChangeListener;
  50. import com.dmdirc.parser.interfaces.callbacks.ConnectErrorListener;
  51. import com.dmdirc.parser.interfaces.callbacks.DataInListener;
  52. import com.dmdirc.parser.interfaces.callbacks.DataOutListener;
  53. import com.dmdirc.parser.interfaces.callbacks.DebugInfoListener;
  54. import com.dmdirc.parser.interfaces.callbacks.ErrorInfoListener;
  55. import com.dmdirc.parser.interfaces.callbacks.GroupListEndListener;
  56. import com.dmdirc.parser.interfaces.callbacks.GroupListEntryListener;
  57. import com.dmdirc.parser.interfaces.callbacks.GroupListStartListener;
  58. import com.dmdirc.parser.interfaces.callbacks.InviteListener;
  59. import com.dmdirc.parser.interfaces.callbacks.MotdEndListener;
  60. import com.dmdirc.parser.interfaces.callbacks.MotdLineListener;
  61. import com.dmdirc.parser.interfaces.callbacks.MotdStartListener;
  62. import com.dmdirc.parser.interfaces.callbacks.NetworkDetectedListener;
  63. import com.dmdirc.parser.interfaces.callbacks.NickChangeListener;
  64. import com.dmdirc.parser.interfaces.callbacks.NickInUseListener;
  65. import com.dmdirc.parser.interfaces.callbacks.NumericListener;
  66. import com.dmdirc.parser.interfaces.callbacks.OtherAwayStateListener;
  67. import com.dmdirc.parser.interfaces.callbacks.PasswordRequiredListener;
  68. import com.dmdirc.parser.interfaces.callbacks.PingFailureListener;
  69. import com.dmdirc.parser.interfaces.callbacks.PingSentListener;
  70. import com.dmdirc.parser.interfaces.callbacks.PingSuccessListener;
  71. import com.dmdirc.parser.interfaces.callbacks.PrivateActionListener;
  72. import com.dmdirc.parser.interfaces.callbacks.PrivateCtcpListener;
  73. import com.dmdirc.parser.interfaces.callbacks.PrivateCtcpReplyListener;
  74. import com.dmdirc.parser.interfaces.callbacks.PrivateMessageListener;
  75. import com.dmdirc.parser.interfaces.callbacks.PrivateNoticeListener;
  76. import com.dmdirc.parser.interfaces.callbacks.QuitListener;
  77. import com.dmdirc.parser.interfaces.callbacks.ServerErrorListener;
  78. import com.dmdirc.parser.interfaces.callbacks.ServerNoticeListener;
  79. import com.dmdirc.parser.interfaces.callbacks.ServerReadyListener;
  80. import com.dmdirc.parser.interfaces.callbacks.SocketCloseListener;
  81. import com.dmdirc.parser.interfaces.callbacks.UnknownActionListener;
  82. import com.dmdirc.parser.interfaces.callbacks.UnknownCtcpListener;
  83. import com.dmdirc.parser.interfaces.callbacks.UnknownCtcpReplyListener;
  84. import com.dmdirc.parser.interfaces.callbacks.UnknownMessageListener;
  85. import com.dmdirc.parser.interfaces.callbacks.UnknownNoticeListener;
  86. import com.dmdirc.parser.interfaces.callbacks.UnknownServerNoticeListener;
  87. import com.dmdirc.parser.interfaces.callbacks.UserModeChangeListener;
  88. import com.dmdirc.parser.interfaces.callbacks.UserModeDiscoveryListener;
  89. import com.dmdirc.parser.interfaces.callbacks.WallDesyncListener;
  90. import com.dmdirc.parser.interfaces.callbacks.WallopListener;
  91. import com.dmdirc.parser.interfaces.callbacks.WalluserListener;
  92. import java.lang.reflect.InvocationHandler;
  93. import java.lang.reflect.Method;
  94. import java.lang.reflect.Proxy;
  95. import java.util.Collections;
  96. import java.util.HashMap;
  97. import java.util.Map;
  98. /**
  99. * Parser Callback Manager.
  100. * Manages adding/removing/calling callbacks.
  101. */
  102. public class CallbackManager {
  103. private static final Class<?>[] CLASSES = {
  104. AwayStateListener.class,
  105. OtherAwayStateListener.class,
  106. ChannelOtherAwayStateListener.class,
  107. ChannelActionListener.class,
  108. ChannelCtcpListener.class,
  109. ChannelCtcpReplyListener.class,
  110. ChannelListModeListener.class,
  111. ChannelNamesListener.class,
  112. ChannelJoinListener.class,
  113. ChannelKickListener.class,
  114. ChannelMessageListener.class,
  115. ChannelModeChangeListener.class,
  116. ChannelNickChangeListener.class,
  117. ChannelNonUserModeChangeListener.class,
  118. ChannelModeMessageListener.class,
  119. ChannelModeNoticeListener.class,
  120. ChannelNoticeListener.class,
  121. ChannelPartListener.class,
  122. ChannelQuitListener.class,
  123. ChannelSelfJoinListener.class,
  124. ChannelSingleModeChangeListener.class,
  125. ChannelTopicListener.class,
  126. ChannelUserModeChangeListener.class,
  127. CompositionStateChangeListener.class,
  128. ConnectErrorListener.class,
  129. DataInListener.class,
  130. DataOutListener.class,
  131. DebugInfoListener.class,
  132. ErrorInfoListener.class,
  133. GroupListStartListener.class,
  134. GroupListEntryListener.class,
  135. GroupListEndListener.class,
  136. NetworkDetectedListener.class,
  137. InviteListener.class,
  138. MotdEndListener.class,
  139. MotdLineListener.class,
  140. MotdStartListener.class,
  141. NickChangeListener.class,
  142. NickInUseListener.class,
  143. AuthNoticeListener.class,
  144. NumericListener.class,
  145. PasswordRequiredListener.class,
  146. PingFailureListener.class,
  147. PingSuccessListener.class,
  148. PingSentListener.class,
  149. PrivateActionListener.class,
  150. PrivateCtcpListener.class,
  151. PrivateCtcpReplyListener.class,
  152. PrivateMessageListener.class,
  153. PrivateNoticeListener.class,
  154. QuitListener.class,
  155. ServerErrorListener.class,
  156. ServerReadyListener.class,
  157. SocketCloseListener.class,
  158. UnknownActionListener.class,
  159. UnknownCtcpListener.class,
  160. UnknownCtcpReplyListener.class,
  161. UnknownMessageListener.class,
  162. UnknownNoticeListener.class,
  163. UserModeChangeListener.class,
  164. UserModeDiscoveryListener.class,
  165. WallDesyncListener.class,
  166. WallopListener.class,
  167. WalluserListener.class,
  168. ServerNoticeListener.class,
  169. UnknownServerNoticeListener.class,
  170. };
  171. /** Hashtable used to store the different types of callback known. */
  172. private final Map<Class<? extends CallbackInterface>, CallbackObject> callbackHash
  173. = new HashMap<>();
  174. /** A map of implementations to use for parser interfaces. */
  175. private final Map<Class<?>, Class<?>> implementationMap;
  176. /**
  177. * Constructor to create a CallbackManager.
  178. *
  179. * @param implementationMap A map of implementations to use
  180. */
  181. public CallbackManager(final Map<Class<?>, Class<?>> implementationMap) {
  182. this.implementationMap = Collections.unmodifiableMap(implementationMap);
  183. }
  184. /**
  185. * Initialises this callback manager.
  186. *
  187. * @param parser The parser associated with this CallbackManager
  188. */
  189. public void initialise(final Parser parser) {
  190. for (Class<?> type : CLASSES) {
  191. if (type.isAnnotationPresent(SpecificCallback.class)) {
  192. addCallbackType(getSpecificCallbackObject(parser, type));
  193. } else {
  194. addCallbackType(getCallbackObject(parser, type));
  195. }
  196. }
  197. }
  198. /**
  199. * Retrieves a relevant {@link CallbackObject} for the specified type.
  200. *
  201. * @param parser The parser that this manager belongs to
  202. * @param type The type of callback to create an object for
  203. * @return The relevant CallbackObject
  204. */
  205. protected CallbackObject getCallbackObject(final Parser parser, final Class<?> type) {
  206. return new CallbackObject(parser, this, type.asSubclass(CallbackInterface.class),
  207. implementationMap);
  208. }
  209. /**
  210. * Retrieves a relevant {@link CallbackObjectSpecific} for the specified type.
  211. *
  212. * @param parser The parser that this manager belongs to
  213. * @param type The type of callback to create an object for
  214. * @return The relevant CallbackObject
  215. */
  216. protected CallbackObjectSpecific getSpecificCallbackObject(
  217. final Parser parser, final Class<?> type) {
  218. return new CallbackObjectSpecific(parser, this, type.asSubclass(CallbackInterface.class),
  219. implementationMap);
  220. }
  221. /**
  222. * Add new callback type.
  223. *
  224. * @param callback CallbackObject subclass for the callback.
  225. */
  226. private void addCallbackType(final CallbackObject callback) {
  227. if (!callbackHash.containsKey(callback.getType())) {
  228. callbackHash.put(callback.getType(), callback);
  229. }
  230. }
  231. /**
  232. * Get reference to callback object.
  233. *
  234. * @param callback Name of type of callback object.
  235. * @return CallbackObject returns the callback object for this type
  236. */
  237. private CallbackObject getCallbackType(final Class<? extends CallbackInterface> callback) {
  238. if (!callbackHash.containsKey(callback)) {
  239. throw new CallbackNotFoundException("Callback not found: " + callback.getName());
  240. }
  241. return callbackHash.get(callback);
  242. }
  243. /**
  244. * Remove all callbacks associated with a specific object.
  245. *
  246. * @param o instance of ICallbackInterface to remove.
  247. */
  248. public void delAllCallback(final CallbackInterface o) {
  249. callbackHash.values().stream()
  250. .filter(cb -> cb != null && cb.getType().isInstance(o))
  251. .forEach(cb -> cb.del(o));
  252. }
  253. /**
  254. * Add all callbacks that this object implements.
  255. *
  256. * @param o instance of ICallbackInterface to add.
  257. */
  258. public void addAllCallback(final CallbackInterface o) {
  259. callbackHash.values().stream()
  260. .filter(cb -> cb != null && cb.getType().isInstance(o))
  261. .forEach(cb -> cb.add(o));
  262. }
  263. /**
  264. * Add a callback.
  265. * This method will throw a CallbackNotFoundException if the callback does not exist.
  266. *
  267. * @param <S> The type of callback
  268. * @param callback Type of callback object
  269. * @param o instance of ICallbackInterface to add.
  270. * @throws CallbackNotFoundException If callback is not found.
  271. * @throws NullPointerException If 'o' is null
  272. */
  273. public <S extends CallbackInterface> void addCallback(
  274. final Class<S> callback, final S o) throws CallbackNotFoundException {
  275. if (o == null) {
  276. throw new NullPointerException("CallbackInterface is null");
  277. }
  278. final CallbackObject cb = getCallbackType(callback);
  279. if (cb != null) {
  280. cb.add(o);
  281. }
  282. }
  283. /**
  284. * Add a callback with a specific target.
  285. * This method will throw a CallbackNotFoundException if the callback does not exist.
  286. *
  287. * @param <S> The type of callback
  288. * @param callback Type of callback object.
  289. * @param o instance of ICallbackInterface to add.
  290. * @param target Parameter to specify that a callback should only fire for specific things
  291. * @throws CallbackNotFoundException If callback is not found.
  292. * @throws NullPointerException If 'o' is null
  293. */
  294. public <S extends CallbackInterface> void addCallback(
  295. final Class<S> callback,
  296. final S o, final String target) throws CallbackNotFoundException {
  297. if (o == null) {
  298. throw new NullPointerException("CallbackInterface is null");
  299. }
  300. ((CallbackObjectSpecific) getCallbackType(callback)).add(o, target);
  301. }
  302. /**
  303. * Remove a callback.
  304. *
  305. * @param callback Type of callback object.
  306. * @param o instance of ICallbackInterface to remove.
  307. */
  308. public void delCallback(final Class<? extends CallbackInterface> callback,
  309. final CallbackInterface o) {
  310. getCallbackType(callback).del(o);
  311. }
  312. /**
  313. * Gets a proxy object which can be used to despatch callbacks of the
  314. * specified type. Callers may pass <code>null</code> for the first two
  315. * arguments of any callback, and these will automatically be replaced
  316. * by the relevant Parser instance and the current date.
  317. *
  318. * @param <T> The type of the callback to retrieve
  319. * @param callback The callback to retrieve
  320. * @return A proxy object which can be used to call the specified callback
  321. */
  322. @SuppressWarnings({"unchecked", "rawtypes"})
  323. public <T extends CallbackInterface> T getCallback(final Class<T> callback) {
  324. return (T) Proxy.newProxyInstance(getClass().getClassLoader(),
  325. new Class[]{ callback }, new CallbackHandler(callback));
  326. }
  327. /**
  328. * A Proxy invocation handler for a specified parser callback.
  329. */
  330. private class CallbackHandler implements InvocationHandler {
  331. /** The callback that should be called. */
  332. private final Class<? extends CallbackInterface> callback;
  333. /**
  334. * Creates a new callback handler for the specified callback.
  335. *
  336. * @param callback The callback to handle
  337. */
  338. public CallbackHandler(final Class<? extends CallbackInterface> callback) {
  339. this.callback = callback;
  340. }
  341. @Override
  342. public Object invoke(final Object proxy, final Method method, final Object[] args) {
  343. getCallbackType(callback).call(args);
  344. return null;
  345. }
  346. }
  347. }