Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: f3b92cb8a44b404168be09f866ea84aefa85607e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
/*******************************************************************************
 * Copyright (c) 2006, 2016 Wind River Systems and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.dsf.ui.viewmodel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;

import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.internal.DsfPlugin;
import org.eclipse.cdt.dsf.internal.LoggingUtils;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.cdt.dsf.ui.concurrent.SimpleDisplayExecutor;
import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor;
import org.eclipse.cdt.dsf.ui.viewmodel.update.UserEditEvent;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate;
import org.eclipse.swt.widgets.Display;

/**
 * View model provider implements the asynchronous view model functionality for 
 * a single view.  This provider is just a holder which further delegates the
 * model provider functionality to the view model nodes that need
 * to be configured with each provider.
 * 
 * <p/>
 * The view model provider, often does not provide the model for the entire 
 * view.  Rather, it needs to be able to plug in at any level in the viewer's
 * content model and provide data for a sub-tree.
 * 
 * <p/>
 * Clients are intended to extend this class.
 * 
 * @see IModelProxy
 * @see IVMNode
 * 
 * @since 1.0
 */
abstract public class AbstractVMProvider implements IVMProvider, IVMEventListener
{
    // debug flags
    /** @since 1.1 */
    public static String DEBUG_PRESENTATION_ID = null;

    /** @since 1.1 */
    public static boolean DEBUG_CONTENT_PROVIDER = false;

    /** @since 1.1 */
    public static boolean DEBUG_DELTA = false;

