bbebc6a0a387bb9e7bf2af4baaabba5a8d19891f
[phpeclipse.git] / net.sourceforge.phpeclipse.debug.core / src / net / sourceforge / phpdt / internal / debug / core / model / PHPStackFrame.java
1 /**********************************************************************
2 Copyright (c) 2000, 2002 IBM Corp. and others.
3 All rights reserved. This program and the accompanying materials
4 are made available under the terms of the Common Public License v1.0
5 which accompanies this distribution, and is available at
6 http://www.eclipse.org/legal/cpl-v10.html
7
8 Contributors:
9     IBM Corporation - Initial implementation
10     Vicente Fernando - www.alfersoft.com.ar
11 **********************************************************************/
12 package net.sourceforge.phpdt.internal.debug.core.model;
13
14 import java.util.Vector;
15
16 import net.sourceforge.phpdt.internal.debug.core.PHPDBGProxy;
17
18 import org.eclipse.debug.core.DebugEvent;
19 import org.eclipse.debug.core.DebugException;
20 import org.eclipse.debug.core.DebugPlugin;
21 import org.eclipse.debug.core.ILaunch;
22 import org.eclipse.debug.core.model.IDebugTarget;
23 import org.eclipse.debug.core.model.IRegisterGroup;
24 import org.eclipse.debug.core.model.IStackFrame;
25 import org.eclipse.debug.core.model.IThread;
26 import org.eclipse.debug.core.model.IVariable;
27
28 /**
29  *
30  * TODO Remove the variables array and use only the varList vector
31  *      Have also to change hasVariables
32  */
33 public class PHPStackFrame extends PHPDebugElement implements IStackFrame, Comparable{
34
35         private PHPThread               thread;                         // The thread to which this stackframe belongs
36         private String                  file;               // The file name???
37         private int                     lineNumber;         //
38         private int                     index;              //
39         private int                     modno;              //
40         private PHPVariable[]   variables;          // The array of variables TODO: better introduce a vector?
41         private Vector          varList  = new Vector ();
42         private String                  description;        // The source file name with the full path on target/remote system
43         private boolean         fUpToDate;          //
44
45         /**
46          *
47          * @param thread
48          * @param file
49          * @param line
50          * @param index
51          * @param desc
52          * @param modno
53          */
54         public PHPStackFrame (PHPThread thread, String file, int line, int index, String desc, int modno) {
55                 super (null);
56
57                 this.lineNumber  = line;
58                 this.index           = index;
59                 this.file            = file;
60                 this.thread      = thread;
61                 this.description = desc;
62                 this.modno               = modno;
63                 this.fUpToDate   = false;
64         }
65
66         /**
67          *
68          * @param thread
69          * @param file
70          * @param line
71          * @param index
72          */
73         public PHPStackFrame (PHPThread thread, String file, int line, int index) {
74                 super (null);
75
76                 this.lineNumber = line;
77                 this.index              = index;
78                 this.file               = file;
79                 this.thread     = thread;
80                 this.fUpToDate  = false;
81         }
82
83         /**
84          *
85          */
86         public IThread getThread () {
87                 return thread;
88         }
89
90         /**
91          * @param thread
92          */
93         public void setThread (PHPThread thread) {
94                 this.thread = thread;
95         }
96
97         /**
98          *
99          */
100         private void setUpToDate (boolean upToDate) {
101                 fUpToDate = upToDate;
102         }
103
104         /**
105          *
106          */
107         private boolean isUpToDate () {
108                 return fUpToDate;
109         }
110
111         /**
112          *
113          *
114          */
115         private void resetHasChangedInfo (Vector varList) {
116                 int             n;
117                 PHPVariable var;
118                 PHPValue    val;
119
120                 for (n = 0; n < varList.size (); n++) {                  // For every variable in 'DBG list'
121                         var = (PHPVariable) varList.get (n);                    // Get the variable
122                         val = (PHPValue) var.getValue ();                       // Get the variable's value
123
124                         try {
125                                 if (val.hasVariables ()) {                              // Do we have other variables within the value
126                                         if (!hasRecursion (var)) {                          // Is this variable (value) branch recursive?
127                                                 resetHasChangedInfo (val.getChildVariables ()); //  No, go into branch
128                                         }
129                                 }
130                         }
131                         catch (DebugException e) {                                                  // That's, because of the hasVariables method
132                         }
133
134                         var.setValueChanged (false);                            // Reset the 'has changed' flag
135                 }
136         }
137
138         /**
139          * Go up the tree of PHPVariables
140          * look whether the PHPValue is a reference to a parent PHPValue
141          *
142          * TODO Check where this recursion can come from.
143          * Whether this back reference is legal or a bug.
144          *
145          * @param var
146          * @return
147          * <ul>
148          * <li> false if the PHPValue is not a child of itself
149          * <li> true if the PHPValue is
150          * </ul>
151          */
152         private boolean hasRecursion (PHPVariable var) {
153                 PHPVariable parentVar;
154                 PHPValue    val;
155
156                 val = (PHPValue) var.getValue ();                                                       // Get the PHPValue from the current PHPVariable
157
158                 while (var != null) {                                                                           // As long as we have PHPVariable
159                         parentVar = var.getParent ();                                                   // Get the parent PHPVariable
160
161                         if (parentVar != null) {                                // Is there a parent?
162                                 if (parentVar.getValue ().equals (val)) {                       // Get the PHPValue for the parent PHPVariable and check
163                                                                                                                                         // whether it is the same
164                                         return true;                                                                    // Return, if we have recursion
165                                 }
166                         }
167
168                         var = parentVar;
169                 }
170
171                 return false;                                                                                           // No recursion found
172         }
173
174         /**
175          * This method updates the 'static' variables list.
176          * It does a replication between the 'static' list (the variable list which
177          * is a member of this DBG interface object) and the DBG variable list
178          * (the list of variables which is received from PHP via DBG with the current suspend)
179          * Replication is done in the following way:
180          * <ul>
181          * <li> It looks for new variables within the DBG variables list and
182          *      adds them to the 'static' list.
183          * <li> It looks for changed variables copies the current value to the variable within
184          *              the 'static list' and mark these variables as 'hasChanged' (which uses the UI
185          *              for showing the variable with a different color).
186          * <li> It looks for variables within the 'static' list, and removes them
187          *              from the 'static' list in case the do not appear within the DBG list.
188          * </ul>
189          *
190          * @param varListOld The 'static' list of variables which are to be updated.
191          * @param varListNew The new list of (current) variables from DBG.
192          */
193         private void updateVariableList (Vector varListOld, Vector varListNew)
194         {
195                 PHPVariable     varOld;                                                                                 // The variable from the 'static' list
196                 PHPVariable varNew;                                                                                     // The variable from the DBG list
197                 PHPValue    valOld;                                                                                     // The value of the current variable from 'static' list
198                 PHPValue    valNew;                                                                                     // The value of the current variable from DBG list
199                 int         n;                                                                                          // Index for the DBG list
200                 int         o;                                                                                          // Index for the static list
201
202                 // Add the variables (and childs) to the static list if they are new
203                 //  and update the values of variables which are already existend within
204                 //  the 'static' list.
205
206                 for (n = 0; n < varListNew.size (); n++) {                  // For every variable in 'DBG list'
207                         varNew = (PHPVariable) varListNew.get (n);                              // Get the DBG variable
208
209                         for (o = 0; o < varListOld.size (); o++) {                              // For every variable in static list
210                                 varOld = (PHPVariable) varListOld.get (o);                      // Get the static variable
211
212                                 if (varNew.getName ().equals (varOld.getName ())) { // Did we found the variable within the 'static' list?
213                                         valOld = (PHPValue) varOld.getValue ();         // Get the value from 'static'
214                                         valNew = (PHPValue) varNew.getValue ();         // Get the value from DBG
215
216                                         try {
217                                                 if (valOld.hasVariables () ||                           // If the 'static' value has child variables
218                                                     valNew.hasVariables ()) {                           //  or if the DBG value has child variables
219                                                         if (!hasRecursion (varOld) &&
220                                                             !hasRecursion (varNew)) {                   // Both branches should not have a recursion
221                                                                 updateVariableList (valOld.getChildVariables (),        // Update the variable list for the child variables
222                                                                                                         valNew.getChildVariables ());
223                                                         }
224                                                 }
225                                                 else if (!valOld.getValueString ().equals (valNew.getValueString ())) { // Has the value changed?
226                                                         valOld.setValueString (valNew.getValueString ());                   // Yes, set the 'static' value (variable) to the new value
227                                                         varOld.setValueChanged (true);                                                                          // and set the 'has changed' flag, so that the variable view
228                                                                                                                                                                                                 // could show the user the changed status with a different
229                                                                                                                                                                                                 // color
230                                                 }
231                                                 //else {
232                                                 //      varOld.setValueChanged (false);                                     // Reset the 'has changed' flag
233                                                 //}
234                                         }
235                                         catch (DebugException e) {                                                  // That's, because of the hasVariables method
236                                         }
237
238                                         break;                                          // Found the variable,
239                                 }
240                         }
241
242                         if (o == varListOld.size ()) {                          // Did we found the variable within the static list?
243                                 varListOld.add (varNew);                                                        //  No, then add the DBG variable to the static list
244                         }
245                 }
246
247                 // Look for the variables we can remove from the 'static' list
248
249                 for (o = 0; o < varListOld.size (); o++) {                                              // For every variable in 'static' list
250                         varOld = (PHPVariable) varListOld.get (o);                                                      // Get the static variable
251
252                         for (n = 0; n < varListNew.size (); n++) {                                                      // For all variables in 'DBG' list
253                                 varNew = (PHPVariable) varListNew.get (n);                                              // Get the variable from the 'DBG' list
254
255                                 if (varNew.getName ().equals (varOld.getName ())) {                     // Did we found the 'static' list variable within the 'DBG' list?
256                                         break;                                                                                                          // Yes we found the variable, then leave the loop
257                                 }
258                         }
259
260                         if (n == varListNew.size ()) {                                          // Did not find the 'static' list variable within the 'DBG' list?
261                                  varListOld.remove (o);                                         // then remove the 'static' list variable from list
262                                  o -= 1;                                                        // Adjust the 'static' list index
263                         }
264                 }
265         }
266
267
268         /**
269          *
270          * This function returns the array of PHPVariables for this stackframe
271          * The PHPVariables should not change (newly build up) between two steps
272          * (or breaks).
273          * A PHPVariable with the same name but with different object ID is
274          * handled as a new variable.
275          *
276          * TODO Remove the intermediate storage array
277          *
278          * @return The array of PHPVariables for this stackframe.
279          */
280         public IVariable[] getVariables() throws DebugException {
281                 PHPVariable[] variablesNew;                                 // The intermediate storage of the variable array we get from DBG proxy
282
283                 //variablesNew = this.getPHPDBGProxy ().readVariables (this); // Get the variable array from DBG proxy
284                 //variables    = variablesNew;                                // Store the array the stackframes member variable
285
286                 if (!isUpToDate ()) {
287                         resetHasChangedInfo (varList);
288                 updateVariableList (varList, this.getPHPDBGProxy ().readVariables (this));
289                         setUpToDate (true);
290
291                         variables = (PHPVariable[]) varList.toArray (new PHPVariable[varList.size ()]);
292                 }
293
294                 return variables;                                           // Give the array back to user interface
295         }
296
297         /**
298          *
299          */
300         private PHPVariable findVariable (Vector varList, String varname) {
301                 PHPVariable     variable;
302                 PHPValue        value;
303                 int                     i;
304
305                 for (i = 0; i < varList.size (); i++) {                                         // For all variables
306                         variable = (PHPVariable) varList.get (i);                               // Get the variable
307                         value    = (PHPValue) variable.getValue ();                             // Get the value of the variable
308
309                         try {
310                                 if (value.hasVariables ()) {                                            // Does the variable/value have children
311                                         if (!hasRecursion (variable)) {                                 // Don't follow recursive variable/values
312                                                 variable = findVariable (value.getChildVariables (), varname);
313
314                                                 if (variable != null) {
315                                                         return variable;
316                                                 }
317                                         }
318                                 }
319                                 else if ((variable.getName ()).equals (varname)) {      //
320                                         return variable;                                                                //
321                                 }
322                         }
323                         catch (DebugException e) {                                                              // That's, because of the hasVariables method
324                         }
325                 }
326
327                 return null;
328         }
329
330         /**
331          * This method is called from the UI (e.g. from PHPDebugHover
332          * to find the variable the mouse is pointing to)
333          *
334          * @param s The variable name we are looking for.
335          * @return
336          */
337         public IVariable findVariable (String s) throws DebugException {
338                 if (!isUpToDate ()) {
339                         resetHasChangedInfo (varList);
340                 updateVariableList (varList, this.getPHPDBGProxy ().readVariables (this));
341                         setUpToDate (true);
342
343                         variables = (PHPVariable[]) varList.toArray (new PHPVariable[varList.size ()]);
344                 }
345
346                 return (findVariable (varList, s));                                                     // Prefix the variable name with $
347         }
348
349         /**
350          *
351          */
352         public boolean hasVariables () throws DebugException {
353                 return (varList.size () > 0);
354         }
355
356         public int getLineNumber() {
357                 return lineNumber;
358         }
359
360         public void setLineNumber(int line) {
361                 lineNumber = line;
362         }
363
364         public int getCharStart() throws DebugException {
365                 // not supported
366                 return -1;
367         }
368
369         public int getCharEnd() throws DebugException {
370                 // not supported
371                 return -1;
372         }
373
374         public String getName() {
375                 StringBuffer name = new StringBuffer();
376                 
377                 if (!this.getDescription().equals ("")) {
378                         name.append (this.getDescription ());
379                 }
380                 else {
381                         name.append (this.getFileName ());
382                 }
383                 
384                 name.append (" [line ");
385                 name.append (this.getLineNumber ());
386                 name.append ("]");
387                 
388                 return name.toString();
389         }
390
391         public String getFileName() {
392                 return file;
393         }
394
395         public void setDescription(String desc) {
396                 this.description= desc;
397         }
398
399         public String getDescription() {
400                 return this.description;
401         }
402
403         public IRegisterGroup[] getRegisterGroups() throws DebugException {
404                 return null;
405         }
406
407         public boolean hasRegisterGroups() throws DebugException {
408                 return false;
409         }
410
411         public String getModelIdentifier() {
412                 return this.getThread().getModelIdentifier();
413         }
414
415         public IDebugTarget getDebugTarget() {
416                 return this.getThread().getDebugTarget();
417         }
418
419         public ILaunch getLaunch() {
420                 return this.getDebugTarget().getLaunch();
421         }
422
423         public boolean canStepInto() {
424                 return canResume();
425         }
426
427         public boolean canStepOver() {
428                 return canResume();
429         }
430
431         public boolean canStepReturn() {
432                 return canResume();
433         }
434
435         public boolean isStepping() {
436                 return false;
437         }
438
439         /**
440          *
441          */
442         public void stepInto () throws DebugException {
443                 DebugEvent      ev;
444
445                 setUpToDate (false);
446
447         thread.prepareForResume (DebugEvent.STEP_INTO);             // Don't know why, but this is necessary
448                 this.getPHPDBGProxy ().readStepIntoEnd (PHPStackFrame.this);
449
450                 ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
451                 DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
452         }
453
454         /**
455          *
456          */
457         public void stepOver () throws DebugException {
458                 DebugEvent      ev;
459
460                 setUpToDate (false);
461
462         thread.prepareForResume (DebugEvent.STEP_OVER);
463                 this.getPHPDBGProxy ().readStepOverEnd (PHPStackFrame.this) ;
464
465                 ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
466                 DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
467         }
468
469         /**
470          *
471          */
472         public void stepReturn () throws DebugException {
473                 DebugEvent      ev;
474
475                 setUpToDate (false);
476
477         thread.prepareForResume (DebugEvent.STEP_RETURN);
478                 this.getPHPDBGProxy ().readStepReturnEnd (PHPStackFrame.this) ;
479
480                 ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
481                 DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
482         }
483
484
485         public boolean canResume() {
486                 return this.getThread().canResume();
487         }
488
489         public boolean canSuspend() {
490                 return this.getThread().canSuspend();
491         }
492
493         public boolean isSuspended() {
494                 return this.getThread().isSuspended();
495         }
496
497         public void resume() throws DebugException {
498                 setUpToDate (false);
499                 this.getThread().resume();
500         }
501
502         public void suspend() throws DebugException {
503         }
504
505         public boolean canTerminate() {
506                 return this.getThread().canTerminate();
507         }
508
509         public boolean isTerminated() {
510                 return this.getThread().isTerminated();
511         }
512
513         public void terminate() throws DebugException {
514                 getPHPDBGProxy().stop();
515         }
516
517         public int getIndex() {
518                 return index;
519         }
520
521         public void setIndex (int index) {
522                 this.index = index;
523         }
524
525         public PHPDBGProxy getPHPDBGProxy() {
526                 PHPDebugTarget DebugTarget;
527
528                 DebugTarget = (PHPDebugTarget) thread.getDebugTarget ();
529
530                 return DebugTarget.getPHPDBGProxy ();
531         }
532
533         public void setFile(String file) {
534                 this.file = file;
535         }
536
537         public int getModNo() {
538                 return modno;
539         }
540
541         /**
542          * This function is needed when sorting the stackframes by their index numbers.
543          *
544          * @param obj The stackframe which this one is compared to.
545          * @return
546          * <ul>
547          * <li> -1 if the index of this stackframe is less.
548          * <li> 0 if the index of both stackfream is equal (should no happen).
549          * <li> 1 if the index of this stackfram is greater.
550          * </ul>
551          */
552         public int compareTo (Object obj)
553         {
554                 if (index < ((PHPStackFrame) obj).getIndex ()) {
555                         return -1;
556                 }
557                 else if (index > ((PHPStackFrame) obj).getIndex ()) {
558                         return 1;
559                 }
560
561                 return 0;
562         }
563 }