KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > navigator > NavigatorSaveablesService


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.ui.internal.navigator;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.Arrays JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Set JavaDoc;
22 import java.util.TreeMap JavaDoc;
23
24 import org.eclipse.core.runtime.Platform;
25 import org.eclipse.jface.viewers.IStructuredSelection;
26 import org.eclipse.jface.viewers.ITreeContentProvider;
27 import org.eclipse.jface.viewers.ITreePathContentProvider;
28 import org.eclipse.jface.viewers.ITreeSelection;
29 import org.eclipse.jface.viewers.StructuredViewer;
30 import org.eclipse.jface.viewers.TreePath;
31 import org.eclipse.swt.events.DisposeEvent;
32 import org.eclipse.swt.events.DisposeListener;
33 import org.eclipse.swt.widgets.Display;
34 import org.eclipse.ui.ISaveablesLifecycleListener;
35 import org.eclipse.ui.ISaveablesSource;
36 import org.eclipse.ui.Saveable;
37 import org.eclipse.ui.SaveablesLifecycleEvent;
38 import org.eclipse.ui.internal.navigator.VisibilityAssistant.VisibilityListener;
39 import org.eclipse.ui.internal.navigator.extensions.ExtensionPriorityComparator;
40 import org.eclipse.ui.internal.navigator.extensions.NavigatorContentDescriptor;
41 import org.eclipse.ui.internal.navigator.extensions.NavigatorContentExtension;
42 import org.eclipse.ui.navigator.INavigatorContentDescriptor;
43 import org.eclipse.ui.navigator.INavigatorSaveablesService;
44 import org.eclipse.ui.navigator.SaveablesProvider;
45 import org.osgi.framework.Bundle;
46 import org.osgi.framework.BundleEvent;
47
48 /**
49  * Implementation of INavigatorSaveablesService.
50  * <p>
51  * Implementation note: all externally callable methods are synchronized. The
52  * private helper methods are not synchronized since they can only be called
53  * from methods that already hold the lock.
54  * </p>
55  * @since 3.2
56  *
57  */

