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}