    static {
        DEBUG_PRESENTATION_ID = Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/presentationId"); //$NON-NLS-1$
        if (!DsfUIPlugin.DEBUG || "".equals(DEBUG_PRESENTATION_ID)) { //$NON-NLS-1$
            DEBUG_PRESENTATION_ID = null;
        }
        DEBUG_CONTENT_PROVIDER = DsfUIPlugin.DEBUG && Boolean.parseBoolean(
         Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/contentProvider")); //$NON-NLS-1$

        DEBUG_DELTA = DsfUIPlugin.DEBUG && Boolean.parseBoolean(
            Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/vm/delta")); //$NON-NLS-1$
    }   

    /** Reference to the VM adapter that owns this provider */
    private final AbstractVMAdapter fVMAdapter;
    
    /** The presentation context that this provider is associated with */
    private final IPresentationContext fPresentationContext;

    /**
     * The executor that this VM provider operates in.  This executor will be 
     * initialized properly when we can access the display from the 
     * IPresentationContext object (bug 213629).  For now utilize the 
     * assumption that there is only one display. 
     */
    private final Executor fExecutor = SimpleDisplayExecutor.getSimpleDisplayExecutor(Display.getDefault());

    /**
     * The element content provider implementation that this provider delegates to.
     * Sub-classes may override the content strategy used for custom functionality.   
     */
    private final IElementContentProvider fContentStrategy;
    
    /**
     * The list of active model proxies in this provider.  A new model
     * proxy is created when a viewer has a new input element 
     * (see {@link #createModelProxy(Object, IPresentationContext)}).  
     * Typically there will be only one active model proxy in a given
     * provider.  However, if a view model provider fills only a sub-tree
     * in a viewer, and there are several sub-trees active in the same viewer
     * at the same time, each of these sub-trees will have it's own model 
     * proxy.
     */
    private List<IVMModelProxy> fActiveModelProxies = new LinkedList<IVMModelProxy>();

    /**
     * Convenience constant.
     */
    private static final IVMNode[] EMPTY_NODES_ARRAY = new IVMNode[0];
    
    
    /**
     * The mapping of parent to child nodes.  
     */
    private Map<IVMNode,IVMNode[]> fChildNodesMap = 
        new HashMap<IVMNode,IVMNode[]>();
        
    /** 
     * Cached array of all the configured view model nodes.  It is generated 
     * based on the child nodes map.
     */
    private IVMNode[] fNodesListCache = null;

    /**
     * Flag indicating that the provider is disposed.
     */
    private boolean fDisposed = false;

    /**
     * The root node for this model provider.  The root layout node could be 
     * null when first created, to allow sub-classes to properly configure the 
     * root node in the sub-class constructor.  
     */
    private IRootVMNode fRootNode;
    
    private class EventInfo {
        EventInfo(Object event, RequestMonitor rm) {
            fEvent = event;
            fClientRm = rm;
        }
        Object fEvent;
        RequestMonitor fClientRm;
    }
    
    private class ModelProxyEventQueue {
        /** The event actively being handled */
        EventInfo fCurrentEvent;

		/**
		 * The request monitor we created to handle fCurrentEvent. It is
		 * responsible for calling <code>done</code> on the client RM of that
		 * event.
		 */
        RequestMonitor fCurrentRm;
        
        /** The queue */
        List<EventInfo> fEventQueue = new LinkedList<EventInfo>();
    }
    
    private Map<IVMModelProxy, ModelProxyEventQueue> fProxyEventQueues = new HashMap<IVMModelProxy, ModelProxyEventQueue>();
    
    /**
     * Constructs the view model provider for given DSF session.  The 
     * constructor is thread-safe to allow VM provider to be constructed
     * synchronously when a call to getAdapter() is made on an element 
     * in a view.
     */
    public AbstractVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext) {
        fVMAdapter = adapter;
        fPresentationContext = presentationContext;
        fContentStrategy = createContentStrategy();
    }    

    @Override
    public IPresentationContext getPresentationContext() {
        return fPresentationContext;
    }

    @Override
    public AbstractVMAdapter getVMAdapter() {
        return fVMAdapter;
    }

    /**
     * Creates the strategy class that will be used to implement the content 
     * provider interface of this view model provider.  This method can be 
     * overridden by sub-classes to provider custom content provider strategy.
     * <p/>
     * Note this method can be called by the base class constructor, therefore 
     * it should not reference any fields initialized in the sub-class.
     * 
     * @return New content provider implementation.
     */
    protected IElementContentProvider createContentStrategy() {
        return new DefaultVMContentProviderStrategy(this);
    }

    /**
     * Access method for the content provider strategy.
     * 
     * @return Content provider implementation currently being used by this 
     * class.
     */
    protected IElementContentProvider getContentStrategy() {
        return fContentStrategy;
    }
    
    /**
     * Creates the strategy class that will be used to implement the content 
     * model proxy of this view model provider.  It is normally called by 
     * {@link #createModelProxy(Object, IPresentationContext)} every time the 
     * input in the viewer is updated. This method can be overridden by 
     * sub-classes to provider custom model proxy strategy.
     * 
     * @return New model proxy implementation.
     */
    protected IVMModelProxy createModelProxyStrategy(Object rootElement) {
        return new DefaultVMModelProxyStrategy(this, rootElement);
    }
    
    /**
     * Returns the list of active proxies in this provider.  The returned
     * list is not a copy and if a sub-class modifies this list, it will
     * modify the current list of active proxies.  This allows the 
     * sub-classes to change how the active proxies are managed and 
     * retained.  
     */
    protected List<IVMModelProxy> getActiveModelProxies() {
        return fActiveModelProxies;
    }
    
    /**
     * Processes the given event in the given provider, sending model 
     * deltas if necessary.
     */
	public void handleEvent(final Object event) {
    	handleEvent(event, null);
    }
	
    /**
     * {@inheritDoc}
	 * @since 1.1
	 */
    @Override
    public void handleEvent(final Object event, RequestMonitor rm) {
        if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
        	trace(event, null, null, EventHandlerAction.received);
        }

    	CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), rm);
        final List<IVMModelProxy> activeModelProxies= new ArrayList<IVMModelProxy>(getActiveModelProxies());
    	crm.setDoneCount(activeModelProxies.size());

    	for (final IVMModelProxy proxyStrategy : activeModelProxies) {
    	    // If the event is generated by the model proxy, only process it for the proxy that created it.
    	    if ( event instanceof ModelProxyInstalledEvent &&
    	         !((ModelProxyInstalledEvent)event).getModelProxy().equals(proxyStrategy) ) 
    	    {
    	        crm.done();
    	        continue;
    	    }

    	    // Process the event only if there are potential delta flags that may be generated.
    	    // Also, process the event if it is a result of the user modifying something
    	    // so that the cache is properly updated. 
            if (proxyStrategy.isDeltaEvent(event) || event instanceof UserEditEvent) {
                if (!fProxyEventQueues.containsKey(proxyStrategy)) {
                    fProxyEventQueues.put(proxyStrategy, new ModelProxyEventQueue());
                }
                // If the event queue is empty, directly handle the new event. Otherwise queue it. 
                final ModelProxyEventQueue queue = fProxyEventQueues.get(proxyStrategy);
                if (queue.fCurrentEvent != null) {
                    assert queue.fCurrentRm != null;
                    // Iterate through the events in the queue and check if 
                    // they can be skipped.  If they can be skipped, then just 
                    // mark their RM as done.  Stop iterating through the queue
                    // if an event that cannot be skipped is encountered.
                    while (!queue.fEventQueue.isEmpty()) {
                        EventInfo eventToSkipInfo = queue.fEventQueue.get(queue.fEventQueue.size() - 1);
                        
                        if (canSkipHandlingEvent(event, eventToSkipInfo.fEvent)) {
                            if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
                            	trace(event, eventToSkipInfo.fEvent, proxyStrategy, EventHandlerAction.skipped);
                            }
                            queue.fEventQueue.remove(queue.fEventQueue.size() - 1);
                            eventToSkipInfo.fClientRm.done();
                        } else {
                            break;
                        }
                    }
                    // If the queue is empty check if the current event
                    // being processed can be skipped.  If so, cancel its
                    // processing 
                    if (queue.fEventQueue.isEmpty() && canSkipHandlingEvent(event, queue.fCurrentEvent.fEvent)) {
                        if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
                        	trace(event, queue.fCurrentEvent.fEvent, proxyStrategy, EventHandlerAction.canceled);
                        }
                        queue.fCurrentRm.cancel();
                    }

                    if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
                    	trace(event, null, proxyStrategy, EventHandlerAction.queued);
                    }
                    queue.fEventQueue.add(new EventInfo(event, crm));
                } else {
                    doHandleEvent(queue, proxyStrategy, new EventInfo(event, crm));
                }
            } else {
            	crm.done();
            }
        }

        // Discard the event queues of proxies that have been removed
        List<IVMModelProxy> activeProxies = getActiveModelProxies();
        for (Iterator<IVMModelProxy> itr = fProxyEventQueues.keySet().iterator(); itr.hasNext();) {
            if (!activeProxies.contains(itr.next())) {
                itr.remove();
            }
        }
    }

    private void doHandleEvent(final ModelProxyEventQueue queue, final IVMModelProxy proxyStrategy, final EventInfo eventInfo) {
        // Do handle event is a sort of a recursive asynchronous method.  It 
        // calls the asynchronous handleEvent() to process the event from the 
        // eventInfo argument.  When handleEvent() completes, this method 
        // (doHandleEvent) checks whether there is any more events in the queue
        // that should be handled.  If there are, doHandleEvent calls itself
        // to process the next event in the queue.
        assert queue.fCurrentEvent == null && queue.fCurrentRm == null;
        
        queue.fCurrentEvent = eventInfo;
        queue.fCurrentRm = new RequestMonitor(getExecutor(), eventInfo.fClientRm) {
            @Override
            protected void handleCompleted() {
                eventInfo.fClientRm.done();
                queue.fCurrentEvent = null;
                queue.fCurrentRm = null;
                if (!queue.fEventQueue.isEmpty() && !fDisposed) {
                    EventInfo nextEventInfo = queue.fEventQueue.remove(0);
                    doHandleEvent(queue, proxyStrategy, nextEventInfo);
                } 
            }
        };
        handleEvent(proxyStrategy, eventInfo.fEvent, queue.fCurrentRm);
    }

    /**
     * Handles the given event for the given proxy strategy.  
     * <p>
     * This method is called by the base {@link #handleEvent(Object)} 
     * implementation to handle the given event using the given model proxy.
     * The default implementation of this method checks whether the given
     * proxy is active and if the proxy is active, it is called to generate the 
     * delta which is then sent to the viewer. 
     * </p>
     * @param proxyStrategy Model proxy strategy to use to process this event.
     * @param event Event to process.
     * @param rm Request monitor to call when processing the event is 
     * completed.
     */
    protected void handleEvent(final IVMModelProxy proxyStrategy, final Object event, final RequestMonitor rm) {   
        if (!proxyStrategy.isDisposed()) {
            if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
            	trace(event, null, proxyStrategy, EventHandlerAction.processing);
            }
            proxyStrategy.createDelta(
                event, 
                new DataRequestMonitor<IModelDelta>(getExecutor(), rm) {
                    @Override
                    public void handleSuccess() {
                        proxyStrategy.fireModelChanged(getData());
                        if (DEBUG_DELTA && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
                        	trace(event, null, proxyStrategy, EventHandlerAction.firedDeltaFor);
                        }
                        rm.done();
                    }
                    @Override public String toString() {
                        return "Result of a delta for event: '" + event.toString() + "' in VMP: '" + AbstractVMProvider.this + "'" + "\n" + getData().toString();  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
                    }   
                });
        } else {
            rm.done();
        }
    }

    /**
     * Determines whether processing of a given event can be skipped.  This 
     * method is called when there are multiple events waiting to be processed
     * by the provider.  As new events are received from the model, they are
     * compared with the events in the queue using this method, events at the 
     * end of the queue are tested for removal.  If this method returns that a 
     * given event can be skipped in favor of the new event, the skipped event
     * is removed from the queue.  This process is repeated with the new event
     * until an event which cannot be stopped is found or the queue goes empty.
     * <p>
     * This method may be overriden by specific view model provider 
     * implementations extending this abstract class. 
     * </p>
     * @param newEvent New event that was received from the model.
     * @param eventToSkip Event which is currently at the end of the queue.  
     * @return True if the event at the end of the queue can be skipped in 
     * favor of the new event.
     */
    protected boolean canSkipHandlingEvent(Object newEvent, Object eventToSkip) {
        return false;
    }
    
    /** @since 1.1 */
    @Override
    public boolean shouldWaitHandleEventToComplete() {
        return false;
    }
    
    @Override
    public IRootVMNode getRootVMNode() {
        return fRootNode;
    }

    @Override
    public IVMNode[] getAllVMNodes() {
        if (fNodesListCache != null) {
            return fNodesListCache;
        }
        List<IVMNode> list = new ArrayList<IVMNode>();
        for (IVMNode node : fChildNodesMap.keySet()) {
            if (node != null) {
                list.add(node);
            }
        }
        fNodesListCache = list.toArray(new IVMNode[list.size()]);; 
        return fNodesListCache; 
    }
        
    @Override
    public IVMNode[] getChildVMNodes(IVMNode node) {
        IVMNode[] retVal = fChildNodesMap.get(node);
        if (retVal != null) {
            return retVal;
        }
        return EMPTY_NODES_ARRAY;
    }    

    /**
     * Configures the given array of nodes as children of the given parent node.
     * Sub-classes should call this method to define the hierarchy of nodes.
     */
    protected void addChildNodes(IVMNode parentNode, IVMNode[] childNodes) {
        // Add to the child nodes array.
        IVMNode[] existingChildNodes = fChildNodesMap.get(parentNode);
        if (existingChildNodes == null) {
            fChildNodesMap.put(parentNode, childNodes);
        } else {
            IVMNode[] newNodes = new IVMNode[existingChildNodes.length + childNodes.length];
            System.arraycopy(existingChildNodes, 0, newNodes, 0, existingChildNodes.length);
            System.arraycopy(childNodes, 0, newNodes, existingChildNodes.length, childNodes.length);
            fChildNodesMap.put(parentNode, newNodes);
        }
        
        // Make sure that each new expression node has an entry of its own.
        for (IVMNode childNode : childNodes) {
            addNode(childNode);
        }
        
        fNodesListCache = null;
    }
    
    /**
     * Adds the given node to configured nodes, without creating any 
     * parent-child relationship for it.  It is useful for providers which do have 
     * a strict tree hierarchy of nodes.
     */
    protected void addNode(IVMNode node) {
        if (!fChildNodesMap.containsKey(node)) {
            fChildNodesMap.put(node, EMPTY_NODES_ARRAY);
        }
    }

    /**
     * Clears all configured nodes, including the root node.  This allows a 
     * subclass to reset and reconfigure its nodes.
     */
    protected void clearNodes() {
        clearNodes(true);
        for (IVMNode node : fChildNodesMap.keySet()) {
            node.dispose();
        }
        fChildNodesMap.clear();
        fRootNode = null;
    }

    /**
     * Clears all configured nodes.  This allows a subclass to reset and 
     * reconfigure its nodes.
     * 
     * @param clearRootNode Flag indicating whether to also clear the root node.
     * @since 2.1
     */
    protected void clearNodes(boolean clearRootNode) {
        for (IVMNode node : fChildNodesMap.keySet()) {
            if ( !clearRootNode || !node.equals(getRootVMNode()) ) { 
                node.dispose();
            }
        }
        fChildNodesMap.clear();
        if (clearRootNode) {
            fRootNode = null;
        } else {
            fChildNodesMap.put(getRootVMNode(), EMPTY_NODES_ARRAY);
        }
    }
    
    /**
     * Sets the root node for this provider.  
     */
    protected void setRootNode(IRootVMNode rootNode) {
        fRootNode = rootNode;
    }
    
    /** Called to dispose the provider. */ 
    @Override
    public void dispose() {
        clearNodes();
        fRootNode = null;
        fDisposed = true;
    }
    
    @Override
    public void update(final IHasChildrenUpdate[] updates) {
        fContentStrategy.update(updates);
    }
    
    @Override
    public void update(final IChildrenCountUpdate[] updates) {
        fContentStrategy.update(updates);
    }
    
    @Override
    public void update(final IChildrenUpdate[] updates) {
        fContentStrategy.update(updates);
    }

	/**
	 * Calls the given view model node to perform the given updates. This method
	 * is called by view model provider and its helper classes instead of
	 * calling the IVMNode method directly, in order to allow additional
	 * processing of the update. For example the AbstractCachingVMProvider
	 * overrides this method to optionally return the results for an update from
	 * a cache.
	 * 
	 * [node] represents the type of the child element, not of the parent. In
	 * other words, the update requests are asking if one or more model elements
	 * of a particular type (thread, e.g.) have children. But [node] does not
	 * represent that type. It represents the type of the potential children
	 * (frame, e.g.)
	 */
    @Override
    public void updateNode(final IVMNode node, IHasChildrenUpdate[] updates) {
        IHasChildrenUpdate[] updateProxies = new IHasChildrenUpdate[updates.length];
        for (int i = 0; i < updates.length; i++) {
            final IHasChildrenUpdate update = updates[i];
            if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
                DsfUIPlugin.debug("updateNodeHasChildren(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            }
            updateProxies[i] = new VMHasChildrenUpdate(
                update,
                new ViewerDataRequestMonitor<Boolean>(getExecutor(), update) {
                    @Override
                    protected void handleSuccess() {
                        update.setHasChilren(getData());
                        update.done();
                    }
                    
                    @Override
                    protected void handleErrorOrWarning() {
                        if (getStatus().getCode() == IDsfStatusConstants.NOT_SUPPORTED) {
                            if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
                                DsfUIPlugin.debug("not-supported:updateNodeHasChildren(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                            }
                            updateNode(
                                node, 
                                new VMChildrenUpdate(
                                    update, -1, -1, 
                                    new ViewerDataRequestMonitor<List<Object>>(getExecutor(), update) {
                                        @Override
                                        protected void handleSuccess() {
                                            update.setHasChilren( !getData().isEmpty() );
                                            update.done();
                                        }
                                    })
                                );
                                    
                        } else {
                            update.setStatus(getStatus());
                            update.done();
                        }
                    }
                    
                });
        }
        node.update(updateProxies);
    }

    /**
     * Calls the given view model node to perform the given updates.  This 
     * method is called by view model provider and it's helper classes instead
     * of calling the IVMNode method directly, in order to allow additional
     * processing of the update.  For example the AbstractCachingVMProvider 
     * overrides this method to optionally return the results for an update from
     * a cache. 
     */
    @Override
    public void updateNode(final IVMNode node, final IChildrenCountUpdate update) {
        if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
            DsfUIPlugin.debug("updateNodeChildCount(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }
        node.update(new IChildrenCountUpdate[] { 
            new VMChildrenCountUpdate(
                update,
                new ViewerDataRequestMonitor<Integer>(getExecutor(), update) {
                    @Override
                    protected void handleSuccess() {
                        update.setChildCount(getData());
                        update.done();
                    }
                    
                    @Override
                    protected void handleErrorOrWarning() {
                        if (getStatus().getCode() == IDsfStatusConstants.NOT_SUPPORTED) {
                            if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
                                DsfUIPlugin.debug("not-supported:updateNodeChildCount(node = " + node + ", update = " + update + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                            }
                            updateNode(
                                node, 
                                new VMChildrenUpdate(
                                    update, -1, -1, 
                                    new ViewerDataRequestMonitor<List<Object>>(getExecutor(), update) {
                                        @Override
                                        protected void handleSuccess() {
                                            update.setChildCount( getData().size() );
                                            update.done();
                                        }
                                    })
                                );
                        } else {
                        	super.handleErrorOrWarning();
                        }
                    }
                    
                })
        });
    }

    /**
     * Calls the given view model node to perform the given updates.  This 
     * method is called by view model provider and it's helper classes instead
     * of calling the IVMNode method directly, in order to allow additional
     * processing of the update.  For example the AbstractCachingVMProvider 
     * overrides this method to optionally return the results for an update from
     * a cache. 
     */
    @Override
    public void updateNode(IVMNode node, IChildrenUpdate update) {
        if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) {
            DsfUIPlugin.debug("updateNodeChildren(node = " + node + ", update = " + update + ")");  //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
        }
        node.update(new IChildrenUpdate[] { update });
    }

    
    /**
     * Returns whether this provider has been disposed.
     */
    protected boolean isDisposed() {
        return fDisposed;
    }

    /**
     * The abstract provider uses a the display-thread executor so that the 
     * provider will operate on the same thread as the viewer.  This way no 
     * synchronization is necessary when the provider is called by the viewer.
     * Also, the display thread is likely to be shut down long after any of the 
     * view models are disposed, so the users of this abstract provider do not 
     * need to worry about the executor throwing the {@link RejectedExecutionException}
     * exception. 
     */
    @Override
    public Executor getExecutor() { 
        return fExecutor; 
    }
    
    @Override
    public IModelProxy createModelProxy(Object element, IPresentationContext context) {
        
        // Iterate through the current active proxies to try to find a proxy with the same
        // element and re-use it if found. Only disposed proxies can be re-used because
        // multiple viewers cannot use the same proxy.  Also at this time purge other proxies 
        // that are no longer installed.
        IVMModelProxy proxy = null;
        for (Iterator<IVMModelProxy> itr = getActiveModelProxies().iterator(); itr.hasNext();) {
            IVMModelProxy next = itr.next();
            if (next != null) {
            	if (next.getRootElement().equals(element) && next.isDisposed()) {
            		proxy = next;
            	} else if (next.isDisposed()) {
            		itr.remove();
            	}
            }
        }
        
        if (proxy == null) {
            proxy = createModelProxyStrategy(element);
            getActiveModelProxies().add(proxy);
        } else if (proxy.isDisposed()) {
            // DSF is capable of re-using old proxies which were previously 
            // disposed.  However, the viewer which installs a proxy using
            // a background job to install the proxy calls 
            // IModelProxy.isDisposed(), to check whether the proxy was disposed
            // before it could be installed.  We need to clear the disposed flag
            // of the re-used proxy here, otherwise the proxy will never get used.
            // Calling init here will cause the init() method to be called twice
            // so the IVMModelProxy needs to be prepared for that.
            // See bug 241024.
            proxy.init(context);
        }
        return proxy;
    }

    /**
     * Creates the column presentation for the given object.  This method is meant
     * to be overriden by deriving class to provide view-specific functionality.
     * The default is to return null, meaning no columns. 
     * <p>
     * The viewer only reads the column presentation for the root/input element of 
     * the tree/table, so the VMProvider must be configured to own the root element 
     * in the view in order for this setting to be effective.   
     * <p>
     * Note: since the IColumnEditorFactory interface is synchronous, and since
     * column info is fairly static, this method is thread-safe, and it will
     * not be called on the executor thread.
     * 
     * @see IColumnPresentationFactory#createColumnPresentation(IPresentationContext, Object)
     */
    @Override
    public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) {
        return null;
    }

    /**
     * Returns the ID of the column presentation for the given object.  This method 
     * is meant to be overriden by deriving class to provide view-specific 
     * functionality. The default is to return null, meaning no columns. 
     * <p>
     * The viewer only reads the column presentation for the root/input element of 
     * the tree/table, so the VMProvider must be configured to own the root element 
     * in the view in order for this setting to be effective.   
     * <p>
     * Note: since the IColumnEditorFactory interface is synchronous, and since
     * column info is fairly static, this method is thread-safe, and it will
     * not be called on the executor thread.
     * 
     * @see IColumnEditorFactory#getColumnEditorId(IPresentationContext, Object)
     */
    @Override
    public String getColumnPresentationId(IPresentationContext context, Object element) {
        return null;
    }

    /**
     * Calculates the proxy input object to be used for the given input in the given
     * viewer.  By default no proxy object is used an the given element is used
     * as the input into the view. 
     * <p>
     * Sub classes can override this method for view-specific behavior.
     * 
     * @see IViewerInputProvider
     */
    @Override
    public void update(IViewerInputUpdate update) {
        update.setInputElement(update.getElement());
        update.done();
    }
    
    /**
     * Used for tracing event handling
     */
    private enum EventHandlerAction {
    	received,
    	queued,
    	processing,
    	firedDeltaFor,
    	skipped,
    	canceled
    }

	/**
	 * Trace that we've reached a particular phase of the handling of an event
	 * for a particular proxy.
	 * 
	 * @param event
	 *            the event being handled
	 * @param skippedOrCanceledEvent
	 *            for a 'skip' or 'cancel' action, this is the event that is
	 *            being dismissed. Otherwise null
	 * @param proxy
	 *            the target proxy; n/a (null) for a 'received' action.
	 * @param action
	 *            what phased of the event handling has been reached
	 */
    private void trace(Object event, Object skippedOrCanceledEvent, IVMModelProxy proxy, EventHandlerAction action) {
    	assert DEBUG_DELTA;
        StringBuilder str = new StringBuilder();
        str.append(DsfPlugin.getDebugTime());
        str.append(' ');
        if (action == EventHandlerAction.skipped || action == EventHandlerAction.canceled) {
	        str.append(LoggingUtils.toString(this) + " " + action.toString() + " event " + LoggingUtils.toString(skippedOrCanceledEvent) + " because of event " + LoggingUtils.toString(event)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$        	
        }
        else {
	        str.append(LoggingUtils.toString(this) + " " + action.toString() + " event " + LoggingUtils.toString(event)); //$NON-NLS-1$ //$NON-NLS-2$
        }
        
        if (action != EventHandlerAction.received) {
        	str.append(" for proxy " + LoggingUtils.toString(proxy) + ", whose root is " + LoggingUtils.toString(proxy.getRootElement())); //$NON-NLS-1$ //$NON-NLS-2$
        }
        DsfUIPlugin.debug(str.toString());
    }
}

Back to the top