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
9 IBM Corporation - Initial implementation
10 Vicente Fernando - www.alfersoft.com.ar
11 **********************************************************************/
12 package net.sourceforge.phpdt.internal.debug.core.model;
14 import java.util.Collections;
15 import java.util.Vector;
17 import net.sourceforge.phpdt.internal.debug.core.PHPDBGProxy;
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;
32 public class PHPStackFrame extends PHPDebugElement implements IStackFrame, Comparable {
34 private PHPThread thread; // The thread to which this stackframe belongs
35 private String file; // The file name???
36 private int lineNumber;
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
44 private boolean fAvailable; // Needed when updating the stackframe list, shows whether the stackframe
45 // is within the list which was received from dbg
57 public PHPStackFrame(PHPThread thread, String file, int line, int index,
58 String desc, int modno, int scope_id) {
61 this.lineNumber = line;
65 this.description = desc;
67 this.scope_id = scope_id;
68 this.varList = new Vector();
69 this.fUpToDate = false;
79 // public PHPStackFrame(PHPThread thread, String file, int line, int index) {
82 // this.lineNumber = line;
83 // this.index = index;
85 // this.thread = thread;
86 // this.fUpToDate = false;
93 public int getScopeID() {
100 public void setScopeID(int scope_id) {
101 this.scope_id = scope_id;
108 public IThread getThread() {
115 public void setThread(PHPThread thread) {
116 this.thread = thread;
122 // private void setUpToDate(boolean upToDate) {
123 // fUpToDate = upToDate;
129 // private boolean isUpToDate() {
136 public void setAvailable(boolean available) {
137 fAvailable = available;
143 public boolean isAvailable() {
148 * @see IAdaptable#getAdapter(Class)
150 public Object getAdapter(Class adapter) {
151 if (adapter == PHPStackFrame.class) {
155 return super.getAdapter(adapter);
161 private void resetHasChangedInfo(Vector varList) {
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
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
176 } catch (DebugException e) { // That's, because of the hasVariables method
179 var.setValueChanged(false); // Reset the 'has changed' flag
184 * Go up the tree of PHPVariables
185 * look whether the PHPValue is a reference to a parent PHPValue
187 * TODO Check where this recursion can come from.
188 * Whether this back reference is legal or a bug.
190 * Typically $GLOBALS contains $GLOBALS
195 * <li> false if the PHPValue is not a child of itself
196 * <li> true if the PHPValue is
199 private boolean hasRecursion(PHPVariable var) {
200 PHPVariable parentVar;
203 val = (PHPValue) var.getValue(); // Get the PHPValue from the current PHPVariable
205 while (var != null) { // As long as we have PHPVariable
206 parentVar = var.getParent(); // Get the parent PHPVariable
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
218 return false; // No recursion found
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:
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.
237 * @param varListOld The 'static' list of variables which are to be updated.
238 * @param varListNew The new list of (current) variables from DBG.
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
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.
252 for (n = 0; n < varListNew.size(); n++) { // For every variable in 'DBG list'
253 varNew = (PHPVariable) varListNew.get(n); // Get the DBG variable
255 for (o = 0; o < varListOld.size(); o++) { // For every variable in static list
256 varOld = (PHPVariable) varListOld.get(o); // Get the static variable
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
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());
270 if (!valOld.getValueString().equals(
271 valNew.getValueString())) { // Has the value changed?
272 valOld.setValueString(valNew.getValueString()); // Yes, set the 'static' value (variable) to the new value
273 varOld.setValueChanged(true); // and set the 'has changed' flag, so that the variable view
274 // could show the user the changed status with a different
277 } catch (DebugException e) { // That's, because of the hasVariables method
280 break; // Found the variable,
284 if (o == varListOld.size()) { // Did we found the variable within the static list?
285 varListOld.add(varNew); // No, then add the DBG variable to the static list
289 // Look for the variables we can remove from the 'static' list
291 for (o = 0; o < varListOld.size(); o++) { // For every variable in 'static' list
292 varOld = (PHPVariable) varListOld.get(o); // Get the static variable
294 for (n = 0; n < varListNew.size(); n++) { // For all variables in 'DBG' list
295 varNew = (PHPVariable) varListNew.get(n); // Get the variable from the 'DBG' list
297 if (varNew.getName().equals(varOld.getName())) { // Did we found the 'static' list variable within the 'DBG' list?
298 break; // Yes we found the variable, then leave the loop
302 if (n == varListNew.size()) { // Did not find the 'static' list variable within the 'DBG' list?
303 varListOld.remove(o--); // then remove the 'static' list variable from list
310 * This function returns the array of PHPVariables for this stackframe
311 * The PHPVariables should not change (newly build up) between two steps
313 * A PHPVariable with the same name but with different object ID is
314 * handled as a new variable.
316 * @return The array of PHPVariables for this stackframe.
318 public IVariable[] getVariables() throws DebugException {
320 resetHasChangedInfo(varList);
321 updateVariableList(varList, this.getPHPDBGProxy().readVariables(this));
323 Collections.sort(varList, new PHPVariableComparator());
326 return (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
332 private PHPVariable findVariable(Vector varList, String varname) {
333 PHPVariable variable;
337 for (i = 0; i < varList.size(); i++) { // For all variables
338 variable = (PHPVariable) varList.get(i); // Get the variable
339 value = (PHPValue) variable.getValue(); // Get the value of the variable
342 if (value.hasVariables()) { // Does the variable/value have children
343 if (!hasRecursion(variable)) { // Don't follow recursive variable/values
344 PHPVariable var = findVariable(value.getChildVariables(), varname);
350 if (variable.getName().equals(varname)) {
353 } catch (DebugException e) { // That's, because of the hasVariables method
361 * This method is called from the UI (e.g. from PHPDebugHover
362 * to find the variable the mouse is pointing to)
364 * @param s The variable name we are looking for.
367 public IVariable findVariable(String s) throws DebugException {
372 return (findVariable(varList, s)); // Prefix the variable name with $
378 public boolean hasVariables() throws DebugException {
383 return (varList.size() > 0);
386 public int getLineNumber() {
390 public void setLineNumber(int line) {
394 public int getCharStart() throws DebugException {
399 public int getCharEnd() throws DebugException {
404 public String getName() {
405 StringBuffer name = new StringBuffer();
407 if (!this.getDescription().equals("")) {
408 name.append(this.getDescription());
410 name.append(this.getFileName());
413 name.append(" [line ");
414 name.append(this.getLineNumber());
417 return name.toString();
420 public String getFileName() {
424 public void setDescription(String desc) {
425 this.description = desc;
428 public String getDescription() {
429 return this.description;
432 public IRegisterGroup[] getRegisterGroups() throws DebugException {
436 public boolean hasRegisterGroups() throws DebugException {
440 public String getModelIdentifier() {
441 return this.getThread().getModelIdentifier();
444 public IDebugTarget getDebugTarget() {
445 return this.getThread().getDebugTarget();
448 public ILaunch getLaunch() {
449 return this.getDebugTarget().getLaunch();
452 public boolean canStepInto() {
456 public boolean canStepOver() {
460 public boolean canStepReturn() {
464 public boolean isStepping() {
471 public void stepInto() throws DebugException {
474 thread.prepareForResume(DebugEvent.STEP_INTO); // Don't know why, but this is necessary
475 this.getPHPDBGProxy().readStepIntoEnd(PHPStackFrame.this);
477 // Commented out sending the RESUME event because it was already sent by prepareForResume.
478 // The second RESUME event leads only to a little flickering within the variables view.
479 // It is also not clear why this event was necessary in eclipse < 3.2
480 // Also sending a SUSPEND event here leads to a total rebuild of the variables view.
481 // (eclipse 3.2 has a build in timeout of 500 ms which leads to a auto suspend, with
482 // no flickering... but why???)
484 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
485 //DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
491 public void stepOver() throws DebugException {
494 thread.prepareForResume(DebugEvent.STEP_OVER);
495 this.getPHPDBGProxy().readStepOverEnd(PHPStackFrame.this);
497 // See comment within the previous stepInto method.
499 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
500 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
506 public void stepReturn() throws DebugException {
509 thread.prepareForResume(DebugEvent.STEP_RETURN);
510 this.getPHPDBGProxy().readStepReturnEnd(PHPStackFrame.this);
512 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
513 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
516 public boolean canResume() {
517 return this.getThread().canResume();
520 public boolean canSuspend() {
521 return this.getThread().canSuspend();
524 public boolean isSuspended() {
525 return this.getThread().isSuspended();
528 public void resume() throws DebugException {
531 this.getThread().resume();
534 public void suspend() throws DebugException {
537 public boolean canTerminate() {
538 return this.getThread().canTerminate();
541 public boolean isTerminated() {
542 return this.getThread().isTerminated();
545 public void terminate() throws DebugException {
546 getPHPDBGProxy().stop();
549 public int getIndex() {
553 public void setIndex(int index) {
557 public PHPDBGProxy getPHPDBGProxy() {
558 PHPDebugTarget DebugTarget;
560 DebugTarget = (PHPDebugTarget) thread.getDebugTarget();
562 return DebugTarget.getPHPDBGProxy();
565 public void setFile(String file) {
569 public int getModNo() {
574 * This function is needed when sorting the stackframes by their index numbers.
576 * @param obj The stackframe which this one is compared to.
579 * <li> -1 if the index of this stackframe is less.
580 * <li> 0 if the index of both stackfream is equal (should no happen).
581 * <li> 1 if the index of this stackfram is greater.
584 public int compareTo(Object obj) {
585 //if (index < ((PHPStackFrame) obj).getIndex()) {
587 //} else if (index > ((PHPStackFrame) obj).getIndex()) {
592 return Integer.signum(index - ((PHPStackFrame) obj).getIndex());