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());
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
277 // varOld.setValueChanged (false); // Reset the 'has changed' flag
279 } catch (DebugException e) { // That's, because of the hasVariables method
282 break; // Found the variable,
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
291 // Look for the variables we can remove from the 'static' list
293 for (o = 0; o < varListOld.size(); o++) { // For every variable in 'static' list
294 varOld = (PHPVariable) varListOld.get(o); // Get the static variable
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
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
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
312 * This function returns the array of PHPVariables for this stackframe
313 * The PHPVariables should not change (newly build up) between two steps
315 * A PHPVariable with the same name but with different object ID is
316 * handled as a new variable.
318 * @return The array of PHPVariables for this stackframe.
320 public IVariable[] getVariables() throws DebugException {
322 resetHasChangedInfo(varList);
323 updateVariableList(varList, this.getPHPDBGProxy().readVariables(this));
325 Collections.sort(varList, new PHPVariableComparator());
328 return (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
334 private PHPVariable findVariable(Vector varList, String varname) {
335 PHPVariable variable;
339 for (i = 0; i < varList.size(); i++) { // For all variables
340 variable = (PHPVariable) varList.get(i); // Get the variable
341 value = (PHPValue) variable.getValue(); // Get the value of the variable
344 if (value.hasVariables()) { // Does the variable/value have children
345 if (!hasRecursion(variable)) { // Don't follow recursive variable/values
346 variable = findVariable(value.getChildVariables(),
349 if (variable != null) {
353 } else if ((variable.getName()).equals(varname)) { //
356 } catch (DebugException e) { // That's, because of the hasVariables method
364 * This method is called from the UI (e.g. from PHPDebugHover
365 * to find the variable the mouse is pointing to)
367 * @param s The variable name we are looking for.
370 public IVariable findVariable(String s) throws DebugException {
375 return (findVariable(varList, s)); // Prefix the variable name with $
381 public boolean hasVariables() throws DebugException {
386 return (varList.size() > 0);
389 public int getLineNumber() {
393 public void setLineNumber(int line) {
397 public int getCharStart() throws DebugException {
402 public int getCharEnd() throws DebugException {
407 public String getName() {
408 StringBuffer name = new StringBuffer();
410 if (!this.getDescription().equals("")) {
411 name.append(this.getDescription());
413 name.append(this.getFileName());
416 name.append(" [line ");
417 name.append(this.getLineNumber());
420 return name.toString();
423 public String getFileName() {
427 public void setDescription(String desc) {
428 this.description = desc;
431 public String getDescription() {
432 return this.description;
435 public IRegisterGroup[] getRegisterGroups() throws DebugException {
439 public boolean hasRegisterGroups() throws DebugException {
443 public String getModelIdentifier() {
444 return this.getThread().getModelIdentifier();
447 public IDebugTarget getDebugTarget() {
448 return this.getThread().getDebugTarget();
451 public ILaunch getLaunch() {
452 return this.getDebugTarget().getLaunch();
455 public boolean canStepInto() {
459 public boolean canStepOver() {
463 public boolean canStepReturn() {
467 public boolean isStepping() {
474 public void stepInto() throws DebugException {
477 thread.prepareForResume(DebugEvent.STEP_INTO); // Don't know why, but this is necessary
478 this.getPHPDBGProxy().readStepIntoEnd(PHPStackFrame.this);
480 // Commented out sending the RESUME event because it was already sent by prepareForResume.
481 // The second RESUME event leads only to a little flickering within the variables view.
482 // It is also not clear why this event was necessary in eclipse < 3.2
483 // Also sending a SUSPEND event here leads to a total rebuild of the variables view.
484 // (eclipse 3.2 has a build in timeout of 500 ms which leads to a auto suspend, with
485 // no flickering... but why???)
487 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
488 //DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
494 public void stepOver() throws DebugException {
497 thread.prepareForResume(DebugEvent.STEP_OVER);
498 this.getPHPDBGProxy().readStepOverEnd(PHPStackFrame.this);
500 // See comment within the previous stepInto method.
502 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
503 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
509 public void stepReturn() throws DebugException {
512 thread.prepareForResume(DebugEvent.STEP_RETURN);
513 this.getPHPDBGProxy().readStepReturnEnd(PHPStackFrame.this);
515 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
516 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
519 public boolean canResume() {
520 return this.getThread().canResume();
523 public boolean canSuspend() {
524 return this.getThread().canSuspend();
527 public boolean isSuspended() {
528 return this.getThread().isSuspended();
531 public void resume() throws DebugException {
534 this.getThread().resume();
537 public void suspend() throws DebugException {
540 public boolean canTerminate() {
541 return this.getThread().canTerminate();
544 public boolean isTerminated() {
545 return this.getThread().isTerminated();
548 public void terminate() throws DebugException {
549 getPHPDBGProxy().stop();
552 public int getIndex() {
556 public void setIndex(int index) {
560 public PHPDBGProxy getPHPDBGProxy() {
561 PHPDebugTarget DebugTarget;
563 DebugTarget = (PHPDebugTarget) thread.getDebugTarget();
565 return DebugTarget.getPHPDBGProxy();
568 public void setFile(String file) {
572 public int getModNo() {
577 * This function is needed when sorting the stackframes by their index numbers.
579 * @param obj The stackframe which this one is compared to.
582 * <li> -1 if the index of this stackframe is less.
583 * <li> 0 if the index of both stackfream is equal (should no happen).
584 * <li> 1 if the index of this stackfram is greater.
587 public int compareTo(Object obj) {
588 //if (index < ((PHPStackFrame) obj).getIndex()) {
590 //} else if (index > ((PHPStackFrame) obj).getIndex()) {
595 return Integer.signum(index - ((PHPStackFrame) obj).getIndex());