Source code: org/gjt/sp/jedit/EditPlugin.java
1 /*
2 * EditPlugin.java - Abstract class all plugins must implement
3 * :tabSize=8:indentSize=8:noTabs=false:
4 * :folding=explicit:collapseFolds=1:
5 *
6 * Copyright (C) 1999, 2003 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
23 package org.gjt.sp.jedit;
24
25 import javax.swing.JMenuItem;
26 import java.util.*;
27 import org.gjt.sp.jedit.browser.VFSBrowser;
28 import org.gjt.sp.jedit.gui.OptionsDialog;
29 import org.gjt.sp.jedit.menu.EnhancedMenu;
30
31 /**
32 * The abstract base class that every plugin must implement.
33 * Alternatively, instead of extending this class, a plugin core class can
34 * extend {@link EBPlugin} to automatically receive EditBus messages.
35 *
36 * <h3>Basic plugin information properties</h3>
37 *
38 * Note that in all cases above where a class name is needed, the fully
39 * qualified class name, including the package name, if any, must be used.<p>
40 *
41 * The following properties are required for jEdit to load the plugin:
42 *
43 * <ul>
44 * <li><code>plugin.<i>class name</i>.activate</code> - set this to
45 * <code>defer</code> if your plugin only needs to be loaded when it is first
46 * invoked; set it to <code>startup</code> if your plugin must be loaded at
47 * startup regardless; set it to a whitespace-separated list of property names
48 * if your plugin should be loaded if at least one of these properties is set.
49 * Note that if this property is <b>not</b> set, the plugin is loaded like an
50 * old-style jEdit 4.1 plugin.
51 * </li>
52 * <li><code>plugin.<i>class name</i>.name</code></li>
53 * <li><code>plugin.<i>class name</i>.version</code></li>
54 * <li><code>plugin.<i>class name</i>.jars</code> - only needed if your plugin
55 * bundles external JAR files. Contains a whitespace-separated list of JAR
56 * file names. Without this property, the plugin manager will leave behind the
57 * external JAR files when removing the plugin.</li>
58 * </ul>
59 *
60 * The following properties are optional but recommended:
61 *
62 * <ul>
63 * <li><code>plugin.<i>class name</i>.author</code></li>
64 * <li><code>plugin.<i>class name</i>.docs</code> - the path to plugin
65 * documentation in HTML format within the JAR file.</li>
66 * </ul>
67 *
68 * <h3>Plugin dependency properties</h3>
69 *
70 * Plugin dependencies are also specified using properties.
71 * Each dependency is defined in a property named with
72 * <code>plugin.<i>class name</i>.depend.</code> followed by a number.
73 * Dependencies must be numbered in order, starting from zero.<p>
74 *
75 * The value of a dependency property has one of the following forms:
76 *
77 * <ul>
78 * <li><code>jdk <i>minimum Java version</i></code></li>
79 * <li><code>jedit <i>minimum jEdit version</i></code> - note that this must be
80 * a version number in the form returned by {@link jEdit#getBuild()},
81 * not {@link jEdit#getVersion()}. Note that the documentation here describes
82 * the jEdit 4.2 plugin API, so this dependency must be set to at least
83 * <code>04.02.01.00</code>.</li>
84 * <li><code>plugin <i>plugin</i> <i>version</i></code> - the fully quailified
85 * plugin class name must be specified.</li>
86 * </ul>
87 *
88 * <h3>Plugin menu item properties</h3>
89 *
90 * To add your plugin to the view's <b>Plugins</b> menu, define one of these two
91 * properties:
92 *
93 * <ul>
94 * <li><code>plugin.<i>class name</i>.menu-item</code> - if this is defined,
95 * the action named by this property is added to the <b>Plugins</b> menu.</li>
96 * <li><code>plugin.<i>class name</i>.menu</code> - if this is defined,
97 * a sub-menu is added to the <b>Plugins</b> menu whose content is the
98 * whitespace-separated list of action names in this property. A separator may
99 * be added to the sub-menu by listing <code>-</code> in the property.</li>
100 * </ul>
101 *
102 * If you want the plugin's menu items to be determined at runtime, define a
103 * property <code>plugin.<i>class name</i>.menu.code</code> to be BeanShell
104 * code that evaluates to an implementation of
105 * {@link org.gjt.sp.jedit.menu.DynamicMenuProvider}.<p>
106 *
107 * To add your plugin to the file system browser's <b>Plugins</b> menu, define
108 * one of these two properties:
109 *
110 * <ul>
111 * <li><code>plugin.<i>class name</i>.browser-menu-item</code> - if this is
112 * defined, the action named by this property is added to the <b>Plugins</b>
113 * menu.</li>
114 * <li><code>plugin.<i>class name</i>.browser-menu</code> - if this is defined,
115 * a sub-menu is added to the <b>Plugins</b> menu whose content is the
116 * whitespace-separated list of action names in this property. A separator may
117 * be added to the sub-menu by listing <code>-</code> in the property.</li>
118 * </ul>
119 *
120 * In all cases, each action's
121 * menu item label is taken from the <code><i>action name</i>.label</code>
122 * property. View actions are defined in an <code>actions.xml</code>
123 * file, file system browser actions are defined in a
124 * <code>browser.actions.xml</code> file; see {@link ActionSet}.
125 *
126 * <h3>Plugin option pane properties</h3>
127 *
128 * To add your plugin to the <b>Plugin Options</b> dialog box, define one of
129 * these two properties:
130 *
131 * <ul>
132 * <li><code>plugin.<i>class name</i>.option-pane</code> - if this is defined,
133 * the option pane named by this property is added to the <b>Plugin Options</b>
134 * menu.</li>
135 * <li><code>plugin.<i>class name</i>.option-group</code> - if this is defined,
136 * a branch node is added to the <b>Plugin Options</b> dialog box whose content
137 * is the whitespace-separated list of option pane names in this property.</li>
138 * </ul>
139 *
140 * Then for each option pane name, define these two properties:
141 *
142 * <ul>
143 * <li><code>options.<i>option pane name</i>.label</code> - the label to show
144 * for the pane in the dialog box.</li>
145 * <li><code>options.<i>option pane name</i>.code</code> - BeanShell code that
146 * evaluates to an instance of the {@link OptionPane} class.</li>
147 *
148 * <h3>Example</h3>
149 *
150 * Here is an example set of plugin properties:
151 *
152 * <pre>plugin.QuickNotepadPlugin.activate=defer
153 *plugin.QuickNotepadPlugin.name=QuickNotepad
154 *plugin.QuickNotepadPlugin.author=John Gellene
155 *plugin.QuickNotepadPlugin.version=4.2
156 *plugin.QuickNotepadPlugin.docs=QuickNotepad.html
157 *plugin.QuickNotepadPlugin.depend.0=jedit 04.02.01.00
158 *plugin.QuickNotepadPlugin.menu=quicknotepad \
159 * - \
160 * quicknotepad.choose-file \
161 * quicknotepad.save-file \
162 * quicknotepad.copy-to-buffer
163 *plugin.QuickNotepadPlugin.option-pane=quicknotepad</pre>
164 *
165 * Note that action and option pane labels are not shown in the above example.
166 *
167 * @see org.gjt.sp.jedit.jEdit#getProperty(String)
168 * @see org.gjt.sp.jedit.jEdit#getPlugin(String)
169 * @see org.gjt.sp.jedit.jEdit#getPlugins()
170 * @see org.gjt.sp.jedit.jEdit#getPluginJAR(String)
171 * @see org.gjt.sp.jedit.jEdit#getPluginJARs()
172 * @see org.gjt.sp.jedit.jEdit#addPluginJAR(String)
173 * @see org.gjt.sp.jedit.jEdit#removePluginJAR(PluginJAR,boolean)
174 * @see org.gjt.sp.jedit.ActionSet
175 * @see org.gjt.sp.jedit.gui.DockableWindowManager
176 * @see org.gjt.sp.jedit.OptionPane
177 * @see org.gjt.sp.jedit.PluginJAR
178 * @see org.gjt.sp.jedit.ServiceManager
179 *
180 * @author Slava Pestov
181 * @author John Gellene (API documentation)
182 * @version $Id: EditPlugin.java,v 1.31 2003/05/03 20:34:25 spestov Exp $
183 * @since jEdit 2.1pre1
184 */
185 public abstract class EditPlugin
186 {
187 //{{{ start() method
188 /**
189 * jEdit calls this method when the plugin is being activated, either
190 * during startup or at any other time. A plugin can get activated for
191 * a number of reasons:
192 *
193 * <ul>
194 * <li>The plugin is written for jEdit 4.1 or older, in which case it
195 * will always be loaded at startup.</li>
196 * <li>The plugin has its <code>activate</code> property set to
197 * <code>startup</code>, in which case it will always be loaded at
198 * startup.</li>
199 * <li>One of the properties listed in the plugin's
200 * <code>activate</code> property is set to <code>true</code>,
201 * in which case it will always be loaded at startup.</li>
202 * <li>One of the plugin's classes is being accessed by another plugin,
203 * a macro, or a BeanShell snippet in a plugin API XML file.</li>
204 * </ul>
205 *
206 * Note that this method is always called from the event dispatch
207 * thread, even if the activation resulted from a class being loaded
208 * from another thread. A side effect of this is that some of your
209 * plugin's code might get executed before this method finishes
210 * running.<p>
211 *
212 * When this method is being called for plugins written for jEdit 4.1
213 * and below, no views or buffers are open. However, this is not the
214 * case for plugins using the new API. For example, if your plugin adds
215 * tool bars to views, make sure you correctly handle the case where
216 * views are already open when the plugin is loaded.<p>
217 *
218 * If your plugin must be loaded on startup, take care to have this
219 * method return as quickly as possible.<p>
220 *
221 * The default implementation of this method does nothing.
222 *
223 * @since jEdit 2.1pre1
224 */
225 public void start() {}
226 //}}}
227
228 //{{{ stop() method
229 /**
230 * jEdit calls this method when the plugin is being unloaded. This can
231 * be when the program is exiting, or at any other time.<p>
232 *
233 * If a plugin uses state information or other persistent data
234 * that should be stored in a special format, this would be a good place
235 * to write the data to storage. If the plugin uses jEdit's properties
236 * API to hold settings, no special processing is needed for them on
237 * exit, since they will be saved automatically.<p>
238 *
239 * With plugins written for jEdit 4.1 and below, this method is only
240 * called when the program is exiting. However, this is not the case
241 * for plugins using the new API. For example, if your plugin adds
242 * tool bars to views, make sure you correctly handle the case where
243 * views are still open when the plugin is unloaded.<p>
244 *
245 * To avoid memory leaks, this method should ensure that no references
246 * to any objects created by this plugin remain in the heap. In the
247 * case of actions, dockable windows and services, jEdit ensures this
248 * automatically. For other objects, your plugin must clean up maually.
249 * <p>
250 *
251 * The default implementation of this method does nothing.
252 *
253 * @since jEdit 2.1pre1
254 */
255 public void stop() {} //}}}
256
257 //{{{ getClassName() method
258 /**
259 * Returns the plugin's class name. This might not be the same as
260 * the class of the actual <code>EditPlugin</code> instance, for
261 * example if the plugin is not loaded yet.
262 *
263 * @since jEdit 2.5pre3
264 */
265 public String getClassName()
266 {
267 return getClass().getName();
268 } //}}}
269
270 //{{{ getPluginJAR() method
271 /**
272 * Returns the JAR file containing this plugin.
273 * @since jEdit 4.2pre1
274 */
275 public PluginJAR getPluginJAR()
276 {
277 return jar;
278 } //}}}
279
280 //{{{ createMenuItems() method
281 /**
282 * Called by the view when constructing its <b>Plugins</b> menu.
283 * See the description of this class for details about how the
284 * menu items are constructed from plugin properties.
285 *
286 * @since jEdit 4.2pre1
287 */
288 public final JMenuItem createMenuItems()
289 {
290 if(this instanceof Broken)
291 return null;
292
293 String menuItemName = jEdit.getProperty("plugin." +
294 getClassName() + ".menu-item");
295 if(menuItemName != null)
296 return GUIUtilities.loadMenuItem(menuItemName);
297
298 String menuProperty = "plugin." + getClassName() + ".menu";
299 String codeProperty = "plugin." + getClassName() + ".menu.code";
300 if(jEdit.getProperty(menuProperty) != null
301 || jEdit.getProperty(codeProperty) != null)
302 {
303 String pluginName = jEdit.getProperty("plugin." +
304 getClassName() + ".name");
305 return new EnhancedMenu(menuProperty,pluginName);
306 }
307
308 return null;
309 } //}}}
310
311 //{{{ createBrowserMenuItems() method
312 /**
313 * Called by the filesystem browser when constructing its
314 * <b>Plugins</b> menu.
315 * See the description of this class for details about how the
316 * menu items are constructed from plugin properties.
317 *
318 * @since jEdit 4.2pre1
319 */
320 public final JMenuItem createBrowserMenuItems()
321 {
322 if(this instanceof Broken)
323 return null;
324
325 String menuItemName = jEdit.getProperty("plugin." +
326 getClassName() + ".browser-menu-item");
327 if(menuItemName != null)
328 {
329 return GUIUtilities.loadMenuItem(
330 VFSBrowser.getActionContext(),
331 menuItemName,
332 false);
333 }
334
335 String menuProperty = "plugin." + getClassName() + ".browser-menu";
336 if(jEdit.getProperty(menuProperty) != null)
337 {
338 String pluginName = jEdit.getProperty("plugin." +
339 getClassName() + ".name");
340 return new EnhancedMenu(menuProperty,pluginName,
341 VFSBrowser.getActionContext());
342 }
343
344 return null;
345 } //}}}
346
347 //{{{ Deprecated methods
348
349 //{{{ createMenuItems() method
350 /**
351 * @deprecated Instead of overriding this method, define properties
352 * as specified in the description of this class.
353 */
354 public void createMenuItems(Vector menuItems) {} //}}}
355
356 //{{{ createOptionPanes() method
357 /**
358 * @deprecated Instead of overriding this method, define properties
359 * as specified in the description of this class.
360 */
361 public void createOptionPanes(OptionsDialog optionsDialog) {} //}}}
362
363 //{{{ getJAR() method
364 /**
365 * @deprecated Call <code>getPluginJAR()</code> instead.
366 */
367 public EditPlugin.JAR getJAR()
368 {
369 return jar;
370 } //}}}
371
372 //}}}
373
374 //{{{ Package-private members
375 EditPlugin.JAR jar;
376 //}}}
377
378 //{{{ Broken class
379 /**
380 * A placeholder for a plugin that didn't load.
381 * @see jEdit#getPlugin(String)
382 * @see PluginJAR#getPlugin()
383 * @see PluginJAR#activatePlugin()
384 */
385 public static class Broken extends EditPlugin
386 {
387 public String getClassName()
388 {
389 return clazz;
390 }
391
392 // package-private members
393 Broken(String clazz)
394 {
395 this.clazz = clazz;
396 }
397
398 // private members
399 private String clazz;
400 } //}}}
401
402 //{{{ Deferred class
403 /**
404 * A placeholder for a plugin that hasn't been loaded yet.
405 * @see jEdit#getPlugin(String)
406 * @see PluginJAR#getPlugin()
407 * @see PluginJAR#activatePlugin()
408 */
409 public static class Deferred extends EditPlugin
410 {
411 public String getClassName()
412 {
413 return clazz;
414 }
415
416 // package-private members
417 Deferred(String clazz)
418 {
419 this.clazz = clazz;
420 }
421
422 EditPlugin loadPluginClass()
423 {
424 return null;
425 }
426
427 public String toString()
428 {
429 return "Deferred[" + clazz + "]";
430 }
431
432 // private members
433 private String clazz;
434 } //}}}
435
436 //{{{ JAR class
437 /**
438 * @deprecated Use <code>PluginJAR</code> instead.
439 */
440 public static class JAR extends PluginJAR
441 {
442 JAR(java.io.File file)
443 {
444 super(file);
445 }
446 }
447 //}}}
448 }