1) Moved the hasRecursion and the updateVariableList to PHPStackframe class.
[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;        //
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                 if(!this.getDescription().equals(""))
376                         return this.getDescription() + " [line: " + this.getLineNumber() + "]";
377                 else
378                         return this.getFileName() + " [line: " + this.getLineNumber() + "]";
379         }
380
381         public String getFileName() {
382                 return file;
383         }
384
385         public void setDescription(String desc) {
386                 this.description= desc;
387         }
388
389         public String getDescription() {
390                 return this.description;
391         }
392
393         public IRegisterGroup[] getRegisterGroups() throws DebugException {
394                 return null;
395         }
396
397         public boolean hasRegisterGroups() throws DebugException {
398                 return false;
399         }
400
401         public String getModelIdentifier() {
402                 return this.getThread().getModelIdentifier();
403         }
404
405         public IDebugTarget getDebugTarget() {
406                 return this.getThread().getDebugTarget();
407         }
408
409         public ILaunch getLaunch() {
410                 return this.getDebugTarget().getLaunch();
411         }
412
413         public boolean canStepInto() {
414                 return canResume();
415         }
416
417         public boolean canStepOver() {
418                 return canResume();
419         }
420
421         public boolean canStepReturn() {
422                 return canResume();
423         }
424
425         public boolean isStepping() {
426                 return false;
427         }
428
429         /**
430          *
431          */
432         public void stepInto () throws DebugException {
433                 DebugEvent      ev;
434
435                 setUpToDate (false);
436
437         thread.prepareForResume (DebugEvent.STEP_INTO);             // Don't know why, but this is necessary
438                 this.getPHPDBGProxy ().readStepIntoEnd (PHPStackFrame.this);
439
440                 ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
441                 DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
442         }
443
444         /**
445          *
446          */
447         public void stepOver () throws DebugException {
448                 DebugEvent      ev;
449
450                 setUpToDate (false);
451
452         thread.prepareForResume (DebugEvent.STEP_OVER);
453                 this.getPHPDBGProxy ().readStepOverEnd (PHPStackFrame.this) ;
454
455                 ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
456                 DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
457         }
458
459         /**
460          *
461          */
462         public void stepReturn () throws DebugException {
463                 DebugEvent      ev;
464
465                 setUpToDate (false);
466
467         thread.prepareForResume (DebugEvent.STEP_RETURN);
468                 this.getPHPDBGProxy ().readStepReturnEnd (PHPStackFrame.this) ;
469
470                 ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
471                 DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
472         }
473
474
475         public boolean canResume() {
476                 return this.getThread().canResume();
477         }
478
479         public boolean canSuspend() {
480                 return this.getThread().canSuspend();
481         }
482
483         public boolean isSuspended() {
484                 return this.getThread().isSuspended();
485         }
486
487         public void resume() throws DebugException {
488                 setUpToDate (false);
489                 this.getThread().resume();
490         }
491
492         public void suspend() throws DebugException {
493         }
494
495         public boolean canTerminate() {
496                 return this.getThread().canTerminate();
497         }
498
499         public boolean isTerminated() {
500                 return this.getThread().isTerminated();
501         }
502
503         public void terminate() throws DebugException {
504                 getPHPDBGProxy().stop();
505         }
506
507         public int getIndex() {
508                 return index;
509         }
510
511         public void setIndex (int index) {
512                 this.index = index;
513         }
514
515         public PHPDBGProxy getPHPDBGProxy() {
516                 PHPDebugTarget DebugTarget;
517
518                 DebugTarget = (PHPDebugTarget) thread.getDebugTarget ();
519
520                 return DebugTarget.getPHPDBGProxy ();
521         }
522
523         public void setFile(String file) {
524                 this.file = file;
525         }
526
527         public int getModNo() {
528                 return modno;
529         }
530
531         /**
532          * This function is needed when sorting the stackframes by their index numbers.
533          *
534          * @param obj The stackframe which this one is compared to.
535          * @return
536          * <ul>
537          * <li> -1 if the index of this stackframe is less.
538          * <li> 0 if the index of both stackfream is equal (should no happen).
539          * <li> 1 if the index of this stackfram is greater.
540          * </ul>
541          */
542         public int compareTo (Object obj)
543         {
544                 if (index < ((PHPStackFrame) obj).getIndex ()) {
545                         return -1;
546                 }
547                 else if (index > ((PHPStackFrame) obj).getIndex ()) {
548                         return 1;
549                 }
550
551                 return 0;
552         }
553 }