X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugStackFrame.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugStackFrame.java index b0e057f..ece1a54 100644 --- a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugStackFrame.java +++ b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugStackFrame.java @@ -8,10 +8,14 @@ package net.sourceforge.phpeclipse.xdebug.php.model; import java.net.MalformedURLException; import java.net.URL; +import java.util.Arrays; +import java.util.Collections; +import java.util.Vector; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IRegisterGroup; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IThread; @@ -24,73 +28,262 @@ import org.w3c.dom.NodeList; * @author Axel * */ -public class XDebugStackFrame extends XDebugElement implements IStackFrame { - private XDebugThread fThread; - private URL fName; - private int fLineNumber; - private int fLevel; - private String fType; - private String fWhere; - private IVariable[] fVariables; - private int fStepCount = 0; - +public class XDebugStackFrame extends XDebugElement implements IStackFrame, Comparable { + private XDebugThread fThread; + private URL fName; + private int fLineNumber; + private int fLevel; + private String fType; + private String fWhere; + private IVariable[] fVariables; + private int fStepCount = 0; + private boolean fUpToDate = false; + private Vector varList; // Variables list + private boolean fAvailable; // Needed when updating the stackframe list, shows whether the stackframe + // is within the list which was received from XDebug + /** * Constructs a stack frame in the given thread with the given * frame data. - * + * * @param thread * @param data frame data * @param id stack frame id (0 is the bottom of the stack) */ public XDebugStackFrame(XDebugThread thread, int id, String type, int lineNumber, String where, /*URL*/String filename) { super(/*thread == null ? null : */(XDebugTarget) thread.getDebugTarget()); - - fLevel = id; - fThread = thread; - fType = type; - fLineNumber = lineNumber; - fWhere = where; - + + this.fLevel = id; + this.fThread = thread; + this.fType = type; + this.fLineNumber = lineNumber; + this.fWhere = where; + this.varList = new Vector(); + try { - fName = new URL(filename); + fName = new URL(filename); } catch (MalformedURLException e) { e.printStackTrace(); } } - + public void incrementStepCounter() { fStepCount++; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStackFrame#getThread() */ public IThread getThread() { return fThread; } - - public IVariable[] getVariables() throws DebugException { + + /** + * @see IAdaptable#getAdapter(Class) + */ + public Object getAdapter(Class adapter) { + if (adapter == XDebugStackFrame.class) { + return this; + } + + return super.getAdapter(adapter); + } + + public IDebugTarget getDebugTarget() { + return this.getThread().getDebugTarget(); + } + + /** + * + */ + private void resetHasChangedInfo(Vector varList) { + int n; + XDebugVariable var; + XDebugAbstractValue val; + + for (n = 0; n < varList.size (); n++) { // For every variable in 'DBG list' + var = (XDebugVariable) varList.get(n); // Get the variable + val = (XDebugAbstractValue) var.getValue(); // Get the variable's value + + try { + if (val.hasVariables()) { // Do we have other variables within the value + if (!hasRecursion(var)) { // Is this variable (value) branch recursive? + resetHasChangedInfo(val.getChildVariables()); // No, go into branch + } + } + } catch (DebugException e) { // That's, because of the hasVariables method + } + + var.setValueChanged(false); // Reset the 'has changed' flag + } + } + + /** + * Go up the tree of PHPVariables + * look whether the PHPValue is a reference to a parent PHPValue + * + * TODO Check where this recursion can come from. + * Whether this back reference is legal or a bug. + * + * Typically $GLOBALS contains $GLOBALS + * + * @param var + * @return + * + */ + private boolean hasRecursion(XDebugVariable var) { + XDebugVariable parentVar; + XDebugAbstractValue val; + + val = (XDebugAbstractValue) var.getValue(); // Get the PHPValue from the current PHPVariable + + while (var != null) { // As long as we have PHPVariable + parentVar = var.getParent(); // Get the parent PHPVariable + + if (parentVar != null) { // Is there a parent? + if (parentVar.getValue().equals(val)) { // Get the PHPValue for the parent PHPVariable and check + // whether it is the same + return true; // Return, if we have recursion + } + } + + var = parentVar; + } + + return false; // No recursion found + } + + /** + * This method updates the 'static' variables list. + * It does a replication between the 'static' list (the variable list which + * is a member of this DBG interface object) and the DBG variable list + * (the list of variables which is received from PHP via DBG with the current suspend) + * Replication is done in the following way: + * + * + * @param varListOld The 'static' list of variables which are to be updated. + * @param varListNew The new list of (current) variables from DBG. + */ + private void updateVariableList(Vector varListOld, Vector varListNew) { + XDebugVariable varOld; // The variable from the 'static' list + XDebugVariable varNew; // The variable from the DBG list + XDebugAbstractValue valOld; // The value of the current variable from 'static' list + XDebugAbstractValue valNew; // The value of the current variable from DBG list + int n; // Index for the DBG list + int o; // Index for the static list + + // Add the variables (and childs) to the static list if they are new + // and update the values of variables which are already existend within + // the 'static' list. + + for (n = 0; n < varListNew.size(); n++) { // For every variable in 'DBG list' + varNew = (XDebugVariable) varListNew.get(n); // Get the DBG variable + + for (o = 0; o < varListOld.size(); o++) { // For every variable in static list + varOld = (XDebugVariable) varListOld.get(o); // Get the static variable + + if (varNew.getName().equals(varOld.getName())) { // Did we found the variable within the 'static' list? + valOld = (XDebugAbstractValue) varOld.getValue(); // Get the value from 'static' + valNew = (XDebugAbstractValue) varNew.getValue(); // Get the value from DBG + + try { + if (valOld.hasVariables() || // If the 'static' value has child variables + valNew.hasVariables()) { // or if the DBG value has child variables + if (!hasRecursion(varOld) && + !hasRecursion(varNew)) { // Both branches should not have a recursion + updateVariableList( + valOld.getChildVariables(), // Update the variable list for the child variables + valNew.getChildVariables()); + } + } + + if (!valOld.getValueString().equals( + valNew.getValueString())) { // Has the value changed? + valOld.setValueString(valNew.getValueString()); // Yes, set the 'static' value (variable) to the new value + varOld.setValueChanged(true); // and set the 'has changed' flag, so that the variable view + // could show the user the changed status with a different + // color + } + } catch (DebugException e) { // That's, because of the hasVariables method + } + + break; // Found the variable, + } + } + + if (o == varListOld.size()) { // Did we found the variable within the static list? + varListOld.add(varNew); // No, then add the DBG variable to the static list + } + } + + // Look for the variables we can remove from the 'static' list + + for (o = 0; o < varListOld.size(); o++) { // For every variable in 'static' list + varOld = (XDebugVariable) varListOld.get(o); // Get the static variable + + for (n = 0; n < varListNew.size(); n++) { // For all variables in 'DBG' list + varNew = (XDebugVariable) varListNew.get(n); // Get the variable from the 'DBG' list + + if (varNew.getName().equals(varOld.getName())) { // Did we found the 'static' list variable within the 'DBG' list? + break; // Yes we found the variable, then leave the loop + } + } + + if (n == varListNew.size()) { // Did not find the 'static' list variable within the 'DBG' list? + varListOld.remove(o--); // then remove the 'static' list variable from list + } + } + } + + /** + * + * This function returns the array of PHPVariables for this stackframe + * The PHPVariables should not change (newly build up) between two steps + * (or breaks). + * A PHPVariable with the same name but with different object ID is + * handled as a new variable. + * + * @return The array of PHPVariables for this stackframe. + */ + + public synchronized IVariable[] getVariables() throws DebugException { /* always read variables, poor performance * but this fix bug #680. * need to investigate on. */ - - //if (fVariables == null) { - Node dfl = ((XDebugTarget) getDebugTarget()).getLocalVariables(fLevel); - Node dfg = ((XDebugTarget) getDebugTarget()).getGlobalVariables(fLevel); - parseVariable(dfl, dfg); - //} - return fVariables; + if (!fUpToDate) { + Node dfl = ((XDebugTarget) getDebugTarget()).getLocalVariables(fLevel); + Node dfg = ((XDebugTarget) getDebugTarget()).getGlobalVariables(fLevel); + parseVariable(dfl, dfg); + + Vector newVariables = new Vector (Arrays.asList(fVariables)); + resetHasChangedInfo (varList); + updateVariableList (varList, newVariables); + fUpToDate = true; + Collections.sort (varList, new XDebugVariableComparator ()); + } + + return (IVariable[]) varList.toArray (new IVariable[varList.size()]); } - + private void parseVariable(Node localVariables, Node globalVariables) throws DebugException { - NodeList property = localVariables.getChildNodes(); - + NodeList property = localVariables.getChildNodes(); NodeList propertyGlobal = globalVariables.getChildNodes(); - + fVariables = new IVariable[property.getLength() + propertyGlobal.getLength()]; - + int length = property.getLength(); for (int i = 0; i < length; i++) { XDebugVariable var = new XDebugVariable(this, property.item(i)); @@ -103,173 +296,228 @@ public class XDebugStackFrame extends XDebugElement implements IStackFrame { fVariables[k + length] = var; } } - + + /** + * + */ + private XDebugVariable findVariable(Vector varList, String varname) { + XDebugVariable variable; + XDebugAbstractValue value; + int i; + + for (i = 0; i < varList.size(); i++) { // For all variables + variable = (XDebugVariable) varList.get(i); // Get the variable + value = (XDebugAbstractValue) variable.getValue(); // Get the value of the variable + + try { + if (value.hasVariables()) { // Does the variable/value have children + if (!hasRecursion(variable)) { // Don't follow recursive variable/values + XDebugVariable var = findVariable(value.getChildVariables(), varname); + + if (var != null) { + return var; + } + } + } + + if (variable.getName().equals(varname)) { + return variable; + } + } catch (DebugException e) { // That's, because of the hasVariables method + } + } + + return null; + } + + /** + * This method is called from the UI (e.g. from PHPDebugHover + * to find the variable the mouse is pointing to) + * + * @param s The variable name we are looking for. + * @return + */ + public IVariable findVariable(String s) throws DebugException { + if (!fUpToDate) { + getVariables(); + } + + return (findVariable(varList, s)); // Prefix the variable name with $ + } + /*public void evaluateChange(IStackFrame OldStackFrame) throws DebugException { IVariable[] OldVariable = ((XDebugStackFrame) OldStackFrame).getVariables(); for (int i = 0; i < fVariables.length; i++) { ((XDebugVariable) fVariables[i]).setChange(OldVariable[i]); } }*/ - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStackFrame#hasVariables() */ public boolean hasVariables() throws DebugException { - /*return fVariables.length > 0;*/ - return true; + if (!fUpToDate) { + getVariables(); + } + + return (varList.size() > 0); + } + + public int getLineNumber() { + return fLineNumber; } - + /* (non-Javadoc) - * @see org.eclipse.debug.core.model.IStackFrame#getLineNumber() + * */ - public int getLineNumber() throws DebugException { - return fLineNumber; + public void setLineNumber(int line) { + fLineNumber = line; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStackFrame#getCharStart() */ public int getCharStart() throws DebugException { return -1; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStackFrame#getCharEnd() */ public int getCharEnd() throws DebugException { return -1; } - + /* (non-Javadoc)fName * @see org.eclipse.debug.core.model.IStackFrame#getName() */ public String getName() throws DebugException { - return fName.toString()+"::"+fWhere+ " line: "+ fLineNumber; + return fName.toString() + "::" + fWhere + " line: " + fLineNumber; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStackFrame#getRegisterGroups() */ public IRegisterGroup[] getRegisterGroups() throws DebugException { return null; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStackFrame#hasRegisterGroups() */ public boolean hasRegisterGroups() throws DebugException { return false; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#canStepInto() */ public boolean canStepInto() { return fThread.canStepInto(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#canStepOver() */ public boolean canStepOver() { return fThread.canStepOver(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#canStepReturn() */ public boolean canStepReturn() { return fThread.canStepReturn(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#isStepping() */ public boolean isStepping() { return fThread.isStepping(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#stepInto() */ public void stepInto() throws DebugException { fThread.stepInto(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#stepOver() */ public void stepOver() throws DebugException { fThread.stepOver(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#stepReturn() */ public void stepReturn() throws DebugException { fThread.stepReturn(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#canResume() */ public boolean canResume() { return fThread.canResume(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend() */ public boolean canSuspend() { return fThread.canSuspend(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended() */ public boolean isSuspended() { return fThread.isSuspended(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#resume() */ public void resume() throws DebugException { fThread.resume(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#suspend() */ public void suspend() throws DebugException { fThread.suspend(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ITerminate#canTerminate() */ public boolean canTerminate() { return fThread.canTerminate(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ITerminate#isTerminated() */ public boolean isTerminated() { return fThread.isTerminated(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ITerminate#terminate() */ public void terminate() throws DebugException { fThread.terminate(); } - + /** * Returns the name of the source file this stack frame is associated * with. - * + * * @return the name of the source file this stack frame is associated * with. If the file associated with this frame does not exists, it returns null. */ @@ -277,57 +525,53 @@ public class XDebugStackFrame extends XDebugElement implements IStackFrame { if (fName == null) { return null; } + IPath a = new Path(fName.getFile()); + return a.lastSegment(); } + public String getFullSourceName() { + if (fName == null) { + return null; + } + + IPath a = new Path(fName.getFile()); + + return a.toString (); + } + public boolean isSameStackFrame(Object obj) { boolean isSameStackFrame = false; - + if (obj instanceof XDebugStackFrame) { - XDebugStackFrame sf = (XDebugStackFrame)obj; - isSameStackFrame = sf.getSourceName().equals(getSourceName()) && - sf.getType().equals(getType()) && - sf.getWhere().equals(getWhere()); //&& + XDebugStackFrame sf = (XDebugStackFrame) obj; + + isSameStackFrame = sf.getSourceName ().equals (getSourceName ()) && + sf.getType().equals (getType ()) && + sf.getWhere().equals (getWhere ()); } return isSameStackFrame; } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - public boolean equals(Object obj) { - if (obj instanceof XDebugStackFrame) { - XDebugStackFrame sf = (XDebugStackFrame)obj; - try { - return sf.getSourceName().equals(new Path(fName.getFile()).lastSegment()) && - sf.getLineNumber() == fLineNumber && - sf.getLevel() == fLevel && - sf.getType().equals(fType) && - sf.getWhere().equals(fWhere); - } catch (DebugException e) { - } - } - return false; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - public int hashCode() { - return getSourceName().hashCode() + fLevel; - } - public URL getFullName() { return fName; } + public void setFullName (URL name) { + fName = name; + } + public int getLevel() { return fLevel; } + public void setLevel(int level) { + this.fLevel = level; + } + + public String getType() { return fType; } @@ -339,8 +583,60 @@ public class XDebugStackFrame extends XDebugElement implements IStackFrame { public boolean setVariableValue(XDebugVariable variable, String expression) throws DebugException { return ((XDebugTarget) getDebugTarget()).setVarValue("$" + variable.getName(), expression); } - - public Node eval(String expression) throws DebugException { - return ((XDebugTarget) getDebugTarget()).eval(expression); + + public Node eval(String expression) throws DebugException { + return ((XDebugTarget) getDebugTarget()).eval(expression); + } + + /** + * + */ + public void setAvailable(boolean available) { + fAvailable = available; + } + + /** + * + */ + public boolean isAvailable() { + return fAvailable; + } + + public void setUpToDate (boolean bValue) { + this.fUpToDate = bValue; + } + + public void setDescription(String desc) { + this.fWhere = desc; + } + + public String getDescription() { + return this.fWhere; + } + + /** + * This function is needed when sorting the stackframes by their index numbers. + * + * @param obj The stackframe which this one is compared to. + * @return + * + */ + public int compareTo(Object obj) { + if (!(obj instanceof XDebugStackFrame)) { + throw new IllegalArgumentException ("A PHPStackFrame can only be compared with another PHPStackFrame"); + } + + int frameLevel = ((XDebugStackFrame) obj).getLevel (); + + if (fLevel < frameLevel) { + return -1; + } else if (fLevel > frameLevel) { + return 1; + } + return 0; } -} \ No newline at end of file +}