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 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
219 } catch (DebugException e) { // That's, because of the hasVariables method
222 break; // Found the variable,
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
231 // Look for the variables we can remove from the 'static' list
233 for (o = 0; o < varListOld.size(); o++) { // For every variable in 'static' list
234 varOld = (XDebugVariable) varListOld.get(o); // Get the static variable
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
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
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
252 * This function returns the array of PHPVariables for this stackframe
253 * The PHPVariables should not change (newly build up) between two steps
255 * A PHPVariable with the same name but with different object ID is
256 * handled as a new variable.
258 * @return The array of PHPVariables for this stackframe.
261 public synchronized IVariable[] getVariables() throws DebugException {
262 /* always read variables, poor performance
263 * but this fix bug #680.
264 * need to investigate on.
268 Node dfl = ((XDebugTarget) getDebugTarget()).getLocalVariables(fLevel);
269 Node dfg = ((XDebugTarget) getDebugTarget()).getGlobalVariables(fLevel);
270 parseVariable(dfl, dfg);
272 Vector newVariables = new Vector (Arrays.asList(fVariables));
273 resetHasChangedInfo (varList);
274 updateVariableList (varList, newVariables);
276 Collections.sort (varList, new XDebugVariableComparator ());
279 return (IVariable[]) varList.toArray (new IVariable[varList.size()]);
282 private void parseVariable(Node localVariables, Node globalVariables) throws DebugException {
283 NodeList property = localVariables.getChildNodes();
284 NodeList propertyGlobal = globalVariables.getChildNodes();
286 fVariables = new IVariable[property.getLength() + propertyGlobal.getLength()];
288 int length = property.getLength();
289 for (int i = 0; i < length; i++) {
290 XDebugVariable var = new XDebugVariable(this, property.item(i), null);
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;
304 private XDebugVariable findVariable(Vector varList, String varname) {
305 XDebugVariable variable;
306 XDebugAbstractValue value;
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
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);
324 if (variable.getName().equals(varname)) {
327 } catch (DebugException e) { // That's, because of the hasVariables method
335 * This method is called from the UI (e.g. from PHPDebugHover
336 * to find the variable the mouse is pointing to)
338 * @param s The variable name we are looking for.
341 public IVariable findVariable(String s) throws DebugException {
346 return (findVariable(varList, s)); // Prefix the variable name with $
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]);
357 * @see org.eclipse.debug.core.model.IStackFrame#hasVariables()
359 public boolean hasVariables() throws DebugException {
364 return (varList.size() > 0);
367 public int getLineNumber() {
374 public void setLineNumber(int line) {
379 * @see org.eclipse.debug.core.model.IStackFrame#getCharStart()
381 public int getCharStart() throws DebugException {
386 * @see org.eclipse.debug.core.model.IStackFrame#getCharEnd()
388 public int getCharEnd() throws DebugException {
392 /* (non-Javadoc)fName
393 * @see org.eclipse.debug.core.model.IStackFrame#getName()
395 public String getName() throws DebugException {
396 return fName.toString() + "::" + fWhere + " line: " + fLineNumber;
400 * @see org.eclipse.debug.core.model.IStackFrame#getRegisterGroups()
402 public IRegisterGroup[] getRegisterGroups() throws DebugException {
407 * @see org.eclipse.debug.core.model.IStackFrame#hasRegisterGroups()
409 public boolean hasRegisterGroups() throws DebugException {
414 * @see org.eclipse.debug.core.model.IStep#canStepInto()
416 public boolean canStepInto() {
417 return fThread.canStepInto();
421 * @see org.eclipse.debug.core.model.IStep#canStepOver()
423 public boolean canStepOver() {
424 return fThread.canStepOver();
428 * @see org.eclipse.debug.core.model.IStep#canStepReturn()
430 public boolean canStepReturn() {
431 return fThread.canStepReturn();
435 * @see org.eclipse.debug.core.model.IStep#isStepping()
437 public boolean isStepping() {
438 return fThread.isStepping();
442 * @see org.eclipse.debug.core.model.IStep#stepInto()
444 public void stepInto() throws DebugException {
449 * @see org.eclipse.debug.core.model.IStep#stepOver()
451 public void stepOver() throws DebugException {
456 * @see org.eclipse.debug.core.model.IStep#stepReturn()
458 public void stepReturn() throws DebugException {
459 fThread.stepReturn();
463 * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
465 public boolean canResume() {
466 return fThread.canResume();
470 * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
472 public boolean canSuspend() {
473 return fThread.canSuspend();
477 * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
479 public boolean isSuspended() {
480 return fThread.isSuspended();
484 * @see org.eclipse.debug.core.model.ISuspendResume#resume()
486 public void resume() throws DebugException {
491 * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
493 public void suspend() throws DebugException {
498 * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
500 public boolean canTerminate() {
501 return fThread.canTerminate();
505 * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
507 public boolean isTerminated() {
508 return fThread.isTerminated();
512 * @see org.eclipse.debug.core.model.ITerminate#terminate()
514 public void terminate() throws DebugException {
519 * Returns the name of the source file this stack frame is associated
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.
525 public String getSourceName() {
530 IPath a = new Path(fName.getFile());
532 return a.lastSegment();
535 public String getFullSourceName() {
540 IPath a = new Path(fName.getFile());
542 return a.toString ();
545 public boolean isSameStackFrame(Object obj) {
546 boolean isSameStackFrame = false;
548 if (obj instanceof XDebugStackFrame) {
549 XDebugStackFrame sf = (XDebugStackFrame) obj;
551 isSameStackFrame = sf.getSourceName ().equals (getSourceName ()) &&
552 sf.getType().equals (getType ()) &&
553 sf.getWhere().equals (getWhere ());
556 return isSameStackFrame;
559 public URL getFullName() {
563 public void setFullName (URL name) {
567 public int getLevel() {
571 public void setLevel(int level) {
576 public String getType() {
580 public String getWhere() {
584 public boolean setVariableValue(XDebugVariable variable, String expression) throws DebugException {
585 return ((XDebugTarget) getDebugTarget()).setVarValue(variable.getName(), expression);
588 public Node eval(String expression) throws DebugException {
589 return ((XDebugTarget) getDebugTarget()).eval(expression);
595 public void setAvailable(boolean available) {
596 fAvailable = available;
602 public boolean isAvailable() {
606 public void setUpToDate (boolean bValue) {
607 this.fUpToDate = bValue;
610 public void setDescription(String desc) {
614 public String getDescription() {
619 * This function is needed when sorting the stackframes by their index numbers.
621 * @param obj The stackframe which this one is compared to.
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.
629 public int compareTo(Object obj) {
630 if (!(obj instanceof XDebugStackFrame)) {
631 throw new IllegalArgumentException ("A PHPStackFrame can only be compared with another PHPStackFrame");
634 int frameLevel = ((XDebugStackFrame) obj).getLevel ();
636 if (fLevel < frameLevel) {
638 } else if (fLevel > frameLevel) {