PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/EditBus.java

#
Java | 493 lines | 258 code | 47 blank | 188 comment | 35 complexity | 10005e2b4a3df4a6e95d24447d2d083e MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * EditBus.java - The EditBus
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit;
  23. import java.awt.*;
  24. import java.lang.annotation.*;
  25. import java.lang.reflect.Method;
  26. import java.lang.reflect.InvocationTargetException;
  27. import java.util.*;
  28. import java.util.List;
  29. import org.gjt.sp.util.Log;
  30. /**
  31. * jEdit's global event notification mechanism.<p>
  32. *
  33. * Plugins register with the EditBus to receive messages reflecting
  34. * changes in the application's state, including changes in buffers,
  35. * views and edit panes, changes in the set of properties maintained
  36. * by the application, and the closing of the application.<p>
  37. *
  38. * The EditBus maintains a list of objects that have requested to receive
  39. * messages. When a message is sent using this class, all registered
  40. * components receive it in turn. Classes for objects that subscribe to
  41. * the EditBus must implement the {@link EBComponent} interface, which
  42. * defines the single method {@link EBComponent#handleMessage(EBMessage)}.<p>
  43. *
  44. * Alternatively, since jEdit4.3pre19, EditBus components can be any
  45. * object. Handlers for EditBus messages are created by annotating
  46. * methods with the {@link EBHandler} annotation. Such methods should
  47. * expect a single parameter - an edit bus message of any desired type.
  48. * If a message matching the type (or any of its super-types, unless the
  49. * annotation requests exact type matching) is being sent, the annotated
  50. * method will be called instead of the default {@link
  51. * EBComponent#handleMessage(EBMessage)}. If a handler exists for a
  52. * specific message type, the default handler will not be called.<p>
  53. *
  54. * A plugin core class that extends the
  55. * {@link EBPlugin} abstract class (and whose name ends with
  56. * <code>Plugin</code> for identification purposes) will automatically be
  57. * added to the EditBus during jEdit's startup routine. Any other
  58. * class - for example, a dockable window that needs to receive
  59. * notification of buffer changes - must perform its own registration by calling
  60. * {@link #addToBus(Object)} during its initialization.
  61. * A convenient place to register in a class derived from <code>JComponent</code>
  62. * would be in an implementation of the <code>JComponent</code> method
  63. * <code>addNotify()</code>.<p>
  64. *
  65. * Message types sent by jEdit can be found in the
  66. * {@link org.gjt.sp.jedit.msg} package.<p>
  67. *
  68. * Plugins can also send their own messages - any object can send a message to
  69. * the EditBus by calling the static method {@link #send(EBMessage)}.
  70. * Most plugins, however, only concern themselves with receiving, not
  71. * sending, messages.
  72. *
  73. * @see org.gjt.sp.jedit.EBComponent
  74. * @see org.gjt.sp.jedit.EBMessage
  75. *
  76. * @author Slava Pestov
  77. * @author John Gellene (API documentation)
  78. * @version $Id: EditBus.java 20015 2011-09-24 11:56:48Z kpouer $
  79. *
  80. * @since jEdit 2.2pre6
  81. */
  82. public class EditBus
  83. {
  84. //{{{ EBHandler annotation
  85. /**
  86. * This annotation should be used in methods that are to be
  87. * considered "edit bus message handlers". When registering
  88. * an object using {@link #addToBus(Object)}, all methods
  89. * tagged with this annotation will be considered as handlers
  90. * for specific edit bus messages.<p>
  91. *
  92. * Each method should expect a single argument (an object of
  93. * some type derived from EBMessage, inclusive). When
  94. * delivering an EBMessage, the bus will search for and invoke
  95. * all handlers matching the outgoing message type.<p>
  96. *
  97. * Since jEdit 4.4pre1, this annotation can also be added to
  98. * classes extending EditPlugin. This will make the plugin
  99. * be added to the bus automatically, similarly to how
  100. * EBPlugin works, but without having to implement the
  101. * EBComponent interface.
  102. *
  103. * @since jEdit 4.3pre19
  104. */
  105. @Retention(RetentionPolicy.RUNTIME)
  106. @Target({ElementType.TYPE, ElementType.METHOD})
  107. public static @interface EBHandler
  108. {
  109. /**
  110. * Whether the message should match the exact type of
  111. * the parameter, instead of a compatible type.
  112. */
  113. boolean exact() default false;
  114. } //}}}
  115. //{{{ addToBus() method
  116. /**
  117. * Adds a component to the bus. It will receive all messages sent
  118. * on the bus.
  119. *
  120. * @param comp The component to add
  121. */
  122. public static void addToBus(EBComponent comp)
  123. {
  124. addToBus((Object)comp);
  125. } //}}}
  126. //{{{ addToBus() method
  127. /**
  128. * Adds a component to the bus. Methods annotated with the
  129. * {@link EBHandler} annotation found in the component will
  130. * be used as EditBus message handlers if a message of a
  131. * matching type is sent on the bus.<p>
  132. *
  133. * If the component implements {@link EBComponent}, then the
  134. * {@link EBComponent#handleMessage(EBMessage)} method will be
  135. * called for every message sent on the bus.
  136. *
  137. * @param comp The component to add
  138. *
  139. * @since jEdit 4.3pre19
  140. */
  141. public static void addToBus(Object comp)
  142. {
  143. components.addComponent(comp);
  144. } //}}}
  145. //{{{ removeFromBus() method
  146. /**
  147. * Removes a component from the bus.
  148. * @param comp The component to remove
  149. */
  150. public static void removeFromBus(EBComponent comp)
  151. {
  152. removeFromBus((Object) comp);
  153. } //}}}
  154. //{{{ removeFromBus() method
  155. /**
  156. * Removes a component from the bus.
  157. * @param comp The component to remove
  158. * @since 4.3pre19
  159. */
  160. public static void removeFromBus(Object comp)
  161. {
  162. components.removeComponent(comp);
  163. } //}}}
  164. //{{{ send() method
  165. /**
  166. * Sends a message to all components on the bus in turn.
  167. * The message is delivered to components in the AWT thread,
  168. * and this method will wait until all handlers receive the
  169. * message before returning.
  170. *
  171. * <p><b>NOTE:</b>
  172. * If the calling thread is not the AWT thread and the
  173. * thread is interrupted before or while the call of this
  174. * method, this method can return before completion of handlers.
  175. * However, the interruption state is set in this case, so the
  176. * caller can detect the interruption after the call. If you
  177. * really need the completion of handlers, you should make sure
  178. * the call is in the AWT thread or the calling thread is never
  179. * interrupted. If you don't care about the completion of
  180. * handlers, it is recommended to use
  181. * {@link #sendAsync(EBMessage)} instead.
  182. * </p>
  183. *
  184. * @param message The message
  185. */
  186. public static void send(EBMessage message)
  187. {
  188. Runnable sender = new SendMessage(message);
  189. if (EventQueue.isDispatchThread())
  190. {
  191. sender.run();
  192. return;
  193. }
  194. /*
  195. * We can't throw any checked exceptions from this
  196. * method. It will break all source that currently
  197. * expects this method to not throw them. So we catch
  198. * them and log them instead.
  199. */
  200. boolean interrupted = false;
  201. try
  202. {
  203. EventQueue.invokeAndWait(sender);
  204. }
  205. catch (InterruptedException ie)
  206. {
  207. interrupted = true;
  208. Log.log(Log.ERROR, EditBus.class, ie);
  209. }
  210. catch (InvocationTargetException ite)
  211. {
  212. Log.log(Log.ERROR, EditBus.class, ite);
  213. }
  214. finally
  215. {
  216. if (interrupted)
  217. {
  218. Thread.currentThread().interrupt();
  219. }
  220. }
  221. } //}}}
  222. //{{{ sendAsync() method
  223. /**
  224. * Schedules a message to be sent on the edit bus as soon as
  225. * the AWT thread is done processing current events. The
  226. * method returns immediately (i.e., before the message is
  227. * sent).
  228. *
  229. * @param message The message
  230. *
  231. * @since jEdit 4.4pre1
  232. */
  233. public static void sendAsync(EBMessage message)
  234. {
  235. EventQueue.invokeLater(new SendMessage(message));
  236. } //}}}
  237. //{{{ Private members
  238. private static final HandlerList components = new HandlerList();
  239. // can't create new instances
  240. private EditBus() {}
  241. //{{{ dispatch() method
  242. private static void dispatch(EBMessageHandler emh,
  243. EBMessage msg)
  244. throws Exception
  245. {
  246. if (emh.handler != null)
  247. emh.handler.invoke(emh.comp, msg);
  248. else
  249. {
  250. assert (emh.comp instanceof EBComponent);
  251. ((EBComponent)emh.comp).handleMessage(msg);
  252. }
  253. } //}}}
  254. //{{{ sendImpl() method
  255. private static void sendImpl(EBMessage message)
  256. {
  257. boolean isExact = true;
  258. Class<?> type = message.getClass();
  259. while (!type.equals(Object.class))
  260. {
  261. List<EBMessageHandler> handlers = components.get(type);
  262. if (handlers != null)
  263. {
  264. try
  265. {
  266. for (EBMessageHandler emh : handlers)
  267. {
  268. if (!isExact &&
  269. emh.source != null &&
  270. emh.source.exact())
  271. {
  272. continue;
  273. }
  274. if(Debug.EB_TIMER)
  275. {
  276. long start = System.nanoTime();
  277. dispatch(emh, message);
  278. long time = System.nanoTime() - start;
  279. if(time >= 1000000)
  280. {
  281. Log.log(Log.DEBUG,EditBus.class,emh.comp + ": " + time + " ns");
  282. }
  283. }
  284. else
  285. dispatch(emh, message);
  286. }
  287. }
  288. catch (InvocationTargetException t)
  289. {
  290. Log.log(Log.ERROR,EditBus.class,"Exception"
  291. + " while sending message on EditBus:");
  292. Log.log(Log.ERROR, EditBus.class, t.getCause());
  293. }
  294. catch(Throwable t)
  295. {
  296. Log.log(Log.ERROR,EditBus.class,"Exception"
  297. + " while sending message on EditBus:");
  298. Log.log(Log.ERROR,EditBus.class,t);
  299. }
  300. }
  301. type = type.getSuperclass();
  302. isExact = false;
  303. }
  304. } //}}}
  305. //}}}
  306. //{{{ EBMessageHandler class
  307. private static class EBMessageHandler
  308. {
  309. EBMessageHandler(Object comp,
  310. Method handler,
  311. EBHandler source)
  312. {
  313. this.comp = comp;
  314. this.handler = handler;
  315. this.source = source;
  316. }
  317. Object comp;
  318. Method handler;
  319. EBHandler source;
  320. } //}}}
  321. //{{{ HandlerList class
  322. /**
  323. * A "special" hash map that has some optimizations for use by
  324. * the EditBus. Notably, it allows setting a "read only" mode
  325. * where modifications to the map are postponed until the map
  326. * is unlocked.
  327. */
  328. private static class HandlerList
  329. extends HashMap<Class<?>, List<EBMessageHandler>>
  330. {
  331. public List<EBMessageHandler> safeGet(Class<?> type)
  332. {
  333. List<EBMessageHandler> lst = super.get(type);
  334. if (lst == null) {
  335. lst = new LinkedList<EBMessageHandler>();
  336. super.put(type, lst);
  337. }
  338. return lst;
  339. }
  340. public synchronized void lock()
  341. {
  342. lock++;
  343. }
  344. public synchronized void unlock()
  345. {
  346. lock--;
  347. if (lock == 0)
  348. {
  349. for (Object comp : add)
  350. addComponent(comp);
  351. for (Object comp : remove)
  352. removeComponent(comp);
  353. add.clear();
  354. remove.clear();
  355. }
  356. }
  357. public synchronized void removeComponent(Object comp)
  358. {
  359. if (lock != 0)
  360. {
  361. remove.add(comp);
  362. return;
  363. }
  364. for (Map.Entry<Class<?>, List<EBMessageHandler>> entry: entrySet())
  365. {
  366. Class<?> msg = entry.getKey();
  367. List<EBMessageHandler> handlers = entry.getValue();
  368. if (handlers == null)
  369. continue;
  370. for (Iterator<EBMessageHandler> it = handlers.iterator();
  371. it.hasNext(); )
  372. {
  373. EBMessageHandler emh = it.next();
  374. if (emh.comp == comp)
  375. it.remove();
  376. }
  377. }
  378. }
  379. public synchronized void addComponent(Object comp)
  380. {
  381. if (lock != 0)
  382. {
  383. add.add(comp);
  384. return;
  385. }
  386. for (Method m : comp.getClass().getMethods())
  387. {
  388. EBHandler source = m.getAnnotation(EBHandler.class);
  389. if (source == null)
  390. continue;
  391. Class[] params = m.getParameterTypes();
  392. if (params.length != 1)
  393. {
  394. Log.log(Log.ERROR, EditBus.class,
  395. "Invalid EBHandler method " + m.getName() +
  396. " in class " + comp.getClass().getName() +
  397. ": too many parameters.");
  398. continue;
  399. }
  400. if (!EBMessage.class.isAssignableFrom(params[0]))
  401. {
  402. Log.log(Log.ERROR, EditBus.class,
  403. "Invalid parameter " + params[0].getName() +
  404. " in method " + m.getName() +
  405. " of class " + comp.getClass().getName());
  406. continue;
  407. }
  408. synchronized (components)
  409. {
  410. safeGet(params[0]).add(new EBMessageHandler(comp, m, source));
  411. }
  412. }
  413. /*
  414. * If the component implements EBComponent, then add the
  415. * default handler for backwards compatibility.
  416. */
  417. if (comp instanceof EBComponent)
  418. safeGet(EBMessage.class).add(new EBMessageHandler(comp, null, null));
  419. }
  420. private int lock;
  421. private List<Object> add = new LinkedList<Object>();
  422. private List<Object> remove = new LinkedList<Object>();
  423. } //}}}
  424. //{{{ SendMessage class
  425. private static class SendMessage implements Runnable
  426. {
  427. public SendMessage(EBMessage message)
  428. {
  429. this.message = message;
  430. }
  431. public void run()
  432. {
  433. Log.log(Log.DEBUG,EditBus.class,message.toString());
  434. components.lock();
  435. try
  436. {
  437. sendImpl(message);
  438. }
  439. finally
  440. {
  441. components.unlock();
  442. }
  443. }
  444. private EBMessage message;
  445. } //}}}
  446. }