001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.plugin;
011:
012: import java.io.BufferedReader;
013: import java.io.File;
014: import java.io.IOException;
015: import java.io.InputStream;
016: import java.io.InputStreamReader;
017: import java.net.MalformedURLException;
018: import java.net.URL;
019:
020: import org.eclipse.core.runtime.CoreException;
021: import org.eclipse.core.runtime.IPath;
022: import org.eclipse.core.runtime.IPluginDescriptor;
023: import org.eclipse.core.runtime.Platform;
024: import org.eclipse.core.runtime.Plugin;
025: import org.eclipse.core.runtime.preferences.InstanceScope;
026: import org.eclipse.jface.dialogs.DialogSettings;
027: import org.eclipse.jface.dialogs.IDialogSettings;
028: import org.eclipse.jface.preference.IPreferenceStore;
029: import org.eclipse.jface.resource.ImageDescriptor;
030: import org.eclipse.jface.resource.ImageRegistry;
031: import org.eclipse.swt.SWT;
032: import org.eclipse.swt.SWTError;
033: import org.eclipse.swt.widgets.Display;
034: import org.eclipse.ui.IWorkbench;
035: import org.eclipse.ui.PlatformUI;
036: import org.eclipse.ui.internal.WWinPluginAction;
037: import org.eclipse.ui.internal.util.BundleUtility;
038: import org.eclipse.ui.preferences.ScopedPreferenceStore;
039: import org.osgi.framework.Bundle;
040: import org.osgi.framework.BundleContext;
041: import org.osgi.framework.BundleEvent;
042: import org.osgi.framework.BundleListener;
043:
044: /**
045: * Abstract base class for plug-ins that integrate with the Eclipse platform UI.
046: * <p>
047: * Subclasses obtain the following capabilities:
048: * </p>
049: * <p>
050: * Preferences
051: * <ul>
052: * <li> The platform core runtime contains general support for plug-in
053: * preferences (<code>org.eclipse.core.runtime.Preferences</code>).
054: * This class provides appropriate conversion to the older JFace preference
055: * API (<code>org.eclipse.jface.preference.IPreferenceStore</code>).</li>
056: * <li> The method <code>getPreferenceStore</code> returns the JFace preference
057: * store (cf. <code>Plugin.getPluginPreferences</code> which returns
058: * a core runtime preferences object.</li>
059: * <li> Subclasses may reimplement <code>initializeDefaultPreferences</code>
060: * to set up any default values for preferences using JFace API. In this
061: * case, <code>initializeDefaultPluginPreferences</code> should not be
062: * overridden.</li>
063: * <li> Subclasses may reimplement
064: * <code>initializeDefaultPluginPreferences</code> to set up any default
065: * values for preferences using core runtime API. In this
066: * case, <code>initializeDefaultPreferences</code> should not be
067: * overridden.</li>
068: * <li> Preferences are also saved automatically on plug-in shutdown.
069: * However, saving preferences immediately after changing them is
070: * strongly recommended, since that ensures that preference settings
071: * are not lost even in the event of a platform crash.</li>
072: * </ul>
073: * Dialogs
074: * <ul>
075: * <li> The dialog store is read the first time <code>getDialogSettings</code>
076: * is called.</li>
077: * <li> The dialog store allows the plug-in to "record" important choices made
078: * by the user in a wizard or dialog, so that the next time the
079: * wizard/dialog is used the widgets can be defaulted to better values. A
080: * wizard could also use it to record the last 5 values a user entered into
081: * an editable combo - to show "recent values". </li>
082: * <li> The dialog store is found in the file whose name is given by the
083: * constant <code>FN_DIALOG_STORE</code>. A dialog store file is first
084: * looked for in the plug-in's read/write state area; if not found there,
085: * the plug-in's install directory is checked.
086: * This allows a plug-in to ship with a read-only copy of a dialog store
087: * file containing initial values for certain settings.</li>
088: * <li> Plug-in code can call <code>saveDialogSettings</code> to cause settings to
089: * be saved in the plug-in's read/write state area. A plug-in may opt to do
090: * this each time a wizard or dialog is closed to ensure the latest
091: * information is always safe on disk. </li>
092: * <li> Dialog settings are also saved automatically on plug-in shutdown.</li>
093: * </ul>
094: * Images
095: * <ul>
096: * <li> A typical UI plug-in will have some images that are used very frequently
097: * and so need to be cached and shared. The plug-in's image registry
098: * provides a central place for a plug-in to store its common images.
099: * Images managed by the registry are created lazily as needed, and will be
100: * automatically disposed of when the plug-in shuts down. Note that the
101: * number of registry images should be kept to a minimum since many OSs
102: * have severe limits on the number of images that can be in memory at once.
103: * </ul>
104: * <p>
105: * For easy access to your plug-in object, use the singleton pattern. Declare a
106: * static variable in your plug-in class for the singleton. Store the first
107: * (and only) instance of the plug-in class in the singleton when it is created.
108: * Then access the singleton when needed through a static <code>getDefault</code>
109: * method.
110: * </p>
111: * <p>
112: * See the description on {@link Plugin}.
113: * </p>
114: */
115: public abstract class AbstractUIPlugin extends Plugin {
116:
117: /**
118: * The name of the dialog settings file (value
119: * <code>"dialog_settings.xml"</code>).
120: */
121: private static final String FN_DIALOG_SETTINGS = "dialog_settings.xml"; //$NON-NLS-1$
122:
123: /**
124: * Storage for dialog and wizard data; <code>null</code> if not yet
125: * initialized.
126: */
127: private DialogSettings dialogSettings = null;
128:
129: /**
130: * Storage for preferences.
131: */
132: private ScopedPreferenceStore preferenceStore;
133:
134: /**
135: * The registry for all graphic images; <code>null</code> if not yet
136: * initialized.
137: */
138: private ImageRegistry imageRegistry = null;
139:
140: /**
141: * The bundle listener used for kicking off refreshPluginActions().
142: *
143: * @since 3.0.1
144: */
145: private BundleListener bundleListener;
146:
147: /**
148: * Creates an abstract UI plug-in runtime object for the given plug-in
149: * descriptor.
150: * <p>
151: * Note that instances of plug-in runtime classes are automatically created
152: * by the platform in the course of plug-in activation.
153: * <p>
154: *
155: * @param descriptor the plug-in descriptor
156: * @see Plugin#Plugin(org.eclipse.core.runtime.IPluginDescriptor descriptor)
157: * @deprecated
158: * In Eclipse 3.0 this constructor has been replaced by
159: * {@link #AbstractUIPlugin()}. Implementations of
160: * <code>MyPlugin(IPluginDescriptor descriptor)</code> should be changed to
161: * <code>MyPlugin()</code> and call <code>super()</code> instead of
162: * <code>super(descriptor)</code>.
163: * The <code>MyPlugin(IPluginDescriptor descriptor)</code> constructor is
164: * called only for plug-ins which explicitly require the
165: * org.eclipse.core.runtime.compatibility plug-in (or, as in this case,
166: * subclasses which might).
167: */
168: public AbstractUIPlugin(IPluginDescriptor descriptor) {
169: super (descriptor);
170: }
171:
172: /**
173: * Creates an abstract UI plug-in runtime object.
174: * <p>
175: * Plug-in runtime classes are <code>BundleActivators</code> and so must
176: * have an default constructor. This method is called by the runtime when
177: * the associated bundle is being activated.
178: * <p>
179: * For more details, see <code>Plugin</code>'s default constructor.
180: *
181: * @see Plugin#Plugin()
182: * @since 3.0
183: */
184: public AbstractUIPlugin() {
185: super ();
186: }
187:
188: /**
189: * Returns a new image registry for this plugin-in. The registry will be
190: * used to manage images which are frequently used by the plugin-in.
191: * <p>
192: * The default implementation of this method creates an empty registry.
193: * Subclasses may override this method if needed.
194: * </p>
195: *
196: * @return ImageRegistry the resulting registry.
197: * @see #getImageRegistry
198: */
199: protected ImageRegistry createImageRegistry() {
200:
201: //If we are in the UI Thread use that
202: if (Display.getCurrent() != null) {
203: return new ImageRegistry(Display.getCurrent());
204: }
205:
206: if (PlatformUI.isWorkbenchRunning()) {
207: return new ImageRegistry(PlatformUI.getWorkbench()
208: .getDisplay());
209: }
210:
211: //Invalid thread access if it is not the UI Thread
212: //and the workbench is not created.
213: throw new SWTError(SWT.ERROR_THREAD_INVALID_ACCESS);
214: }
215:
216: /**
217: * Returns the dialog settings for this UI plug-in.
218: * The dialog settings is used to hold persistent state data for the various
219: * wizards and dialogs of this plug-in in the context of a workbench.
220: * <p>
221: * If an error occurs reading the dialog store, an empty one is quietly created
222: * and returned.
223: * </p>
224: * <p>
225: * Subclasses may override this method but are not expected to.
226: * </p>
227: *
228: * @return the dialog settings
229: */
230: public IDialogSettings getDialogSettings() {
231: if (dialogSettings == null) {
232: loadDialogSettings();
233: }
234: return dialogSettings;
235: }
236:
237: /**
238: * Returns the image registry for this UI plug-in.
239: * <p>
240: * The image registry contains the images used by this plug-in that are very
241: * frequently used and so need to be globally shared within the plug-in. Since
242: * many OSs have a severe limit on the number of images that can be in memory at
243: * any given time, a plug-in should only keep a small number of images in their
244: * registry.
245: * <p>
246: * Subclasses should reimplement <code>initializeImageRegistry</code> if they have
247: * custom graphic images to load.
248: * </p>
249: * <p>
250: * Subclasses may override this method but are not expected to.
251: * </p>
252: *
253: * @return the image registry
254: */
255: public ImageRegistry getImageRegistry() {
256: if (imageRegistry == null) {
257: imageRegistry = createImageRegistry();
258: initializeImageRegistry(imageRegistry);
259: }
260: return imageRegistry;
261: }
262:
263: /**
264: * Returns the preference store for this UI plug-in.
265: * This preference store is used to hold persistent settings for this plug-in in
266: * the context of a workbench. Some of these settings will be user controlled,
267: * whereas others may be internal setting that are never exposed to the user.
268: * <p>
269: * If an error occurs reading the preference store, an empty preference store is
270: * quietly created, initialized with defaults, and returned.
271: * </p>
272: * <p>
273: * <strong>NOTE:</strong> As of Eclipse 3.1 this method is
274: * no longer referring to the core runtime compatibility layer and so
275: * plug-ins relying on Plugin#initializeDefaultPreferences
276: * will have to access the compatibility layer themselves.
277: * </p>
278: *
279: * @return the preference store
280: */
281: public IPreferenceStore getPreferenceStore() {
282: // Create the preference store lazily.
283: if (preferenceStore == null) {
284: preferenceStore = new ScopedPreferenceStore(
285: new InstanceScope(), getBundle().getSymbolicName());
286:
287: }
288: return preferenceStore;
289: }
290:
291: /**
292: * Returns the Platform UI workbench.
293: * <p>
294: * This method exists as a convenience for plugin implementors. The
295: * workbench can also be accessed by invoking <code>PlatformUI.getWorkbench()</code>.
296: * </p>
297: * @return IWorkbench the workbench for this plug-in
298: */
299: public IWorkbench getWorkbench() {
300: return PlatformUI.getWorkbench();
301: }
302:
303: /**
304: * Initializes a preference store with default preference values
305: * for this plug-in.
306: * <p>
307: * This method is called after the preference store is initially loaded
308: * (default values are never stored in preference stores).
309: * </p>
310: * <p>
311: * The default implementation of this method does nothing.
312: * Subclasses should reimplement this method if the plug-in has any preferences.
313: * </p>
314: * <p>
315: * A subclass may reimplement this method to set default values for the
316: * preference store using JFace API. This is the older way of initializing
317: * default values. If this method is reimplemented, do not override
318: * <code>initializeDefaultPluginPreferences()</code>.
319: * </p>
320: *
321: * @param store the preference store to fill
322: *
323: * @deprecated this is only called if the runtime compatibility layer is
324: * present. See {@link #initializeDefaultPluginPreferences}.
325: */
326: protected void initializeDefaultPreferences(IPreferenceStore store) {
327: // spec'ed to do nothing
328: }
329:
330: /**
331: * The <code>AbstractUIPlugin</code> implementation of this
332: * <code>Plugin</code> method forwards to
333: * <code>initializeDefaultPreferences(IPreferenceStore)</code>.
334: * <p>
335: * A subclass may reimplement this method to set default values for the core
336: * runtime preference store in the standard way. This is the recommended way
337: * to do this. The older
338: * <code>initializeDefaultPreferences(IPreferenceStore)</code> method
339: * serves a similar purpose. If this method is reimplemented, do not send
340: * super, and do not override
341: * <code>initializeDefaultPreferences(IPreferenceStore)</code>.
342: * </p>
343: *
344: * @deprecated this is only called if the runtime compatibility layer is
345: * present. See the deprecated comment in
346: * {@link Plugin#initializeDefaultPluginPreferences}.
347: *
348: * @see #initializeDefaultPreferences
349: * @since 2.0
350: */
351: protected void initializeDefaultPluginPreferences() {
352: // N.B. by the time this method is called, the plug-in has a
353: // core runtime preference store (no default values)
354:
355: // call loadPreferenceStore (only) for backwards compatibility with Eclipse 1.0
356: loadPreferenceStore();
357: // call initializeDefaultPreferences (only) for backwards compatibility
358: // with Eclipse 1.0
359: initializeDefaultPreferences(getPreferenceStore());
360: }
361:
362: /**
363: * Initializes an image registry with images which are frequently used by the
364: * plugin.
365: * <p>
366: * The image registry contains the images used by this plug-in that are very
367: * frequently used and so need to be globally shared within the plug-in. Since
368: * many OSs have a severe limit on the number of images that can be in memory
369: * at any given time, each plug-in should only keep a small number of images in
370: * its registry.
371: * </p><p>
372: * Implementors should create a JFace image descriptor for each frequently used
373: * image. The descriptors describe how to create/find the image should it be needed.
374: * The image described by the descriptor is not actually allocated until someone
375: * retrieves it.
376: * </p><p>
377: * Subclasses may override this method to fill the image registry.
378: * </p>
379: * @param reg the registry to initalize
380: *
381: * @see #getImageRegistry
382: */
383: protected void initializeImageRegistry(ImageRegistry reg) {
384: // spec'ed to do nothing
385: }
386:
387: /**
388: * Loads the dialog settings for this plug-in.
389: * The default implementation first looks for a standard named file in the
390: * plug-in's read/write state area; if no such file exists, the plug-in's
391: * install directory is checked to see if one was installed with some default
392: * settings; if no file is found in either place, a new empty dialog settings
393: * is created. If a problem occurs, an empty settings is silently used.
394: * <p>
395: * This framework method may be overridden, although this is typically
396: * unnecessary.
397: * </p>
398: */
399: protected void loadDialogSettings() {
400: dialogSettings = new DialogSettings("Workbench"); //$NON-NLS-1$
401:
402: // bug 69387: The instance area should not be created (in the call to
403: // #getStateLocation) if -data @none or -data @noDefault was used
404: IPath dataLocation = getStateLocationOrNull();
405: if (dataLocation != null) {
406: // try r/w state area in the local file system
407: String readWritePath = dataLocation.append(
408: FN_DIALOG_SETTINGS).toOSString();
409: File settingsFile = new File(readWritePath);
410: if (settingsFile.exists()) {
411: try {
412: dialogSettings.load(readWritePath);
413: } catch (IOException e) {
414: // load failed so ensure we have an empty settings
415: dialogSettings = new DialogSettings("Workbench"); //$NON-NLS-1$
416: }
417:
418: return;
419: }
420: }
421:
422: // otherwise look for bundle specific dialog settings
423: URL dsURL = BundleUtility.find(getBundle(), FN_DIALOG_SETTINGS);
424: if (dsURL == null) {
425: return;
426: }
427:
428: InputStream is = null;
429: try {
430: is = dsURL.openStream();
431: BufferedReader reader = new BufferedReader(
432: new InputStreamReader(is, "utf-8")); //$NON-NLS-1$
433: dialogSettings.load(reader);
434: } catch (IOException e) {
435: // load failed so ensure we have an empty settings
436: dialogSettings = new DialogSettings("Workbench"); //$NON-NLS-1$
437: } finally {
438: try {
439: if (is != null) {
440: is.close();
441: }
442: } catch (IOException e) {
443: // do nothing
444: }
445: }
446: }
447:
448: /**
449: * Loads the preference store for this plug-in.
450: * The default implementation looks for a standard named file in the
451: * plug-in's read/write state area. If no file is found or a problem
452: * occurs, a new empty preference store is silently created.
453: * <p>
454: * This framework method may be overridden, although this is typically
455: * unnecessary.
456: * </p>
457: *
458: * @deprecated As of Eclipse 2.0, a basic preference store exists for all
459: * plug-ins. This method now exists only for backwards compatibility.
460: * It is called as the plug-in's preference store is being initialized.
461: * The plug-ins preferences are loaded from the file regardless of what
462: * this method does.
463: */
464: protected void loadPreferenceStore() {
465: // do nothing by default
466: }
467:
468: /**
469: * Refreshes the actions for the plugin.
470: * This method is called from <code>startup</code>.
471: * <p>
472: * This framework method may be overridden, although this is typically
473: * unnecessary.
474: * </p>
475: */
476: protected void refreshPluginActions() {
477: // If the workbench is not started yet, or is no longer running, do nothing.
478: if (!PlatformUI.isWorkbenchRunning()) {
479: return;
480: }
481:
482: // startup() is not guaranteed to be called in the UI thread,
483: // but refreshPluginActions must run in the UI thread,
484: // so use asyncExec. See bug 6623 for more details.
485: Display.getDefault().asyncExec(new Runnable() {
486: public void run() {
487: WWinPluginAction.refreshActionList();
488: }
489: });
490: }
491:
492: /**
493: * Saves this plug-in's dialog settings.
494: * Any problems which arise are silently ignored.
495: */
496: protected void saveDialogSettings() {
497: if (dialogSettings == null) {
498: return;
499: }
500:
501: try {
502: IPath path = getStateLocationOrNull();
503: if (path == null) {
504: return;
505: }
506: String readWritePath = path.append(FN_DIALOG_SETTINGS)
507: .toOSString();
508: dialogSettings.save(readWritePath);
509: } catch (IOException e) {
510: // spec'ed to ignore problems
511: } catch (IllegalStateException e) {
512: // spec'ed to ignore problems
513: }
514: }
515:
516: /**
517: * Saves this plug-in's preference store.
518: * Any problems which arise are silently ignored.
519: *
520: * @see Plugin#savePluginPreferences()
521: * @deprecated As of Eclipse 2.0, preferences exist for all plug-ins. The
522: * equivalent of this method is <code>Plugin.savePluginPreferences</code>.
523: * This method now calls <code>savePluginPreferences</code>, and exists only for
524: * backwards compatibility.
525: */
526: protected void savePreferenceStore() {
527: savePluginPreferences();
528: }
529:
530: /**
531: * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
532: * method does nothing. Subclasses may extend this method, but must send
533: * super first.
534: * <p>
535: * WARNING: Plug-ins may not be started in the UI thread.
536: * The <code>startup()</code> method should not assume that its code runs in
537: * the UI thread, otherwise SWT thread exceptions may occur on startup.'
538: * @deprecated
539: * In Eclipse 3.0, <code>startup</code> has been replaced by {@link Plugin#start(BundleContext context)}.
540: * Implementations of <code>startup</code> should be changed to extend
541: * <code>start(BundleContext context)</code> and call <code>super.start(context)</code>
542: * instead of <code>super.startup()</code>. Like <code>super.startup()</code>,
543: * <code>super.stop(context)</code> must be called as the very first thing.
544: * The <code>startup</code> method is called only for plug-ins which explicitly require the
545: * org.eclipse.core.runtime.compatibility plug-in; in contrast,
546: * the <code>start</code> method is always called.
547: */
548: public void startup() throws CoreException {
549: // this method no longer does anything
550: // the code that used to be here in 2.1 has moved to start(BundleContext)
551: super .startup();
552: }
553:
554: /**
555: * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
556: * method does nothing. Subclasses may extend this method, but must send
557: * super first.
558: * @deprecated
559: * In Eclipse 3.0, <code>shutdown</code> has been replaced by {@link Plugin#stop(BundleContext context)}.
560: * Implementations of <code>shutdown</code> should be changed to extend
561: * <code>stop(BundleContext context)</code> and call <code>super.stop(context)</code>
562: * instead of <code>super.shutdown()</code>. Unlike <code>super.shutdown()</code>,
563: * <code>super.stop(context)</code> must be called as the very <b>last</b> thing rather
564: * than as the very first thing. The <code>shutdown</code> method is called
565: * only for plug-ins which explicitly require the
566: * org.eclipse.core.runtime.compatibility plug-in;
567: * in contrast, the <code>stop</code> method is always called.
568: */
569: public void shutdown() throws CoreException {
570: // this method no longer does anything interesting
571: // the code that used to be here in 2.1 has moved to stop(BundleContext),
572: // which is called regardless of whether the plug-in being instantiated
573: // requires org.eclipse.core.runtime.compatibility
574: super .shutdown();
575: }
576:
577: /**
578: * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
579: * method refreshes the plug-in actions. Subclasses may extend this method,
580: * but must send super <b>first</b>.
581: * {@inheritDoc}
582: *
583: * @since 3.0
584: */
585: public void start(BundleContext context) throws Exception {
586: super .start(context);
587: final BundleContext fc = context;
588: // Should only attempt refreshPluginActions() once the bundle
589: // has been fully started. Otherwise, action delegates
590: // can be created while in the process of creating
591: // a triggering action delegate (if UI events are processed during startup).
592: // Also, if the start throws an exception, the bundle will be shut down.
593: // We don't want to have created any delegates if this happens.
594: // See bug 63324 for more details.
595: bundleListener = new BundleListener() {
596: public void bundleChanged(BundleEvent event) {
597: if (event.getBundle() == getBundle()) {
598: if (event.getType() == BundleEvent.STARTED) {
599: // We're getting notified that the bundle has been started.
600: // Make sure it's still active. It may have been shut down between
601: // the time this event was queued and now.
602: if (getBundle().getState() == Bundle.ACTIVE) {
603: refreshPluginActions();
604: }
605: fc.removeBundleListener(this );
606: }
607: }
608: }
609: };
610: context.addBundleListener(bundleListener);
611: // bundleListener is removed in stop(BundleContext)
612: }
613:
614: /**
615: * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
616: * method saves this plug-in's preference and dialog stores and shuts down
617: * its image registry (if they are in use). Subclasses may extend this
618: * method, but must send super <b>last</b>. A try-finally statement should
619: * be used where necessary to ensure that <code>super.shutdown()</code> is
620: * always done.
621: * {@inheritDoc}
622: *
623: * @since 3.0
624: */
625: public void stop(BundleContext context) throws Exception {
626: try {
627: if (bundleListener != null) {
628: context.removeBundleListener(bundleListener);
629: }
630: saveDialogSettings();
631: savePreferenceStore();
632: preferenceStore = null;
633: if (imageRegistry != null)
634: imageRegistry.dispose();
635: imageRegistry = null;
636: } finally {
637: super .stop(context);
638: }
639: }
640:
641: /**
642: * Creates and returns a new image descriptor for an image file located
643: * within the specified plug-in.
644: * <p>
645: * This is a convenience method that simply locates the image file in
646: * within the plug-in (no image registries are involved). The path is
647: * relative to the root of the plug-in, and takes into account files
648: * coming from plug-in fragments. The path may include $arg$ elements.
649: * However, the path must not have a leading "." or path separator.
650: * Clients should use a path like "icons/mysample.gif" rather than
651: * "./icons/mysample.gif" or "/icons/mysample.gif".
652: * </p>
653: *
654: * @param pluginId the id of the plug-in containing the image file;
655: * <code>null</code> is returned if the plug-in does not exist
656: * @param imageFilePath the relative path of the image file, relative to the
657: * root of the plug-in; the path must be legal
658: * @return an image descriptor, or <code>null</code> if no image
659: * could be found
660: * @since 3.0
661: */
662: public static ImageDescriptor imageDescriptorFromPlugin(
663: String pluginId, String imageFilePath) {
664: if (pluginId == null || imageFilePath == null) {
665: throw new IllegalArgumentException();
666: }
667:
668: // if the bundle is not ready then there is no image
669: Bundle bundle = Platform.getBundle(pluginId);
670: if (!BundleUtility.isReady(bundle)) {
671: return null;
672: }
673:
674: // look for the image (this will check both the plugin and fragment folders
675: URL fullPathString = BundleUtility.find(bundle, imageFilePath);
676: if (fullPathString == null) {
677: try {
678: fullPathString = new URL(imageFilePath);
679: } catch (MalformedURLException e) {
680: return null;
681: }
682: }
683:
684: if (fullPathString == null) {
685: return null;
686: }
687: return ImageDescriptor.createFromURL(fullPathString);
688: }
689:
690: /**
691: * FOR INTERNAL WORKBENCH USE ONLY.
692: *
693: * Returns the path to a location in the file system that can be used
694: * to persist/restore state between workbench invocations.
695: * If the location did not exist prior to this call it will be created.
696: * Returns <code>null</code> if no such location is available.
697: *
698: * @return path to a location in the file system where this plug-in can
699: * persist data between sessions, or <code>null</code> if no such
700: * location is available.
701: * @since 3.1
702: */
703: private IPath getStateLocationOrNull() {
704: try {
705: return getStateLocation();
706: } catch (IllegalStateException e) {
707: // This occurs if -data=@none is explicitly specified, so ignore this silently.
708: // Is this OK? See bug 85071.
709: return null;
710: }
711: }
712:
713: }
|