KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > debug > internal > ui > viewers > model > ModelContentProvider


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 package org.eclipse.debug.internal.ui.viewers.model;
12
13 import java.io.IOException JavaDoc;
14 import java.io.StringWriter JavaDoc;
15 import java.util.ArrayList JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.LinkedHashMap JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.Map JavaDoc;
22 import java.util.Set JavaDoc;
23 import java.util.Map.Entry;
24
25 import org.eclipse.core.runtime.IAdaptable;
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.eclipse.core.runtime.ISafeRunnable;
28 import org.eclipse.core.runtime.IStatus;
29 import org.eclipse.core.runtime.ListenerList;
30 import org.eclipse.core.runtime.Platform;
31 import org.eclipse.core.runtime.PlatformObject;
32 import org.eclipse.core.runtime.SafeRunner;
33 import org.eclipse.core.runtime.Status;
34 import org.eclipse.core.runtime.jobs.Job;
35 import org.eclipse.debug.core.IRequest;
36 import org.eclipse.debug.internal.ui.DebugUIPlugin;
37 import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
38 import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest;
39 import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider;
40 import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider;
41 import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest;
42 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener;
43 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
44 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor;
45 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
46 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory;
47 import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
48 import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
49 import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
50 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
51 import org.eclipse.debug.internal.ui.views.launch.DebugElementAdapterFactory;
52 import org.eclipse.jface.viewers.IContentProvider;
53 import org.eclipse.jface.viewers.StructuredViewer;
54 import org.eclipse.jface.viewers.TreePath;
55 import org.eclipse.jface.viewers.Viewer;
56 import org.eclipse.jface.viewers.ViewerFilter;
57 import org.eclipse.ui.IMemento;
58 import org.eclipse.ui.XMLMemento;
59 import org.eclipse.ui.progress.UIJob;
60 import org.eclipse.ui.progress.WorkbenchJob;
61
62 /**
63  * Content provider for a virtual viewer.
64  *
65  * @since 3.3
66  */

