2 * Created on 23.11.2004
4 * TODO To change the template for this generated file go to
5 * Window - Preferences - Java - Code Style - Code Templates
7 package net.sourceforge.phpeclipse.xdebug.php.model;
9 import java.net.MalformedURLException;
11 import java.util.Arrays;
12 import java.util.Collections;
13 import java.util.Vector;
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;
27 * @author PHPeclipse team
31 public class XDebugStackFrame extends XDebugElement implements IStackFrame, Comparable {
32 private XDebugThread fThread;
34 private int fLineNumber;
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
46 * Constructs a stack frame in the given thread with the given
50 * @param data frame data
51 * @param id stack frame id (0 is the bottom of the stack)
53 public XDebugStackFrame(XDebugThread thread, int id, String type, int lineNumber, String where, /*URL*/String filename) {
54 super(/*thread == null ? null : */(XDebugTarget) thread.getDebugTarget());
57 this.fThread = thread;
59 this.fLineNumber = lineNumber;
61 this.varList = new Vector();
64 fName = new URL(filename);
65 } catch (MalformedURLException e) {
70 public void incrementStepCounter() {
75 * @see org.eclipse.debug.core.model.IStackFrame#getThread()
77 public IThread getThread() {
82 * @see IAdaptable#getAdapter(Class)
84 public Object getAdapter(Class adapter) {
85 if (adapter == XDebugStackFrame.class) {
89 return super.getAdapter(adapter);
92 public IDebugTarget getDebugTarget() {
93 return this.getThread().getDebugTarget();
99 private void resetHasChangedInfo(Vector varList) {
102 XDebugAbstractValue val;
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
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
114 } catch (DebugException e) { // That's, because of the hasVariables method
117 var.setValueChanged(false); // Reset the 'has changed' flag
122 * Go up the tree of PHPVariables
123 * look whether the PHPValue is a reference to a parent PHPValue
125 * TODO Check where this recursion can come from.
126 * Whether this back reference is legal or a bug.
128 * Typically $GLOBALS contains $GLOBALS
133 * <li> false if the PHPValue is not a child of itself
134 * <li> true if the PHPValue is
137 private boolean hasRecursion(XDebugVariable var) {
138 XDebugVariable parentVar;
139 XDebugAbstractValue val;
141 val = (XDebugAbstractValue) var.getValue(); // Get the PHPValue from the current PHPVariable
143 while (var != null) { // As long as we have PHPVariable
144 parentVar = var.getParent(); // Get the parent PHPVariable
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
156 return false; // No recursion found
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:
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.
175 * @param varListOld The 'static' list of variables which are to be updated.
176 * @param varListNew The new list of (current) variables from DBG.
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
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.
190 for (n = 0; n < varListNew.size(); n++) { // For every variable in 'DBG list'
191 varNew = (XDebugVariable) varListNew.get(n); // Get the DBG variable
193 for (o = 0; o < varListOld.size(); o++) { // For every variable in static list
194 varOld = (XDebugVariable) varListOld.get(o); // Get the static variable
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
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
206 valOld.getChildVariables(), // Update the variable list for the child variables
207 valNew.getChildVariables());
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 varOld.setValueChanged(true); // and set the 'has changed' flag, so that the variable view
215 // could show the user the changed status with a different
218 } catch (DebugException e) { // That's, because of the hasVariables method
221 break; // Found the variable,
225 if (o == varListOld.size()) { // Did we found the variable within the static list?
226 varListOld.add(varNew); // No, then add the DBG variable to the static list
230 // Look for the variables we can remove from the 'static' list
232 for (o = 0; o < varListOld.size(); o++) { // For every variable in 'static' list
233 varOld = (XDebugVariable) varListOld.get(o); // Get the static variable
235 for (n = 0; n < varListNew.size(); n++) { // For all variables in 'DBG' list
236 varNew = (XDebugVariable) varListNew.get(n); // Get the variable from the 'DBG' list
238 if (varNew.getName().equals(varOld.getName())) { // Did we found the 'static' list variable within the 'DBG' list?
239 break; // Yes we found the variable, then leave the loop
243 if (n == varListNew.size()) { // Did not find the 'static' list variable within the 'DBG' list?
244 varListOld.remove(o--); // then remove the 'static' list variable from list
251 * This function returns the array of PHPVariables for this stackframe
252 * The PHPVariables should not change (newly build up) between two steps
254 * A PHPVariable with the same name but with different object ID is
255 * handled as a new variable.
257 * @return The array of PHPVariables for this stackframe.
260 public synchronized IVariable[] getVariables() throws DebugException {
261 /* always read variables, poor performance
262 * but this fix bug #680.
263 * need to investigate on.
267 Node dfl = ((XDebugTarget) getDebugTarget()).getLocalVariables(fLevel);
268 Node dfg = ((XDebugTarget) getDebugTarget()).getGlobalVariables(fLevel);
269 parseVariable(dfl, dfg);
271 Vector newVariables = new Vector (Arrays.asList(fVariables));
272 resetHasChangedInfo (varList);
273 updateVariableList (varList, newVariables);
275 Collections.sort (varList, new XDebugVariableComparator ());
278 return (IVariable[]) varList.toArray (new IVariable[varList.size()]);
281 private void parseVariable(Node localVariables, Node globalVariables) throws DebugException {
282 NodeList property = localVariables.getChildNodes();
283 NodeList propertyGlobal = globalVariables.getChildNodes();
285 fVariables = new IVariable[property.getLength() + propertyGlobal.getLength()];
287 int length = property.getLength();
288 for (int i = 0; i < length; i++) {
289 XDebugVariable var = new XDebugVariable(this, property.item(i));
293 int globalLength = propertyGlobal.getLength();
294 for (int k = 0; k < globalLength; k++) {
295 XDebugVariable var = new XDebugVariable(this, propertyGlobal.item(k));
296 fVariables[k + length] = var;
303 private XDebugVariable findVariable(Vector varList, String varname) {
304 XDebugVariable variable;
305 XDebugAbstractValue value;
308 for (i = 0; i < varList.size(); i++) { // For all variables
309 variable = (XDebugVariable) varList.get(i); // Get the variable
310 value = (XDebugAbstractValue) variable.getValue(); // Get the value of the variable
313 if (value.hasVariables()) { // Does the variable/value have children
314 if (!hasRecursion(variable)) { // Don't follow recursive variable/values
315 XDebugVariable var = findVariable(value.getChildVariables(), varname);
323 if (variable.getName().equals(varname)) {
326 } catch (DebugException e) { // That's, because of the hasVariables method
334 * This method is called from the UI (e.g. from PHPDebugHover
335 * to find the variable the mouse is pointing to)
337 * @param s The variable name we are looking for.
340 public IVariable findVariable(String s) throws DebugException {
345 return (findVariable(varList, s)); // Prefix the variable name with $
348 /*public void evaluateChange(IStackFrame OldStackFrame) throws DebugException {
349 IVariable[] OldVariable = ((XDebugStackFrame) OldStackFrame).getVariables();
350 for (int i = 0; i < fVariables.length; i++) {
351 ((XDebugVariable) fVariables[i]).setChange(OldVariable[i]);
356 * @see org.eclipse.debug.core.model.IStackFrame#hasVariables()
358 public boolean hasVariables() throws DebugException {
363 return (varList.size() > 0);
366 public int getLineNumber() {
373 public void setLineNumber(int line) {
378 * @see org.eclipse.debug.core.model.IStackFrame#getCharStart()
380 public int getCharStart() throws DebugException {
385 * @see org.eclipse.debug.core.model.IStackFrame#getCharEnd()
387 public int getCharEnd() throws DebugException {
391 /* (non-Javadoc)fName
392 * @see org.eclipse.debug.core.model.IStackFrame#getName()
394 public String getName() throws DebugException {
395 return fName.toString() + "::" + fWhere + " line: " + fLineNumber;
399 * @see org.eclipse.debug.core.model.IStackFrame#getRegisterGroups()
401 public IRegisterGroup[] getRegisterGroups() throws DebugException {
406 * @see org.eclipse.debug.core.model.IStackFrame#hasRegisterGroups()
408 public boolean hasRegisterGroups() throws DebugException {
413 * @see org.eclipse.debug.core.model.IStep#canStepInto()
415 public boolean canStepInto() {
416 return fThread.canStepInto();
420 * @see org.eclipse.debug.core.model.IStep#canStepOver()
422 public boolean canStepOver() {
423 return fThread.canStepOver();
427 * @see org.eclipse.debug.core.model.IStep#canStepReturn()
429 public boolean canStepReturn() {
430 return fThread.canStepReturn();
434 * @see org.eclipse.debug.core.model.IStep#isStepping()
436 public boolean isStepping() {
437 return fThread.isStepping();
441 * @see org.eclipse.debug.core.model.IStep#stepInto()
443 public void stepInto() throws DebugException {
448 * @see org.eclipse.debug.core.model.IStep#stepOver()
450 public void stepOver() throws DebugException {
455 * @see org.eclipse.debug.core.model.IStep#stepReturn()
457 public void stepReturn() throws DebugException {
458 fThread.stepReturn();
462 * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
464 public boolean canResume() {
465 return fThread.canResume();
469 * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
471 public boolean canSuspend() {
472 return fThread.canSuspend();
476 * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
478 public boolean isSuspended() {
479 return fThread.isSuspended();
483 * @see org.eclipse.debug.core.model.ISuspendResume#resume()
485 public void resume() throws DebugException {
490 * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
492 public void suspend() throws DebugException {
497 * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
499 public boolean canTerminate() {
500 return fThread.canTerminate();
504 * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
506 public boolean isTerminated() {
507 return fThread.isTerminated();
511 * @see org.eclipse.debug.core.model.ITerminate#terminate()
513 public void terminate() throws DebugException {
518 * Returns the name of the source file this stack frame is associated
521 * @return the name of the source file this stack frame is associated
522 * with. If the file associated with this frame does not exists, it returns null.
524 public String getSourceName() {
529 IPath a = new Path(fName.getFile());
531 return a.lastSegment();
534 public String getFullSourceName() {
539 IPath a = new Path(fName.getFile());
541 return a.toString ();
544 public boolean isSameStackFrame(Object obj) {
545 boolean isSameStackFrame = false;
547 if (obj instanceof XDebugStackFrame) {
548 XDebugStackFrame sf = (XDebugStackFrame) obj;
550 isSameStackFrame = sf.getSourceName ().equals (getSourceName ()) &&
551 sf.getType().equals (getType ()) &&
552 sf.getWhere().equals (getWhere ());
555 return isSameStackFrame;
558 public URL getFullName() {
562 public void setFullName (URL name) {
566 public int getLevel() {
570 public void setLevel(int level) {
575 public String getType() {
579 public String getWhere() {
583 public boolean setVariableValue(XDebugVariable variable, String expression) throws DebugException {
584 return ((XDebugTarget) getDebugTarget()).setVarValue("$" + variable.getName(), expression);
587 public Node eval(String expression) throws DebugException {
588 return ((XDebugTarget) getDebugTarget()).eval(expression);
594 public void setAvailable(boolean available) {
595 fAvailable = available;
601 public boolean isAvailable() {
605 public void setUpToDate (boolean bValue) {
606 this.fUpToDate = bValue;
609 public void setDescription(String desc) {
613 public String getDescription() {
618 * This function is needed when sorting the stackframes by their index numbers.
620 * @param obj The stackframe which this one is compared to.
623 * <li> -1 if the index of this stackframe is less.
624 * <li> 0 if the index of both stackframes are equal (should no happen).
625 * <li> 1 if the index of this stackframe is greater.
628 public int compareTo(Object obj) {
629 if (!(obj instanceof XDebugStackFrame)) {
630 throw new IllegalArgumentException ("A PHPStackFrame can only be compared with another PHPStackFrame");
633 int frameLevel = ((XDebugStackFrame) obj).getLevel ();
635 if (fLevel < frameLevel) {
637 } else if (fLevel > frameLevel) {