Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/ui/plugin/AbstractUIPlugin.java


1   /*******************************************************************************
2    * Copyright (c) 2000, 2004 IBM Corporation and others.
3    * All rights reserved. This program and the accompanying materials 
4    * are made available under the terms of the Common Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/cpl-v10.html
7    * 
8    * Contributors:
9    *     IBM Corporation - initial API and implementation
10   *******************************************************************************/
11  package org.eclipse.ui.plugin;
12  
13  import java.io.BufferedReader;
14  import java.io.File;
15  import java.io.IOException;
16  import java.io.InputStream;
17  import java.io.InputStreamReader;
18  import java.net.MalformedURLException;
19  import java.net.URL;
20  import org.eclipse.core.runtime.CoreException;
21  import org.eclipse.core.runtime.Platform;
22  import org.eclipse.core.runtime.Plugin;
23  import org.eclipse.core.runtime.Preferences;
24  import org.eclipse.jface.dialogs.DialogSettings;
25  import org.eclipse.jface.dialogs.IDialogSettings;
26  import org.eclipse.jface.preference.IPersistentPreferenceStore;
27  import org.eclipse.jface.preference.IPreferenceStore;
28  import org.eclipse.jface.resource.ImageDescriptor;
29  import org.eclipse.jface.resource.ImageRegistry;
30  import org.eclipse.jface.resource.JFaceResources;
31  import org.eclipse.jface.util.IPropertyChangeListener;
32  import org.eclipse.jface.util.ListenerList;
33  import org.eclipse.jface.util.PropertyChangeEvent;
34  import org.eclipse.jface.util.SafeRunnable;
35  import org.eclipse.swt.widgets.Display;
36  import org.eclipse.ui.IWorkbench;
37  import org.eclipse.ui.PlatformUI;
38  import org.eclipse.ui.internal.WWinPluginAction;
39  import org.eclipse.ui.internal.Workbench;
40  import org.eclipse.ui.internal.util.BundleUtility;
41  import org.osgi.framework.Bundle;
42  import org.osgi.framework.BundleContext;
43  import org.osgi.framework.BundleEvent;
44  import org.osgi.framework.BundleListener;
45  
46  /**
47   * Abstract base class for plug-ins that integrate with the Eclipse platform UI.
48   * <p>
49   * Subclasses obtain the following capabilities:
50   * </p>
51   * <p>
52   * Preferences
53   * <ul>
54   * <li> The platform core runtime contains general support for plug-in
55   *      preferences (<code>org.eclipse.core.runtime.Preferences</code>). 
56   *      This class provides appropriate conversion to the older JFace preference 
57   *      API (<code>org.eclipse.jface.preference.IPreferenceStore</code>).</li>
58   * <li> The method <code>getPreferenceStore</code> returns the JFace preference
59   *      store (cf. <code>Plugin.getPluginPreferences</code> which returns
60   *      a core runtime preferences object.</li>
61   * <li> Subclasses may reimplement <code>initializeDefaultPreferences</code>
62   *      to set up any default values for preferences using JFace API. In this
63   *      case, <code>initializeDefaultPluginPreferences</code> should not be
64   *      overridden.</li>
65   * <li> Subclasses may reimplement
66   *      <code>initializeDefaultPluginPreferences</code> to set up any default
67   *      values for preferences using core runtime API. In this
68   *      case, <code>initializeDefaultPreferences</code> should not be
69   *      overridden.</li>
70   * <li> Preferences are also saved automatically on plug-in shutdown.
71   *      However, saving preferences immediately after changing them is
72   *      strongly recommended, since that ensures that preference settings
73   *      are not lost even in the event of a platform crash.</li>
74   * </ul>
75   * Dialogs
76   * <ul>
77   * <li> The dialog store is read the first time <code>getDialogSettings</code> 
78   *      is called.</li>
79   * <li> The dialog store allows the plug-in to "record" important choices made
80   *      by the user in a wizard or dialog, so that the next time the
81   *      wizard/dialog is used the widgets can be defaulted to better values. A
82   *      wizard could also use it to record the last 5 values a user entered into
83   *      an editable combo - to show "recent values". </li>
84   * <li> The dialog store is found in the file whose name is given by the
85   *      constant <code>FN_DIALOG_STORE</code>. A dialog store file is first
86   *      looked for in the plug-in's read/write state area; if not found there,
87   *      the plug-in's install directory is checked.
88   *      This allows a plug-in to ship with a read-only copy of a dialog store
89   *      file containing initial values for certain settings.</li>
90   * <li> Plug-in code can call <code>saveDialogSettings</code> to cause settings to
91   *      be saved in the plug-in's read/write state area. A plug-in may opt to do
92   *      this each time a wizard or dialog is closed to ensure the latest 
93   *      information is always safe on disk. </li>
94   * <li> Dialog settings are also saved automatically on plug-in shutdown.</li>
95   * </ul>
96   * Images
97   * <ul>
98   * <li> A typical UI plug-in will have some images that are used very frequently
99   *      and so need to be cached and shared.  The plug-in's image registry 
100  *      provides a central place for a plug-in to store its common images. 
101  *      Images managed by the registry are created lazily as needed, and will be
102  *      automatically disposed of when the plug-in shuts down. Note that the
103  *      number of registry images should be kept to a minimum since many OSs
104  *      have severe limits on the number of images that can be in memory at once.
105  * </ul>
106  * <p>
107  * For easy access to your plug-in object, use the singleton pattern. Declare a
108  * static variable in your plug-in class for the singleton. Store the first
109  * (and only) instance of the plug-in class in the singleton when it is created.
110  * Then access the singleton when needed through a static <code>getDefault</code>
111  * method.
112  * </p>
113  * <p>
114  * See the description on {@link Plugin}.
115  * </p>
116  */
117 public abstract class AbstractUIPlugin extends Plugin {
118 
119   /**
120    * The name of the dialog settings file (value 
121    * <code>"dialog_settings.xml"</code>).
122    */
123   private static final String FN_DIALOG_SETTINGS = "dialog_settings.xml"; //$NON-NLS-1$
124 
125   /**
126    * Storage for dialog and wizard data; <code>null</code> if not yet
127    * initialized.
128    */
129   private DialogSettings dialogSettings = null;
130 
131   /**
132    * Storage for preferences.
133    */
134   private CompatibilityPreferenceStore preferenceStore;
135 
136   /**
137    * The registry for all graphic images; <code>null</code> if not yet
138    * initialized.
139    */
140   private ImageRegistry imageRegistry = null;
141 
142     /**
143      * The bundle listener used for kicking off refreshPluginActions().
144      * 
145      * @since 3.0.1
146      */
147     private BundleListener bundleListener;
148     
149   /**
150    * Internal implementation of a JFace preference store atop a core runtime
151    * preference store.
152    * 
153    * @since 2.0
154    */
155   private class CompatibilityPreferenceStore implements IPersistentPreferenceStore {
156 
157     /**
158      * Flag to indicate that the listener has been added.
159      */
160     private boolean listenerAdded = false;
161 
162     /**
163      * The underlying core runtime preference store; <code>null</code> if it
164      * has not been initialized yet.
165      */
166     private Preferences prefs = null;
167 
168     /**
169      * Identity list of old listeners (element type: 
170      * <code>org.eclipse.jface.util.IPropertyChangeListener</code>).
171      */
172     private ListenerList listeners = new ListenerList();
173 
174     /**
175      * Indicates whether property change events should be suppressed
176      * (used in implementation of <code>putValue</code>). Initially
177      * and usually <code>false</code>.
178      * 
179      * @see IPreferenceStore#putValue
180      */
181     private boolean silentRunning = false;
182 
183     /**
184      * Creates a new instance for the this plug-in.
185      */
186     public CompatibilityPreferenceStore() {
187       // Important: do not call initialize() here
188       // due to heinous reentrancy problems.
189     }
190 
191     /**
192      * Initializes this preference store.
193      */
194     void initialize() {
195       // ensure initialization is only done once.
196       if (this.prefs != null) {
197         return;
198       }
199       // here's where we first ask for the plug-in's core runtime 
200       // preferences;
201       // note that this causes this method to be reentered
202       this.prefs = getPluginPreferences();
203       // avoid adding the listener a second time when reentered
204       if (!this.listenerAdded) {
205         // register listener that funnels everything to firePropertyChangeEvent
206         this
207           .prefs
208           .addPropertyChangeListener(
209             new Preferences
210             .IPropertyChangeListener() {
211           public void propertyChange(
212             Preferences.PropertyChangeEvent event) {
213             if (!silentRunning) {
214               firePropertyChangeEvent(
215                 event.getProperty(),
216                 event.getOldValue(),
217                 event.getNewValue());
218             }
219           }
220         });
221         this.listenerAdded = true;
222       }
223     }
224 
225     /**
226      * Returns the underlying preference store.
227      * 
228      * @return the underlying preference store
229      */
230     private Preferences getPrefs() {
231       if (prefs == null) {
232         // although we try to ensure initialization is done eagerly,
233         // this cannot be guaranteed, so ensure it is done here
234         initialize();
235       }
236       return prefs;
237     }
238 
239     /* (non-javadoc)
240      * Method declared on IPreferenceStore
241      */
242     public void addPropertyChangeListener(final IPropertyChangeListener listener) {
243       listeners.add(listener);
244     }
245 
246     /* (non-javadoc)
247      * Method declared on IPreferenceStore
248      */
249     public void removePropertyChangeListener(IPropertyChangeListener listener) {
250       listeners.remove(listener);
251     }
252 
253     /* (non-javadoc)
254      * Method declared on IPreferenceStore
255      */
256     public void firePropertyChangeEvent(
257       String name,
258       Object oldValue,
259       Object newValue) {
260 
261       // efficiently handle case of 0 listeners
262       if (listeners.isEmpty()) {
263         // no one interested
264         return;
265       }
266 
267       // important: create intermediate array to protect against listeners 
268       // being added/removed during the notification
269       final Object[] list = listeners.getListeners();
270       final PropertyChangeEvent event =
271         new PropertyChangeEvent(this, name, oldValue, newValue);
272       for (int i = 0; i < list.length; i++) {
273         final IPropertyChangeListener listener = (IPropertyChangeListener) list[i];
274         Platform.run(new SafeRunnable(JFaceResources.getString("PreferenceStore.changeError")) { //$NON-NLS-1$
275           public void run() {
276             listener.propertyChange(
277                 event);
278           }
279         });
280       }
281 
282     }
283 
284     /* (non-javadoc)
285      * Method declared on IPreferenceStore
286      */
287     public boolean contains(String name) {
288       return getPrefs().contains(name);
289     }
290 
291     /* (non-javadoc)
292      * Method declared on IPreferenceStore
293      */
294     public boolean getBoolean(String name) {
295       return getPrefs().getBoolean(name);
296     }
297 
298     /* (non-javadoc)
299      * Method declared on IPreferenceStore
300      */
301     public boolean getDefaultBoolean(String name) {
302       return getPrefs().getDefaultBoolean(name);
303     }
304 
305     /* (non-javadoc)
306      * Method declared on IPreferenceStore
307      */
308     public double getDefaultDouble(String name) {
309       return getPrefs().getDefaultDouble(name);
310     }
311 
312     /* (non-javadoc)
313      * Method declared on IPreferenceStore
314      */
315     public float getDefaultFloat(String name) {
316       return getPrefs().getDefaultFloat(name);
317     }
318 
319     /* (non-javadoc)
320      * Method declared on IPreferenceStore
321      */
322     public int getDefaultInt(String name) {
323       return getPrefs().getDefaultInt(name);
324     }
325 
326     /* (non-javadoc)
327      * Method declared on IPreferenceStore
328      */
329     public long getDefaultLong(String name) {
330       return getPrefs().getDefaultLong(name);
331     }
332 
333     /* (non-javadoc)
334      * Method declared on IPreferenceStore
335      */
336     public String getDefaultString(String name) {
337       return getPrefs().getDefaultString(name);
338     }
339 
340     /* (non-javadoc)
341      * Method declared on IPreferenceStore
342      */
343     public double getDouble(String name) {
344       return getPrefs().getDouble(name);
345     }
346 
347     /* (non-javadoc)
348      * Method declared on IPreferenceStore
349      */
350     public float getFloat(String name) {
351       return getPrefs().getFloat(name);
352     }
353 
354     /* (non-javadoc)
355      * Method declared on IPreferenceStore
356      */
357     public int getInt(String name) {
358       return getPrefs().getInt(name);
359     }
360 
361     /* (non-javadoc)
362      * Method declared on IPreferenceStore
363      */
364     public long getLong(String name) {
365       return getPrefs().getLong(name);
366     }
367 
368     /* (non-javadoc)
369      * Method declared on IPreferenceStore
370      */
371     public String getString(String name) {
372       return getPrefs().getString(name);
373     }
374 
375     /* (non-javadoc)
376      * Method declared on IPreferenceStore
377      */
378     public boolean isDefault(String name) {
379       return getPrefs().isDefault(name);
380     }
381 
382     /* (non-javadoc)
383      * Method declared on IPreferenceStore
384      */
385     public boolean needsSaving() {
386       return getPrefs().needsSaving();
387     }
388 
389     /* (non-javadoc)
390      * Method declared on IPreferenceStore
391      */
392     public void putValue(String name, String value) {
393       try {
394         // temporarily suppress event notification while setting value
395         silentRunning = true;
396         getPrefs().setValue(name, value);
397       } finally {
398         silentRunning = false;
399       }
400     }
401 
402     /* (non-javadoc)
403      * Method declared on IPreferenceStore
404      */
405     public void setDefault(String name, double value) {
406       getPrefs().setDefault(name, value);
407     }
408 
409     /* (non-javadoc)
410      * Method declared on IPreferenceStore
411      */
412     public void setDefault(String name, float value) {
413       getPrefs().setDefault(name, value);
414     }
415 
416     /* (non-javadoc)
417      * Method declared on IPreferenceStore
418      */
419     public void setDefault(String name, int value) {
420       getPrefs().setDefault(name, value);
421     }
422 
423     /* (non-javadoc)
424      * Method declared on IPreferenceStore
425      */
426     public void setDefault(String name, long value) {
427       getPrefs().setDefault(name, value);
428     }
429 
430     /* (non-javadoc)
431      * Method declared on IPreferenceStore
432      */
433     public void setDefault(String name, String value) {
434       getPrefs().setDefault(name, value);
435     }
436 
437     /* (non-javadoc)
438      * Method declared on IPreferenceStore
439      */
440     public void setDefault(String name, boolean value) {
441       getPrefs().setDefault(name, value);
442     }
443 
444     /* (non-javadoc)
445      * Method declared on IPreferenceStore
446      */
447     public void setToDefault(String name) {
448       getPrefs().setToDefault(name);
449     }
450 
451     /* (non-javadoc)
452      * Method declared on IPreferenceStore
453      */
454     public void setValue(String name, double value) {
455       getPrefs().setValue(name, value);
456     }
457 
458     /* (non-javadoc)
459      * Method declared on IPreferenceStore
460      */
461     public void setValue(String name, float value) {
462       getPrefs().setValue(name, value);
463     }
464 
465     /* (non-javadoc)
466      * Method declared on IPreferenceStore
467      */
468     public void setValue(String name, int value) {
469       getPrefs().setValue(name, value);
470     }
471 
472     /* (non-javadoc)
473      * Method declared on IPreferenceStore
474      */
475     public void setValue(String name, long value) {
476       getPrefs().setValue(name, value);
477     }
478 
479     /* (non-javadoc)
480      * Method declared on IPreferenceStore
481      */
482     public void setValue(String name, String value) {
483       getPrefs().setValue(name, value);
484     }
485 
486     /* (non-javadoc)
487      * Method declared on IPreferenceStore
488      */
489     public void setValue(String name, boolean value) {
490       getPrefs().setValue(name, value);
491     }
492     /**
493      * @see org.eclipse.jface.preference.IPersistentPreferenceStore#save()
494      */
495     public void save() throws IOException {
496       AbstractUIPlugin.this.savePreferenceStore();
497     }
498 
499   }
500 
501   /**
502    * Creates an abstract UI plug-in runtime object for the given plug-in
503    * descriptor.
504    * <p>
505    * Note that instances of plug-in runtime classes are automatically created
506    * by the platform in the course of plug-in activation.
507    * <p>
508    * 
509    * @param descriptor the plug-in descriptor
510    * @see Plugin#Plugin(org.eclipse.core.runtime.IPluginDescriptor descriptor)
511    * @deprecated
512    * In Eclipse 3.0 this constructor has been replaced by
513    * {@link #AbstractUIPlugin()}. Implementations of
514    * <code>MyPlugin(IPluginDescriptor descriptor)</code> should be changed to 
515    * <code>MyPlugin()</code> and call <code>super()</code> instead of
516    * <code>super(descriptor)</code>.
517    * The <code>MyPlugin(IPluginDescriptor descriptor)</code> constructor is
518    * called only for plug-ins which explicitly require the
519    * org.eclipse.core.runtime.compatibility plug-in (or, as in this case,
520    * subclasses which might).
521    */
522   public AbstractUIPlugin(org.eclipse.core.runtime.IPluginDescriptor descriptor) {
523     super(descriptor);
524   }
525   
526   /**
527    * Creates an abstract UI plug-in runtime object.
528    * <p>
529    * Plug-in runtime classes are <code>BundleActivators</code> and so must
530    * have an default constructor.  This method is called by the runtime when 
531    * the associated bundle is being activated.  
532    * <p>
533    * For more details, see <code>Plugin</code>'s default constructor.
534      *
535    * @see Plugin#Plugin()
536      * @since 3.0
537    */
538   public AbstractUIPlugin() {
539     super();
540   }
541 
542   /** 
543    * Returns a new image registry for this plugin-in.  The registry will be
544    * used to manage images which are frequently used by the plugin-in.
545    * <p>
546    * The default implementation of this method creates an empty registry.
547    * Subclasses may override this method if needed.
548    * </p>
549    *
550    * @return ImageRegistry the resulting registry.
551    * @see #getImageRegistry
552    */
553   protected ImageRegistry createImageRegistry() {
554     return new ImageRegistry();
555   }
556   /**
557    * Returns the dialog settings for this UI plug-in.
558    * The dialog settings is used to hold persistent state data for the various
559    * wizards and dialogs of this plug-in in the context of a workbench. 
560    * <p>
561    * If an error occurs reading the dialog store, an empty one is quietly created
562    * and returned.
563    * </p>
564    * <p>
565    * Subclasses may override this method but are not expected to.
566    * </p>
567    *
568    * @return the dialog settings
569    */
570   public IDialogSettings getDialogSettings() {
571     if (dialogSettings == null)
572       loadDialogSettings();
573     return dialogSettings;
574   }
575   /**
576    * Returns the image registry for this UI plug-in. 
577    * <p>
578    * The image registry contains the images used by this plug-in that are very 
579    * frequently used and so need to be globally shared within the plug-in. Since 
580    * many OSs have a severe limit on the number of images that can be in memory at 
581    * any given time, a plug-in should only keep a small number of images in their 
582    * registry.
583    * <p>
584    * Subclasses should reimplement <code>initializeImageRegistry</code> if they have
585    * custom graphic images to load.
586    * </p>
587    * <p>
588    * Subclasses may override this method but are not expected to.
589    * </p>
590    *
591    * @return the image registry
592    */
593   public ImageRegistry getImageRegistry() {
594     if (imageRegistry == null) {
595       imageRegistry = createImageRegistry();
596       initializeImageRegistry(imageRegistry);
597     }
598     return imageRegistry;
599   }
600   /**
601    * Returns the preference store for this UI plug-in.
602    * This preference store is used to hold persistent settings for this plug-in in
603    * the context of a workbench. Some of these settings will be user controlled, 
604    * whereas others may be internal setting that are never exposed to the user.
605    * <p>
606    * If an error occurs reading the preference store, an empty preference store is
607    * quietly created, initialized with defaults, and returned.
608    * </p>
609    * <p>
610    * Subclasses should reimplement <code>initializeDefaultPreferences</code> if
611    * they have custom graphic images to load.
612    * </p>
613    *
614    * @return the preference store 
615    */
616   public IPreferenceStore getPreferenceStore() {
617     // Create the preference store lazily.
618     if (preferenceStore == null) {
619       // must assign field before calling initialize(), since
620       // this method can be reentered during initialization
621       preferenceStore = new CompatibilityPreferenceStore();
622       // force initialization
623       preferenceStore.initialize();
624     }
625     return preferenceStore;
626   }
627 
628   /**
629    * Returns the Platform UI workbench.  
630    * <p> 
631    * This method exists as a convenience for plugin implementors.  The
632    * workbench can also be accessed by invoking <code>PlatformUI.getWorkbench()</code>.
633    * </p>
634    * @return IWorkbench the workbench for this plug-in
635    */
636   public IWorkbench getWorkbench() {
637     return PlatformUI.getWorkbench();
638   }
639 
640   /** 
641    * Initializes a preference store with default preference values 
642    * for this plug-in.
643    * <p>
644    * This method is called after the preference store is initially loaded
645    * (default values are never stored in preference stores).
646    * </p>
647    * <p>
648    * The default implementation of this method does nothing.
649    * Subclasses should reimplement this method if the plug-in has any preferences.
650    * </p>
651    * <p>
652    * A subclass may reimplement this method to set default values for the 
653    * preference store using JFace API. This is the older way of initializing 
654    * default values. If this method is reimplemented, do not override
655    * <code>initializeDefaultPluginPreferences()</code>.
656    * </p>
657    * 
658    * @param store the preference store to fill
659    * 
660      * @deprecated this is only called if the runtime compatibility layer is
661      *             present. See {@link #initializeDefaultPluginPreferences}.
662    */
663   protected void initializeDefaultPreferences(IPreferenceStore store) {
664       // spec'ed to do nothing
665   }
666 
667   /**
668      * The <code>AbstractUIPlugin</code> implementation of this
669      * <code>Plugin</code> method forwards to
670      * <code>initializeDefaultPreferences(IPreferenceStore)</code>.
671      * <p>
672      * A subclass may reimplement this method to set default values for the core
673      * runtime preference store in the standard way. This is the recommended way
674      * to do this. The older
675      * <code>initializeDefaultPreferences(IPreferenceStore)</code> method
676      * serves a similar purpose. If this method is reimplemented, do not send
677      * super, and do not override
678      * <code>initializeDefaultPreferences(IPreferenceStore)</code>.
679      * </p>
680      * 
681      * @deprecated this is only called if the runtime compatibility layer is
682      *             present. See the deprecated comment in
683      *             {@link Plugin#initializeDefaultPluginPreferences}.
684      * 
685      * @see #initializeDefaultPreferences
686      * @since 2.0
687      */
688   protected void initializeDefaultPluginPreferences() {
689     // N.B. by the time this method is called, the plug-in has a 
690     // core runtime preference store (no default values)
691 
692     // call loadPreferenceStore (only) for backwards compatibility with Eclipse 1.0
693     loadPreferenceStore();
694     // call initializeDefaultPreferences (only) for backwards compatibility 
695     // with Eclipse 1.0
696     initializeDefaultPreferences(getPreferenceStore());
697   }
698 
699   /** 
700    * Initializes an image registry with images which are frequently used by the 
701    * plugin.
702    * <p>
703    * The image registry contains the images used by this plug-in that are very
704    * frequently used and so need to be globally shared within the plug-in. Since
705    * many OSs have a severe limit on the number of images that can be in memory
706    * at any given time, each plug-in should only keep a small number of images in 
707    * its registry.
708    * </p><p>
709    * Implementors should create a JFace image descriptor for each frequently used
710    * image.  The descriptors describe how to create/find the image should it be needed. 
711    * The image described by the descriptor is not actually allocated until someone 
712    * retrieves it.
713    * </p><p>
714    * Subclasses may override this method to fill the image registry.
715    * </p>
716    * @param reg the registry to initalize
717    *
718    * @see #getImageRegistry
719    */
720   protected void initializeImageRegistry(ImageRegistry reg) {
721       // spec'ed to do nothing
722   }
723 
724   /**
725    * Loads the dialog settings for this plug-in.
726    * The default implementation first looks for a standard named file in the 
727    * plug-in's read/write state area; if no such file exists, the plug-in's
728    * install directory is checked to see if one was installed with some default
729    * settings; if no file is found in either place, a new empty dialog settings
730    * is created. If a problem occurs, an empty settings is silently used.
731    * <p>
732    * This framework method may be overridden, although this is typically
733    * unnecessary.
734    * </p>
735    */
736   protected void loadDialogSettings() {
737     dialogSettings = new DialogSettings("Workbench"); //$NON-NLS-1$
738 
739     // try r/w state area in the local file system
740     String readWritePath =
741       getStateLocation().append(FN_DIALOG_SETTINGS).toOSString();
742     File settingsFile = new File(readWritePath);
743     if (settingsFile.exists()) {
744       try {
745         dialogSettings.load(readWritePath);
746       } catch (IOException e) {
747         // load failed so ensure we have an empty settings
748         dialogSettings = new DialogSettings("Workbench"); //$NON-NLS-1$
749       }
750     } else {
751       URL dsURL = BundleUtility.find(getBundle(), FN_DIALOG_SETTINGS);
752       if(dsURL == null)
753         return;
754 
755       InputStream is = null;
756       try {
757         is = dsURL.openStream();
758         BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8")); //$NON-NLS-1$
759         dialogSettings.load(reader);
760       } catch (IOException e) {
761         // load failed so ensure we have an empty settings
762         dialogSettings = new DialogSettings("Workbench"); //$NON-NLS-1$
763       } finally {
764         try {
765           if (is != null)
766             is.close();
767         } catch (IOException e) {
768           // do nothing
769         }
770       }
771     }
772   }
773 
774   /**
775    * Loads the preference store for this plug-in.
776    * The default implementation looks for a standard named file in the 
777    * plug-in's read/write state area. If no file is found or a problem
778    * occurs, a new empty preference store is silently created. 
779    * <p>
780    * This framework method may be overridden, although this is typically 
781    * unnecessary.
782    * </p>
783    * 
784    * @deprecated As of Eclipse 2.0, a basic preference store exists for all
785    * plug-ins. This method now exists only for backwards compatibility.
786    * It is called as the plug-in's preference store is being initialized.
787    * The plug-ins preferences are loaded from the file regardless of what
788    * this method does.
789    */
790   protected void loadPreferenceStore() {
791       // do nothing by default 
792   }
793 
794   /**
795    * Refreshes the actions for the plugin.
796    * This method is called from <code>startup</code>.
797    * <p>
798    * This framework method may be overridden, although this is typically 
799    * unnecessary.
800    * </p>
801    */
802   protected void refreshPluginActions() {
803     // If the workbench is not created yet, do nothing.
804     if (Workbench.getInstance() == null)
805       return;
806 
807     // startup() is not guaranteed to be called in the UI thread,
808     // but refreshPluginActions must run in the UI thread, 
809     // so use asyncExec.  See bug 6623 for more details.
810     Display.getDefault().asyncExec(new Runnable() {
811       public void run() {
812         WWinPluginAction.refreshActionList();
813       }
814     });
815   }
816   
817   /**
818    * Saves this plug-in's dialog settings.
819    * Any problems which arise are silently ignored.
820    */
821   protected void saveDialogSettings() {
822     if (dialogSettings == null) {
823       return;
824     }
825 
826     try {
827       String readWritePath =
828         getStateLocation().append(FN_DIALOG_SETTINGS).toOSString();
829       dialogSettings.save(readWritePath);
830     } catch (IOException e) {
831         // spec'ed to ignore problems
832     }
833   }
834 
835   /**
836    * Saves this plug-in's preference store.
837    * Any problems which arise are silently ignored.
838    * 
839    * @see Plugin#savePluginPreferences()
840    * @deprecated As of Eclipse 2.0, preferences exist for all plug-ins. The 
841    * equivalent of this method is <code>Plugin.savePluginPreferences</code>. 
842    * This method now calls <code>savePluginPreferences</code>, and exists only for
843    * backwards compatibility.
844    */
845   protected void savePreferenceStore() {
846     savePluginPreferences();
847   }
848 
849   /**
850    * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
851    * method does nothing.  Subclasses may extend this method, but must send
852    * super first.
853    * <p>
854    * WARNING: Plug-ins may not be started in the UI thread.
855    * The <code>startup()</code> method should not assume that its code runs in
856    * the UI thread, otherwise SWT thread exceptions may occur on startup.'
857    * @deprecated 
858    * In Eclipse 3.0, <code>startup</code> has been replaced by {@link Plugin#start(BundleContext context)}.
859    * Implementations of <code>startup</code> should be changed to extend
860    * <code>start(BundleContext context)</code> and call <code>super.start(context)</code>
861    * instead of <code>super.startup()</code>. Like <code>super.startup()</code>,
862    * <code>super.stop(context)</code> must be called as the very first thing.
863    * The <code>startup</code> method is called only for plug-ins which explicitly require the 
864    * org.eclipse.core.runtime.compatibility plug-in; in contrast,
865    * the <code>start</code> method is always called.
866    */
867   public void startup() throws CoreException {
868     // this method no longer does anything
869     // the code that used to be here in 2.1 has moved to start(BundleContext)
870     super.startup();
871   }
872   
873   /**
874    * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
875    * method does nothing. Subclasses may extend this method, but must send
876    * super first.
877    * @deprecated 
878    * In Eclipse 3.0, <code>shutdown</code> has been replaced by {@link Plugin#stop(BundleContext context)}.
879    * Implementations of <code>shutdown</code> should be changed to extend 
880    * <code>stop(BundleContext context)</code> and call <code>super.stop(context)</code> 
881    * instead of <code>super.shutdown()</code>. Unlike <code>super.shutdown()</code>, 
882    * <code>super.stop(context)</code> must be called as the very <b>last</b> thing rather
883    * than as the very first thing. The <code>shutdown</code> method is called
884    * only for plug-ins which explicitly require the 
885    * org.eclipse.core.runtime.compatibility plug-in; 
886    * in contrast, the <code>stop</code> method is always called.
887    */
888   public void shutdown() throws CoreException {
889     // this method no longer does anything interesting
890     // the code that used to be here in 2.1 has moved to stop(BundleContext),
891     //   which is called regardless of whether the plug-in being instantiated
892     //   requires org.eclipse.core.runtime.compatibility
893     super.shutdown();
894   }
895   
896   /**
897    * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
898    * method refreshes the plug-in actions.  Subclasses may extend this method,
899    * but must send super <b>first</b>.
900    * {@inheritDoc}
901    * 
902    * @since 3.0
903    */
904   public void start(BundleContext context) throws Exception {
905     super.start(context);
906     final BundleContext fc = context;
907         // Should only attempt refreshPluginActions() once the bundle
908         // has been fully started.  Otherwise, action delegates
909         // can be created while in the process of creating 
910         // a triggering action delegate (if UI events are processed during startup).  
911         // Also, if the start throws an exception, the bundle will be shut down.  
912         // We don't want to have created any delegates if this happens.
913         // See bug 63324 for more details.
914         bundleListener = new BundleListener() {
915             public void bundleChanged(BundleEvent event) {
916                 if (event.getBundle() == getBundle()) {
917                     if (event.getType() == BundleEvent.STARTED) {
918                         refreshPluginActions();
919                         fc.removeBundleListener(this);
920                     }
921                 }
922             }
923         };
924         context.addBundleListener(bundleListener);
925         // bundleListener is removed in stop(BundleContext)
926   }
927   
928   /**
929    * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
930    * method saves this plug-in's preference and dialog stores and shuts down 
931    * its image registry (if they are in use). Subclasses may extend this
932    * method, but must send super <b>last</b>. A try-finally statement should
933    * be used where necessary to ensure that <code>super.shutdown()</code> is
934    * always done.
935    * {@inheritDoc}
936    * 
937    * @since 3.0
938    */
939   public void stop(BundleContext context) throws Exception {
940     try {
941             if (bundleListener != null) {
942                 context.removeBundleListener(bundleListener);
943             }
944       saveDialogSettings();
945       savePreferenceStore();
946       preferenceStore = null;
947       imageRegistry = null;
948        } 
949     finally {
950       super.stop(context);
951     }
952   }
953 
954   /**
955    * Creates and returns a new image descriptor for an image file located
956    * within the specified plug-in.
957    * <p>
958    * This is a convenience method that simply locates the image file in
959    * within the plug-in (no image registries are involved). The path is
960    * relative to the root of the plug-in, and takes into account files
961    * coming from plug-in fragments. The path may include $arg$ elements.
962    * However, the path must not have a leading "." or path separator.
963    * Clients should use a path like "icons/mysample.gif" rather than 
964    * "./icons/mysample.gif" or "/icons/mysample.gif".
965    * </p>
966    * 
967    * @param pluginId the id of the plug-in containing the image file; 
968    * <code>null</code> is returned if the plug-in does not exist
969    * @param imageFilePath the relative path of the image file, relative to the
970    * root of the plug-in; the path must be legal
971    * @return an image descriptor, or <code>null</code> if no image
972    * could be found
973    * @since 3.0
974    */
975   public static ImageDescriptor imageDescriptorFromPlugin(String pluginId,
976             String imageFilePath) {
977         if (pluginId == null || imageFilePath == null) {
978             throw new IllegalArgumentException();
979         }
980 
981         // if the bundle is not ready then there is no image
982         Bundle bundle = Platform.getBundle(pluginId);
983         if (!BundleUtility.isReady(bundle))
984             return null;
985 
986         // look for the image (this will check both the plugin and fragment folders
987         URL fullPathString = BundleUtility.find(bundle, imageFilePath);
988         if (fullPathString == null) {
989           try {
990         fullPathString = new URL(imageFilePath);
991       } catch (MalformedURLException e) {
992         return null;
993       }
994         }
995 
996         if (fullPathString == null)
997           return null;
998         return ImageDescriptor.createFromURL(fullPathString);
999     }
1000}