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