4f56af665b49c40b1ae74fba6187875940c9af41
[phpeclipse.git] / net.sourceforge.phpeclipse.xdebug.core / src / net / sourceforge / phpeclipse / xdebug / php / model / XDebugStackFrame.java
1 /*
2  * Created on 23.11.2004
3  *
4  * TODO To change the template for this generated file go to
5  * Window - Preferences - Java - Code Style - Code Templates
6  */
7 package net.sourceforge.phpeclipse.xdebug.php.model;
8
9 import java.net.MalformedURLException;
10 import java.net.URL;
11 import java.util.Arrays;
12 import java.util.Collections;
13 import java.util.Vector;
14
15 import org.eclipse.core.runtime.IPath;
16 import org.eclipse.core.runtime.Path;
17 import org.eclipse.debug.core.DebugException;
18 import org.eclipse.debug.core.model.IDebugTarget;
19 import org.eclipse.debug.core.model.IRegisterGroup;
20 import org.eclipse.debug.core.model.IStackFrame;
21 import org.eclipse.debug.core.model.IThread;
22 import org.eclipse.debug.core.model.IVariable;
23 import org.w3c.dom.Node;
24 import org.w3c.dom.NodeList;
25
26 /**
27  * @author PHPeclipse team
28  * @author Axel
29  *
30  */
31 public class XDebugStackFrame  extends XDebugElement implements IStackFrame, Comparable {
32         private XDebugThread    fThread;
33         private URL                     fName;
34         private int                     fLineNumber;
35         private int                     fLevel;
36         private String                  fType;
37         private String                  fWhere;
38         private IVariable[]     fVariables;
39         private int                     fStepCount = 0;
40         private boolean         fUpToDate = false;
41         private Vector                  varList;                                // Variables list
42         private boolean                 fAvailable;                     // Needed when updating the stackframe list, shows whether the stackframe
43                                                                                                         // is within the list which was received from XDebug
44
45         /**
46          * Constructs a stack frame in the given thread with the given
47          * frame data.
48          *
49          * @param thread
50          * @param data frame data
51          * @param id stack frame id (0 is the bottom of the stack)
52          */
53         public XDebugStackFrame(XDebugThread thread, int id, String type, int lineNumber, String where, /*URL*/String filename) {
54                 super(/*thread == null ? null : */(XDebugTarget) thread.getDebugTarget());
55
56                 this.fLevel       = id;
57                 this.fThread      = thread;
58                 this.fType        = type;
59                 this.fLineNumber  = lineNumber;
60                 this.fWhere       = where;
61                 this.varList      = new Vector();
62
63                 try {
64                         fName = new URL(filename);
65                 } catch (MalformedURLException e) {
66                         e.printStackTrace();
67                 }
68         }
69
70         public void incrementStepCounter() {
71                 fStepCount++;
72         }
73
74         /* (non-Javadoc)
75          * @see org.eclipse.debug.core.model.IStackFrame#getThread()
76          */
77         public IThread getThread() {
78                 return fThread;
79         }
80
81     /**
82      * @see IAdaptable#getAdapter(Class)
83      */
84     public Object getAdapter(Class adapter) {
85         if (adapter == XDebugStackFrame.class) {
86             return this;
87         }
88
89         return super.getAdapter(adapter);
90     }
91
92     public IDebugTarget getDebugTarget() {
93         return this.getThread().getDebugTarget();
94     }
95
96         /**
97          *
98          */
99         private void resetHasChangedInfo(Vector varList) {
100                 int                     n;
101                 XDebugVariable          var;
102                 XDebugAbstractValue     val;
103
104                 for (n = 0; n < varList.size (); n++) {                                         // For every variable in 'DBG list'
105                         var = (XDebugVariable) varList.get(n);                                  // Get the variable
106                         val = (XDebugAbstractValue) var.getValue();                     // Get the variable's value
107
108                         try {
109                                 if (val.hasVariables()) {                                                       // Do we have other variables within the value
110                                         if (!hasRecursion(var)) {                                               // Is this variable (value) branch recursive?
111                                                 resetHasChangedInfo(val.getChildVariables()); //  No, go into branch
112                                         }
113                                 }
114                         } catch (DebugException e) {                                                    // That's, because of the hasVariables method
115                         }
116
117                         var.setValueChanged(false);                                                     // Reset the 'has changed' flag
118                 }
119         }
120
121         /**
122          * Go up the tree of PHPVariables
123          * look whether the PHPValue is a reference to a parent PHPValue
124          *
125          * TODO Check where this recursion can come from.
126          * Whether this back reference is legal or a bug.
127          *
128          * Typically $GLOBALS contains $GLOBALS
129          *
130          * @param var
131          * @return
132          * <ul>
133          * <li> false if the PHPValue is not a child of itself
134          * <li> true if the PHPValue is
135          * </ul>
136          */
137         private boolean hasRecursion(XDebugVariable var) {
138                 XDebugVariable                  parentVar;
139                 XDebugAbstractValue     val;
140
141                 val = (XDebugAbstractValue) var.getValue();                                             // Get the PHPValue from the current PHPVariable
142
143                 while (var != null) {                                                                           // As long as we have PHPVariable
144                         parentVar = var.getParent();                                                    // Get the parent PHPVariable
145
146                         if (parentVar != null) {                                                                // Is there a parent?
147                                 if (parentVar.getValue().equals(val)) {                         // Get the PHPValue for the parent PHPVariable and check
148                                                                                                                                         // whether it is the same
149                                         return true;                                                                    // Return, if we have recursion
150                                 }
151                         }
152
153                         var = parentVar;
154                 }
155
156                 return false;                                                                                           // No recursion found
157         }
158
159         /**
160          * This method updates the 'static' variables list.
161          * It does a replication between the 'static' list (the variable list which
162          * is a member of this DBG interface object) and the DBG variable list
163          * (the list of variables which is received from PHP via DBG with the current suspend)
164          * Replication is done in the following way:
165          * <ul>
166          * <li> It looks for new variables within the DBG variables list and
167          *      adds them to the 'static' list.
168          * <li> It looks for changed variables copies the current value to the variable within
169          *              the 'static list' and mark these variables as 'hasChanged' (which uses the UI
170          *              for showing the variable with a different color).
171          * <li> It looks for variables within the 'static' list, and removes them
172          *              from the 'static' list in case the do not appear within the DBG list.
173          * </ul>
174          *
175          * @param varListOld The 'static' list of variables which are to be updated.
176          * @param varListNew The new list of (current) variables from DBG.
177          */
178         private void updateVariableList(Vector varListOld, Vector varListNew) {
179                 XDebugVariable          varOld;                                                                         // The variable from the 'static' list
180                 XDebugVariable          varNew;                                                                         // The variable from the DBG list
181                 XDebugAbstractValue valOld;                                                                             // The value of the current variable from 'static' list
182                 XDebugAbstractValue valNew;                                                                             // The value of the current variable from DBG list
183                 int                             n;                                                                                      // Index for the DBG list
184                 int                             o;                                                                                      // Index for the static list
185
186                 // Add the variables (and childs) to the static list if they are new
187                 //  and update the values of variables which are already existend within
188                 //  the 'static' list.
189
190                 for (n = 0; n < varListNew.size(); n++) {                                       // For every variable in 'DBG list'
191                         varNew = (XDebugVariable) varListNew.get(n);                    // Get the DBG variable
192
193                         for (o = 0; o < varListOld.size(); o++) {                               // For every variable in static list
194                                 varOld = (XDebugVariable) varListOld.get(o);            // Get the static variable
195
196                                 if (varNew.getName().equals(varOld.getName())) {                        // Did we found the variable within the 'static' list?
197                                         valOld = (XDebugAbstractValue) varOld.getValue();               // Get the value from 'static'
198                                         valNew = (XDebugAbstractValue) varNew.getValue();               // Get the value from DBG
199
200                                         try {
201                                                 if (valOld.hasVariables() ||                                                            // If the 'static' value has child variables
202                                                         valNew.hasVariables()) {                                                                //  or if the DBG value has child variables
203                                                         if (!hasRecursion(varOld) &&
204                                                             !hasRecursion(varNew)) {                                                    // Both branches should not have a recursion
205                                                                 updateVariableList(
206                                                                         valOld.getChildVariables(),             // Update the variable list for the child variables
207                                                                         valNew.getChildVariables());
208                                                         }
209                                                 }
210
211                                                 if (!valOld.getValueString().equals(
212                                                                 valNew.getValueString())) {             // Has the value changed?
213                                                         valOld.setValueString(valNew.getValueString()); // Yes, set the 'static' value (variable) to the new value
214                                                         valOld.setReferenceTypeName (valNew.getReferenceTypeName ());
215                                                         varOld.setValueChanged(true);                   // and set the 'has changed' flag, so that the variable view
216                                                                                                                                         // could show the user the changed status with a different
217                                                                                                                                         // color
218                                                 }
219                                         } catch (DebugException e) {                                    // That's, because of the hasVariables method
220                                         }
221
222                                         break;                                                                                  // Found the variable,
223                                 }
224                         }
225
226                         if (o == varListOld.size()) {                                                   // Did we found the variable within the static list?
227                                 varListOld.add(varNew);                                                         //  No, then add the DBG variable to the static list
228                         }
229                 }
230
231                 // Look for the variables we can remove from the 'static' list
232
233                 for (o = 0; o < varListOld.size(); o++) {                                       // For every variable in 'static' list
234                         varOld = (XDebugVariable) varListOld.get(o);                    // Get the static variable
235
236                         for (n = 0; n < varListNew.size(); n++) {                               // For all variables in 'DBG' list
237                                 varNew = (XDebugVariable) varListNew.get(n);            // Get the variable from the 'DBG' list
238
239                                 if (varNew.getName().equals(varOld.getName())) {        // Did we found the 'static' list variable within the 'DBG' list?
240                                         break;                                                                                  // Yes we found the variable, then leave the loop
241                                 }
242                         }
243
244                         if (n == varListNew.size()) {                                                   // Did not find the 'static' list variable within the 'DBG' list?
245                                 varListOld.remove(o--);                                                         // then remove the 'static' list variable from list
246                         }
247                 }
248         }
249
250         /**
251          *
252          * This function returns the array of PHPVariables for this stackframe
253          * The PHPVariables should not change (newly build up) between two steps
254          * (or breaks).
255          * A PHPVariable with the same name but with different object ID is
256          * handled as a new variable.
257          *
258          * @return The array of PHPVariables for this stackframe.
259          */
260
261         public synchronized IVariable[] getVariables() throws DebugException {
262                 /* always read variables, poor performance
263                  * but this fix bug #680.
264                  * need to investigate on.
265                  */
266
267         if (!fUpToDate) {
268             Node dfl = ((XDebugTarget) getDebugTarget()).getLocalVariables(fLevel);
269             Node dfg = ((XDebugTarget) getDebugTarget()).getGlobalVariables(fLevel);
270             parseVariable(dfl, dfg);
271
272                     Vector newVariables = new Vector (Arrays.asList(fVariables));
273                         resetHasChangedInfo (varList);
274                         updateVariableList (varList, newVariables);
275                         fUpToDate = true;
276                         Collections.sort (varList, new XDebugVariableComparator ());
277         }
278
279                 return (IVariable[]) varList.toArray (new IVariable[varList.size()]);
280         }
281
282         private void parseVariable(Node localVariables, Node globalVariables) throws DebugException {
283                 NodeList property       = localVariables.getChildNodes();
284                 NodeList propertyGlobal = globalVariables.getChildNodes();
285
286                 fVariables = new IVariable[property.getLength() + propertyGlobal.getLength()];
287
288                 int length = property.getLength();
289                 for (int i = 0; i < length; i++) {
290                         XDebugVariable var = new XDebugVariable(this, property.item(i), null);
291                         fVariables[i] = var;
292                 }
293
294                 int globalLength = propertyGlobal.getLength();
295                 for (int k = 0; k < globalLength; k++) {
296                         XDebugVariable var = new XDebugVariable(this, propertyGlobal.item(k), null);
297                         fVariables[k + length] = var;
298                 }
299         }
300
301         /**
302          *
303          */
304         private XDebugVariable findVariable(Vector varList, String varname) {
305                 XDebugVariable                  variable;
306                 XDebugAbstractValue     value;
307                 int                                     i;
308
309                 for (i = 0; i < varList.size(); i++) {                                          // For all variables
310                         variable = (XDebugVariable) varList.get(i);                     // Get the variable
311                         value = (XDebugAbstractValue) variable.getValue();              // Get the value of the variable
312
313                         try {
314                                 if (value.hasVariables()) {                                             // Does the variable/value have children
315                                         if (!hasRecursion(variable)) {                                  // Don't follow recursive variable/values
316                                                 XDebugVariable var = findVariable(value.getChildVariables(), varname);
317
318                                                 if (var != null) {
319                                                         return var;
320                                                 }
321                                         }
322                                 }
323
324                                 if (variable.getName().equals(varname)) {
325                                         return variable;
326                                 }
327                         } catch (DebugException e) {                                                    // That's, because of the hasVariables method
328                         }
329                 }
330
331                 return null;
332         }
333
334         /**
335          * This method is called from the UI (e.g. from PHPDebugHover
336          * to find the variable the mouse is pointing to)
337          *
338          * @param s The variable name we are looking for.
339          * @return
340          */
341         public IVariable findVariable(String s) throws DebugException {
342                 if (!fUpToDate) {
343                         getVariables();
344                 }
345
346                 return (findVariable(varList, s));                                                      // Prefix the variable name with $
347         }
348
349         /*public void evaluateChange(IStackFrame OldStackFrame) throws DebugException {
350                 IVariable[] OldVariable = ((XDebugStackFrame) OldStackFrame).getVariables();
351                 for (int i = 0; i < fVariables.length; i++) {
352                         ((XDebugVariable) fVariables[i]).setChange(OldVariable[i]);
353                 }
354         }*/
355
356         /* (non-Javadoc)
357          * @see org.eclipse.debug.core.model.IStackFrame#hasVariables()
358          */
359         public boolean hasVariables() throws DebugException {
360                 if (!fUpToDate) {
361                         getVariables();
362                 }
363
364                 return (varList.size() > 0);
365         }
366
367         public int getLineNumber() {
368                 return fLineNumber;
369         }
370
371         /* (non-Javadoc)
372          *
373          */
374         public void setLineNumber(int line) {
375                 fLineNumber = line;
376         }
377
378         /* (non-Javadoc)
379          * @see org.eclipse.debug.core.model.IStackFrame#getCharStart()
380          */
381         public int getCharStart() throws DebugException {
382                 return -1;
383         }
384
385         /* (non-Javadoc)
386          * @see org.eclipse.debug.core.model.IStackFrame#getCharEnd()
387          */
388         public int getCharEnd() throws DebugException {
389                 return -1;
390         }
391
392         /* (non-Javadoc)fName
393          * @see org.eclipse.debug.core.model.IStackFrame#getName()
394          */
395         public String getName() throws DebugException {
396                 return fName.toString() + "::" + fWhere + " line: " + fLineNumber;
397         }
398
399         /* (non-Javadoc)
400          * @see org.eclipse.debug.core.model.IStackFrame#getRegisterGroups()
401          */
402         public IRegisterGroup[] getRegisterGroups() throws DebugException {
403                 return null;
404         }
405
406         /* (non-Javadoc)
407          * @see org.eclipse.debug.core.model.IStackFrame#hasRegisterGroups()
408          */
409         public boolean hasRegisterGroups() throws DebugException {
410                 return false;
411         }
412
413         /* (non-Javadoc)
414          * @see org.eclipse.debug.core.model.IStep#canStepInto()
415          */
416         public boolean canStepInto() {
417                 return fThread.canStepInto();
418         }
419
420         /* (non-Javadoc)
421          * @see org.eclipse.debug.core.model.IStep#canStepOver()
422          */
423         public boolean canStepOver() {
424                 return fThread.canStepOver();
425         }
426
427         /* (non-Javadoc)
428          * @see org.eclipse.debug.core.model.IStep#canStepReturn()
429          */
430         public boolean canStepReturn() {
431                 return fThread.canStepReturn();
432         }
433
434         /* (non-Javadoc)
435          * @see org.eclipse.debug.core.model.IStep#isStepping()
436          */
437         public boolean isStepping() {
438                 return fThread.isStepping();
439         }
440
441         /* (non-Javadoc)
442          * @see org.eclipse.debug.core.model.IStep#stepInto()
443          */
444         public void stepInto() throws DebugException {
445                 fThread.stepInto();
446         }
447
448         /* (non-Javadoc)
449          * @see org.eclipse.debug.core.model.IStep#stepOver()
450          */
451         public void stepOver() throws DebugException {
452                 fThread.stepOver();
453         }
454
455         /* (non-Javadoc)
456          * @see org.eclipse.debug.core.model.IStep#stepReturn()
457          */
458         public void stepReturn() throws DebugException {
459                 fThread.stepReturn();
460         }
461
462         /* (non-Javadoc)
463          * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
464          */
465         public boolean canResume() {
466                 return fThread.canResume();
467         }
468
469         /* (non-Javadoc)
470          * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
471          */
472         public boolean canSuspend() {
473                 return fThread.canSuspend();
474         }
475
476         /* (non-Javadoc)
477          * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
478          */
479         public boolean isSuspended() {
480                 return fThread.isSuspended();
481         }
482
483         /* (non-Javadoc)
484          * @see org.eclipse.debug.core.model.ISuspendResume#resume()
485          */
486         public void resume() throws DebugException {
487                 fThread.resume();
488         }
489
490         /* (non-Javadoc)
491          * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
492          */
493         public void suspend() throws DebugException {
494                 fThread.suspend();
495         }
496
497         /* (non-Javadoc)
498          * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
499          */
500         public boolean canTerminate() {
501                 return fThread.canTerminate();
502         }
503
504         /* (non-Javadoc)
505          * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
506          */
507         public boolean isTerminated() {
508                 return fThread.isTerminated();
509         }
510
511         /* (non-Javadoc)
512          * @see org.eclipse.debug.core.model.ITerminate#terminate()
513          */
514         public void terminate() throws DebugException {
515                 fThread.terminate();
516         }
517
518         /**
519          * Returns the name of the source file this stack frame is associated
520          * with.
521          *
522          * @return the name of the source file this stack frame is associated
523          * with. If the file associated with this frame does not exists, it returns null.
524          */
525         public String getSourceName() {
526                 if (fName == null) {
527                         return null;
528                 }
529
530                 IPath a = new Path(fName.getFile());
531
532                 return a.lastSegment();
533         }
534
535         public String getFullSourceName() {
536                 if (fName == null) {
537                         return null;
538                 }
539
540                 IPath a = new Path(fName.getFile());
541
542                 return a.toString ();
543         }
544
545         public boolean isSameStackFrame(Object obj) {
546                 boolean isSameStackFrame = false;
547
548                 if (obj instanceof XDebugStackFrame) {
549                         XDebugStackFrame sf = (XDebugStackFrame) obj;
550
551                         isSameStackFrame = sf.getSourceName ().equals (getSourceName ()) &&
552                                                sf.getType().equals (getType ()) &&
553                                                sf.getWhere().equals (getWhere ());
554                 }
555
556                 return isSameStackFrame;
557         }
558
559         public URL getFullName() {
560                 return fName;
561         }
562
563         public void setFullName (URL name) {
564             fName = name;
565         }
566
567         public int getLevel() {
568                 return fLevel;
569         }
570
571         public void setLevel(int level) {
572                 this.fLevel = level;
573         }
574
575
576         public String getType() {
577                 return fType;
578         }
579
580         public String getWhere() {
581                 return fWhere;
582         }
583
584         public boolean setVariableValue(XDebugVariable variable, String expression)  throws DebugException {
585                 return ((XDebugTarget) getDebugTarget()).setVarValue(variable.getName(), expression);
586         }
587
588     public Node eval(String expression) throws DebugException {
589                 return ((XDebugTarget) getDebugTarget()).eval(expression);
590     }
591
592         /**
593          *
594          */
595         public void setAvailable(boolean available) {
596                 fAvailable = available;
597         }
598
599         /**
600          *
601          */
602         public boolean isAvailable() {
603                 return fAvailable;
604         }
605
606         public void setUpToDate (boolean bValue) {
607                 this.fUpToDate = bValue;
608         }
609
610         public void setDescription(String desc) {
611                 this.fWhere = desc;
612         }
613
614         public String getDescription() {
615                 return this.fWhere;
616         }
617
618         /**
619          * This function is needed when sorting the stackframes by their index numbers.
620          *
621          * @param obj The stackframe which this one is compared to.
622          * @return
623          * <ul>
624          * <li> -1 if the index of this stackframe is less.
625          * <li> 0 if the index of both stackframes are equal (should no happen).
626          * <li> 1 if the index of this stackframe is greater.
627          * </ul>
628          */
629         public int compareTo(Object obj) {
630                 if (!(obj instanceof XDebugStackFrame)) {
631                         throw new IllegalArgumentException ("A PHPStackFrame can only be compared with another PHPStackFrame");
632                 }
633
634                 int frameLevel = ((XDebugStackFrame) obj).getLevel ();
635
636                 if (fLevel < frameLevel) {
637                         return -1;
638                 } else if (fLevel > frameLevel) {
639                         return 1;
640                 }
641                 return 0;
642         }
643 }