001: /*
002: * EditPlugin.java - Abstract class all plugins must implement
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 1999, 2003 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit;
024:
025: import org.gjt.sp.jedit.browser.VFSBrowser;
026: import org.gjt.sp.jedit.gui.OptionsDialog;
027: import org.gjt.sp.jedit.menu.EnhancedMenu;
028: import org.gjt.sp.util.Log;
029:
030: import javax.swing.*;
031: import java.io.*;
032: import java.util.Vector;
033:
034: /**
035: * The abstract base class that every plugin must implement.
036: * Alternatively, instead of extending this class, a plugin core class can
037: * extend {@link EBPlugin} to automatically receive EditBus messages.
038: *
039: * <h3>Basic plugin information properties</h3>
040: *
041: * Note that in all cases above where a <i>className</i> is needed, the fully
042: * qualified class name, including the package name, if any, must be used.<p>
043: *
044: * The following properties are required for jEdit to load the plugin:
045: *
046: * <ul>
047: * <li><code>plugin.<i>className</i>.activate</code> - set this to
048: * <code>defer</code> if your plugin only needs to be loaded when it is first
049: * invoked; set it to <code>startup</code> if your plugin must be loaded at
050: * startup regardless; set it to a whitespace-separated list of property names
051: * if your plugin should be loaded if at least one of these properties is set.
052: * Note that if this property is <b>not</b> set, the plugin will not work with
053: * jEdit 4.3final.
054: * </li>
055: * <li><code>plugin.<i>className</i>.name</code></li>
056: * <li><code>plugin.<i>className</i>.version</code></li>
057: * <li><code>plugin.<i>className</i>.jars</code> - only needed if your plugin
058: * bundles external JAR files. Contains a whitespace-separated list of JAR
059: * file names. Without this property, the plugin manager will leave behind the
060: * external JAR files when removing the plugin.</li>
061: * <li><code>plugin.<i>className</i>.files</code> - only needed if your plugin
062: * bundles external files like libraries which MUST reside in the local
063: * filesystem. Contains a whitespace-separated list of file names.
064: * Without this property, the plugin manager will leave behind the
065: * external files when removing the plugin.</li>
066: * <li><code>plugin.<i>className</i>.description</code> - the short description
067: * associated with the plugin. The short description is used by the Plugin
068: * Manager and on the list pages on Plugin Central. </li>
069: * </ul>
070: *
071: * The following properties are optional but recommended:
072: *
073: * <ul>
074: * <li><code>plugin.<i>className</i>.author</code></li>
075: * <li><code>plugin.<i>className</i>.usePluginHome</code> - whether
076: * the plugin uses the EditPlugin.getPluginHome API or not. Even
077: * if the plugin doesn't store any data, this property should be set
078: * so that the plugin manager can tell that there is no data stored.</li>
079: * <li><code>plugin.<i>className</i>.docs</code> - the path to plugin
080: * documentation in HTML format. </li>
081: * <li><code>plugin.<i>className</i>.longdescription</code> - the path to
082: * the long description in XHTML (no fancy stuff here, please - just proper
083: * XHTML subset with the basic tags: <tt>html, h1, h2, p, li, ul, ol, a href,b ,i, u, br/ </tt>)
084: * <p> The long description is extracted from the plugin at various times,
085: * primarily at plugin packaging time to update the data on the
086: * plugin detail pages of Plugin Central. </p>
087: * <p>
088: * If this property is left out, the default will be to look in a file
089: * called <description.html>. </p>
090: *</li>
091: *</ul>
092: *<p>
093: * For the previous two properties, if a relative path is supplied,
094: * it should be both </p>
095: * <ol>
096: * <li> relative to the location of the .props file (when it is in the source tree) </li>
097: * <li> relative to the root of the JAR (when it is packaged in the JAR file) </li>
098: *</ol>
099: *
100: *<p> Both conditions are easily satisfied if the .props file as well as
101: * description.html are both located in the root directory of the plugin,
102: * as well as the generated JAR. </p>
103: *
104: * <h3>Plugin dependency properties</h3>
105: *
106: * <p>Plugin dependencies are also specified using properties.
107: * Each dependency is defined in a property named with
108: * <code>plugin.<i>className</i>.depend.</code> followed by a number.
109: * Dependencies must be numbered in order, starting from zero.
110: * This determines the order that dependent plugins get loaded and activated,
111: * so order is very important. </p>
112: *
113: * <p> The value of a dependency property has one of the following forms: </p>
114: *
115: * <ul>
116: * <li> <code>jdk <i>minimumJavaVersion</i></code> </li>
117: * <li> <code>jedit <i>minimumjEditVersion</i></code> - note that this must be
118: * a version number in the form returned by {@link jEdit#getBuild()},
119: * not {@link jEdit#getVersion()}. Note that the documentation here describes
120: * the jEdit 4.2 plugin API, so this dependency must be set to at least
121: * <code>04.02.99.00</code> (4.2final).</li>
122: * <li><code><i>pluginClassName pluginVersion</i></code> - the fully quailified
123: * plugin class name with package must be specified.</li>
124: * <li><code>optional plugin <i>pluginClassName pluginVersion</i></code> -
125: * an optional dependency, indicating that the plugin will work without it,
126: * but that the dependency should be loaded before this plugin. </li>
127: </ul>
128:
129: <p>In this example, the ProjectViewer plugin is an optional dependency of
130: the Console, beacause the Console only listens to events from the ProjectViewer.
131: It requires Jedit 4.2 final. </p>
132:
133: <pre>
134: plugin.console.ConsolePlugin.depend.0=jedit 04.02.99.00
135: plugin.console.ConsolePlugin.depend.1=jdk 1.5
136: plugin.console.ConsolePlugin.depend.2=plugin errorlist.ErrorListPlugin 1.4
137: plugin.console.ConsolePlugin.depend.3=optional plugin projectviewer.ProjectPlugin 2.1.0.92
138: </pre>
139:
140: * <h3>Plugin menu item properties</h3>
141: *
142: *<p> To add your plugin to the view's <b>Plugins</b> menu, define one of these two
143: * properties: </p>
144: *
145: * <ul>
146: * <li><code>plugin.<i>className</i>.menu-item</code> - if this is defined,
147: * the action named by this property is added to the <b>Plugins</b> menu.</li>
148: * <li><code>plugin.<i>className</i>.menu</code> - if this is defined,
149: * a sub-menu is added to the <b>Plugins</b> menu whose content is the
150: * whitespace-separated list of action names in this property. A separator may
151: * be added to the sub-menu by listing <code>-</code> in the property.</li>
152: * </ul>
153: *
154: * <p>If you want the plugin's menu items to be determined at runtime, define a
155: * property <code>plugin.<i>className</i>.menu.code</code> to be BeanShell
156: * code that evaluates to an implementation of
157: * {@link org.gjt.sp.jedit.menu.DynamicMenuProvider}.</p>
158: *<p>
159: * To add your plugin to the file system browser's <b>Plugins</b> menu, define
160: * one of these two properties:
161: *</p>
162: * <ul>
163: * <li><code>plugin.<i>className</i>.browser-menu-item</code> - if this is
164: * defined, the action named by this property is added to the <b>Plugins</b>
165: * menu.</li>
166: * <li><code>plugin.<i>className</i>.browser-menu</code> - if this is defined,
167: * a sub-menu is added to the <b>Plugins</b> menu whose content is the
168: * whitespace-separated list of action names in this property. A separator may
169: * be added to the sub-menu by listing <code>-</code> in the property.</li>
170: * </ul>
171: *
172: *<p> In all cases, each action's
173: * menu item label is taken from the <code><i>actionName</i>.label</code>
174: * property. View actions are defined in an <code>actions.xml</code>
175: * file, file system browser actions are defined in a
176: * <code>browser.actions.xml</code> file; see {@link ActionSet}.
177: *</p>
178: * <h3>Plugin option pane properties</h3>
179: *
180: * <p>To add your plugin to the <b>Plugin Options</b> dialog box, define one of
181: * these two properties:
182: *</p>
183: * <ul>
184: * <li><code>plugin.<i>className</i>.option-pane=<i>paneName</i></code> - if this is defined,
185: * a single option pane with this name is added to the <b>Plugin Options</b>
186: * menu.</li>
187: * <li><code>plugin.<i>className</i>.option-group=<i>paneName1</i> [<i>paneName2 paneName3</i> ...]</code> - if this is defined,
188: * a branch node is added to the <b>Plugin Options</b> dialog box whose content
189: * is the whitespace-separated list of <i>paneNames</i> in this property.</li>
190: * </ul>
191: *
192: * Then for each option <i>paneName</i>, define these two properties:
193: *
194: * <ul>
195: * <li><code>options.<i>paneName</i>.label</code> - the label to show
196: * for the pane in the dialog box.</li>
197: * <li><code>options.<i>paneName</i>.code</code> - BeanShell code that
198: * evaluates to an instance of the {@link OptionPane} class.</li>
199: *
200: * <h3>Example</h3>
201: *
202: * Here is an example set of plugin properties:
203: *
204: * <pre>plugin.QuickNotepadPlugin.activate=defer
205: *plugin.QuickNotepadPlugin.name=QuickNotepad
206: *plugin.QuickNotepadPlugin.author=John Gellene
207: *plugin.QuickNotepadPlugin.version=4.2
208: *plugin.QuickNotepadPlugin.docs=QuickNotepad.html
209: *plugin.QuickNotepadPlugin.depend.0=jedit 04.02.01.00
210: *plugin.QuickNotepadPlugin.menu=quicknotepad \
211: * - \
212: * quicknotepad.choose-file \
213: * quicknotepad.save-file \
214: * quicknotepad.copy-to-buffer
215: *plugin.QuickNotepadPlugin.option-pane=quicknotepad
216: *
217: * plugin.QuickNotepadPlugin.option-pane=quicknotepad
218: * plugin.QuickNotepadPlugin.usePluginHome=false
219: * options.quicknotepad.code=new QuickNotepadOptionPane();
220: * options.quicknotepad.label=QuickNotepad
221: * options.quicknotepad.file=File:
222: * options.quicknotepad.choose-file=Choose
223: * options.quicknotepad.choose-file.title=Choose a notepad file
224: * options.quicknotepad.choose-font=Font:
225: * options.quicknotepad.show-filepath.title=Display notepad file path
226: </pre>
227: *
228: * Note that action and option pane labels are not shown in the above example.
229: *
230: * @see org.gjt.sp.jedit.jEdit#getProperty(String)
231: * @see org.gjt.sp.jedit.jEdit#getPlugin(String)
232: * @see org.gjt.sp.jedit.jEdit#getPlugins()
233: * @see org.gjt.sp.jedit.jEdit#getPluginJAR(String)
234: * @see org.gjt.sp.jedit.jEdit#getPluginJARs()
235: * @see org.gjt.sp.jedit.jEdit#addPluginJAR(String)
236: * @see org.gjt.sp.jedit.jEdit#removePluginJAR(PluginJAR,boolean)
237: * @see org.gjt.sp.jedit.ActionSet
238: * @see org.gjt.sp.jedit.gui.DockableWindowManager
239: * @see org.gjt.sp.jedit.OptionPane
240: * @see org.gjt.sp.jedit.PluginJAR
241: * @see org.gjt.sp.jedit.ServiceManager
242: *
243: * @author Slava Pestov
244: * @author John Gellene (API documentation)
245: * @author Alan Ezust (API documentation)
246: * @since jEdit 2.1pre1
247: */
248: public abstract class EditPlugin {
249: //{{{ start() method
250: /**
251: * jEdit calls this method when the plugin is being activated, either
252: * during startup or at any other time. A plugin can get activated for
253: * a number of reasons:
254: *
255: * <ul>
256: * <li>The plugin is written for jEdit 4.1 or older, in which case it
257: * will always be loaded at startup.</li>
258: * <li>The plugin has its <code>activate</code> property set to
259: * <code>startup</code>, in which case it will always be loaded at
260: * startup.</li>
261: * <li>One of the properties listed in the plugin's
262: * <code>activate</code> property is set to <code>true</code>,
263: * in which case it will always be loaded at startup.</li>
264: * <li>One of the plugin's classes is being accessed by another plugin,
265: * a macro, or a BeanShell snippet in a plugin API XML file.</li>
266: * </ul>
267: *
268: * Note that this method is always called from the event dispatch
269: * thread, even if the activation resulted from a class being loaded
270: * from another thread. A side effect of this is that some of your
271: * plugin's code might get executed before this method finishes
272: * running.<p>
273: *
274: * When this method is being called for plugins written for jEdit 4.1
275: * and below, no views or buffers are open. However, this is not the
276: * case for plugins using the new API. For example, if your plugin adds
277: * tool bars to views, make sure you correctly handle the case where
278: * views are already open when the plugin is loaded.<p>
279: *
280: * If your plugin must be loaded on startup, take care to have this
281: * method return as quickly as possible.<p>
282: *
283: * The default implementation of this method does nothing.
284: *
285: * @since jEdit 2.1pre1
286: */
287: public void start() {
288: }
289:
290: //}}}
291:
292: //{{{ stop() method
293: /**
294: * jEdit calls this method when the plugin is being unloaded. This can
295: * be when the program is exiting, or at any other time.<p>
296: *
297: * If a plugin uses state information or other persistent data
298: * that should be stored in a special format, this would be a good place
299: * to write the data to storage. If the plugin uses jEdit's properties
300: * API to hold settings, no special processing is needed for them on
301: * exit, since they will be saved automatically.<p>
302: *
303: * With plugins written for jEdit 4.1 and below, this method is only
304: * called when the program is exiting. However, this is not the case
305: * for plugins using the new API. For example, if your plugin adds
306: * tool bars to views, make sure you correctly handle the case where
307: * views are still open when the plugin is unloaded.<p>
308: *
309: * To avoid memory leaks, this method should ensure that no references
310: * to any objects created by this plugin remain in the heap. In the
311: * case of actions, dockable windows and services, jEdit ensures this
312: * automatically. For other objects, your plugin must clean up maually.
313: * <p>
314: *
315: * The default implementation of this method does nothing.
316: *
317: * @since jEdit 2.1pre1
318: */
319: public void stop() {
320: } //}}}
321:
322: //{{{ getPluginHome() method
323: /**
324: * Returns the home of your plugin.
325: *
326: * @return the plugin home. It can be null if there is no
327: * settings directory
328: * @since 4.3pre10
329: * @see #getResourceAsStream
330: * @see #getResourceAsOutputStream
331: * @see #getResourcePath
332: */
333: public File getPluginHome() {
334: return getPluginHome(getClassName());
335: } //}}}
336:
337: //{{{ getPluginHome() method
338: /**
339: * <p>Returns the home of the specified plugin.</p>
340: *
341: * <p>Since the first parameter is a reference to the
342: * {@code Class} instance for the plugin,
343: * this method requires the plugin to be activated.</p>
344: *
345: * <p>See {@link #getPluginHome(EditPlugin)} method, as
346: * an alternate, for when the plugin doesn't need
347: * to be activated, or when you do not have the
348: * {@code Class} instance available.</p>
349: *
350: * @param clazz the class of the plugin
351: * @return the plugin home. It can be null if there is no
352: * settings directory
353: * @since 4.3pre10
354: * @see #getPluginHome(EditPlugin)
355: * @see #getResourceAsStream
356: * @see #getResourceAsOutputStream
357: * @see #getResourcePath
358: */
359: public static File getPluginHome(Class<? extends EditPlugin> clazz) {
360: return getPluginHome(clazz.getName());
361: } //}}}
362:
363: //{{{ getPluginHome() method
364: /**
365: * <p>Returns the home of the specified plugin.</p>
366: *
367: * <p>This method doesn't need the plugin to be activated. You can pass
368: * an {@code EditPlugin.Deferred} instance that you get from
369: * {@code jEdit.getPlugin(String)} or {@code jEdit.getPlugins()} if
370: * the plugin in question is not activated yet and this method doesn't
371: * cause the plugin to get activated. If you have a reference to the
372: * plugins {@code Class} instance available, consider using the
373: * {@code Class} method.</p>
374: *
375: * @param plugin the plugin
376: * @return the plugin home. It can be null if there is no settings directory
377: * @since 4.3pre10
378: * @see #getPluginHome(Class)
379: * @see #getResourceAsStream
380: * @see #getResourceAsOutputStream
381: * @see #getResourcePath
382: */
383: public static File getPluginHome(EditPlugin plugin) {
384: return getPluginHome(plugin.getClassName());
385: } //}}}
386:
387: //{{{ getPluginHome() method
388: /**
389: * Returns the home of the specified plugin.
390: *
391: * @param pluginClassName the plugin class name (fully qualified)
392: * @return the plugin home. It can be null if there is no settings directory
393: * @since 4.3pre10
394: * @see #getResourceAsStream
395: * @see #getResourceAsOutputStream
396: * @see #getResourcePath
397: */
398: private static File getPluginHome(String pluginClassName) {
399: String settingsDirectory = jEdit.getSettingsDirectory();
400: if (settingsDirectory == null)
401: return null;
402:
403: File file = new File(settingsDirectory, "plugins");
404: return new File(file, pluginClassName);
405: } //}}}
406:
407: //{{{ getResourceAsStream() method
408: /**
409: * <p>Returns an input stream to the specified resource, or {@code null}
410: * if none is found.</p>
411: *
412: * <p>Since the first parameter is a reference to the
413: * {@code Class} instance for the plugin,
414: * this method requires the plugin to be activated.</p>
415: *
416: * <p>See {@link #getResourceAsStream(EditPlugin,String)} method, as
417: * an alternate, for when the plugin doesn't need
418: * to be activated, or when you do not have the
419: * {@code Class} instance available.</p>
420: *
421: * @param clazz the plugin class
422: * @param path The path to the resource to be returned, relative to
423: * the plugin's resource path.
424: * @return An input stream for the resource, or <code>null</code>.
425: * @since 4.3pre10
426: * @see #getPluginHome
427: * @see #getResourceAsStream(EditPlugin,String)
428: * @see #getResourceAsOutputStream
429: * @see #getResourcePath
430: */
431: public static InputStream getResourceAsStream(
432: Class<? extends EditPlugin> clazz, String path) {
433: return getResourceAsStream(clazz.getName(), path);
434: } //}}}
435:
436: //{{{ getResourceAsStream() method
437: /**
438: * <p>Returns an input stream to the specified resource, or <code>null</code>
439: * if none is found.</p>
440: *
441: * <p>This method doesn't need the plugin to be activated. You can pass
442: * an {@code EditPlugin.Deferred} instance that you get from
443: * {@code jEdit.getPlugin(String)} or {@code jEdit.getPlugins()} if
444: * the plugin in question is not activated yet and this method doesn't
445: * cause the plugin to get activated. If you have a reference to the
446: * plugins {@code Class} instance available, consider using the
447: * {@code Class} method.</p>
448: *
449: * @param plugin the plugin
450: * @param path The path to the resource to be returned, relative to
451: * the plugin's resource path.
452: * @return An input stream for the resource, or <code>null</code>.
453: * @since 4.3pre10
454: * @see #getPluginHome
455: * @see #getResourceAsStream(Class,String)
456: * @see #getResourceAsOutputStream
457: * @see #getResourcePath
458: */
459: public static InputStream getResourceAsStream(EditPlugin plugin,
460: String path) {
461: return getResourceAsStream(plugin.getClassName(), path);
462: } //}}}
463:
464: //{{{ getResourceAsStream() method
465: /**
466: * Returns an input stream to the specified resource, or <code>null</code>
467: * if none is found.
468: *
469: * @param pluginClassName the plugin class name (fully qualified)
470: * @param path The path to the resource to be returned, relative to
471: * the plugin's resource path.
472: * @return An input stream for the resource, or <code>null</code>.
473: * @since 4.3pre10
474: * @see #getPluginHome
475: * @see #getResourceAsOutputStream
476: * @see #getResourcePath
477: */
478: private static InputStream getResourceAsStream(
479: String pluginClassName, String path) {
480: try {
481: File file = getResourcePath(pluginClassName, path);
482: if (file == null || !file.exists())
483: return null;
484: return new FileInputStream(file);
485: } catch (IOException e) {
486: return null;
487: }
488: } //}}}
489:
490: //{{{ getResourceAsOutputStream() method
491: /**
492: * <p>Returns an output stream to the specified resource, or {@code null}
493: * if access to that resource is denied.</p>
494: *
495: * <p>Since the first parameter is a reference to the
496: * {@code Class} instance for the plugin,
497: * this method requires the plugin to be activated.</p>
498: *
499: * <p>See {@link #getResourceAsOutputStream(EditPlugin,String)} method, as
500: * an alternate, for when the plugin doesn't need
501: * to be activated, or when you do not have the
502: * {@code Class} instance available.</p>
503: *
504: * @param clazz the plugin class
505: * @param path The path to the resource to be returned, relative to
506: * the plugin's resource path.
507: * @return An output stream for the resource, or <code>null</code>.
508: * @since 4.3pre10
509: * @see #getPluginHome
510: * @see #getResourceAsOutputStream(EditPlugin,String)
511: * @see #getResourceAsStream
512: * @see #getResourcePath
513: */
514: public static OutputStream getResourceAsOutputStream(
515: Class<? extends EditPlugin> clazz, String path) {
516: return getResourceAsOutputStream(clazz.getName(), path);
517: } //}}}
518:
519: //{{{ getResourceAsOutputStream() method
520: /**
521: * <p>Returns an output stream to the specified resource, or <code>null</node> if access
522: * to that resource is denied.</p>
523: *
524: * <p>This method doesn't need the plugin to be activated. You can pass
525: * an {@code EditPlugin.Deferred} instance that you get from
526: * {@code jEdit.getPlugin(String)} or {@code jEdit.getPlugins()} if
527: * the plugin in question is not activated yet and this method doesn't
528: * cause the plugin to get activated. If you have a reference to the
529: * plugins {@code Class} instance available, consider using the
530: * {@code Class} method.</p>
531: *
532: * @param plugin the plugin
533: * @param path The path to the resource to be returned, relative to
534: * the plugin's resource path.
535: * @return An output stream for the resource, or <code>null</code>.
536: * @since 4.3pre10
537: * @see #getPluginHome
538: * @see #getResourceAsOutputStream(Class,String)
539: * @see #getResourceAsStream
540: * @see #getResourcePath
541: */
542: public static OutputStream getResourceAsOutputStream(
543: EditPlugin plugin, String path) {
544: return getResourceAsOutputStream(plugin.getClassName(), path);
545: } //}}}
546:
547: //{{{ getResourceAsOutputStream() method
548: /**
549: * Returns an output stream to the specified resource, or <code>null</node> if access
550: * to that resource is denied.
551: *
552: * @param pluginClassName the plugin class name (fully qualified)
553: * @param path The path to the resource to be returned, relative to
554: * the plugin's resource path.
555: * @return An output stream for the resource, or <code>null</code>.
556: * @since 4.3pre10
557: * @see #getPluginHome
558: * @see #getResourceAsStream
559: * @see #getResourcePath
560: */
561: private static OutputStream getResourceAsOutputStream(
562: String pluginClassName, String path) {
563: try {
564: File file = getResourcePath(pluginClassName, path);
565: if (file == null)
566: return null;
567: File parentFile = file.getParentFile();
568: if (!parentFile.exists()) {
569: if (!parentFile.mkdirs()) {
570: Log.log(Log.ERROR, EditPlugin.class,
571: "Unable to create folder "
572: + parentFile.getPath());
573: return null;
574: }
575: }
576: return new FileOutputStream(file);
577: } catch (IOException e) {
578: return null;
579: }
580: } //}}}
581:
582: //{{{ getResourcePath() method
583: /**
584: * <p>Returns the full path of the specified plugin resource.</p>
585: *
586: * <p>Since the first parameter is a reference to the
587: * {@code Class} instance for the plugin,
588: * this method requires the plugin to be activated.</p>
589: *
590: * <p>See {@link #getResourcePath(EditPlugin,String)} method, as
591: * an alternate, for when the plugin doesn't need
592: * to be activated, or when you do not have the
593: * {@code Class} instance available.</p>
594: *
595: * @param clazz the plugin class
596: * @param path The relative path to the resource from the plugin's
597: * resource path.
598: * @return The absolute path to the resource or null if there is no plugin home.
599: * @since 4.3pre10
600: * @see #getPluginHome
601: * @see #getResourceAsOutputStream
602: * @see #getResourceAsStream
603: * @see #getResourcePath(EditPlugin,String)
604: */
605: public static File getResourcePath(
606: Class<? extends EditPlugin> clazz, String path) {
607: return getResourcePath(clazz.getName(), path);
608: } //}}}
609:
610: //{{{ getResourcePath() method
611: /**
612: * <p>Returns the full path of the specified plugin resource.</p>
613: *
614: * <p>This method doesn't need the plugin to be activated. You can pass
615: * an {@code EditPlugin.Deferred} instance that you get from
616: * {@code jEdit.getPlugin(String)} or {@code jEdit.getPlugins()} if
617: * the plugin in question is not activated yet and this method doesn't
618: * cause the plugin to get activated. If you have a reference to the
619: * plugins {@code Class} instance available, consider using the
620: * {@code Class} method.</p>
621: *
622: * @param plugin the plugin
623: * @param path The relative path to the resource from the plugin's
624: * resource path.
625: * @return The absolute path to the resource or null if there is no plugin home.
626: * @since 4.3pre10
627: * @see #getPluginHome
628: * @see #getResourceAsOutputStream
629: * @see #getResourceAsStream
630: * @see #getResourcePath(Class,String)
631: */
632: public static File getResourcePath(EditPlugin plugin, String path) {
633: return getResourcePath(plugin.getClassName(), path);
634: } //}}}
635:
636: //{{{ getResourcePath() method
637: /**
638: * Returns the full path of the specified plugin resource.
639: *
640: * @param pluginClassName the plugin class name (fully qualified)
641: * @param path The relative path to the resource from the plugin's
642: * resource path.
643: * @return The absolute path to the resource or null if there is no plugin home.
644: * @since 4.3pre10
645: * @see #getPluginHome
646: * @see #getResourceAsOutputStream
647: * @see #getResourceAsStream
648: */
649: private static File getResourcePath(String pluginClassName,
650: String path) {
651: File home = getPluginHome(pluginClassName);
652: if (home == null)
653: return null;
654: return new File(home, path);
655: } //}}}
656:
657: //{{{ getClassName() method
658: /**
659: * Returns the plugin's class name. This might not be the same as
660: * the class of the actual <code>EditPlugin</code> instance, for
661: * example if the plugin is not loaded yet.
662: *
663: * @since jEdit 2.5pre3
664: */
665: public String getClassName() {
666: return getClass().getName();
667: } //}}}
668:
669: //{{{ getPluginJAR() method
670: /**
671: * Returns the JAR file containing this plugin.
672: * @since jEdit 4.2pre1
673: */
674: public PluginJAR getPluginJAR() {
675: return jar;
676: } //}}}
677:
678: //{{{ createMenuItems() method
679: /**
680: * Called by the view when constructing its <b>Plugins</b> menu.
681: * See the description of this class for details about how the
682: * menu items are constructed from plugin properties.
683: *
684: * @since jEdit 4.2pre1
685: */
686: public final JMenuItem createMenuItems() {
687: if (this instanceof Broken)
688: return null;
689:
690: String menuItemName = jEdit.getProperty("plugin."
691: + getClassName() + ".menu-item");
692: if (menuItemName != null)
693: return GUIUtilities.loadMenuItem(menuItemName);
694:
695: String menuProperty = "plugin." + getClassName() + ".menu";
696: String codeProperty = "plugin." + getClassName() + ".menu.code";
697: if (jEdit.getProperty(menuProperty) != null
698: || jEdit.getProperty(codeProperty) != null) {
699: String pluginName = jEdit.getProperty("plugin."
700: + getClassName() + ".name");
701: return new EnhancedMenu(menuProperty, pluginName);
702: }
703:
704: return null;
705: } //}}}
706:
707: //{{{ createBrowserMenuItems() method
708: /**
709: * Called by the filesystem browser when constructing its
710: * <b>Plugins</b> menu.
711: * See the description of this class for details about how the
712: * menu items are constructed from plugin properties.
713: *
714: * @since jEdit 4.2pre1
715: */
716: public final JMenuItem createBrowserMenuItems() {
717: if (this instanceof Broken)
718: return null;
719:
720: String menuItemName = jEdit.getProperty("plugin."
721: + getClassName() + ".browser-menu-item");
722: if (menuItemName != null) {
723: return GUIUtilities.loadMenuItem(VFSBrowser
724: .getActionContext(), menuItemName, false);
725: }
726:
727: String menuProperty = "plugin." + getClassName()
728: + ".browser-menu";
729: if (jEdit.getProperty(menuProperty) != null) {
730: String pluginName = jEdit.getProperty("plugin."
731: + getClassName() + ".name");
732: return new EnhancedMenu(menuProperty, pluginName,
733: VFSBrowser.getActionContext());
734: }
735:
736: return null;
737: } //}}}
738:
739: //{{{ Deprecated methods
740:
741: //{{{ createMenuItems() method
742: /**
743: * @deprecated Instead of overriding this method, define properties
744: * as specified in the description of this class.
745: */
746: public void createMenuItems(Vector menuItems) {
747: } //}}}
748:
749: //{{{ createOptionPanes() method
750: /**
751: * @deprecated Instead of overriding this method, define properties
752: * as specified in the description of this class.
753: */
754: public void createOptionPanes(OptionsDialog optionsDialog) {
755: } //}}}
756:
757: //}}}
758:
759: //{{{ Package-private members
760: PluginJAR jar;
761:
762: //}}}
763:
764: //{{{ Broken class
765: /**
766: * A placeholder for a plugin that didn't load.
767: * @see jEdit#getPlugin(String)
768: * @see PluginJAR#getPlugin()
769: * @see PluginJAR#activatePlugin()
770: */
771: public static class Broken extends EditPlugin {
772: public String getClassName() {
773: return clazz;
774: }
775:
776: // package-private members
777: Broken(PluginJAR jar, String clazz) {
778: this .jar = jar;
779: this .clazz = clazz;
780: }
781:
782: // private members
783: private String clazz;
784: } //}}}
785:
786: //{{{ Deferred class
787: /**
788: * A placeholder for a plugin that hasn't been loaded yet.
789: * @see jEdit#getPlugin(String)
790: * @see PluginJAR#getPlugin()
791: * @see PluginJAR#activatePlugin()
792: */
793: public static class Deferred extends EditPlugin {
794: public String getClassName() {
795: return clazz;
796: }
797:
798: // package-private members
799: Deferred(PluginJAR jar, String clazz) {
800: this .jar = jar;
801: this .clazz = clazz;
802: }
803:
804: EditPlugin loadPluginClass() {
805: return null;
806: }
807:
808: public String toString() {
809: return "Deferred[" + clazz + ']';
810: }
811:
812: // private members
813: private String clazz;
814: } //}}}
815: }
|