1) Improvements for the XDebug plugin.
[phpeclipse.git] / net.sourceforge.phpeclipse.xdebug.core / src / net / sourceforge / phpeclipse / xdebug / php / model / XDebugThread.java
1 /*
2  * Created on 23.11.2004
3  *
4  * TODO To change the template for this generated file go to
5  * Window - Preferences - Java - Code Style - Code Templates
6  */
7 package net.sourceforge.phpeclipse.xdebug.php.model;
8
9 import java.util.Arrays;
10 import java.util.Collections;
11 import java.util.Vector;
12
13 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
14 import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils;
15 import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugResponse;
16
17 import org.eclipse.debug.core.DebugEvent;
18 import org.eclipse.debug.core.DebugException;
19 import org.eclipse.debug.core.DebugPlugin;
20 import org.eclipse.debug.core.IDebugEventSetListener;
21 import org.eclipse.debug.core.model.IBreakpoint;
22 import org.eclipse.debug.core.model.IStackFrame;
23 import org.eclipse.debug.core.model.IThread;
24 import org.eclipse.jface.resource.ImageDescriptor;
25 import org.eclipse.ui.model.IWorkbenchAdapter;
26 import org.w3c.dom.Node;
27 import org.w3c.dom.NodeList;
28
29 /**
30  * @author Axel
31  *
32  * TODO To change the template for this generated type comment go to
33  * Window - Preferences - Java - Code Style - Code Templates
34  */
35 public class XDebugThread extends XDebugElement implements IThread, IDebugEventSetListener {
36         private XDebugStackFrame[]  fStackFrames;
37         private IBreakpoint[]       fBreakpoints;
38
39         private boolean             fStepping         = false;          // Whether this thread is stepping
40         private boolean             fTerminated       = false;
41
42         private int                 fStepCount        = 0;
43         private int                 fCurrentStepCount = 0;
44
45         private Vector                  stackListOld      = new Vector();   // The 'static' list of stack frames
46
47         /**
48          * Constructs a new thread for the given target
49          *
50          * @param target VM
51          */
52         public XDebugThread(XDebugTarget target) {
53                 super(target);
54                 DebugPlugin.getDefault().addDebugEventListener(this);
55                 fStackFrames = null;
56         }
57
58         /**
59          *
60          */
61         public void setStackFrames (XDebugStackFrame[] frames) {
62                 this.fStackFrames = frames;
63         }
64
65         public void incrementStepCounter() {
66                 fStepCount++;
67         }
68
69    /**
70     *
71     * @param arg0
72     * @return
73     */
74    public Object getAdapter (Class arg0) {
75        if (IWorkbenchAdapter.class.equals (arg0)) {
76            return new IWorkbenchAdapter() {
77                public Object[] getChildren(Object o) {
78                    try {
79                        return getStackFrames ();
80                    } catch (DebugException x) {
81                        PHPeclipsePlugin.log ("Unable to get stack frames.", x);
82                    }
83
84                    return new Object[0];
85                }
86
87                public ImageDescriptor getImageDescriptor(Object object) {
88                    return null;
89                }
90
91                public String getLabel(Object o) {
92                    throw new UnsupportedOperationException();
93                }
94
95                public Object getParent(Object o) {
96                    return getDebugTarget();
97                }
98            };
99        }
100        return super.getAdapter(arg0);
101    }
102
103         /**
104          * Reset the availability flag for all stackframes in the list.
105          *
106          * @param list          The list of old stackframes
107          */
108         private void resetAvailability (Vector list) {
109                 int             i;
110
111                 for (i = 0; i < list.size (); i++) {
112                         ((XDebugStackFrame) list.get(i)).setAvailable (false);                                          //
113                 }
114         }
115
116         /**
117          * Check whether the new stackframe is in the list of old stackframes.
118          * Test for identical stackframe (identical means same description and same line number).
119          *
120          * @param stackFrameNew The stackframe to check whether he is already within the old stackframe list
121          * @param list          The list of old stackframes
122          * @return
123          *  - true if we have found the identical stackframe within the list
124          *  - false if we did not find the identical stackframe within the list
125          */
126         private boolean isStackFrameInList (XDebugStackFrame stackFrameNew, Vector list) {
127                 int                     i;
128                 XDebugStackFrame        stackFrameOld;
129
130                 for (i = 0; i < list.size (); i++) {
131                         stackFrameOld = (XDebugStackFrame) list.get (i);                                                //
132
133                         if (stackFrameNew.getFullName ().equals (stackFrameOld.getFullName ()) &&
134                 stackFrameNew.getDescription ().equals (stackFrameOld.getDescription ()) &&
135                                 stackFrameNew.getLineNumber () == stackFrameOld.getLineNumber ()) {     // Did we find the sent stackframe within the list of old stackframes?
136                                 stackFrameOld.setAvailable (true);                                      // We found the new stackframe in the list of old stack frames
137                                 stackFrameOld.setLevel (stackFrameNew.getLevel ());
138                                 stackFrameOld.setUpToDate (false);                                      // Need to update the variables
139
140                                 return true;                                                            // The stackframe was found in the list
141                         }
142                 }
143
144                 return false;
145         }
146
147         /**
148          * Check whether the new stackframe is in the list of old stackframes.
149          * Test for exact stackframe (exact means same description and same line number).
150          *
151          * @param stackFrameNew The stackframe to check whether he is already within the old stackframe list
152          * @param list          The list of old stackframes
153          * @return
154          *  - true if we have exactly this stackframe within the list
155          *  - false if we did not find the exact stackframe within the list
156          */
157         private void markIdenticalStackFrames (Vector oldList, Vector newList) {
158                 int                 i;
159                 XDebugStackFrame        stackFrameNew;
160
161                 resetAvailability (oldList);                                                    // Reset the availability flag of the old stack frames
162                 resetAvailability (newList);                                                    // Reset the availability flag of the old stack frames
163
164                 for (i = 0; i < newList.size (); i++) {                                                                                 // For all stackList entries
165                         stackFrameNew = (XDebugStackFrame) newList.get (i);
166
167                         if (isStackFrameInList (stackFrameNew, oldList)) {                          // Is this stackframe in the list
168                                 stackFrameNew.setAvailable (true);                                                                              //
169                                                                                                                                                                                 //
170 //                              break;
171                         }
172                 }
173         }
174
175         /**
176          *
177          * The stackList contains the currently read stackframes which were sent
178          * from DBG. The DBG interface holds a list of the active stack frames.
179          * This method replicates the 'static' stackframe list with the DBG stackframe list
180          * Replication is done in the following way:
181          * <ul>
182          * <li> It looks for new stackframes within the DBG stackframe list and
183          *      adds them to the 'static' list.
184          * <li> It looks for stackframes within the 'static' list, and removes them
185          *              from the 'static' list in case they do not appear within the DBG list.
186          * <li> It looks for stackframes which are already existent and replicates the
187          *              line number and the index number.
188          * <li> At the end, the 'static' stackframe list has to be sorted by the stackframes
189          *              index numbers.
190          * </ul>
191          *
192          * Removes the unused stackframes from the list, or adds stackframes which
193          * are not yet in the list.
194          *
195          *
196          * @param stackList
197          */
198         private void updateStackFrameList (Vector stackList) {
199                 int                         i;
200                 int                 n;
201                 XDebugStackFrame        stackFrameNew;
202                 XDebugStackFrame    stackFrameOld;
203                 XDebugStackFrame[]  newStackList;
204
205                 markIdenticalStackFrames (stackListOld, stackList);                     // Check whether the newly send stack frames can be found in the list
206                                                                                                                                                                 // of old stack frames
207
208                 for (i = 0; i < stackList.size (); i++) {                                                               // For all stackList entries
209                         stackFrameNew = (XDebugStackFrame) stackList.get(i);
210
211                         for (n = 0; n < stackListOld.size (); n++) {                                    // For all StackFrames in the StackFrame list
212                                 stackFrameOld = (XDebugStackFrame) stackListOld.get (n);                //
213
214                                 if (stackFrameOld.isAvailable ()) {                             // If this stack frame was already found in the new list skip it
215                                         continue;
216                                 }
217
218                                 if (stackFrameNew.getFullName ().equals (stackFrameOld.getFullName ()) &&      // Did we find the sent stackframe within the list of old stackframes?
219                     stackFrameNew.getDescription ().equals (stackFrameOld.getDescription ())) {// Did we find the sent stackframe within the list of old stackframes?
220                                         stackFrameOld.setLineNumber (stackFrameNew.getLineNumber ());
221                                         stackFrameOld.setLevel      (stackFrameNew.getLevel ());
222                                         stackFrameOld.setUpToDate (false);                          // Need to update the variables
223
224                                         stackFrameOld.setAvailable (true);                                                      // And mark this stack frame as available
225                                         stackFrameNew.setAvailable (true);                                                      // And mark this stack frame as available
226
227                                         break;                                                                  //  Yes, then break;
228                                 }
229                         }
230
231                         if (!stackFrameNew.isAvailable ()) {                                // Did not find the new stackframe within the list?
232                                  stackFrameNew.setAvailable (true);                                                             // Mark the stack frame as available and
233                                  stackListOld.add (stackFrameNew);                              //  then add the new stackframe
234                         }
235                 }
236
237                 // And now for removing unused stackframes from list
238
239                 for (n = 0; n < stackListOld.size(); n++) {
240                         stackFrameOld = (XDebugStackFrame) stackListOld.get(n);
241
242                         if (!stackFrameOld.isAvailable()) {
243                                 stackListOld.remove(n--);
244                         }
245                 }
246
247                 Collections.sort (stackListOld);                                                                                // Sort the 'static' stackframe list by the stackframe index numbers.
248                                                                                                                                                                 //
249                 newStackList = new XDebugStackFrame[stackListOld.size ()];
250                 newStackList = (XDebugStackFrame[]) stackListOld.toArray (newStackList);
251
252 //              DBGStackList = newStackList;
253                 fStackFrames = newStackList;
254         }
255
256
257         public IStackFrame[] getStackFrames() throws DebugException {
258                 if (!isSuspended()) {
259                         return new IStackFrame[0];
260                 }
261
262                 if (fStepCount > fCurrentStepCount) {                                        // Do we need to update the list of stackframes
263                         XDebugResponse     dr             = ((XDebugTarget) getDebugTarget()).getStackFrames();  // Get the stackframes list from XDebug
264                         XDebugStackFrame[] newStackFrames = _getStackFrames(dr);                 // Parse the stackframes list
265
266                         updateStackFrameList (new Vector (Arrays.asList(newStackFrames)));       // update the 'static' list of stackframes
267
268                         fCurrentStepCount++;                                                     //
269                 }
270
271                 return fStackFrames;
272         }
273
274
275         private XDebugStackFrame[] _getStackFrames(XDebugResponse lastResponse) {
276                 if (lastResponse.isError()) {
277                         return new XDebugStackFrame[0];
278                 }
279
280                 Node               response    = lastResponse.getParentNode();
281                 NodeList           frames      = response.getChildNodes();
282                 XDebugStackFrame[] theFrames   = new XDebugStackFrame[frames.getLength()];
283
284                 for (int i = 0; i < frames.getLength(); i++) {
285                         Node   stackNode   = frames.item(i);
286                         String fileName    = PHPDebugUtils.unescapeString(PHPDebugUtils.getAttributeValue(stackNode,"filename"));
287                         String lineNo      = PHPDebugUtils.getAttributeValue(stackNode,"lineno");
288
289                         XDebugStackFrame frame = new XDebugStackFrame (this,
290                                                                        i,
291                                                                                                                    PHPDebugUtils.getAttributeValue(stackNode,"type"),
292                                                                                                                    Integer.parseInt(lineNo),
293                                                                                                                    PHPDebugUtils.getAttributeValue(stackNode,"where"),
294                                                                                                                    fileName);
295
296                         frame.incrementStepCounter();
297
298                         theFrames[i] = frame;
299                 }
300
301                 return theFrames;
302         }
303
304         /* (non-Javadoc)
305          * @see org.eclipse.debug.core.model.IThread#hasStackFrames()
306          */
307         public boolean hasStackFrames() throws DebugException {
308                 if (fStackFrames == null) {
309                         return false;
310                 }
311
312                 return fStackFrames.length > 0;
313         }
314
315         /* (non-Javadoc)
316          * @see org.eclipse.debug.core.model.IThread#getPriority()
317          */
318         public int getPriority() throws DebugException {
319                 return 0;
320         }
321
322         /* (non-Javadoc)
323          * @see org.eclipse.debug.core.model.IThread#getTopStackFrame()
324          */
325         public IStackFrame getTopStackFrame() throws DebugException {
326                 IStackFrame[] frames = getStackFrames();
327                 if (frames.length > 0) {
328                         return frames[0];
329                 }
330
331         return null;
332         }
333
334         /* (non-Javadoc)
335          * @see org.eclipse.debug.core.model.IThread#getName()
336          */
337         public String getName() throws DebugException {
338                 return "Thread[1]";
339         }
340
341         /* (non-Javadoc)
342          * @see org.eclipse.debug.core.model.IThread#getBreakpoints()
343          */
344         public IBreakpoint[] getBreakpoints() {
345                 if (fBreakpoints == null) {
346                         return new IBreakpoint[0];
347                 }
348                 return fBreakpoints;
349         }
350
351         /**
352          * Sets the breakpoints this thread is suspended at, or <code>null</code>
353          * if none.
354          *
355          * @param breakpoints the breakpoints this thread is suspended at, or <code>null</code>
356          * if none
357          */
358         protected void setBreakpoints(IBreakpoint[] breakpoints) {
359                 fBreakpoints = breakpoints;
360         }
361
362         /* (non-Javadoc)
363          * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
364          */
365         public boolean canResume() {
366                 return isSuspended();
367         }
368
369         /* (non-Javadoc)
370          * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
371          */
372         public boolean canSuspend() {
373                 return !isTerminated() && !isSuspended();
374         }
375
376         /* (non-Javadoc)
377          * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
378          */
379         public boolean isSuspended() {
380                 return getDebugTarget().isSuspended();
381         }
382
383         /* (non-Javadoc)
384          * @see org.eclipse.debug.core.model.ISuspendResume#resume()
385          */
386         public void resume() throws DebugException {
387                 fBreakpoints = null;
388                 getDebugTarget().resume();
389         }
390
391         /* (non-Javadoc)
392          * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
393          */
394         public void suspend() throws DebugException {
395                 getDebugTarget().suspend();
396         }
397
398         /* (non-Javadoc)
399          * @see org.eclipse.debug.core.model.IStep#canStepInto()
400          */
401         public boolean canStepInto() {
402                 return isSuspended();
403         }
404
405         /* (non-Javadoc)
406          * @see org.eclipse.debug.core.model.IStep#canStepOver()
407          */
408         public boolean canStepOver() {
409                 return isSuspended();
410         }
411
412         /* (non-Javadoc)
413          * @see org.eclipse.debug.core.model.IStep#canStepReturn()
414          */
415         public boolean canStepReturn() {
416                 if (fStackFrames != null) {
417                         return (fStackFrames.length > 1);
418                 } else {
419                         return false;
420                 }
421         }
422
423         /* (non-Javadoc)
424          * @see org.eclipse.debug.core.model.IStep#isStepping()
425          */
426         public boolean isStepping() {
427                 return fStepping;
428         }
429
430         /* (non-Javadoc)
431          * @see org.eclipse.debug.core.model.IStep#stepInto()
432          */
433         public void stepInto() throws DebugException {
434                 fBreakpoints = null;
435                 ((XDebugTarget) getDebugTarget()).step_into();
436         }
437
438         /* (non-Javadoc)
439          * @see org.eclipse.debug.core.model.IStep#stepOver()
440          */
441         public void stepOver() throws DebugException {
442                 fBreakpoints = null;
443                 ((XDebugTarget) getDebugTarget()).step_over();
444         }
445
446         /* (non-Javadoc)
447          * @see org.eclipse.debug.core.model.IStep#stepReturn()
448          */
449         public void stepReturn() throws DebugException {
450                 fBreakpoints = null;
451                 ((XDebugTarget) getDebugTarget()).step_out();
452         }
453
454         /* (non-Javadoc)
455          * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
456          */
457         public boolean canTerminate() {
458                 return !isTerminated();
459         }
460
461         /* (non-Javadoc)
462          * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
463          */
464         public boolean isTerminated() {
465                 return fTerminated;
466         }
467
468         /* (non-Javadoc)
469          * @see org.eclipse.debug.core.model.ITerminate#terminate()
470          */
471         public void terminate() throws DebugException {
472                 ((XDebugTarget) getDebugTarget()).getDebugConnection().stop();
473                 fTerminated = true;
474         }
475
476         public void terminated() throws DebugException {
477                 fTerminated = true;
478         }
479
480         /**
481          * Sets whether this thread is stepping
482          *
483          * @param stepping whether stepping
484          */
485         protected void setStepping(boolean stepping) {
486                 fStepping = stepping;
487         }
488
489         public void handleDebugEvents(DebugEvent[] events) {
490                 DebugEvent de = events[0];
491                 System.out.println(de.toString());
492         }
493
494         public void removeEventListeners() {
495                 DebugPlugin.getDefault().removeDebugEventListener(this);
496         }
497
498         /**
499          * Fires a <code>RESUME</code> event for this element with
500          * the given detail.
501          *
502          * @param detail event detail code
503          */
504         public void fireResumeEvent(int detail) {
505                 fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
506         }
507
508         /**
509          * Fires a <code>SUSPEND</code> event for this element with
510          * the given detail.
511          *
512          * @param detail event detail code
513          */
514         public void fireSuspendEvent (int detail) {
515                 fireEvent (new DebugEvent (this, DebugEvent.SUSPEND, detail));
516         }
517 }