67 abstract class ModelContentProvider implements IContentProvider, IModelChangedListener {
68
69     private Viewer fViewer;
70
71     private Map JavaDoc fModelProxies = new HashMap JavaDoc(); // model proxy by element
72

73     /**
74      * Map of nodes that have been filtered from the viewer.
75      */

76     private FilterTransform fTransform = new FilterTransform();
77     
78     /**
79      * Model listeners
80      */

81     private ListenerList fModelListeners = new ListenerList();
82     
83     /**
84      * Update listeners
85      */

86     private ListenerList fUpdateListeners = new ListenerList();
87     
88     /**
89      * Map of updates in progress: element path -> list of requests
90      */

91     private Map JavaDoc fRequestsInProgress = new HashMap JavaDoc();
92     
93     /**
94      * Map of dependent requests waiting for parent requests to complete:
95      * element path -> list of requests
96      */

97     private Map JavaDoc fWaitingRequests = new HashMap JavaDoc();
98     
99     /**
100      * Map of viewer states keyed by viewer input mementos
101      */

102     private Map JavaDoc fViewerStates = new LRUMap(20);
103     
104     /**
105      * Pending viewer state to be restored
106      */

107     private ModelDelta fPendingState = null;
108     
109     /**
110      * Set of IMementoManager's that are currently saving state
111      */

112     private Set JavaDoc fPendingStateSaves = new HashSet JavaDoc();
113     
114     /**
115      * Used to queue a viewer input for state restore
116      */

117     private Object JavaDoc fQueuedRestore = null;
118     
119     /**
120      * Used to determine when restoration delta has been processed
121      */

122     class CheckState implements IModelDeltaVisitor {
123         private boolean complete = true;
124         private IModelDelta topDelta = null;
125         /* (non-Javadoc)
126          * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor#visit(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta, int)
127          */

128         public boolean visit(IModelDelta delta, int depth) {
129             if (delta.getFlags() != IModelDelta.NO_CHANGE) {
130                 if (delta.getFlags() == IModelDelta.REVEAL && !(delta.getElement() instanceof IMemento)) {
131                     topDelta = delta;
132                 } else {
133                     complete = false;
134                     return false;
135                 }
136             }
137             return true;
138         }
139         
140         public boolean isComplete() {
141             return complete;
142         }
143         
144         public IModelDelta getTopItemDelta() {
145             return topDelta;
146         }
147     }
148     
149     /**
150      * LRU cache for viewer states
151      */

152     class LRUMap extends LinkedHashMap JavaDoc {
153         private static final long serialVersionUID= 1L;
154         private int fMaxSize;
155         LRUMap(int maxSize) {
156             super();
157             fMaxSize = maxSize;
158         }
159         protected boolean removeEldestEntry(Entry eldest) {
160             return size() > fMaxSize;
161         }
162     }
163     
164     /**
165      * Update type constants
166      */

167     static final int UPDATE_SEQUENCE_BEGINS = 0;
168     static final int UPDATE_SEQUENCE_COMPLETE = 1;
169     static final int UPDATE_BEGINS = 2;
170     static final int UPDATE_COMPLETE = 3;
171     
172     /**
173      * Constant for an empty tree path.
174      */

175     protected static final TreePath EMPTY_TREE_PATH = new TreePath(new Object JavaDoc[]{});
176     
177     // debug flags
178
public static boolean DEBUG_CONTENT_PROVIDER = false;
179     public static boolean DEBUG_UPDATE_SEQUENCE = false;
180     
181     static {
182         DEBUG_CONTENT_PROVIDER = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$
183
Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/contentProvider")); //$NON-NLS-1$
184
DEBUG_UPDATE_SEQUENCE = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$
185
Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/updateSequence")); //$NON-NLS-1$
186
}
187
188     /*
189      * (non-Javadoc)
190      *
191      * @see org.eclipse.jface.viewers.IContentProvider#dispose()
192      */

193     public synchronized void dispose() {
194         // cancel pending updates
195
synchronized (fRequestsInProgress) {
196             Iterator JavaDoc iterator = fRequestsInProgress.values().iterator();
197             while (iterator.hasNext()) {
198                 List JavaDoc requests = (List JavaDoc) iterator.next();
199                 Iterator JavaDoc reqIter = requests.iterator();
200                 while (reqIter.hasNext()) {
201                     ((IRequest) reqIter.next()).cancel();
202                 }
203             }
204             fWaitingRequests.clear();
205         }
206         fModelListeners.clear();
207         fUpdateListeners.clear();
208         disposeAllModelProxies();
209         fViewer = null;
210     }
211     
212     public synchronized boolean isDisposed() {
213         return fViewer == null;
214     }
215
216     /*
217      * (non-Javadoc)
218      *
219      * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
220      * java.lang.Object, java.lang.Object)
221      */

222     public synchronized void inputChanged(Viewer viewer, Object JavaDoc oldInput, Object JavaDoc newInput) {
223         fViewer = viewer;
224         if (oldInput != null) {
225             saveViewerState(oldInput);
226         }
227         if (newInput != oldInput) {
228             disposeAllModelProxies();
229             fTransform.clear();
230             if (newInput != null) {
231                 installModelProxy(newInput);
232                 restoreViewerState(newInput);
233             }
234         }
235     }
236
237     /**
238      * Restores the viewer state unless a save is taking place. If a save is
239      * taking place, the restore is queued.
240      * @param input viewer input
241      */

242     protected synchronized void restoreViewerState(final Object JavaDoc input) {
243         fPendingState = null;
244         if (isSavingState()) {
245             fQueuedRestore = input;
246         } else {
247             startRestoreViewerState(input);
248         }
249     }
250     
251     /**
252      * Restores viewer state for the given input
253      *
254      * @param input viewer input
255      */

256     private synchronized void startRestoreViewerState(final Object JavaDoc input) {
257         fPendingState = null;
258         final IElementMementoProvider defaultProvider = getViewerStateAdapter(input);
259         if (defaultProvider != null) {
260             // build a model delta representing expansion and selection state
261
final ModelDelta delta = new ModelDelta(input, IModelDelta.NO_CHANGE);
262             final XMLMemento inputMemento = XMLMemento.createWriteRoot("VIEWER_INPUT_MEMENTO"); //$NON-NLS-1$
263
final IMementoManager manager = new IMementoManager() {
264             
265                 private IElementMementoRequest fRequest;
266                 
267                 /* (non-Javadoc)
268                  * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.IMementoManager#requestComplete(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest)
269                  */

270                 public synchronized void requestComplete(IElementMementoRequest request) {
271                     if (!request.isCanceled() && (request.getStatus() == null || request.getStatus().isOK())) {
272                         XMLMemento keyMemento = (XMLMemento) delta.getElement();
273                         StringWriter JavaDoc writer = new StringWriter JavaDoc();
274                         try {
275                             keyMemento.save(writer);
276                             final ModelDelta stateDelta = (ModelDelta) fViewerStates.remove(writer.toString());
277                             if (stateDelta != null) {
278                                 if (DEBUG_CONTENT_PROVIDER) {
279                                     System.out.println("RESTORE: " + stateDelta.toString()); //$NON-NLS-1$
280
}
281                                 stateDelta.setElement(input);
282                                 // begin restoration
283
UIJob job = new UIJob("restore state") { //$NON-NLS-1$
284
public IStatus runInUIThread(IProgressMonitor monitor) {
285                                         if (input.equals(getViewer().getInput())) {
286                                             fPendingState = stateDelta;
287                                             doInitialRestore();
288                                         }
289                                         return Status.OK_STATUS;
290                                     }
291                                 
292                                 };
293                                 job.setSystem(true);
294                                 job.schedule();
295                             }
296                         } catch (IOException JavaDoc e) {
297                             DebugUIPlugin.log(e);
298                         }
299                     }
300                 }
301             
302                 /* (non-Javadoc)
303                  * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.IMementoManager#processReqeusts()
304                  */

305                 public void processReqeusts() {
306                     defaultProvider.encodeElements(new IElementMementoRequest[]{fRequest});
307                 }
308             
309                 /* (non-Javadoc)
310                  * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.IMementoManager#addRequest(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest)
311                  */

312                 public synchronized void addRequest(IElementMementoRequest req) {
313                     fRequest = req;
314                 }
315             
316             };
317             manager.addRequest(new ElementMementoRequest(ModelContentProvider.this, manager, getPresentationContext(),
318                                     delta.getElement(), getViewerTreePath(delta), inputMemento, delta));
319             manager.processReqeusts();
320         }
321     }
322     
323     /**
324      * Restore selection/expansion based on items already in the viewer
325      */

326     abstract protected void doInitialRestore();
327     
328     /**
329      * @param delta
330      */

331     abstract void doRestore(final ModelDelta delta);
332     
333     /**
334      * Perform any restoration required for the given tree path.
335      *
336      * @param path
337      */

338     protected synchronized void doRestore(final TreePath path) {
339         if (fPendingState == null) {
340             return;
341         }
342         final IElementMementoProvider defaultProvider = getViewerStateAdapter(getViewer().getInput());
343         IModelDeltaVisitor visitor = new IModelDeltaVisitor() {
344             public boolean visit(IModelDelta delta, int depth) {
345                 if (delta.getParentDelta() == null) {
346                     return true;
347                 }
348                 Object JavaDoc element = delta.getElement();
349                 Object JavaDoc potentialMatch = path.getSegment(depth - 1);
350                 if (element instanceof IMemento) {
351                     if (defaultProvider != null) {
352                         defaultProvider.compareElements(new IElementCompareRequest[]{
353                                 new ElementCompareRequest(ModelContentProvider.this,
354                                         potentialMatch, path, (IMemento) element, (ModelDelta)delta)});
355                     }
356                 } else {
357                     if (element.equals(potentialMatch)) {
358                         // already processed - visit children
359
return path.getSegmentCount() > depth;
360                     }
361                 }
362                 return false;
363             }
364         };
365         fPendingState.accept(visitor);
366     }
367
368     /**
369      * Saves the viewer's state for the previous input.
370      *
371      * @param oldInput
372      */

373     protected void saveViewerState(Object JavaDoc input) {
374         IElementMementoProvider stateProvider = getViewerStateAdapter(input);
375         if (stateProvider != null) {
376             // build a model delta representing expansion and selection state
377
ModelDelta delta = new ModelDelta(input, IModelDelta.NO_CHANGE);
378             buildViewerState(delta);
379             if (delta.getChildDeltas().length > 0) {
380                 // encode delta with mementos in place of elements, in non-UI thread
381
encodeDelta(delta, stateProvider);
382             }
383         }
384     }
385
386     /**
387      * Encodes delta elements into mementos using the given provider.
388      *
389      * @param delta
390      * @param stateProvider
391      */

392     protected void encodeDelta(final ModelDelta rootDelta, final IElementMementoProvider defaultProvider) {
393         final XMLMemento inputMemento = XMLMemento.createWriteRoot("VIEWER_INPUT_MEMENTO"); //$NON-NLS-1$
394
final XMLMemento childrenMemento = XMLMemento.createWriteRoot("CHILDREN_MEMENTO"); //$NON-NLS-1$
395
final IMementoManager manager = new IMementoManager() {
396         
397             /**
398              * list of memento requests
399              */

400             private List JavaDoc requests = new ArrayList JavaDoc();
401             private boolean abort = false;
402             
403             /* (non-Javadoc)
404              * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.IMementoManager#requestComplete(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest)
405              */

406             public synchronized void requestComplete(IElementMementoRequest request) {
407                 if (!abort) {
408                     if (!request.isCanceled() && (request.getStatus() == null || request.getStatus().isOK())) {
409                         requests.remove(request);
410                         if (requests.isEmpty()) {
411                             XMLMemento keyMemento = (XMLMemento) rootDelta.getElement();
412                             StringWriter JavaDoc writer = new StringWriter JavaDoc();
413                             try {
414                                 keyMemento.save(writer);
415                                 fViewerStates.put(writer.toString(), rootDelta);
416                             } catch (IOException JavaDoc e) {
417                                 DebugUIPlugin.log(e);
418                             }
419                             stateSaveComplete(this);
420                         }
421                     } else {
422                         abort = true;
423                         Iterator JavaDoc iterator = requests.iterator();
424                         while (iterator.hasNext()) {
425                             IElementMementoRequest req = (IElementMementoRequest) iterator.next();
426                             req.cancel();
427                         }
428                         requests.clear();
429                         stateSaveComplete(this);
430                     }
431                 }
432             }
433         
434             /* (non-Javadoc)
435              * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.IMementoManager#processReqeusts()
436              */

437             public synchronized void processReqeusts() {
438                 defaultProvider.encodeElements((IElementMementoRequest[]) requests.toArray(new IElementMementoRequest[requests.size()]));
439             }
440         
441             /* (non-Javadoc)
442              * @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.IMementoManager#addRequest(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest)
443              */

444             public synchronized void addRequest(IElementMementoRequest request) {
445                 requests.add(request);
446             }
447         
448         };
449         IModelDeltaVisitor visitor = new IModelDeltaVisitor() {
450             public boolean visit(IModelDelta delta, int depth) {
451                 if (delta.getParentDelta() == null) {
452                     manager.addRequest(
453                         new ElementMementoRequest(ModelContentProvider.this, manager, getPresentationContext(),
454                                 delta.getElement(), getViewerTreePath(delta), inputMemento, (ModelDelta)delta));
455                 } else {
456                     manager.addRequest(
457                         new ElementMementoRequest(ModelContentProvider.this, manager, getPresentationContext(),
458                                 delta.getElement(), getViewerTreePath(delta), childrenMemento.createChild("CHILD_ELEMENT"), (ModelDelta)delta)); //$NON-NLS-1$
459
}
460                 return true;
461             }
462         };
463         rootDelta.accept(visitor);
464         stateSaveStarted(manager);
465         manager.processReqeusts();
466     }
467     
468     /**
469      * Called when a state save is starting.
470      *
471      * @param manager
472      */

473     private synchronized void stateSaveStarted(IMementoManager manager) {
474         fPendingStateSaves.add(manager);
475     }
476     
477     /**
478      * Called when a state save is complete.
479      *
480      * @param manager
481      */

482     private synchronized void stateSaveComplete(IMementoManager manager) {
483         fPendingStateSaves.remove(manager);
484         if (fQueuedRestore != null) {
485             Object JavaDoc temp = fQueuedRestore;
486             fQueuedRestore = null;
487             restoreViewerState(temp);
488         }
489     }
490     
491     /**
492      * Returns whether any state saving is in progress.
493      *
494      * @return whether any state saving is in progress
495      */

496     private synchronized boolean isSavingState() {
497         return !fPendingStateSaves.isEmpty();
498     }
499
500     /**
501      * Builds a delta with the given root delta for expansion/selection state.
502      *
503      * @param delta root delta
504      */

505     protected abstract void buildViewerState(ModelDelta delta);
506
507     /**
508      * Uninstalls the model proxy installed for the given element, if any.
509      *
510      * @param element
511      */

512     protected synchronized void disposeModelProxy(Object JavaDoc element) {
513         IModelProxy proxy = (IModelProxy) fModelProxies.remove(element);
514         if (proxy != null) {
515             proxy.dispose();
516         }
517     }
518
519     /**
520      * Uninstalls each model proxy
521      */

522     protected synchronized void disposeAllModelProxies() {
523         Iterator JavaDoc updatePolicies = fModelProxies.values().iterator();
524         while (updatePolicies.hasNext()) {
525             IModelProxy proxy = (IModelProxy) updatePolicies.next();
526             proxy.dispose();
527         }
528         fModelProxies.clear();
529     }
530
531     /**
532      * Installs the model proxy for the given element into this content provider
533      * if not already installed.
534      *
535      * @param element
536      * element to install an update policy for
537      */

538     protected synchronized void installModelProxy(Object JavaDoc element) {
539         if (!fModelProxies.containsKey(element)) {
540             IModelProxyFactory modelProxyFactory = getModelProxyFactoryAdapter(element);
541             if (modelProxyFactory != null) {
542                 final IModelProxy proxy = modelProxyFactory.createModelProxy(
543                         element, getPresentationContext());
544                 if (proxy != null) {
545                     fModelProxies.put(element, proxy);
546                     Job job = new Job("Model Proxy installed notification job") {//$NON-NLS-1$
547
protected IStatus run(IProgressMonitor monitor) {
548                             if (!monitor.isCanceled()) {
549                                 proxy.init(getPresentationContext());
550                                 Object JavaDoc[] mcls = fModelListeners.getListeners();
551                                 for (int i = 0; i < mcls.length; i++) {
552                                     proxy.addModelChangedListener((IModelChangedListener) mcls[i]);
553                                 }
554                                 proxy
555                                         .addModelChangedListener(ModelContentProvider.this);
556                                 proxy.installed(getViewer());
557                             }
558                             return Status.OK_STATUS;
559                         }
560                     };
561                     job.setSystem(true);
562                     job.schedule();
563                 }
564             }
565         }
566     }
567
568     /**
569      * Returns the model proxy factory for the given element or
570      * <code>null</code> if none.
571      *
572      * @param element
573      * element to retrieve adapter for
574      * @return model proxy factory adapter or <code>null</code>
575      */

576     protected IModelProxyFactory getModelProxyFactoryAdapter(Object JavaDoc element) {
577         IModelProxyFactory adapter = null;
578         if (element instanceof IModelProxyFactory) {
579             adapter = (IModelProxyFactory) element;
580         } else if (element instanceof IAdaptable) {
581             IAdaptable adaptable = (IAdaptable) element;
582             adapter = (IModelProxyFactory) adaptable.getAdapter(IModelProxyFactory.class);
583             if (adapter == null && !(element instanceof PlatformObject)) {
584                 // for objects that don't properly subclass PlatformObject to inherit default
585
// adapters, just delegate to the adapter factory
586
adapter = (IModelProxyFactory) new DebugElementAdapterFactory().getAdapter(element, IModelProxyFactory.class);
587             }
588         }
589         return adapter;
590     }
591     
592     /**
593      * Returns the viewer state adapter for the given element or
594      * <code>null</code> if none.
595      *
596      * @param element
597      * element to retrieve adapter for
598      * @return viewer state adapter or <code>null</code>
599      */

600     protected IElementMementoProvider getViewerStateAdapter(Object JavaDoc element) {
601         IElementMementoProvider adapter = null;
602         if (element instanceof IElementMementoProvider) {
603             adapter = (IElementMementoProvider) element;
604         } else if (element instanceof IAdaptable) {
605             IAdaptable adaptable = (IAdaptable) element;
606             adapter = (IElementMementoProvider) adaptable.getAdapter(IElementMementoProvider.class);
607         }
608         return adapter;
609     }
610
611     /**
612      * Returns the presentation context for this content provider.
613      *
614      * @return presentation context
615      */

616     protected abstract IPresentationContext getPresentationContext();
617
618     /*
619      * (non-Javadoc)
620      *
621      * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelChangedListener#modelChanged(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta)
622      */

623     public void modelChanged(final IModelDelta delta, final IModelProxy proxy) {
624         WorkbenchJob job = new WorkbenchJob("process model delta") { //$NON-NLS-1$
625
public IStatus runInUIThread(IProgressMonitor monitor) {
626                 if (!proxy.isDisposed()) {
627                     updateNodes(new IModelDelta[] { delta });
628                 }
629                 return Status.OK_STATUS;
630             }
631         };
632         job.setSystem(true);
633         job.schedule();
634         
635     }
636
637     protected void updateNodes(IModelDelta[] nodes) {
638         for (int i = 0; i < nodes.length; i++) {
639             IModelDelta node = nodes[i];
640             int flags = node.getFlags();
641
642             if ((flags & IModelDelta.ADDED) != 0) {
643                 handleAdd(node);
644             }
645             if ((flags & IModelDelta.REMOVED) != 0) {
646                 handleRemove(node);
647             }
648             if ((flags & IModelDelta.CONTENT) != 0) {
649                 handleContent(node);
650             }
651             if ((flags & IModelDelta.EXPAND) != 0) {
652                 handleExpand(node);
653             }
654             if ((flags & IModelDelta.COLLAPSE) != 0) {
655                 handleCollapse(node);
656             }
657             if ((flags & IModelDelta.SELECT) != 0) {
658                 handleSelect(node);
659             }
660             if ((flags & IModelDelta.STATE) != 0) {
661                 handleState(node);
662             }
663             if ((flags & IModelDelta.INSERTED) != 0) {
664                 handleInsert(node);
665             }
666             if ((flags & IModelDelta.REPLACED) != 0) {
667                 handleReplace(node);
668             }
669             if ((flags & IModelDelta.INSTALL) != 0) {
670                 handleInstall(node);
671             }
672             if ((flags & IModelDelta.UNINSTALL) != 0) {
673                 handleUninstall(node);
674             }
675             if ((flags & IModelDelta.REVEAL) != 0) {
676                 handleReveal(node);
677             }
678             updateNodes(node.getChildDeltas());
679         }
680     }
681     
682     /**
683      * Returns the content adapter for the given element or
684      * <code>null</code> if none.
685      *
686      * @param element
687      * element to retrieve adapter for
688      * @return content adapter or <code>null</code>
689      */

690     protected IElementContentProvider getContentAdapter(Object JavaDoc element) {
691         IElementContentProvider adapter = null;
692         if (element instanceof IElementContentProvider) {
693             adapter = (IElementContentProvider) element;
694         } else if (element instanceof IAdaptable) {
695             IAdaptable adaptable = (IAdaptable) element;
696             adapter = (IElementContentProvider) adaptable.getAdapter(IElementContentProvider.class);
697             if (adapter == null && !(element instanceof PlatformObject)) {
698                 // for objects that don't properly subclass PlatformObject to inherit default
699
// adapters, just delegate to the adapter factory
700
adapter = (IElementContentProvider) new DebugElementAdapterFactory().getAdapter(element, IElementContentProvider.class);
701             }
702         }
703         return adapter;
704     }
705
706     protected abstract void handleState(IModelDelta delta);
707
708     protected abstract void handleSelect(IModelDelta delta);
709
710     protected abstract void handleExpand(IModelDelta delta);
711     
712     protected abstract void handleCollapse(IModelDelta delta);
713
714     protected abstract void handleContent(IModelDelta delta);
715
716     protected abstract void handleRemove(IModelDelta delta);
717
718     protected abstract void handleAdd(IModelDelta delta);
719
720     protected abstract void handleInsert(IModelDelta delta);
721
722     protected abstract void handleReplace(IModelDelta delta);
723     
724     protected abstract void handleReveal(IModelDelta delta);
725     
726     protected void handleInstall(IModelDelta delta) {
727         installModelProxy(delta.getElement());
728     }
729     
730     protected void handleUninstall(IModelDelta delta) {
731         disposeModelProxy(delta.getElement());
732     }
733
734     /**
735      * Returns a tree path for the node, *not* including the root element.
736      *
737      * @param node
738      * model delta
739      * @return corresponding tree path
740      */

741     protected TreePath getViewerTreePath(IModelDelta node) {
742         ArrayList JavaDoc list = new ArrayList JavaDoc();
743         IModelDelta parentDelta = node.getParentDelta();
744         while (parentDelta != null) {
745             list.add(0, node.getElement());
746             node = parentDelta;
747             parentDelta = node.getParentDelta();
748         }
749         return new TreePath(list.toArray());
750     }
751
752     /**
753      * Returns the viewer this content provider is working for.
754      *
755      * @return viewer
756      */

757     protected Viewer getViewer() {
758         return fViewer;
759     }
760     
761     /**
762      * Translates and returns the given child index from the viewer coordinate
763      * space to the model coordinate space.
764      *
765      * @param parentPath path to parent element
766      * @param index index of child element in viewer (filtered) space
767      * @return index of child element in model (raw) space
768      */

769     public /* protected */ int viewToModelIndex(TreePath parentPath, int index) {
770         return fTransform.viewToModelIndex(parentPath, index);
771     }
772     
773     /**
774      * Translates and returns the given child count from the viewer coordinate
775      * space to the model coordinate space.
776      *
777      * @param parentPath path to parent element
778      * @param count number of child elements in viewer (filtered) space
779      * @return number of child elements in model (raw) space
780      */

781     public /* protected */ int viewToModelCount(TreePath parentPath, int count) {
782         return fTransform.viewToModelCount(parentPath, count);
783     }
784     
785     /**
786      * Translates and returns the given child index from the model coordinate
787      * space to the viewer coordinate space.
788      *
789      * @param parentPath path to parent element
790      * @param index index of child element in model (raw) space
791      * @return index of child element in viewer (filtered) space or -1 if filtered
792      */

793     protected int modelToViewIndex(TreePath parentPath, int index) {
794         return fTransform.modelToViewIndex(parentPath, index);
795     }
796     
797     /**
798      * Translates and returns the given child count from the model coordinate
799      * space to the viewer coordinate space.
800      *
801      * @param parentPath path to parent element
802      * @param count child count element in model (raw) space
803      * @return child count in viewer (filtered) space
804      */

805     protected int modelToViewChildCount(TreePath parentPath, int count) {
806         return fTransform.modelToViewCount(parentPath, count);
807     }
808     
809     /**
810      * Notes that the child at the specified index of the given parent element
811      * has been filtered from the viewer. Returns whether the child at the given
812      * index was already filtered.
813      *
814      * @param parentPath path to parent element
815      * @param index index of child element to be filtered
816      * @param element the filtered element
817      * @return whether the child was already filtered
818      */

819     protected boolean addFilteredIndex(TreePath parentPath, int index, Object JavaDoc element) {
820         return fTransform.addFilteredIndex(parentPath, index, element);
821     }
822     
823     /**
824      * Notes that the element at the given index has been removed from its parent
825      * and filtered indexes should be updated accordingly.
826      *
827      * @param parentPath path to parent element
828      * @param index index of element that was removed
829      */

830     protected void removeElementFromFilters(TreePath parentPath, int index) {
831         fTransform.removeElementFromFilters(parentPath, index);
832     }
833     
834     /**
835      * Removes the given element from filtered elements of the given parent
836      * element. Return true if the element was removed, otherwise false.
837      *
838      * @param parentPath path to parent element
839      * @param element element to remove
840      * @return whether the element was removed
841      */

842     protected boolean removeElementFromFilters(TreePath parentPath, Object JavaDoc element) {
843         return fTransform.removeElementFromFilters(parentPath, element);
844     }
845     
846     /**
847      * The child count for a parent has been computed. Ensure any filtered items
848      * above the given count are cleared.
849      *
850      * @param parentPath path to parent element
851      * @param childCount number of children
852      */

853     protected void setModelChildCount(TreePath parentPath, int childCount) {
854         fTransform.setModelChildCount(parentPath, childCount);
855     }
856     
857     /**
858      * Returns whether the given element is filtered.
859      *
860      * @param parentElementOrTreePath
861      * the parent element or path
862      * @param element
863      * the child element
864      * @return whether to filter the element
865      */

866     protected boolean shouldFilter(Object JavaDoc parentElementOrTreePath, Object JavaDoc element) {
867         ViewerFilter[] filters = ((StructuredViewer)fViewer).getFilters();
868         if (filters.length > 0) {
869             for (int j = 0; j < filters.length; j++) {
870                 if (!(filters[j].select(fViewer, parentElementOrTreePath, element))) {
871                     return true;
872                 }
873             }
874         }
875         return false;
876     }
877     
878     /**
879      * Returns whether the given index of the specified parent was previously filtered.
880      *
881      * @param parentPath
882      * @param index
883      * @return whether the element at the given index was filtered
884      */

885     protected boolean isFiltered(TreePath parentPath, int index) {
886         return fTransform.isFiltered(parentPath, index);
887     }
888     
889     /**
890      * Notification the given element is being unmapped.
891      *
892      * @param path
893      */

894     protected void unmapPath(TreePath path) {
895         //System.out.println("Unmap " + path.getLastSegment());
896
fTransform.clear(path);
897         cancelSubtreeUpdates(path);
898     }
899
900     /**
901      * Returns filtered children or <code>null</code>
902      * @param parent
903      * @return filtered children or <code>null</code>
904      */

905     protected int[] getFilteredChildren(TreePath parent) {
906         return fTransform.getFilteredChildren(parent);
907     }
908     
909     protected void clearFilteredChild(TreePath parent, int modelIndex) {
910         fTransform.clear(parent, modelIndex);
911     }
912     
913     protected void clearFilters(TreePath parent) {
914         fTransform.clear(parent);
915     }
916
917     protected synchronized IModelDelta checkIfRestoreComplete() {
918         if (fPendingState == null) {
919             return null;
920         }
921         CheckState state = new CheckState();
922         fPendingState.accept(state);
923         if (state.isComplete()) {
924             fPendingState = null;
925             if (DEBUG_CONTENT_PROVIDER) {
926                 System.out.println("RESTORE COMPELTE"); //$NON-NLS-1$
927
}
928             return state.getTopItemDelta();
929         }
930         return null;
931     }
932     
933     void addViewerUpdateListener(IViewerUpdateListener listener) {
934         fUpdateListeners.add(listener);
935     }
936     
937     void removeViewerUpdateListener(IViewerUpdateListener listener) {
938         fUpdateListeners.remove(listener);
939     }
940     
941     /**
942      * Notification an update request has started
943      *
944      * @param update
945      */

946     void updateStarted(ViewerUpdateMonitor update) {
947         boolean begin = false;
948         synchronized (fRequestsInProgress) {
949             begin = fRequestsInProgress.isEmpty();
950             List JavaDoc requests = (List JavaDoc) fRequestsInProgress.get(update.getSchedulingPath());
951             if (requests == null) {
952                 requests = new ArrayList JavaDoc();
953                 fRequestsInProgress.put(update.getSchedulingPath(), requests);
954             }
955             requests.add(update);
956         }
957         if (begin) {
958             if (DEBUG_UPDATE_SEQUENCE) {
959                 System.out.println("MODEL SEQUENCE BEGINS"); //$NON-NLS-1$
960
}
961             notifyUpdate(UPDATE_SEQUENCE_BEGINS, null);
962         }
963         if (DEBUG_UPDATE_SEQUENCE) {
964             System.out.println("\tBEGIN - " + update); //$NON-NLS-1$
965
}
966         notifyUpdate(UPDATE_BEGINS, update);
967     }
968     
969     /**
970      * Notification an update request has completed
971      *
972      * @param update
973      */

974     void updateComplete(ViewerUpdateMonitor update) {
975         boolean end = false;
976         synchronized (fRequestsInProgress) {
977             List JavaDoc requests = (List JavaDoc) fRequestsInProgress.get(update.getSchedulingPath());
978             if (requests != null) {
979                 requests.remove(update);
980                 trigger(update);
981                 if (requests.isEmpty()) {
982                     fRequestsInProgress.remove(update.getSchedulingPath());
983                 }
984             }
985             end = fRequestsInProgress.isEmpty();
986         }
987         notifyUpdate(UPDATE_COMPLETE, update);
988         if (DEBUG_UPDATE_SEQUENCE) {
989             System.out.println("\tEND - " + update); //$NON-NLS-1$
990
}
991         if (end) {
992             if (DEBUG_UPDATE_SEQUENCE) {
993                 System.out.println("MODEL SEQUENCE ENDS"); //$NON-NLS-1$
994
}
995             notifyUpdate(UPDATE_SEQUENCE_COMPLETE, null);
996         }
997     }
998     
999     protected void notifyUpdate(final int type, final IViewerUpdate update) {
1000        if (!fUpdateListeners.isEmpty()) {
1001            Object JavaDoc[] listeners = fUpdateListeners.getListeners();
1002            for (int i = 0; i < listeners.length; i++) {
1003                final IViewerUpdateListener listener = (IViewerUpdateListener) listeners[i];
1004                SafeRunner.run(new ISafeRunnable() {
1005                    public void run() throws Exception JavaDoc {
1006                        switch (type) {
1007                            case UPDATE_SEQUENCE_BEGINS:
1008                                listener.viewerUpdatesBegin();
1009                                break;
1010                            case UPDATE_SEQUENCE_COMPLETE:
1011                                listener.viewerUpdatesComplete();
1012                                break;
1013                            case UPDATE_BEGINS:
1014                                listener.updateStarted(update);
1015                                break;
1016                            case UPDATE_COMPLETE:
1017                                listener.updateComplete(update);
1018                                break;
1019                        }
1020                    }
1021                    public void handleException(Throwable JavaDoc exception) {
1022                        DebugUIPlugin.log(exception);
1023                    }
1024                });
1025            }
1026        }
1027    }
1028    
1029    protected void cancelSubtreeUpdates(TreePath path) {
1030        synchronized (fRequestsInProgress) {
1031            Iterator JavaDoc iterator = fRequestsInProgress.entrySet().iterator();
1032            while (iterator.hasNext()) {
1033                Entry entry = (Entry) iterator.next();
1034                TreePath entryPath = (TreePath) entry.getKey();
1035                if (entryPath.startsWith(path, null)) {
1036                    List JavaDoc requests = (List JavaDoc) entry.getValue();
1037                    Iterator JavaDoc reqIter = requests.iterator();
1038                    while (reqIter.hasNext()) {
1039                        ((IRequest)reqIter.next()).cancel();
1040                    }
1041                }
1042            }
1043            List JavaDoc purge = new ArrayList JavaDoc();
1044            iterator = fWaitingRequests.keySet().iterator();
1045            while (iterator.hasNext()) {
1046                TreePath entryPath = (TreePath) iterator.next();
1047                if (entryPath.startsWith(path, null)) {
1048                    purge.add(entryPath);
1049                }
1050            }
1051            iterator = purge.iterator();
1052            while (iterator.hasNext()) {
1053                fWaitingRequests.remove(iterator.next());
1054            }
1055        }
1056    }
1057    
1058    /**
1059     * Returns whether this given request should be run, or should wait for parent
1060     * update to complete.
1061     *
1062     * @param update
1063     * @return whether to start the given request
1064     */

1065    void schedule(ViewerUpdateMonitor update) {
1066        synchronized (fRequestsInProgress) {
1067            TreePath schedulingPath = update.getSchedulingPath();
1068            List JavaDoc requests = (List JavaDoc) fWaitingRequests.get(schedulingPath);
1069            if (requests == null) {
1070                // no waiting requests
1071
TreePath parentPath = schedulingPath;
1072                while (fRequestsInProgress.get(parentPath) == null) {
1073                    parentPath = parentPath.getParentPath();
1074                    if (parentPath == null) {
1075                        // no running requests: start request
1076
update.start();
1077                        return;
1078                    }
1079                }
1080                // request running on parent, add to waiting list
1081
requests = new ArrayList JavaDoc();
1082                requests.add(update);
1083                fWaitingRequests.put(schedulingPath, requests);
1084            } else {
1085                // there are waiting requests: coalesce with existing request?
1086
Iterator JavaDoc reqIter = requests.iterator();
1087                while (reqIter.hasNext()) {
1088                    ViewerUpdateMonitor waiting = (ViewerUpdateMonitor) reqIter.next();
1089                    if (waiting.coalesce(update)) {
1090                        // coalesced with existing request, done
1091
return;
1092                    }
1093                }
1094                // add to list of waiting requests
1095
requests.add(update);
1096                return;
1097            }
1098        }
1099    }
1100    
1101    /**
1102     * Triggers waiting requests based on the given request that just completed.
1103     *
1104     * TODO: should we cancel child updates if a request has been canceled?
1105     *
1106     * @param request
1107     */

1108    void trigger(ViewerUpdateMonitor request) {
1109        if (fWaitingRequests.isEmpty()) {
1110            return;
1111        }
1112        TreePath schedulingPath = request.getSchedulingPath();
1113        List JavaDoc waiting = (List JavaDoc) fWaitingRequests.get(schedulingPath);
1114        if (waiting == null) {
1115            // no waiting, update the entry with the shortest path
1116
int length = Integer.MAX_VALUE;
1117            Iterator JavaDoc entries = fWaitingRequests.entrySet().iterator();
1118            Entry candidate = null;
1119            while (entries.hasNext()) {
1120                Entry entry = (Entry) entries.next();
1121                TreePath key = (TreePath) entry.getKey();
1122                if (key.getSegmentCount() < length) {
1123                    candidate = entry;
1124                    length = key.getSegmentCount();
1125                }
1126            }
1127            if (candidate != null) {
1128                startHighestPriorityRequest((TreePath) candidate.getKey(), (List JavaDoc) candidate.getValue());
1129            }
1130        } else {
1131            // start the highest priority request
1132
startHighestPriorityRequest(schedulingPath, waiting);
1133        }
1134    }
1135
1136    /**
1137     * @param key
1138     * @param waiting
1139     */

1140    private void startHighestPriorityRequest(TreePath key, List JavaDoc waiting) {
1141        int priority = 4;
1142        ViewerUpdateMonitor next = null;
1143        Iterator JavaDoc requests = waiting.iterator();
1144        while (requests.hasNext()) {
1145            ViewerUpdateMonitor vu = (ViewerUpdateMonitor) requests.next();
1146            if (vu.getPriority() < priority) {
1147                next = vu;
1148                priority = next.getPriority();
1149            }
1150        }
1151        waiting.remove(next);
1152        if (waiting.isEmpty()) {
1153            fWaitingRequests.remove(key);
1154        }
1155        next.start();
1156    }
1157    
1158    /**
1159     * Registers the given listener for model delta notification.
1160     *
1161     * @param listener model delta listener
1162     */

1163    void addModelChangedListener(IModelChangedListener listener) {
1164        fModelListeners.add(listener);
1165        Iterator JavaDoc proxies = fModelProxies.values().iterator();
1166        while (proxies.hasNext()) {
1167            IModelProxy proxy = (IModelProxy) proxies.next();
1168            proxy.addModelChangedListener(listener);
1169        }
1170    }
1171    
1172    /**
1173     * Unregisters the given listener from model delta notification.
1174     *
1175     * @param listener model delta listener
1176     */

1177    void removeModelChangedListener(IModelChangedListener listener) {
1178        fModelListeners.remove(listener);
1179        Iterator JavaDoc proxies = fModelProxies.values().iterator();
1180        while (proxies.hasNext()) {
1181            IModelProxy proxy = (IModelProxy) proxies.next();
1182            proxy.removeModelChangedListener(listener);
1183        }
1184    }
1185    
1186    /**
1187     * Returns the element corresponding to the given tree path.
1188     *
1189     * @param path tree path
1190     * @return model element
1191     */

1192    protected Object JavaDoc getElement(TreePath path) {
1193        if (path.getSegmentCount() > 0) {
1194            return path.getLastSegment();
1195        }
1196        return getViewer().getInput();
1197    }
1198    
1199    /**
1200     * Reschedule any children updates in progress for the given parent
1201     * that have a start index greater than the given index. An element
1202     * has been removed at this index, invalidating updates in progress.
1203     *
1204     * @param parentPath view tree path to parent element
1205     * @param modelIndex index at which an element was removed
1206     */

1207    protected void rescheduleUpdates(TreePath parentPath, int modelIndex) {
1208        synchronized (fRequestsInProgress) {
1209            List JavaDoc requests = (List JavaDoc)fRequestsInProgress.get(parentPath);
1210            List JavaDoc reCreate = null;
1211            if (requests != null) {
1212                Iterator JavaDoc iterator = requests.iterator();
1213                while (iterator.hasNext()) {
1214                    IViewerUpdate update = (IViewerUpdate) iterator.next();
1215                    if (update instanceof IChildrenUpdate) {
1216                        IChildrenUpdate childrenUpdate = (IChildrenUpdate) update;
1217                        if (childrenUpdate.getOffset() > modelIndex) {
1218                            childrenUpdate.cancel();
1219                            if (reCreate == null) {
1220                                reCreate = new ArrayList JavaDoc();
1221                            }
1222                            reCreate.add(childrenUpdate);
1223                            if (DEBUG_CONTENT_PROVIDER) {
1224                                System.out.println("canceled update in progress handling REMOVE: " + childrenUpdate); //$NON-NLS-1$
1225
}
1226                        }
1227                    }
1228                }
1229            }
1230            requests = (List JavaDoc)fWaitingRequests.get(parentPath);
1231            if (requests != null) {
1232                Iterator JavaDoc iterator = requests.iterator();
1233                while (iterator.hasNext()) {
1234                    IViewerUpdate update = (IViewerUpdate) iterator.next();
1235                    if (update instanceof IChildrenUpdate) {
1236                        IChildrenUpdate childrenUpdate = (IChildrenUpdate) update;
1237                        if (childrenUpdate.getOffset() > modelIndex) {
1238                            ((ChildrenUpdate)childrenUpdate).setOffset(childrenUpdate.getOffset() - 1);
1239                            if (DEBUG_CONTENT_PROVIDER) {
1240                                System.out.println("modified waiting update handling REMOVE: " + childrenUpdate); //$NON-NLS-1$
1241
}
1242                        }
1243                    }
1244                }
1245            }
1246            // re-schedule canceled updates at new position.
1247
// have to do this last else the requests would be waiting and
1248
// get modified.
1249
if (reCreate != null) {
1250                Iterator JavaDoc iterator = reCreate.iterator();
1251                while (iterator.hasNext()) {
1252                    IChildrenUpdate childrenUpdate = (IChildrenUpdate) iterator.next();
1253                    int start = childrenUpdate.getOffset() - 1;
1254                    int end = start + childrenUpdate.getLength();
1255                    for (int i = start; i < end; i++) {
1256                        ((TreeModelContentProvider)this).doUpdateElement(parentPath, i);
1257                    }
1258                }
1259            }
1260        }
1261    }
1262}
1263
Popular Tags