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