19a08437c8c4352a3e698e59964b86f42686a0a4
[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  * 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 int                     scope_id;                               // scope id
42         //private PHPVariable[]         variables;                              // The array of variables TODO: better introduce a vector?
43         private Vector          varList = new Vector();
44         private String          description;                    // The source file name with the full path on target/remote system
45         private boolean                 fUpToDate;                              // Indicates whether the variable list within this stackframe is
46                                                                                                         // up-to-date
47         private boolean                 fAvailable;                     // Needed when updating the stackframe list, shows whether the stackframe
48                                                                                                         // is within the list which was received from dbg
49
50         /**
51          *
52          * @param thread
53          * @param file
54          * @param line
55          * @param index
56          * @param desc
57          * @param modno
58          * @param scope_id
59          */
60         public PHPStackFrame(PHPThread thread, String file, int line, int index,
61                         String desc, int modno, int scope_id) {
62                 super(null);
63
64                 this.lineNumber = line;
65                 this.index = index;
66                 this.file = file;
67                 this.thread = thread;
68                 this.description = desc;
69                 this.modno = modno;
70                 this.scope_id = scope_id;
71                 this.fUpToDate = false;
72         }
73
74         /**
75          *
76          * @param thread
77          * @param file
78          * @param line
79          * @param index
80          */
81         public PHPStackFrame(PHPThread thread, String file, int line, int index) {
82                 super(null);
83
84                 this.lineNumber = line;
85                 this.index = index;
86                 this.file = file;
87                 this.thread = thread;
88                 this.fUpToDate = false;
89         }
90
91         /**
92          * 
93          * @return scope id
94          */
95         public int getScopeID() {
96                 return scope_id;
97         }
98
99         /**
100          * 
101          */
102         public void setScopeID(int scope_id) {
103                 this.scope_id = scope_id;
104                 setUpToDate(false);
105         }
106
107         /**
108          *
109          */
110         public IThread getThread() {
111                 return thread;
112         }
113
114         /**
115          * @param thread
116          */
117         public void setThread(PHPThread thread) {
118                 this.thread = thread;
119         }
120
121         /**
122          *
123          */
124         private void setUpToDate(boolean upToDate) {
125                 fUpToDate = upToDate;
126         }
127
128         /**
129          *
130          */
131         private boolean isUpToDate() {
132                 return fUpToDate;
133         }
134
135         /**
136          *
137          */
138         public void setAvailable(boolean available) {
139                 fAvailable = available;
140         }
141
142         /**
143          *
144          */
145         public boolean isAvailable() {
146                 return fAvailable;
147         }
148
149         /**
150          * @see IAdaptable#getAdapter(Class)
151          */
152         public Object getAdapter(Class adapter) {
153                 if (adapter == PHPStackFrame.class) {
154                         return this;
155                 }
156
157                 return super.getAdapter(adapter);
158         }
159
160         /**
161          *
162          */
163         private void resetHasChangedInfo(Vector varList) {
164                 int             n;
165                 PHPVariable var;
166                 PHPValue        val;
167
168                 for (n = 0; n < varList.size(); n++) {                                          // For every variable in 'DBG list'
169                         var = (PHPVariable) varList.get(n);                                     // Get the variable
170                         val = (PHPValue) var.getValue();                                                // Get the variable's value
171
172                         try {
173                                 if (val.hasVariables()) {                                                       // Do we have other variables within the value
174                                         if (!hasRecursion(var)) {                                               // Is this variable (value) branch recursive?
175                                                 resetHasChangedInfo(val.getChildVariables()); //  No, go into branch
176                                         }
177                                 }
178                         } catch (DebugException e) {                                                    // That's, because of the hasVariables method
179                         }
180
181                         var.setValueChanged(false);                                                     // Reset the 'has changed' flag
182                 }
183         }
184
185         /**
186          * Go up the tree of PHPVariables
187          * look whether the PHPValue is a reference to a parent PHPValue
188          *
189          * TODO Check where this recursion can come from.
190          * Whether this back reference is legal or a bug.
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                                 o -= 1;                                                                                         // Adjust the 'static' list index
307                         }
308                 }
309         }
310
311         /**
312          *
313          * This function returns the array of PHPVariables for this stackframe
314          * The PHPVariables should not change (newly build up) between two steps
315          * (or breaks).
316          * A PHPVariable with the same name but with different object ID is
317          * handled as a new variable.
318          *
319          * TODO Remove the intermediate storage array
320          *
321          * @return The array of PHPVariables for this stackframe.
322          */
323         public IVariable[] getVariables() throws DebugException {
324                 if (!isUpToDate()) {
325                         resetHasChangedInfo(varList);
326                         updateVariableList(varList, this.getPHPDBGProxy().readVariables(this));
327                         setUpToDate(true);
328                         Collections.sort(varList, new PHPVariableComparator());
329                         //variables = (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
330                         //Arrays.sort(variables, new PHPVariableComparator());
331                 }
332
333                 //return variables;                                           // Give the array back to user interface
334                 return (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
335         }
336
337         /**
338          *
339          */
340         private PHPVariable findVariable(Vector varList, String varname) {
341                 PHPVariable variable;
342                 PHPValue        value;
343                 int             i;
344
345                 for (i = 0; i < varList.size(); i++) {                                          // For all variables
346                         variable = (PHPVariable) varList.get(i);                                // Get the variable
347                         value = (PHPValue) variable.getValue();                                 // Get the value of the variable
348
349                         try {
350                                 if (value.hasVariables()) {                                             // Does the variable/value have children
351                                         if (!hasRecursion(variable)) {                                  // Don't follow recursive variable/values
352                                                 variable = findVariable(value.getChildVariables(),
353                                                                 varname);
354
355                                                 if (variable != null) {
356                                                         return variable;
357                                                 }
358                                         }
359                                 } else if ((variable.getName()).equals(varname)) {      //
360                                         return variable; //
361                                 }
362                         } catch (DebugException e) {                                                    // That's, because of the hasVariables method
363                         }
364                 }
365
366                 return null;
367         }
368
369         /**
370          * This method is called from the UI (e.g. from PHPDebugHover
371          * to find the variable the mouse is pointing to)
372          *
373          * @param s The variable name we are looking for.
374          * @return
375          */
376         public IVariable findVariable(String s) throws DebugException {
377                 if (!isUpToDate()) {
378                         //resetHasChangedInfo(varList);
379                         //updateVariableList(varList, this.getPHPDBGProxy().readVariables(this));
380                         //setUpToDate(true);
381                         //variables = (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
382                         getVariables();
383                 }
384
385                 return (findVariable(varList, s));                                                      // Prefix the variable name with $
386         }
387
388         /**
389          *
390          */
391         public boolean hasVariables() throws DebugException {
392                 return true;
393                 // return (varList.size () > 0);
394         }
395
396         public int getLineNumber() {
397                 return lineNumber;
398         }
399
400         public void setLineNumber(int line) {
401                 lineNumber = line;
402         }
403
404         public int getCharStart() throws DebugException {
405                 // not supported
406                 return -1;
407         }
408
409         public int getCharEnd() throws DebugException {
410                 // not supported
411                 return -1;
412         }
413
414         public String getName() {
415                 StringBuffer name = new StringBuffer();
416
417                 if (!this.getDescription().equals("")) {
418                         name.append(this.getDescription());
419                 } else {
420                         name.append(this.getFileName());
421                 }
422
423                 name.append(" [line ");
424                 name.append(this.getLineNumber());
425                 name.append("]");
426
427                 return name.toString();
428         }
429
430         public String getFileName() {
431                 return file;
432         }
433
434         public void setDescription(String desc) {
435                 this.description = desc;
436         }
437
438         public String getDescription() {
439                 return this.description;
440         }
441
442         public IRegisterGroup[] getRegisterGroups() throws DebugException {
443                 return null;
444         }
445
446         public boolean hasRegisterGroups() throws DebugException {
447                 return false;
448         }
449
450         public String getModelIdentifier() {
451                 return this.getThread().getModelIdentifier();
452         }
453
454         public IDebugTarget getDebugTarget() {
455                 return this.getThread().getDebugTarget();
456         }
457
458         public ILaunch getLaunch() {
459                 return this.getDebugTarget().getLaunch();
460         }
461
462         public boolean canStepInto() {
463                 return canResume();
464         }
465
466         public boolean canStepOver() {
467                 return canResume();
468         }
469
470         public boolean canStepReturn() {
471                 return canResume();
472         }
473
474         public boolean isStepping() {
475                 return false;
476         }
477
478         /**
479          *
480          */
481         public void stepInto() throws DebugException {
482                 DebugEvent ev;
483
484                 setUpToDate(false);
485
486                 thread.prepareForResume(DebugEvent.STEP_INTO);                          // Don't know why, but this is necessary
487                 this.getPHPDBGProxy().readStepIntoEnd(PHPStackFrame.this);
488                 
489         // Commented out sending the RESUME event because it was already sent by prepareForResume.
490         // The second RESUME event leads only to a little flickering within the variables view.
491         // It is also not clear why this event was necessary in eclipse < 3.2
492         // Also sending a SUSPEND event here leads to a total rebuild of the variables view.
493         // (eclipse 3.2 has a build in timeout of 500 ms which leads to a auto suspend, with
494         // no flickering... but why???)
495         // 
496                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
497                 //DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
498         }
499
500         /**
501          *
502          */
503         public void stepOver() throws DebugException {
504                 DebugEvent ev;
505
506                 setUpToDate(false);
507
508                 thread.prepareForResume(DebugEvent.STEP_OVER);
509                 this.getPHPDBGProxy().readStepOverEnd(PHPStackFrame.this);
510
511         // See comment within the previous stepInto method.
512         // 
513                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
514                 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
515         }
516
517         /**
518          *
519          */
520         public void stepReturn() throws DebugException {
521                 DebugEvent ev;
522
523                 setUpToDate(false);
524
525                 thread.prepareForResume(DebugEvent.STEP_RETURN);
526                 this.getPHPDBGProxy().readStepReturnEnd(PHPStackFrame.this);
527
528                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
529                 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
530         }
531
532         public boolean canResume() {
533                 return this.getThread().canResume();
534         }
535
536         public boolean canSuspend() {
537                 return this.getThread().canSuspend();
538         }
539
540         public boolean isSuspended() {
541                 return this.getThread().isSuspended();
542         }
543
544         public void resume() throws DebugException {
545                 setUpToDate(false);
546                 this.getThread().resume();
547         }
548
549         public void suspend() throws DebugException {
550         }
551
552         public boolean canTerminate() {
553                 return this.getThread().canTerminate();
554         }
555
556         public boolean isTerminated() {
557                 return this.getThread().isTerminated();
558         }
559
560         public void terminate() throws DebugException {
561                 getPHPDBGProxy().stop();
562         }
563
564         public int getIndex() {
565                 return index;
566         }
567
568         public void setIndex(int index) {
569                 this.index = index;
570         }
571
572         public PHPDBGProxy getPHPDBGProxy() {
573                 PHPDebugTarget DebugTarget;
574
575                 DebugTarget = (PHPDebugTarget) thread.getDebugTarget();
576
577                 return DebugTarget.getPHPDBGProxy();
578         }
579
580         public void setFile(String file) {
581                 this.file = file;
582         }
583
584         public int getModNo() {
585                 return modno;
586         }
587
588         /**
589          * This function is needed when sorting the stackframes by their index numbers.
590          *
591          * @param obj The stackframe which this one is compared to.
592          * @return
593          * <ul>
594          * <li> -1 if the index of this stackframe is less.
595          * <li> 0 if the index of both stackfream is equal (should no happen).
596          * <li> 1 if the index of this stackfram is greater.
597          * </ul>
598          */
599         public int compareTo(Object obj) {
600                 //if (index < ((PHPStackFrame) obj).getIndex()) {
601                 //      return -1;
602                 //} else if (index > ((PHPStackFrame) obj).getIndex()) {
603                 //      return 1;
604                 //}
605
606                 //return 0;
607                 return Integer.signum(index - ((PHPStackFrame) obj).getIndex());
608         }
609 }