43b2117f13bf0b55d8330f6720529cc4fd8273aa
[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                                                 } else if (!valOld.getValueString().equals(
270                                                                 valNew.getValueString())) {             // Has the value changed?
271                                                         valOld.setValueString(valNew.getValueString()); // Yes, set the 'static' value (variable) to the new value
272                                                         varOld.setValueChanged(true);                   // and set the 'has changed' flag, so that the variable view
273                                                                                                                                         // could show the user the changed status with a different
274                                                                                                                                         // color
275                                                 }
276                                                 //else {
277                                                 //      varOld.setValueChanged (false);                                     // Reset the 'has changed' flag
278                                                 //}
279                                         } catch (DebugException e) {                                    // That's, because of the hasVariables method
280                                         }
281
282                                         break;                                                                                  // Found the variable,
283                                 }
284                         }
285
286                         if (o == varListOld.size()) {                                                   // Did we found the variable within the static list?
287                                 varListOld.add(varNew);                                                         //  No, then add the DBG variable to the static list
288                         }
289                 }
290
291                 // Look for the variables we can remove from the 'static' list
292
293                 for (o = 0; o < varListOld.size(); o++) {                                       // For every variable in 'static' list
294                         varOld = (PHPVariable) varListOld.get(o);                               // Get the static variable
295
296                         for (n = 0; n < varListNew.size(); n++) {                               // For all variables in 'DBG' list
297                                 varNew = (PHPVariable) varListNew.get(n);                       // Get the variable from the 'DBG' list
298
299                                 if (varNew.getName().equals(varOld.getName())) {        // Did we found the 'static' list variable within the 'DBG' list?
300                                         break;                                                                                  // Yes we found the variable, then leave the loop
301                                 }
302                         }
303
304                         if (n == varListNew.size()) {                                                   // Did not find the 'static' list variable within the 'DBG' list?
305                                 varListOld.remove(o--);                                                         // then remove the 'static' list variable from list
306                         }
307                 }
308         }
309
310         /**
311          *
312          * This function returns the array of PHPVariables for this stackframe
313          * The PHPVariables should not change (newly build up) between two steps
314          * (or breaks).
315          * A PHPVariable with the same name but with different object ID is
316          * handled as a new variable.
317          *
318          * @return The array of PHPVariables for this stackframe.
319          */
320         public IVariable[] getVariables() throws DebugException {
321                 if (!fUpToDate) {
322                         resetHasChangedInfo(varList);
323                         updateVariableList(varList, this.getPHPDBGProxy().readVariables(this));
324                         fUpToDate = true;
325                         Collections.sort(varList, new PHPVariableComparator());
326                 }
327
328                 return (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
329         }
330
331         /**
332          *
333          */
334         private PHPVariable findVariable(Vector varList, String varname) {
335                 PHPVariable variable;
336                 PHPValue        value;
337                 int             i;
338
339                 for (i = 0; i < varList.size(); i++) {                                          // For all variables
340                         variable = (PHPVariable) varList.get(i);                                // Get the variable
341                         value = (PHPValue) variable.getValue();                                 // Get the value of the variable
342
343                         try {
344                                 if (value.hasVariables()) {                                             // Does the variable/value have children
345                                         if (!hasRecursion(variable)) {                                  // Don't follow recursive variable/values
346                                                 variable = findVariable(value.getChildVariables(),
347                                                                 varname);
348
349                                                 if (variable != null) {
350                                                         return variable;
351                                                 }
352                                         }
353                                 } else if ((variable.getName()).equals(varname)) {      //
354                                         return variable; //
355                                 }
356                         } catch (DebugException e) {                                                    // That's, because of the hasVariables method
357                         }
358                 }
359
360                 return null;
361         }
362
363         /**
364          * This method is called from the UI (e.g. from PHPDebugHover
365          * to find the variable the mouse is pointing to)
366          *
367          * @param s The variable name we are looking for.
368          * @return
369          */
370         public IVariable findVariable(String s) throws DebugException {
371                 if (!fUpToDate) {
372                         getVariables();
373                 }
374
375                 return (findVariable(varList, s));                                                      // Prefix the variable name with $
376         }
377
378         /**
379          *
380          */
381         public boolean hasVariables() throws DebugException {
382                 if (!fUpToDate) {
383                         getVariables();
384                 }
385
386                 return (varList.size() > 0);
387         }
388
389         public int getLineNumber() {
390                 return lineNumber;
391         }
392
393         public void setLineNumber(int line) {
394                 lineNumber = line;
395         }
396
397         public int getCharStart() throws DebugException {
398                 // not supported
399                 return -1;
400         }
401
402         public int getCharEnd() throws DebugException {
403                 // not supported
404                 return -1;
405         }
406
407         public String getName() {
408                 StringBuffer name = new StringBuffer();
409
410                 if (!this.getDescription().equals("")) {
411                         name.append(this.getDescription());
412                 } else {
413                         name.append(this.getFileName());
414                 }
415
416                 name.append(" [line ");
417                 name.append(this.getLineNumber());
418                 name.append("]");
419
420                 return name.toString();
421         }
422
423         public String getFileName() {
424                 return file;
425         }
426
427         public void setDescription(String desc) {
428                 this.description = desc;
429         }
430
431         public String getDescription() {
432                 return this.description;
433         }
434
435         public IRegisterGroup[] getRegisterGroups() throws DebugException {
436                 return null;
437         }
438
439         public boolean hasRegisterGroups() throws DebugException {
440                 return false;
441         }
442
443         public String getModelIdentifier() {
444                 return this.getThread().getModelIdentifier();
445         }
446
447         public IDebugTarget getDebugTarget() {
448                 return this.getThread().getDebugTarget();
449         }
450
451         public ILaunch getLaunch() {
452                 return this.getDebugTarget().getLaunch();
453         }
454
455         public boolean canStepInto() {
456                 return canResume();
457         }
458
459         public boolean canStepOver() {
460                 return canResume();
461         }
462
463         public boolean canStepReturn() {
464                 return canResume();
465         }
466
467         public boolean isStepping() {
468                 return false;
469         }
470
471         /**
472          *
473          */
474         public void stepInto() throws DebugException {
475                 fUpToDate = false;
476
477                 thread.prepareForResume(DebugEvent.STEP_INTO);                          // Don't know why, but this is necessary
478                 this.getPHPDBGProxy().readStepIntoEnd(PHPStackFrame.this);
479                 
480         // Commented out sending the RESUME event because it was already sent by prepareForResume.
481         // The second RESUME event leads only to a little flickering within the variables view.
482         // It is also not clear why this event was necessary in eclipse < 3.2
483         // Also sending a SUSPEND event here leads to a total rebuild of the variables view.
484         // (eclipse 3.2 has a build in timeout of 500 ms which leads to a auto suspend, with
485         // no flickering... but why???)
486         // 
487                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
488                 //DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
489         }
490
491         /**
492          *
493          */
494         public void stepOver() throws DebugException {
495                 fUpToDate = false;
496
497                 thread.prepareForResume(DebugEvent.STEP_OVER);
498                 this.getPHPDBGProxy().readStepOverEnd(PHPStackFrame.this);
499
500         // See comment within the previous stepInto method.
501         // 
502                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
503                 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
504         }
505
506         /**
507          *
508          */
509         public void stepReturn() throws DebugException {
510                 fUpToDate = false;
511
512                 thread.prepareForResume(DebugEvent.STEP_RETURN);
513                 this.getPHPDBGProxy().readStepReturnEnd(PHPStackFrame.this);
514
515                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
516                 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
517         }
518
519         public boolean canResume() {
520                 return this.getThread().canResume();
521         }
522
523         public boolean canSuspend() {
524                 return this.getThread().canSuspend();
525         }
526
527         public boolean isSuspended() {
528                 return this.getThread().isSuspended();
529         }
530
531         public void resume() throws DebugException {
532                 fUpToDate = false;
533
534                 this.getThread().resume();
535         }
536
537         public void suspend() throws DebugException {
538         }
539
540         public boolean canTerminate() {
541                 return this.getThread().canTerminate();
542         }
543
544         public boolean isTerminated() {
545                 return this.getThread().isTerminated();
546         }
547
548         public void terminate() throws DebugException {
549                 getPHPDBGProxy().stop();
550         }
551
552         public int getIndex() {
553                 return index;
554         }
555
556         public void setIndex(int index) {
557                 this.index = index;
558         }
559
560         public PHPDBGProxy getPHPDBGProxy() {
561                 PHPDebugTarget DebugTarget;
562
563                 DebugTarget = (PHPDebugTarget) thread.getDebugTarget();
564
565                 return DebugTarget.getPHPDBGProxy();
566         }
567
568         public void setFile(String file) {
569                 this.file = file;
570         }
571
572         public int getModNo() {
573                 return modno;
574         }
575
576         /**
577          * This function is needed when sorting the stackframes by their index numbers.
578          *
579          * @param obj The stackframe which this one is compared to.
580          * @return
581          * <ul>
582          * <li> -1 if the index of this stackframe is less.
583          * <li> 0 if the index of both stackfream is equal (should no happen).
584          * <li> 1 if the index of this stackfram is greater.
585          * </ul>
586          */
587         public int compareTo(Object obj) {
588                 //if (index < ((PHPStackFrame) obj).getIndex()) {
589                 //      return -1;
590                 //} else if (index > ((PHPStackFrame) obj).getIndex()) {
591                 //      return 1;
592                 //}
593
594                 //return 0;
595                 return Integer.signum(index - ((PHPStackFrame) obj).getIndex());
596         }
597 }