PageRenderTime 58ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre5/doc/users-guide/host-design.xml

#
XML | 473 lines | 372 code | 53 blank | 48 comment | 0 complexity | 74fa48f7499dadaf2cd652982182ded6 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. <!-- jEdit 4.0 Plugin Guide, (C) 2001 John Gellene -->
  2. <!-- jEdit buffer-local properties: -->
  3. <!-- :indentSize=1:tabSize=2:noTabs=true:maxLineLen=72: -->
  4. <!-- This file contains a discussion of the host architecture -->
  5. <!-- in the jEdit Plugin API -->
  6. <!-- $Id: host-design.xml 4003 2002-01-31 21:09:13Z jgellene $
  7. -->
  8. <chapter id="host-design"><title>jEdit as a Plugin Host</title>
  9. <para>
  10. A good way to start learning what a plugin requires is to look at what
  11. the host application does with one. We start our discussion of
  12. plugins by outlining how jEdit loads and displays them. This section
  13. only provides a broad overview of the more important components that
  14. make up jEdit; specifics of the API will be documented in
  15. subsequent chapters.
  16. </para>
  17. <sect1 id="host-design-load"><title>
  18. <indexterm>
  19. <primary>Plugin API</primary>
  20. <secondary>loading at startup</secondary>
  21. </indexterm>
  22. Loading Plugins</title>
  23. <para>
  24. As part of its startup routine, jEdit's <function>main</function>
  25. method calls various methods to load and initialize plugins.
  26. This occurs after the application has done the following:
  27. </para>
  28. <itemizedlist>
  29. <listitem><para>
  30. parsed command line options;
  31. </para></listitem>
  32. <listitem><para>
  33. started the edit server (unless instructed not to
  34. by a command line option) and;
  35. </para></listitem>
  36. <listitem><para>
  37. loaded application properties, any user-supplied properties, and the
  38. application's set of actions that will be available from jEdit's menu
  39. bar (as well as the toolbar and keyboard shortcuts);
  40. </para></listitem>
  41. </itemizedlist>
  42. <para>
  43. Plugin loading occurs before jEdit creates any windows or loads any files
  44. for editing. It also occurs before jEdit runs any startup scripts.
  45. </para>
  46. <sect2 id="host-design-loader"><title>
  47. <indexterm>
  48. <primary>JAR class loader</primary>
  49. </indexterm>
  50. The JARClassLoader</title>
  51. <para>
  52. Plugins are loaded from files with the <filename>.jar</filename>
  53. filename extension located in the <filename>jars</filename>
  54. subdirectories of the jEdit installation and user settings directories
  55. (see <xref linkend="settings-directory" />).
  56. </para>
  57. <para>
  58. For each JAR archive file it finds, jEdit creates an instance of the
  59. <classname>JARClassLoader</classname> class. This is a jEdit-specific
  60. class
  61. that implements the Java platform's abstract class
  62. <classname>ClassLoader</classname>. The constructor for the
  63. <classname>JARClassLoader</classname> object does the following:
  64. <itemizedlist>
  65. <listitem><para>
  66. Adds any class file with a name ending with
  67. <filename>Plugin.class</filename> to an internal collection of
  68. plugin class names maintained by the
  69. <classname>JARClassLoader</classname>. See <xref
  70. linkend="plugin-classes" />.
  71. </para></listitem>
  72. <listitem><para>
  73. Loads any properties defined in files ending with
  74. the extension <filename>.props</filename> that are contained
  75. in the archive. See <xref linkend="api-resource-properties" />.
  76. </para></listitem>
  77. <listitem><para>
  78. Loads as stored BeanShell code the routine for creating a
  79. docking or floating window containing the visible components of
  80. each plugin contained in the archive. This code is contained in an
  81. XML file named <filename>dockables.xml</filename> and located at the top
  82. level of the archive file. <!-- See <xref linkend="api-resources-activation" />. -->
  83. This file is required if the plugin provides for a docking or
  84. floating window.
  85. </para></listitem>
  86. <listitem><para>
  87. Loads any data on the plugin's actions from a file named
  88. <filename>actions.xml</filename> (if it exists) located at the
  89. top level of the archive file. See <xref
  90. linkend="api-resources-action" />.
  91. </para></listitem>
  92. <listitem><para>
  93. Adds to a collection maintained by jEdit a new object of
  94. type <classname>EditPlugin.JAR</classname>. This is a data structure
  95. holding the name of the jar archive file, a reference to the
  96. <classname>JARClassLoader</classname> and a collection,
  97. initially empty, of plugins found in the archive file.
  98. </para></listitem>
  99. </itemizedlist>
  100. </para>
  101. <para>
  102. Once all plugin JAR files have been examined for the above resources,
  103. jEdit initializes all class files whose names end in
  104. <filename>Plugin.class</filename>, as identified in the first pass
  105. through the JAR archive. We will call these classes
  106. <indexterm>
  107. <primary>Plugin API</primary>
  108. <secondary>plugin core class</secondary>
  109. </indexterm>
  110. <firstterm>plugin core classes</firstterm>. Plugin core classes provide
  111. the principal point of contact between jEdit and the plugin, and
  112. must extend jEdit's abstract <classname>EditPlugin</classname> class.
  113. Use of a class name ending in <classname>Plugin</classname> is also
  114. required.
  115. </para>
  116. <para>
  117. <indexterm>
  118. <primary>Plugins</primary>
  119. <secondary>dependencies</secondary>
  120. </indexterm>
  121. For each plugin core class, the loader first checks the plugin's
  122. properties to see if it is subject to any dependencies. For example, a
  123. plugin may require that the version of the Java runtime environment or
  124. of jEdit itself be equal to or above some threshold version. A plugin
  125. can also require the presence of another plugin. If any dependency is
  126. not satisified, the loader marks the plugin as <quote>broken</quote> and
  127. logs an error message.
  128. </para>
  129. <para>
  130. If all dependencies are satisfied, a new instance
  131. of the plugin core class is created and added to the collection
  132. maintained by the appropriate <classname>EditPlugin.JAR</classname>
  133. object. By
  134. accessing that object, jEdit can keep track of plugins it has
  135. successfully loaded, and call methods or perform routines on them.
  136. </para>
  137. <sidebar><title>
  138. <indexterm>
  139. <primary>Plugin API</primary>
  140. <secondary>using class libraries</secondary>
  141. </indexterm>
  142. Additional class libraries for plugins</title>
  143. <para>
  144. JAR files with no plugin core classes are also loaded by jEdit; no special
  145. initialization is performed on them, and the classes they contain are
  146. made available to other plugins. For example, many plugins that rely on
  147. third-party class libraries ship them as separate JAR archives.
  148. </para>
  149. <para>
  150. A plugin that bundles extra JAR archives must define a property that
  151. lists these JAR files in order for the plugin manager to be able to
  152. remove the plugin completely. See <xref
  153. linkend="api-resource-properties" />.
  154. </para>
  155. </sidebar>
  156. </sect2>
  157. <sect2 id="host-design-plugin-start"><title>Starting the Plugin</title>
  158. <para>
  159. After creating and storing the plugin core object, jEdit calls the
  160. <function>start()</function> method of the plugin core class.
  161. Because this method is defined as an empty <quote>no-op</quote> in the
  162. <classname>EditPlugin</classname> abstract class, a plugin need not
  163. provide its own implementation if the plugin does not require its
  164. own initialization. If the only initialization required is to register
  165. the plugin core object with the EditBus, it is better to do that
  166. simply by deriving the class from <classname>EBPlugin</classname>
  167. instead of <classname>EditPlugin</classname>.
  168. </para>
  169. <!--
  170. <para>
  171. The
  172. <function>start()</function> method can perform initialization of the
  173. object's data
  174. members. It can also register itself with
  175. jEdit's <firstterm>EditBus</firstterm> object, which manages messaging
  176. between plugins and the host application. We will discuss the
  177. EditBus in more detail in <xref linkend="host-display-manager" />
  178. and <xref linkend="api-message" />.
  179. </para>
  180. -->
  181. <para>
  182. At this point, we can identify the following practical requirements
  183. for a plugin:
  184. </para>
  185. <itemizedlist>
  186. <listitem><para>
  187. it must be packaged as a JAR archive;
  188. </para></listitem>
  189. <listitem><para>
  190. the JAR archive must contain an XML data file named
  191. <filename>dockables.xml</filename> containing code on how the plugin will
  192. create a docking or floating window when it is activated;
  193. </para></listitem>
  194. <listitem><para>
  195. if the plugin requires initialization when jEdit starts up,
  196. the JAR archive must contain at least one plugin core
  197. class whose name ends in <filename>Plugin</filename> and which
  198. extends the <classname>EditPlugin</classname> abstract class;
  199. </para></listitem>
  200. <listitem><para>
  201. the JAR archive may contain data concerning actions for display
  202. in jEdit's menu bar and elsewhere in a file entitled
  203. <filename>actions.xml</filename>; and
  204. </para></listitem>
  205. <listitem><para>
  206. the archive must contain
  207. at least one properties file having a <filename>.props</filename>
  208. extension. Certain properties giving information
  209. about the plugin must be defined.
  210. </para></listitem>
  211. </itemizedlist>
  212. <para>
  213. We will provide more detail on these requirements later.
  214. </para>
  215. </sect2>
  216. </sect1>
  217. <sect1 id="host-design-display"><title>The User Interface of a Plugin</title>
  218. <para>
  219. <!-- To display a user interface, plugins can either directly extend Java's
  220. <classname>JFrame</classname>, <classname>JDialog</classname>, or
  221. <classname>JWindow</classname> classes, or use jEdit's dockable window
  222. API. Plugin windows are typically defined in classes that are
  223. part of the plugin package but separate from the plugin core
  224. class. -->
  225. To display a user interface, plugins provide a top-level component
  226. derived (directly or indirectly) from the Swing
  227. <classname>JComponent</classname> class. This component will be
  228. embedded in a docking of floating window created by the Plugin API. It
  229. is typically defined in a class that is part of the plugin package but
  230. separate from the plugin core class (if one exists).
  231. </para>
  232. <sect2 id="host-display-view"><title>
  233. <indexterm>
  234. <primary>View</primary>
  235. <secondary>use in Plugin API</secondary>
  236. </indexterm>
  237. <indexterm>
  238. <primary>Plugin API</primary>
  239. <secondary>use of View object</secondary>
  240. </indexterm>
  241. The Role of the View Object</title>
  242. <para>
  243. A <classname>View</classname> is jEdit's top-level frame window that
  244. contains one or more (if the view is split) text areas, a menu bar,
  245. a toolbar and other window
  246. decorations, as well as docked plugin components. The
  247. <classname>View</classname> class
  248. performs two important operations that deal
  249. with plugins: creating plugin menu items, and managing dockable
  250. windows.
  251. </para>
  252. <para>
  253. <indexterm>
  254. <primary>Plugin API</primary>
  255. <secondary>menu creation</secondary>
  256. </indexterm>
  257. When a view is being created, its initialization routine
  258. iterates through the collection of loaded plugins and calls
  259. the <function>createMenuItems()</function> method of
  260. each plugin core class. In the parent class,
  261. <classname>EditPlugin</classname>, this method is an empty
  262. <quote>no-op</quote>. In order to add items to jEdit's menu bar
  263. under the <guilabel>Plugins</guilabel> heading, the plugin core class
  264. supplies its own version of <function>createMenuItems()</function>.
  265. As we will explain in the next chapter,
  266. the typical plugin, instead of creating Java
  267. <classname>JMenu</classname> and <classname>JMenuItem</classname>
  268. objects directly, relies on a method in a utility class to
  269. create menu entries.
  270. </para>
  271. <para>
  272. The <classname>View</classname> also creates and initializes a
  273. <classname>DockableWindowManager</classname> object. This object is
  274. responsible for creating, closing and managing dockable windows.
  275. </para>
  276. <para>
  277. The <classname>View</classname> class contains a number of methods
  278. that can be called from plugins; see <xref linkend="class-view" /> for
  279. details.
  280. </para>
  281. </sect2>
  282. <sect2 id="host-display-manager">
  283. <title>
  284. <indexterm>
  285. <primary>Plugin API</primary>
  286. <secondary>DockableWindowManager class</secondary>
  287. </indexterm>
  288. <indexterm>
  289. <primary>DockableWindowManager</primary>
  290. <secondary>use in Plugin API</secondary>
  291. </indexterm>
  292. The DockableWindowManager and the EditBus</title>
  293. <para>
  294. The <classname>DockableWindowManager</classname> keeps track of docked
  295. and floating windows. When the <classname>View</classname> object
  296. initializes its <classname>DockableWindowManager</classname>, the
  297. manager iterates through the list of registered dockable windows and
  298. examines options supplied by the user in the <guilabel>Global
  299. Options</guilabel> dialog box. It displays any windows that the user
  300. designated as <quote>auto open</quote>. The
  301. <classname>DockableWindowManager</classname> object is also invoked
  302. whenever the user requests a dockable window is opened or closed.
  303. </para>
  304. <para>
  305. The <classname>DockableWindowManager</classname> creates and displays
  306. plugin windows by routing messages through the application's
  307. <classname>EditBus</classname> object that we mentioned earlier. The
  308. EditBus mantains a list of objects that have requested to receive
  309. messages. When a message is sent using this class, all registered
  310. components receive it in turn.
  311. </para>
  312. <para>
  313. Plugins register with the EditBus to receive messages reflecting
  314. changes in the application's state, including changes in buffers,
  315. views and editing pane, changes in the set of propoerties maintained
  316. by the application, and the closing of the application. A full list
  317. of message classes used by the EditBus are summarized beginning with
  318. <xref linkend="class-EBMessage"/>.
  319. </para>
  320. <para>
  321. <indexterm>
  322. <primary>Plugin API</primary>
  323. <secondary>EBComponent class</secondary>
  324. </indexterm>
  325. Classes for objects that subscribe to the EditBus must implement the
  326. <classname>EBComponent</classname> interface, which defines the single
  327. method <function>handleMessage()</function>. A
  328. <classname>View</classname>, for example, can receive and handle EditBus
  329. messages because it also implements <classname>EBComponent</classname>.
  330. </para>
  331. <para>
  332. Any class in a plugin can receive messages by providing an
  333. implementation of the <classname>EBComponent</classname>
  334. interface. A <quote>plugin core class</quote> that implements the
  335. <classname>EBPlugin</classname> interface (and whose name ends with
  336. <quote>Plugin</quote> for identification purposes) will automtically be
  337. added to the EditBus during jEdit's startup routine. Any other
  338. class - for example, a docking window component that needs to receive
  339. notification of buffer changes - must perform its own registration by calling
  340. <function>EditBus.addToBus(this)</function> during its initialization.
  341. If this class if derived from <classname>JComponent</classname>, a
  342. convenient place to register would be in an implementation of the
  343. <classname>JComponent</classname> method
  344. <function>addNotify()</function>.
  345. </para>
  346. </sect2>
  347. <sect2 id="host-display-message">
  348. <title>
  349. <indexterm>
  350. <primary>Plugin API</primary>
  351. <secondary>creating dockable windows</secondary>
  352. </indexterm>
  353. Dockable Window Creation</title>
  354. <para>
  355. To activate a plugin window, the
  356. <classname>DockableWindowManager</classname> finds and executes the
  357. BeanShell code extracted from the plugin's
  358. <filename>dockables.xml</filename> file during application startup. This
  359. code will typically consist of a call to the constructor of the docking
  360. window component that passes two parameters: the
  361. <classname>View</classname> associated with the docking window
  362. component, and a<classname>String</classname> representing the compnent's
  363. docking or floating position. The return value of the constructor call
  364. is stored
  365. </para>
  366. <para>
  367. As a final step in plugin activation, the manager creates another window
  368. object that will contain the object returned by this constructor call.
  369. This object
  370. implements the <classname>DockableWindowContainer</classname> interface;
  371. depending on the settings for the plugin selected by the user, it will
  372. either be a tabbed window pane in one of the docked windows attached
  373. to the <classname>View</classname> object, or a separate, floating frame
  374. window. Plugins need not be aware of the implementation details of the
  375. container.
  376. </para>
  377. <para>
  378. Eventually the <classname>DockableWindowManager</classname> destroys
  379. the plugin window,
  380. whether docking or floating, in response to user action or as
  381. part of the destruction of the corresponding <classname>View</classname>
  382. object.
  383. </para>
  384. <para>
  385. The <classname>DockableWindowManager</classname> and
  386. <classname>EditBus</classname> classes contain a number of methods
  387. that can be called from plugins; see <xref linkend="class-view" /> for
  388. details.
  389. </para>
  390. <!--
  391. <para>
  392. This summary shows that a plugin wishing to use the dockable window
  393. API has the following additional requirements:
  394. </para>
  395. <itemizedlist>
  396. <listitem><para>
  397. the plugin class must extend <classname>EBPlugin</classname>
  398. instead of <classname>EditPlugin</classname> in order to
  399. receive the <classname>CreateDockableWindow</classname> message;
  400. </para></listitem>
  401. <listitem><para>
  402. it must register its dockable windows in its
  403. <function>start()</function> method; and
  404. </para></listitem>
  405. <listitem><para>
  406. it must create and arrange any dockable windows it provides
  407. in response to the appropriate
  408. <classname>CreateDockableWindow</classname> message;
  409. </para></listitem>
  410. </itemizedlist>
  411. -->
  412. <para>
  413. With this broad outline of how jEdit behaves as a plugin host in the
  414. background, we will next review the programming elements that make up
  415. a plugin.
  416. </para>
  417. </sect2>
  418. </sect1>
  419. </chapter>