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