58 public class NavigatorSaveablesService implements INavigatorSaveablesService, VisibilityListener {
59
60     private NavigatorContentService contentService;
61
62     private static List JavaDoc instances = new ArrayList JavaDoc();
63
64     /**
65      * @param contentService
66      */

67     public NavigatorSaveablesService(NavigatorContentService contentService) {
68         this.contentService = contentService;
69     }
70
71     private static void addInstance(NavigatorSaveablesService saveablesService) {
72         synchronized (instances) {
73             instances.add(saveablesService);
74         }
75     }
76
77     private static void removeInstance(
78             NavigatorSaveablesService saveablesService) {
79         synchronized (instances) {
80             instances.remove(saveablesService);
81         }
82     }
83
84     /**
85      * @param event
86      */

87     /* package */ static void bundleChanged(BundleEvent event) {
88         synchronized(instances) {
89             if (event.getType() == BundleEvent.STARTED) {
90                 // System.out.println("bundle started: " + event.getBundle().getSymbolicName()); //$NON-NLS-1$
91
for (Iterator JavaDoc it = instances.iterator(); it.hasNext();) {
92                     NavigatorSaveablesService instance = (NavigatorSaveablesService) it
93                             .next();
94                     instance.handleBundleStarted(event.getBundle()
95                             .getSymbolicName());
96                 }
97             } else if (event.getType() == BundleEvent.STOPPED) {
98                 // System.out.println("bundle stopped: " + event.getBundle().getSymbolicName()); //$NON-NLS-1$
99
for (Iterator JavaDoc it = instances.iterator(); it.hasNext();) {
100                     NavigatorSaveablesService instance = (NavigatorSaveablesService) it
101                             .next();
102                     instance.handleBundleStopped(event.getBundle()
103                             .getSymbolicName());
104                 }
105             }
106         }
107     }
108
109     private class LifecycleListener implements ISaveablesLifecycleListener {
110         public void handleLifecycleEvent(SaveablesLifecycleEvent event) {
111             Saveable[] saveables = event.getSaveables();
112             switch (event.getEventType()) {
113             case SaveablesLifecycleEvent.POST_OPEN:
114                 recomputeSaveablesAndNotify(false, null);
115                 break;
116             case SaveablesLifecycleEvent.POST_CLOSE:
117                 recomputeSaveablesAndNotify(false, null);
118                 break;
119             case SaveablesLifecycleEvent.DIRTY_CHANGED:
120                 Saveable[] shownSaveables = getShownSaveables(saveables);
121                 if (shownSaveables.length > 0) {
122                     outsideListener
123                             .handleLifecycleEvent(new SaveablesLifecycleEvent(
124                                     saveablesSource,
125                                     SaveablesLifecycleEvent.DIRTY_CHANGED,
126                                     shownSaveables, false));
127                 }
128                 break;
129             }
130         }
131     }
132
133     private Saveable[] currentSaveables;
134
135     private ISaveablesLifecycleListener outsideListener;
136
137     private ISaveablesLifecycleListener saveablesLifecycleListener = new LifecycleListener();
138
139     private ISaveablesSource saveablesSource;
140
141     private StructuredViewer viewer;
142
143     private SaveablesProvider[] saveablesProviders;
144
145     private DisposeListener disposeListener = new DisposeListener() {
146
147         public void widgetDisposed(DisposeEvent e) {
148             // synchronize in the same order as in the init method.
149
synchronized (instances) {
150                 synchronized (NavigatorSaveablesService.this) {
151                     if (saveablesProviders != null) {
152                         for (int i = 0; i < saveablesProviders.length; i++) {
153                             saveablesProviders[i].dispose();
154                         }
155                     }
156                     removeInstance(NavigatorSaveablesService.this);
157                     contentService = null;
158                     currentSaveables = null;
159                     outsideListener = null;
160                     saveablesLifecycleListener = null;
161                     saveablesSource = null;
162                     viewer = null;
163                     saveablesProviders = null;
164                     disposeListener = null;
165                 }
166             }
167         }
168     };
169
170     private Map JavaDoc inactivePluginsWithSaveablesProviders;
171
172     /**
173      * a TreeMap (NavigatorContentDescriptor->SaveablesProvider) which uses
174      * ExtensionPriorityComparator.INSTANCE as its Comparator
175      */

176     private Map JavaDoc saveablesProviderMap;
177
178     /**
179      * Implementation note: This is not synchronized at the method level because it needs to
180      * synchronize on "instances" first, then on "this", to avoid potential deadlock.
181      *
182      * @param saveablesSource
183      * @param viewer
184      * @param outsideListener
185      *
186      */

187     public void init(final ISaveablesSource saveablesSource,
188             final StructuredViewer viewer,
189             ISaveablesLifecycleListener outsideListener) {
190         // Synchronize on instances to make sure that we don't miss bundle started events.
191
synchronized (instances) {
192             // Synchronize on this because we are calling computeSaveables.
193
// Synchronization must remain in this order to avoid deadlock.
194
// This might not be necessary because at this time, no other
195
// concurrent calls should be possible, but it doesn't hurt either.
196
// For example, the initialization sequence might change in the
197
// future.
198
synchronized (this) {
199                 this.saveablesSource = saveablesSource;
200                 this.viewer = viewer;
201                 this.outsideListener = outsideListener;
202                 currentSaveables = computeSaveables();
203                 // add this instance after we are fully inialized.
204
addInstance(this);
205             }
206         }
207         viewer.getControl().addDisposeListener(disposeListener);
208     }
209
210     /** helper to compute the saveables for which elements are part of the tree.
211      * Must be called from a synchronized method.
212      *
213      * @return the saveables
214      */

215     private Saveable[] computeSaveables() {
216         ITreeContentProvider contentProvider = (ITreeContentProvider) viewer
217                 .getContentProvider();
218         boolean isTreepathContentProvider = contentProvider instanceof ITreePathContentProvider;
219         Object JavaDoc viewerInput = viewer.getInput();
220         List JavaDoc result = new ArrayList JavaDoc();
221         Set JavaDoc roots = new HashSet JavaDoc(Arrays.asList(contentProvider
222                 .getElements(viewerInput)));
223         SaveablesProvider[] saveablesProviders = getSaveablesProviders();
224         for (int i = 0; i < saveablesProviders.length; i++) {
225             SaveablesProvider saveablesProvider = saveablesProviders[i];
226             Saveable[] saveables = saveablesProvider.getSaveables();
227             for (int j = 0; j < saveables.length; j++) {
228                 Saveable saveable = saveables[j];
229                 Object JavaDoc[] elements = saveablesProvider.getElements(saveable);
230                 // the saveable is added to the result if at least one of the
231
// elements representing the saveable appears in the tree, i.e.
232
// if its parent chain leads to a root node.
233
boolean foundRoot = false;
234                 for (int k = 0; !foundRoot && k < elements.length; k++) {
235                     Object JavaDoc element = elements[k];
236                     if (roots.contains(element)) {
237                         result.add(saveable);
238                         foundRoot = true;
239                     } else if (isTreepathContentProvider) {
240                         ITreePathContentProvider treePathContentProvider = (ITreePathContentProvider) contentProvider;
241                         TreePath[] parentPaths = treePathContentProvider.getParents(element);
242                         for (int l = 0; !foundRoot && l < parentPaths.length; l++) {
243                             TreePath parentPath = parentPaths[l];
244                             for (int m = 0; !foundRoot && m < parentPath.getSegmentCount(); m++) {
245                                 if (roots.contains(parentPath.getSegment(m))) {
246                                     result.add(saveable);
247                                     foundRoot = true;
248                                 }
249                             }
250                         }
251                     } else {
252                         while (!foundRoot && element != null) {
253                             if (roots.contains(element)) {
254                                 // found a parent chain leading to a root. The
255
// saveable is part of the tree.
256
result.add(saveable);
257                                 foundRoot = true;
258                             } else {
259                                 element = contentProvider.getParent(element);
260                             }
261                         }
262                     }
263                 }
264             }
265         }
266         return (Saveable[]) result.toArray(new Saveable[result.size()]);
267     }
268
269     public synchronized Saveable[] getActiveSaveables() {
270         ITreeContentProvider contentProvider = (ITreeContentProvider) viewer
271                 .getContentProvider();
272         IStructuredSelection selection = (IStructuredSelection) viewer
273                 .getSelection();
274         if (selection instanceof ITreeSelection) {
275             return getActiveSaveablesFromTreeSelection((ITreeSelection) selection);
276         } else if (contentProvider instanceof ITreePathContentProvider) {
277             return getActiveSaveablesFromTreePathProvider(selection, (ITreePathContentProvider) contentProvider);
278         } else {
279             return getActiveSaveablesFromTreeProvider(selection, contentProvider);
280         }
281     }
282     
283     /**
284      * @param selection
285      * @return the active saveables
286      */

287     private Saveable[] getActiveSaveablesFromTreeSelection(
288             ITreeSelection selection) {
289         Set JavaDoc result = new HashSet JavaDoc();
290         TreePath[] paths = selection.getPaths();
291         for (int i = 0; i < paths.length; i++) {
292             TreePath path = paths[i];
293             Saveable saveable = findSaveable(path);
294             if (saveable != null) {
295                 result.add(saveable);
296             }
297         }
298         return (Saveable[]) result.toArray(new Saveable[result.size()]);
299     }
300
301     /**
302      * @param selection
303      * @param provider
304      * @return the active saveables
305      */

306     private Saveable[] getActiveSaveablesFromTreePathProvider(
307             IStructuredSelection selection, ITreePathContentProvider provider) {
308         Set JavaDoc result = new HashSet JavaDoc();
309         for (Iterator JavaDoc it = selection.iterator(); it.hasNext();) {
310             Object JavaDoc element = it.next();
311             Saveable saveable = getSaveable(element);
312             if (saveable != null) {
313                 result.add(saveable);
314             } else {
315                 TreePath[] paths = provider.getParents(element);
316                 saveable = findSaveable(paths);
317                 if (saveable != null) {
318                     result.add(saveable);
319                 }
320             }
321         }
322         return (Saveable[]) result.toArray(new Saveable[result.size()]);
323     }
324
325     /**
326      * @param selection
327      * @param contentProvider
328      * @return the active saveables
329      */

330     private Saveable[] getActiveSaveablesFromTreeProvider(
331             IStructuredSelection selection, ITreeContentProvider contentProvider) {
332         Set JavaDoc result = new HashSet JavaDoc();
333         for (Iterator JavaDoc it = selection.iterator(); it.hasNext();) {
334             Object JavaDoc element = it.next();
335             Saveable saveable = findSaveable(element, contentProvider);
336             if (saveable != null) {
337                 result.add(saveable);
338             }
339         }
340         return (Saveable[]) result.toArray(new Saveable[result.size()]);
341     }
342
343     /**
344      * @param element
345      * @param contentProvider
346      * @return the saveable, or null
347      */

348     private Saveable findSaveable(Object JavaDoc element,
349             ITreeContentProvider contentProvider) {
350         while (element != null) {
351             Saveable saveable = getSaveable(element);
352             if (saveable != null) {
353                 return saveable;
354             }
355             element = contentProvider.getParent(element);
356         }
357         return null;
358     }
359
360     /**
361      * @param paths
362      * @return the saveable, or null
363      */

364     private Saveable findSaveable(TreePath[] paths) {
365         for (int i = 0; i < paths.length; i++) {
366             Saveable saveable = findSaveable(paths[i]);
367             if (saveable != null) {
368                 return saveable;
369             }
370         }
371         return null;
372     }
373     
374     /**
375      * @param path
376      * @return a saveable, or null
377      */

378     private Saveable findSaveable(TreePath path) {
379         int count = path.getSegmentCount();
380         for (int j = count - 1; j >= 0; j--) {
381             Object JavaDoc parent = path.getSegment(j);
382             Saveable saveable = getSaveable(parent);
383             if (saveable != null) {
384                 return saveable;
385             }
386         }
387         return null;
388     }
389
390     /**
391      * @param element
392      * @return the saveable associated with the given element
393      */

394     private Saveable getSaveable(Object JavaDoc element) {
395         if (saveablesProviderMap==null) {
396             // has the side effect of recomputing saveablesProviderMap:
397
getSaveablesProviders();
398         }
399         for(Iterator JavaDoc sItr = saveablesProviderMap.keySet().iterator(); sItr.hasNext();) {
400             NavigatorContentDescriptor descriptor = (NavigatorContentDescriptor) sItr.next();
401                 if(descriptor.isTriggerPoint(element) || descriptor.isPossibleChild(element)) {
402                     SaveablesProvider provider = (SaveablesProvider) saveablesProviderMap.get(descriptor);
403                     Saveable saveable = provider.getSaveable(element);
404                         if(saveable != null) {
405                                 return saveable;
406                         }
407                 }
408         }
409         return null;
410     }
411
412     /**
413      * @return the saveables
414      */

415     public synchronized Saveable[] getSaveables() {
416         return currentSaveables;
417     }
418
419     /**
420      * @return all SaveablesProvider objects
421      */

422     private SaveablesProvider[] getSaveablesProviders() {
423         // TODO optimize this
424
if (saveablesProviders == null) {
425             inactivePluginsWithSaveablesProviders = new HashMap JavaDoc();
426             saveablesProviderMap = new TreeMap JavaDoc(ExtensionPriorityComparator.INSTANCE);
427             INavigatorContentDescriptor[] descriptors = contentService
428                     .getActiveDescriptorsWithSaveables();
429             List JavaDoc result = new ArrayList JavaDoc();
430             for (int i = 0; i < descriptors.length; i++) {
431                 NavigatorContentDescriptor descriptor = (NavigatorContentDescriptor) descriptors[i];
432                 String JavaDoc pluginId = ((NavigatorContentDescriptor) descriptor)
433                         .getContribution().getPluginId();
434                 if (Platform.getBundle(pluginId).getState() != Bundle.ACTIVE) {
435                     List JavaDoc inactiveDescriptors = (List JavaDoc) inactivePluginsWithSaveablesProviders
436                             .get(pluginId);
437                     if (inactiveDescriptors == null) {
438                         inactiveDescriptors = new ArrayList JavaDoc();
439                         inactivePluginsWithSaveablesProviders.put(pluginId,
440                                 inactiveDescriptors);
441                     }
442                     inactiveDescriptors.add(descriptor);
443                 } else {
444                     SaveablesProvider saveablesProvider = createSaveablesProvider(descriptor);
445                     if (saveablesProvider != null) {
446                         saveablesProvider.init(saveablesLifecycleListener);
447                         result.add(saveablesProvider);
448                         saveablesProviderMap.put(descriptor, saveablesProvider);
449                     }
450                 }
451             }
452             saveablesProviders = (SaveablesProvider[]) result
453                     .toArray(new SaveablesProvider[result.size()]);
454         }
455         return saveablesProviders;
456     }
457
458     /**
459      * @param descriptor
460      * @return the SaveablesProvider, or null
461      */

462     private SaveablesProvider createSaveablesProvider(NavigatorContentDescriptor descriptor) {
463         NavigatorContentExtension extension = contentService
464                 .getExtension(descriptor, true);
465         ITreeContentProvider contentProvider = extension
466                 .getContentProvider();
467         
468         return (SaveablesProvider)AdaptabilityUtility.getAdapter(contentProvider, SaveablesProvider.class);
469     }
470
471     private Saveable[] getShownSaveables(Saveable[] saveables) {
472         Set JavaDoc result = new HashSet JavaDoc(Arrays.asList(currentSaveables));
473         result.retainAll(Arrays.asList(saveables));
474         return (Saveable[]) result.toArray(new Saveable[result.size()]);
475     }
476
477     private void recomputeSaveablesAndNotify(boolean recomputeProviders,
478             String JavaDoc startedBundleIdOrNull) {
479         if (recomputeProviders && startedBundleIdOrNull == null
480                 && saveablesProviders != null) {
481             // a bundle was stopped, dispose of all saveablesProviders and
482
// recompute
483
// TODO optimize this
484
for (int i = 0; i < saveablesProviders.length; i++) {
485                 saveablesProviders[i].dispose();
486             }
487             saveablesProviders = null;
488         } else if (startedBundleIdOrNull != null){
489             if(inactivePluginsWithSaveablesProviders.containsKey(startedBundleIdOrNull)) {
490                 updateSaveablesProviders(startedBundleIdOrNull);
491             }
492         }
493         Set JavaDoc oldSaveables = new HashSet JavaDoc(Arrays.asList(currentSaveables));
494         currentSaveables = computeSaveables();
495         Set JavaDoc newSaveables = new HashSet JavaDoc(Arrays.asList(currentSaveables));
496         final Set JavaDoc removedSaveables = new HashSet JavaDoc(oldSaveables);
497         removedSaveables.removeAll(newSaveables);
498         final Set JavaDoc addedSaveables = new HashSet JavaDoc(newSaveables);
499         addedSaveables.removeAll(oldSaveables);
500         if (addedSaveables.size() > 0) {
501             Display.getDefault().asyncExec(new Runnable JavaDoc() {
502                 public void run() {
503                     // We might be disposed at this point.
504
// One indication of this is that saveablesSource is null.
505
if (saveablesSource == null) {
506                         return;
507                     }
508                     outsideListener.handleLifecycleEvent(new SaveablesLifecycleEvent(
509                             saveablesSource, SaveablesLifecycleEvent.POST_OPEN,
510                             (Saveable[]) addedSaveables
511                             .toArray(new Saveable[addedSaveables.size()]),
512                             false));
513                 }
514             });
515         }
516         // TODO this will make the closing of saveables non-cancelable.
517
// Ideally, we should react to PRE_CLOSE events and fire
518
// an appropriate PRE_CLOSE
519
if (removedSaveables.size() > 0) {
520             Display.getDefault().asyncExec(new Runnable JavaDoc() {
521                 public void run() {
522                     // we might be disposed at this point
523
// One indication of this is that saveablesSource is null.
524
if (saveablesSource == null) {
525                         return;
526                     }
527                     outsideListener
528                             .handleLifecycleEvent(new SaveablesLifecycleEvent(
529                                     saveablesSource,
530                                     SaveablesLifecycleEvent.PRE_CLOSE,
531                                     (Saveable[]) removedSaveables
532                                             .toArray(new Saveable[removedSaveables
533                                                     .size()]), true));
534                     outsideListener
535                             .handleLifecycleEvent(new SaveablesLifecycleEvent(
536                                     saveablesSource,
537                                     SaveablesLifecycleEvent.POST_CLOSE,
538                                     (Saveable[]) removedSaveables
539                                             .toArray(new Saveable[removedSaveables
540                                                     .size()]), false));
541                 }
542             });
543         }
544     }
545
546     /**
547      * @param startedBundleId
548      */

549     private void updateSaveablesProviders(String JavaDoc startedBundleId) {
550         List JavaDoc result = new ArrayList JavaDoc(Arrays.asList(saveablesProviders));
551         List JavaDoc descriptors = (List JavaDoc) inactivePluginsWithSaveablesProviders
552                 .get(startedBundleId);
553         for (Iterator JavaDoc it = descriptors.iterator(); it.hasNext();) {
554             NavigatorContentDescriptor descriptor = (NavigatorContentDescriptor) it
555                     .next();
556             SaveablesProvider saveablesProvider = createSaveablesProvider(descriptor);
557             if (saveablesProvider != null) {
558                 saveablesProvider.init(saveablesLifecycleListener);
559                 result.add(saveablesProvider);
560                 saveablesProviderMap.put(descriptor, saveablesProvider);
561             }
562         }
563         saveablesProviders = (SaveablesProvider[]) result
564                 .toArray(new SaveablesProvider[result.size()]);
565     }
566
567     /**
568      * @param symbolicName
569      */

570     private synchronized void handleBundleStarted(String JavaDoc symbolicName) {
571         // Guard against the case that this instance is not yet initialized,
572
// or already disposed.
573
if (saveablesSource != null) {
574             if (inactivePluginsWithSaveablesProviders.containsKey(symbolicName)) {
575                 recomputeSaveablesAndNotify(true, symbolicName);
576             }
577         }
578     }
579
580     /**
581      * @param symbolicName
582      */

583     private synchronized void handleBundleStopped(String JavaDoc symbolicName) {
584         // Guard against the case that this instance is not yet initialized,
585
// or already disposed.
586
if (saveablesSource != null) {
587             recomputeSaveablesAndNotify(true, null);
588         }
589     }
590
591     /* (non-Javadoc)
592      * @see org.eclipse.ui.internal.navigator.VisibilityAssistant.VisibilityListener#onVisibilityOrActivationChange()
593      */

594     public synchronized void onVisibilityOrActivationChange() {
595         // Guard against the case that this instance is not yet initialized,
596
// or already disposed.
597
if (saveablesSource != null) {
598             recomputeSaveablesAndNotify(true, null);
599         }
600     }
601
602 }
603
Popular Tags