KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > debug > internal > ui > views > DebugViewLabelDecorator


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 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.views;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.HashSet JavaDoc;
15 import java.util.List JavaDoc;
16 import java.util.ListIterator JavaDoc;
17 import java.util.Set JavaDoc;
18 import java.util.Vector JavaDoc;
19
20 import org.eclipse.core.runtime.IProgressMonitor;
21 import org.eclipse.core.runtime.IStatus;
22 import org.eclipse.core.runtime.Status;
23 import org.eclipse.core.runtime.jobs.ISchedulingRule;
24 import org.eclipse.core.runtime.jobs.Job;
25 import org.eclipse.debug.core.DebugEvent;
26 import org.eclipse.debug.core.DebugException;
27 import org.eclipse.debug.core.DebugPlugin;
28 import org.eclipse.debug.core.IDebugEventSetListener;
29 import org.eclipse.debug.core.model.IDebugTarget;
30 import org.eclipse.debug.core.model.IStackFrame;
31 import org.eclipse.debug.core.model.IThread;
32 import org.eclipse.debug.internal.ui.DebugUIPlugin;
33 import org.eclipse.debug.ui.IDebugModelPresentation;
34 import org.eclipse.jface.viewers.ILabelDecorator;
35 import org.eclipse.jface.viewers.LabelProvider;
36 import org.eclipse.swt.graphics.Image;
37
38 /**
39  * A label decorator which computes text for debug elements
40  * in the background and updates them asynchronously.
41  */

42 public class DebugViewLabelDecorator extends LabelProvider implements ILabelDecorator, IDebugEventSetListener {
43
44     /**
45      * The presentation used to compute text.
46      */

47     private IDebugModelPresentation fPresentation;
48     /**
49      * The label provider notified when text is computed.
50      */

51     private DebugViewDecoratingLabelProvider fLabelProvider;
52     /**
53      * The job which will be executed next. All new label requests
54      * are appended to this job.
55      */

56     protected LabelJob fNextJob= null;
57     /**
58      * A collection of threads that were resumed while their stack
59      * frames' labels were being computed. By maintaining this collection,
60      * the label decorator can refresh stack frame labels for these
61      * threads when they suspend again.
62      */

63     private Set JavaDoc resumedThreads= new HashSet JavaDoc();
64     /**
65      * The stack frame whose label is currently being computed
66      * or <code>null</code> if no stack frame label is being computed.
67      */

68     private IStackFrame fCurrentStackFrame= null;
69     /**
70      * An object to be used as a lock for thread-safe access to the
71      * current frame.
72      */

73     private Object JavaDoc fCurrentStackFrameLock= new Object JavaDoc();
74     
75     /**
76      * Creates a new label decorator which will query the
77      * given model presentation for text in the background.
78      * @param presentation
79      */

80     public DebugViewLabelDecorator(IDebugModelPresentation presentation) {
81         fPresentation= presentation;
82         DebugPlugin.getDefault().addDebugEventListener(this);
83     }
84     
85     /**
86      * Sets the label provider which will be notified when a
87      * label has been computed in the background.
88      *
89      * @param labelProvider the label provider to notify when text
90      * is computed
91      */

92     public void setLabelProvider(DebugViewDecoratingLabelProvider labelProvider) {
93         fLabelProvider= labelProvider;
94     }
95     
96     /**
97      * @see org.eclipse.jface.viewers.ILabelDecorator#decorateImage(org.eclipse.swt.graphics.Image, java.lang.Object)
98      */

99     public Image decorateImage(Image image, Object JavaDoc element) {
100         return image;
101     }
102
103     /**
104      * @see org.eclipse.jface.viewers.ILabelDecorator#decorateText(java.lang.String, java.lang.Object)
105      */

106     public String JavaDoc decorateText(String JavaDoc text, final Object JavaDoc element) {
107         computeText(element);
108         return text;
109     }
110     
111     /**
112      * Queues up computation of text for the given element.
113      *
114      * @param element
115      */

116     public void computeText(Object JavaDoc element) {
117         synchronized(this) {
118             if (fNextJob == null) {
119                 fNextJob= new LabelJob(DebugUIViewsMessages.DebugViewLabelDecorator_0, fPresentation); //$NON-NLS-1$
120
}
121             fNextJob.computeText(element);
122         }
123     }
124     
125     /**
126      * Labels have been computed for the given elements. Fire notification
127      * asynchronously.
128      *
129      * @param computedElements the elements whose labels have been
130      * computed.
131      */

132     public void labelsComputed(final Object JavaDoc[] computedElements) {
133         DebugUIPlugin.getStandardDisplay().asyncExec(new Runnable JavaDoc() {
134             public void run() {
135                 fLabelProvider.labelsComputed(computedElements);
136             }
137         });
138     }
139     
140     /* (non-Javadoc)
141      * @see org.eclipse.debug.core.IDebugEventSetListener#handleDebugEvents(org.eclipse.debug.core.DebugEvent[])
142      */

143     public void handleDebugEvents(DebugEvent[] events) {
144         for (int i = 0; i < events.length; i++) {
145             DebugEvent event= events[i];
146             if (event.getKind() == DebugEvent.SUSPEND) {
147                 handleSuspendEvent(event);
148             } else if (event.getKind() == DebugEvent.TERMINATE) {
149                 handleTerminateEvent(event);
150             } else if (event.getKind() == DebugEvent.RESUME) {
151                 handleResumeEvent(event);
152             }
153         }
154     }
155
156     /**
157      * When a thread resumes for an evaluation or step while computing
158      * labels for one of that thread's stack frames, add the thread to the
159      * collection of "resumed threads". This allows any stack frames whose
160      * label computation was interrupted when the thread was resumed
161      * to be cleaned up later.
162      *
163      * @param event the resume event
164      */

165     private void handleResumeEvent(DebugEvent event) {
166         if (event.getSource() instanceof IThread && (event.isEvaluation() || event.isStepStart())) {
167             IThread thread= (IThread) event.getSource();
168             IStackFrame frame;
169             synchronized (fCurrentStackFrameLock) {
170                 if (fCurrentStackFrame == null) {
171                     return;
172                 }
173                 frame= fCurrentStackFrame;
174             }
175             if (thread == frame.getThread()) {
176                 resumedThreads.add(thread);
177             }
178         }
179     }
180
181     /**
182      * When a thread suspends after an evaluation or step, recompute labels
183      * for its stack frames. This ensures that any stack frames whose
184      * label computation was interrupted when the thread was resumed
185      * will be cleaned up.
186      *
187      * @param event the suspend event
188      */

189     private void handleSuspendEvent(DebugEvent event) {
190         Object JavaDoc source= event.getSource();
191         synchronized (resumedThreads) {
192             if (!resumedThreads.remove(source)) {
193                 return;
194             }
195         }
196         if (!event.isEvaluation() && (event.getDetail() & DebugEvent.STEP_END) == 0) {
197             return;
198         }
199         IThread thread= (IThread) source;
200         try {
201             IStackFrame[] frames= thread.getStackFrames();
202             for (int i = 0; i < frames.length; i++) {
203                 computeText(frames[i]);
204             }
205         } catch (DebugException e) {
206         }
207     }
208
209     /**
210      * When a terminate event is received for a debug target, remove
211      * any of its threads from the resumed threads collection. This not only
212      * prevents unnecessary stack frame label computations, it is a
213      * backstop for cleaning up threads in the collection.
214      *
215      * @param event the terminate event
216      */

217     private void handleTerminateEvent(DebugEvent event) {
218         Object JavaDoc source= event.getSource();
219         if (source instanceof IDebugTarget) {
220             List JavaDoc copiedThreads= new ArrayList JavaDoc(resumedThreads);
221             ListIterator JavaDoc iterator = copiedThreads.listIterator();
222             while (iterator.hasNext()) {
223                 IThread thread = (IThread) iterator.next();
224                 if (thread.getDebugTarget() == source) {
225                     iterator.remove();
226                 }
227             }
228             synchronized(resumedThreads) {
229                 resumedThreads.retainAll(copiedThreads);
230             }
231         }
232     }
233     
234     /**
235      * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
236      */

237     public void dispose() {
238         super.dispose();
239         DebugPlugin.getDefault().removeDebugEventListener(this);
240     }
241     
242     /**
243      * A job which computes text for a queue of elements. The job's label
244      * decorator is notified when text has been computed for some number
245      * of elements.
246      */

247     protected class LabelJob extends Job implements ISchedulingRule {
248         private Vector JavaDoc fElementQueue= new Vector JavaDoc();
249         private IDebugModelPresentation fJobPresentation;
250         
251         /**
252          * Creates a new job with the given name which will use the given
253          * presentation to compute labels in the background
254          * @param name the job's name
255          * @param presentation the presentation to use for label
256          * computation
257          */

258         public LabelJob(String JavaDoc name, IDebugModelPresentation presentation) {
259             super(name);
260             fJobPresentation= presentation;
261             // TODO: why was this rule needed?
262
//setRule(this);
263
setSystem(true);
264         }
265         
266         /**
267          * Queues up the given element to have its text computed.
268          * @param element the element whose text should be computed
269          * in this background job
270          */

271         public void computeText(Object JavaDoc element) {
272             if (!fElementQueue.contains(element)) {
273                 if (element instanceof IStackFrame) {
274                     fElementQueue.add(element);
275                 } else {
276                     // Add non-stack frame elements (debug target, thread, etc.)
277
// to the beginning of the queue so they're computed first.
278
fElementQueue.add(0, element);
279                 }
280             }
281             schedule();
282         }
283
284         /**
285          * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
286          */

287         public IStatus run(IProgressMonitor monitor) {
288             while (!fElementQueue.isEmpty() && !monitor.isCanceled()) {
289                 int blockSize= 10;
290                 if (fElementQueue.size() < blockSize) {
291                     blockSize= fElementQueue.size();
292                 }
293                 final List JavaDoc computedElements= new ArrayList JavaDoc();
294                 for (int i= 0; i < blockSize; i++) {
295                     Object JavaDoc element= fElementQueue.remove(0);
296                     if (element == null) {
297                         break;
298                     }
299                     if (element instanceof IStackFrame) {
300                         synchronized (fCurrentStackFrameLock) {
301                             fCurrentStackFrame= (IStackFrame) element;
302                         }
303                         // If a stack frame's thread has been resumed, make sure it is added to the collection
304
// of resumed threads. There's a (small) chance of a race condition here if
305
// the thread manages to resume after we check its suspended status and then
306
// suspend before we check the status for the next frame.
307
IThread thread= fCurrentStackFrame.getThread();
308                         synchronized(resumedThreads) {
309                             if (!thread.isTerminated() && !thread.isSuspended()) {
310                                 resumedThreads.add(thread);
311                                 // no need to compute label for "running" stack frame
312
continue;
313                             }
314                         }
315                     }
316                     fLabelProvider.textComputed(element, fJobPresentation.getText(element));
317                     synchronized (fCurrentStackFrameLock) {
318                         fCurrentStackFrame= null;
319                     }
320                     computedElements.add(element);
321                 }
322                 labelsComputed(computedElements.toArray());
323             }
324             monitor.done();
325             return Status.OK_STATUS;
326         }
327
328         /*
329          * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
330          */

331         public boolean contains(ISchedulingRule rule) {
332             return (rule instanceof LabelJob) && fJobPresentation == ((LabelJob)rule).fJobPresentation;
333         }
334
335         /*
336          * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
337          */

338         public boolean isConflicting(ISchedulingRule rule) {
339             return (rule instanceof LabelJob) && fJobPresentation == ((LabelJob)rule).fJobPresentation;
340         }
341     }
342 }
343
Popular Tags