From: robekras Date: Sun, 13 Mar 2011 12:33:44 +0000 (+0000) Subject: 1) Improvements for the XDebug plugin. X-Git-Url: http://git.phpeclipse.com 1) Improvements for the XDebug plugin. - Avoids rebuild of stackframe tree - Avoids rebuild of varible tree - Alphabetic sorting of variables --- diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/XDebugCorePlugin.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/XDebugCorePlugin.java index 4acc25f..10f7582 100644 --- a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/XDebugCorePlugin.java +++ b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/XDebugCorePlugin.java @@ -55,7 +55,7 @@ public class XDebugCorePlugin extends Plugin { } public static IBreakpoint[] getBreakpoints() { - return getBreakpointManager().getBreakpoints(IXDebugConstants.ID_PHP_DEBUG_MODEL); + return getBreakpointManager().getBreakpoints(IXDebugConstants.ID_PHP_BREAKPOINT_MODEL); } public static IBreakpointManager getBreakpointManager() { diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/xdebug/XDebugConnection.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/xdebug/XDebugConnection.java index a2b9a03..8fa7006 100644 --- a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/xdebug/XDebugConnection.java +++ b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/xdebug/XDebugConnection.java @@ -101,7 +101,7 @@ public class XDebugConnection { } catch (IOException e) { if (e instanceof EOFException == false) { if (!fIsClosed) { - e.printStackTrace(); +// e.printStackTrace(); } } return null; diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/launching/IXDebugConstants.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/launching/IXDebugConstants.java index 7d4f892..e2f8ca0 100644 --- a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/launching/IXDebugConstants.java +++ b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/launching/IXDebugConstants.java @@ -5,21 +5,22 @@ package net.sourceforge.phpeclipse.xdebug.php.launching; */ public interface IXDebugConstants { /** - * Unique identifier for the PHP debug model (value + * Unique identifier for the PHP debug model (value * et.sourceforge.phpeclipse.debug.). */ public static final String ID_PHP_DEBUG_MODEL = "net.sourceforge.phpeclipse.xdebug.php"; - + public static final String ID_PHP_BREAKPOINT_MODEL = "net.sourceforge.phpeclipse.debug.core"; + /** * Launch configuration key. Value is a PHPProject name * program. The path is a string representing a full path - * to a perl program in the workspace. + * to a perl program in the workspace. */ public static final String ATTR_PHP_PROJECT = ID_PHP_DEBUG_MODEL + ".ATTR_PDA_PROFECT"; /** * Launch configuration key. Value is a php program. * The path is a string representing a relative path - * to a php program in the project. + * to a php program in the project. */ public static final String ATTR_PHP_FILE = ID_PHP_DEBUG_MODEL + ".ATTR_PDA_FILE"; @@ -34,4 +35,4 @@ public interface IXDebugConstants { public static final String ATTR_PHP_IDE_ID = ID_PHP_DEBUG_MODEL + ".ATTR_PHP_IDE_ID"; public static final String ATTR_PHP_PATHMAP = ID_PHP_DEBUG_MODEL + ".ATTR_PHP_PATHMAP"; -} \ No newline at end of file +} diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/launching/PHPSourceLookupParticipant.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/launching/PHPSourceLookupParticipant.java index aa6bed2..5397391 100644 --- a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/launching/PHPSourceLookupParticipant.java +++ b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/launching/PHPSourceLookupParticipant.java @@ -1,38 +1,168 @@ package net.sourceforge.phpeclipse.xdebug.php.launching; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import net.sourceforge.phpeclipse.PHPeclipsePlugin; import net.sourceforge.phpeclipse.xdebug.core.PathMapItem; import net.sourceforge.phpeclipse.xdebug.php.model.XDebugStackFrame; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Path; import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupParticipant; import org.eclipse.debug.core.sourcelookup.ISourceContainer; import org.eclipse.debug.internal.core.sourcelookup.SourceLookupMessages; public class PHPSourceLookupParticipant extends AbstractSourceLookupParticipant { + private Map pathMap = null; + private boolean remoteDebug = true; + private IPath remoteSourcePath; + private String projectName; + /* (non-Javadoc) * @see org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant#getSourceName(Object) */ public String getSourceName(Object object) throws CoreException { if (object instanceof XDebugStackFrame) { - return ((XDebugStackFrame) object).getSourceName(); + return ((XDebugStackFrame) object).getFullSourceName(); } return null; } + /** + * @see org.eclipse.debug.core.model.ISourceLocator#getSourceElement(IStackFrame) + * + * Return the client side source filename for the server side source file. + * E.g. when cross debugging, the server side filename could be /var/www/index.php + * on the client side it is either a Eclipse_PHP_projectname\index.php (when it is a linked file) + * + * + * @param stackFrame The stackframe for which we want the client side source file name + * @return The filename as it appears on the client side + */ + public Object getSourceElement (IStackFrame stackFrame) throws CoreException { + IPath projectPath; + IPath remotePath; + IPath path; + IPath localPath; + Iterator iterator; + String fileName; + //String file; + String local; + List pathMap; + PathMapItem pmi = null; + + projectName = getDirector().getLaunchConfiguration().getAttribute (IXDebugConstants.ATTR_PHP_PROJECT, ""); + pathMap = getDirector().getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_PHP_PATHMAP, (List) null); + + fileName = ((XDebugStackFrame) stackFrame).getFullSourceName (); // Get the filename as it is submitted by XDebug + //file = ""; + + if (remoteDebug) { // Is it a remote debugging session + path = new Path (fileName); // Create a IPath object for the server side filename +/* + if (!remoteSourcePath.isEmpty()) { + if (remoteSourcePath.isPrefixOf (path)) { // Is the server side filename with the remote source path + path = path.removeFirstSegments (remoteSourcePath.matchingFirstSegments (path)); // Remove the remote source path + //file = path.toString (); // The filename without the remote source path + projectPath = (PHPeclipsePlugin.getWorkspace().getRoot().getProject(projectName).getLocation()); // Get the absolute project path + + return (projectPath.append (path)).toOSString (); // Return the filename as absolute client side path + } + } + else { +*/ + if (pathMap == null) { // Do we have path mapping (e.g. for cross platform debugging) + return fileName; // No, then return the filename as it given by DBG (the full server side path) + } + + pmi = null; + + for (int k = 0; k < pathMap.size(); k++) { + pmi = new PathMapItem ((String) pathMap.get(k)); + + local = pmi.getLocalPath ().toString(); // + localPath = new Path (pmi.getLocalPath().toString()); // Get the local/client side path of the mapping + remotePath = new Path (pmi.getRemotePath().toString()); // Get the remote/server side path of the mapping + + if (remotePath.isPrefixOf (path)) { // Starts the remote/server side file path with the remote/server side mapping path + path = path.removeFirstSegments (remotePath.matchingFirstSegments (path)); // Remove the absolute path from filename + + return path.toString (); +/* + localPath = new Path (local); // Create new IPath object for the local/client side path + path = localPath.append (path); // Prepend the project relative path to filename + + projectPath = (PHPeclipsePlugin.getWorkspace().getRoot().getProject(projectName).getLocation()); // Get the absolute project path + +// return path; + return (projectPath.append (path)).toString (); // Return the filename as absolute client side path +*/ + } + } +// } + + if (pathMap == null) { // Do we have path mapping (e.g. for cross platform debugging) + return fileName; // No, then return the filename as it given by DBG (the full server side path) + } + + pmi = null; + + for (int k = 0; k < pathMap.size(); k++) { + pmi = new PathMapItem((String) pathMap.get(k)); + + local = pmi.getLocalPath().toString(); // Get the local/client side path of the mapping + localPath = new Path (pmi.getLocalPath().toString()); // Get the local/client side path of the mapping + remotePath = new Path (pmi.getRemotePath().toString()); // Get the remote/server side path of the mapping + + if (remotePath.isPrefixOf (path)) { // Starts the remote/server side file path with the remote/server side mapping path + path = path.removeFirstSegments (remotePath.matchingFirstSegments (path)); // Remove the absolute path from filename + localPath = new Path (local); // Create new IPath object for the local/client side path + + return localPath.append (path).toOSString (); // Append the remote filename to the client side path (So we return the absolute path + // to the source file as the client side sees it. + } + } + + return fileName; + + } else { // no remote debug +/* + IWorkspaceRoot root = PHPLaunchingPlugin.getWorkspace().getRoot(); + Path filePath = new Path(fileName); + + if (root.getFileForLocation(filePath) == null) { + IProject proj = root.getProject(projectName); + IFile[] files = root.findFilesForLocation(filePath); + for (int i = 0; i < files.length; i++) { + if (files[i].getProject().equals(proj)) { + fileName = proj.getFullPath().append(files[i].getProjectRelativePath()).toOSString(); + break; + } + } + } +*/ + return fileName; + } + } + /* (non-Javadoc) * @see org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant#findSourceElements(java.lang.Object) */ public Object[] findSourceElements(Object object) throws CoreException { + String name = null; + if (object == null) { return new Object[] {}; } @@ -51,7 +181,12 @@ public class PHPSourceLookupParticipant extends AbstractSourceLookupParticipant results = new ArrayList(); } - String name = getSourceName(object); + try { + name = (String) getSourceElement (stackFrame); + } + catch (CoreException e) { + } + if (name == null || name.length() == 0) { return new Object[] {}; } @@ -63,20 +198,20 @@ public class PHPSourceLookupParticipant extends AbstractSourceLookupParticipant IPath sPath = new Path(stackFrame.getFullName().getPath()); List pathMap = getDirector().getLaunchConfiguration() .getAttribute(IXDebugConstants.ATTR_PHP_PATHMAP, (List) null); - + PathMapItem pmi = null; for (int k = 0; k < pathMap.size(); k++) { pmi = new PathMapItem((String) pathMap.get(k)); - + IPath local = new Path(pmi.getLocalPath().toString()); IPath remote = new Path(pmi.getRemotePath().toString()); - + if (remote.matchingFirstSegments(sPath) == remote.segmentCount()) { sLocalPath = local; } } } else { - + } String Type = stackFrame.getType(); @@ -116,7 +251,7 @@ public class PHPSourceLookupParticipant extends AbstractSourceLookupParticipant multiStatus = new MultiStatus(DebugPlugin .getUniqueIdentifier(), DebugPlugin.INTERNAL_ERROR, new IStatus[] { single.getStatus() }, - SourceLookupMessages.DefaultSourceContainer_0/*CompositeSourceContainer_0*/, + SourceLookupMessages.DefaultSourceContainer_0, null); multiStatus.add(e.getStatus()); } else { @@ -132,6 +267,7 @@ public class PHPSourceLookupParticipant extends AbstractSourceLookupParticipant } return EMPTY; } + return results.toArray(); } @@ -152,4 +288,4 @@ public class PHPSourceLookupParticipant extends AbstractSourceLookupParticipant } } } -} \ No newline at end of file +} diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugAbstractValue.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugAbstractValue.java index eb91e5b..df5c92a 100644 --- a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugAbstractValue.java +++ b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugAbstractValue.java @@ -5,6 +5,9 @@ */ package net.sourceforge.phpeclipse.xdebug.php.model; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Vector; import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils; import org.eclipse.debug.core.DebugException; @@ -20,83 +23,125 @@ import org.w3c.dom.Node; */ public /*abstract*/ class XDebugAbstractValue extends XDebugElement implements IValue { - private IVariable[] fVariables; - private String fValueString; - private String fTypeName; - private boolean fhasChanged; - protected String rowValue; - + private String fValueString; + private String fTypeName; + private boolean fhasChanged; + protected String rowValue; + private boolean fSorted; + private Vector fVariables; // The children of this variable (other variables) if any + public XDebugAbstractValue(XDebugStackFrame frame, Node value) throws DebugException { super(frame == null ? null : (XDebugTarget)frame.getDebugTarget()); - fTypeName = PHPDebugUtils.getAttributeValue(value, "type"); - - fVariables = new IVariable[0]; + fTypeName = PHPDebugUtils.getAttributeValue(value, "type"); + + fVariables = new Vector(); // Create an empty vector + fValueString = ""; + rowValue = ""; + fSorted = false; - rowValue = ""; try { rowValue = value.getFirstChild().getNodeValue(); } catch (NullPointerException e) { rowValue = ""; } } - + public boolean hasChanged() { return fhasChanged; } - + + public void setChanged(boolean changed) { + fhasChanged = changed; + } + + /** + * + * @param item + */ + public Vector addVariable(Vector item) { + if (item != null) { // If there is something we want to add + fVariables.addAll(item); + fSorted = false; + } + + return this.fVariables; + } + + /** + * + * @param parent + */ + public void setParent(XDebugVariable parent) { + if (!fVariables.isEmpty()) { // If we have child variables + Iterator iter = fVariables.iterator(); // Create an iterator for the children + + while (iter.hasNext()) { // As long as we have children + ((XDebugVariable) iter.next()).setParent(parent); // Set all child's parent + } + } + } + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IValue#getReferenceTypeName() */ public String getReferenceTypeName() throws DebugException { return fTypeName; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IValue#getValueString() */ public String getValueString() throws DebugException { return fValueString; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IValue#isAllocated() */ public boolean isAllocated() throws DebugException { return true; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IValue#getVariables() */ public IVariable[] getVariables() throws DebugException { + return (XDebugVariable[]) getChildVariables().toArray( + new XDebugVariable[fVariables.size()]); + } + + /** + * + */ + public Vector getChildVariables() { return fVariables; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IValue#hasVariables() */ public boolean hasVariables() throws DebugException { - return (fVariables.length > 0); + return (fVariables.size() != 0); } - + public boolean setValue(String expression) throws DebugException { return true; }; - + protected boolean verifyValue(String expression) { return true; } - + protected boolean supportsValueModification() { return false; } - + protected void setValueString(String valueString) { fValueString = valueString; } protected void setChildren(IVariable[] newChildren) { - fVariables = newChildren; + fVariables = new Vector (Arrays.asList(newChildren)); } -} \ No newline at end of file +} 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 +} diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugTarget.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugTarget.java index 06064eb..79e6366 100644 --- a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugTarget.java +++ b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugTarget.java @@ -1,5 +1,5 @@ /** - * + * */ package net.sourceforge.phpeclipse.xdebug.php.model; @@ -11,6 +11,7 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import net.sourceforge.phpeclipse.PHPeclipsePlugin; import net.sourceforge.phpeclipse.xdebug.core.IPHPDebugEvent; import net.sourceforge.phpeclipse.xdebug.core.IProxyEventListener; import net.sourceforge.phpeclipse.xdebug.core.IXDebugPreferenceConstants; @@ -36,6 +37,8 @@ import org.eclipse.debug.core.model.ILineBreakpoint; import org.eclipse.debug.core.model.IMemoryBlock; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.IThread; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.model.IWorkbenchAdapter; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; @@ -51,18 +54,18 @@ import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugResponse; */ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugEventSetListener, IProxyEventListener { private IProcess fProcess; - + private ILaunch fLaunch; - + private int fDebugPort; - + private boolean fSuspended = false; - + private boolean fTerminated = false; - + private XDebugThread fThread; private IThread[] fThreads; - + private XDebugConnection fDebugConnection; private ResponseListener fResponseListener; @@ -73,13 +76,13 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE /** * Constructs a new debug target in the given launch and waits until * someone with the ideKey connects to the Debugproxy - * - * + * + * * @param launch containing launch * @param process process of the interpreter - * @param ideKey + * @param ideKey * @exception CoreException if unable to connect to host - */ + */ public XDebugTarget(ILaunch launch, IProcess process, String ideKey) throws CoreException { fLaunch = launch; fProcess = process; @@ -87,16 +90,58 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE fThread = null; fThreads = new IThread[0]; fIdeKey = ideKey; - - fDebugPort = XDebugCorePlugin.getDefault().getPreferenceStore().getInt(IXDebugPreferenceConstants.DEBUGPORT_PREFERENCE); + + fDebugPort = XDebugCorePlugin.getDefault().getPreferenceStore().getInt(IXDebugPreferenceConstants.DEBUGPORT_PREFERENCE); if (fDebugPort == 0) { fDebugPort = IXDebugPreferenceConstants.DEFAULT_DEBUGPORT; } - + DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this); DebugPlugin.getDefault().addDebugEventListener(this); } + public Object getAdapter(Class arg0) { + if (IWorkbenchAdapter.class.equals(arg0)) { + return new IWorkbenchAdapter() { + public Object[] getChildren(Object o) { + Object[] children = null; + IThread[] threads = getThreads(); + if (null != threads) { + children = new Object[threads.length]; + for (int i = 0; i < threads.length; ++i) + children[i] = threads[i]; + } + return children; + } + + public ImageDescriptor getImageDescriptor(Object object) { + return null; + } + + public String getLabel(Object o) { + String label = "(Unable to look up name... check error log)"; + try { + label = getName(); + } catch (DebugException x) { + PHPeclipsePlugin.log(label, x); + } + return label; + } + + public Object getParent(Object o) { + return XDebugTarget.this.getLaunch(); + } + }; + } + else { + if (arg0 == XDebugElement.class) { + return this; + } + + return super.getAdapter(arg0); + } + } + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IDebugTarget#getProcess() */ @@ -107,7 +152,7 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE /* (non-Javadoc) * @see org.eclipse.debug.core.model.IDebugTarget#getThreads() */ - public IThread[] getThreads() throws DebugException { + public IThread[] getThreads() { return fThreads; } @@ -129,7 +174,7 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint) */ public boolean supportsBreakpoint(IBreakpoint breakpoint) { - if (breakpoint.getModelIdentifier().equals(IXDebugConstants.ID_PHP_DEBUG_MODEL)) { + if (breakpoint.getModelIdentifier().equals(IXDebugConstants.ID_PHP_BREAKPOINT_MODEL)) { return true; } return false; @@ -173,16 +218,16 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE if(fTerminated) { return; } - + if (XDebugCorePlugin.getDefault() != null) { XDebugProxy proxy = XDebugCorePlugin.getDefault().getXDebugProxy(); proxy.removeProxyEventListener(this, fIdeKey); - + System.out.println("XDebug.Target: ProxyEventlistener removed"); - + fTerminated = true; fSuspended = false; - + XDebugCorePlugin.getBreakpointManager().removeBreakpointListener(this); fireEvent(new DebugEvent(this, DebugEvent.TERMINATE)); DebugPlugin.getDefault().removeDebugEventListener(this); @@ -218,29 +263,29 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE fThread.setBreakpoints(null); resumed(DebugEvent.RESUME); fDebugConnection.run(); - } + } } - + /** * Notification the target has resumed for the given reason - * + * * @param detail reason for the resume */ private void resumed(int detail) { fSuspended = false; fThread.fireResumeEvent(detail); } - + /** * Notification the target has suspended for the given reason - * + * * @param detail reason for the suspend */ public void suspended(int detail) { fSuspended = true; fThread.fireSuspendEvent(detail); - } - + } + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#suspend() */ @@ -251,10 +296,11 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint) */ public void breakpointAdded(IBreakpoint breakpoint) { - IMarker marker = breakpoint.getMarker(); - IPath path = marker.getResource().getLocation(); - IPath cp = path.removeLastSegments(1); - List pathMap = null; + IMarker marker = breakpoint.getMarker(); // Get the breakpoints marker info (It's the local workspace path) + IPath path = marker.getResource().getLocation(); // Get the full path + file for the given breakpoint (It's the local real path) + IPath cp = path.removeLastSegments(1); // Get the full path only (without the file name) + List pathMap = null; + try { pathMap = fLaunch.getLaunchConfiguration().getAttribute(IXDebugConstants.ATTR_PHP_PATHMAP,(List)null); } catch (CoreException e2) { @@ -262,63 +308,68 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE e2.printStackTrace(); } - if (fDebugConnection != null) - if (!fDebugConnection.isClosed()) { - if (fProcess == null) { - PathMapItem pmi = null; - for (int i = 0; i < pathMap.size(); i++) { - pmi = new PathMapItem((String) pathMap.get(i)); - IPath local = (IPath)pmi.getLocalPath().clone(); - local = local.makeAbsolute(); - int matchedSegments = local.segmentCount(); - if (local.matchingFirstSegments(cp) == matchedSegments) { - IPath newPath = pmi.getRemotePath(); - newPath = newPath.append(path.removeFirstSegments(matchedSegments)); - newPath = newPath.makeAbsolute(); - if (supportsBreakpoint(breakpoint)) { - try { - if (breakpoint.isEnabled()) { - if (marker != null) { - int id = fDebugConnection.breakpointSet(newPath.toString(), ((ILineBreakpoint)breakpoint).getLineNumber(), marker.getAttribute(XDebugBreakpoint.HIT_COUNT,-1)); - XDebugResponse dr = getResponse(id); - - String bpid = dr.getAttributeValue("id"); - - if (!"".equals(bpid)) - marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid)); - } + if ((fDebugConnection != null) && // If there is a connection to XDebug + (!fDebugConnection.isClosed ()) && // and this connection is not closed + (fProcess == null)) { // + PathMapItem pmi = null; + + for (int i = 0; i < pathMap.size(); i++) { // For every path map pair the user have set + pmi = new PathMapItem((String) pathMap.get(i)); // Get the path map pair + IPath local = (IPath)pmi.getLocalPath().clone(); // Get the local + local = local.makeAbsolute(); + int matchedSegments = local.segmentCount(); + + if (local.matchingFirstSegments(cp) == matchedSegments) { + IPath newPath = pmi.getRemotePath(); + newPath = newPath.append(path.removeFirstSegments(matchedSegments)); + newPath = newPath.makeAbsolute(); + + if (supportsBreakpoint(breakpoint)) { + try { + if (breakpoint.isEnabled()) { + if (marker != null) { + int id = fDebugConnection.breakpointSet(newPath.toString(), ((ILineBreakpoint)breakpoint).getLineNumber(), marker.getAttribute(XDebugBreakpoint.HIT_COUNT,-1)); + XDebugResponse dr = getResponse(id); + + String bpid = dr.getAttributeValue("id"); + + if (!"".equals(bpid)) + marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid)); } - } catch (DebugException e) { - e.printStackTrace(); - } catch (CoreException e) { - e.printStackTrace(); } + } catch (DebugException e) { + e.printStackTrace(); + } catch (CoreException e) { + e.printStackTrace(); } } - } - } else { - if (supportsBreakpoint(breakpoint)) { - try { - if (breakpoint.isEnabled()) { - if (marker != null) { - int id = fDebugConnection.breakpointSet(path.toString(), ((ILineBreakpoint)breakpoint).getLineNumber(), marker.getAttribute(XDebugBreakpoint.HIT_COUNT,-1)); - XDebugResponse dr = getResponse(id); - String bpid = dr.getAttributeValue("id"); - - if (!"".equals(bpid)) - marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid)); + } + } + } else { + if (supportsBreakpoint(breakpoint)) { + try { + if (breakpoint.isEnabled()) { + if (marker != null) { + int id = fDebugConnection.breakpointSet (path.toString(), + ((ILineBreakpoint) breakpoint).getLineNumber(), + marker.getAttribute (XDebugBreakpoint.HIT_COUNT, -1)); + XDebugResponse dr = getResponse(id); + String bpid = dr.getAttributeValue("id"); + + if (!"".equals(bpid)) { + marker.setAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,Integer.parseInt(bpid)); } } - } catch (DebugException e) { - e.printStackTrace(); - } catch (CoreException e) { - e.printStackTrace(); } + } catch (DebugException e) { + e.printStackTrace(); + } catch (CoreException e) { + e.printStackTrace(); } } } } - + /* (non-Javadoc) * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) */ @@ -410,7 +461,7 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE if (response2.getAttributeValue("success").equals("1") ) { System.out.println("Set children to 1024 (hack)"); } - + installDeferredBreakpoints(); try { resume(); @@ -418,7 +469,7 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE e.printStackTrace(); } } - + /** * Install breakpoints that are already registered with the breakpoint * manager. @@ -429,10 +480,10 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE breakpointAdded(breakpoints[i]); } } - + /** * Returns the current stack frames in the target. - * + * * @return the current stack frames in the target * @throws DebugException if unable to perform the request */ @@ -441,10 +492,10 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE XDebugResponse lastResponse = getResponse(id); return lastResponse; } - + /** * Single step the interpreter. - * + * * @throws DebugException if the request fails */ protected void step_over() throws DebugException { @@ -452,10 +503,10 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE resumed(DebugEvent.STEP_OVER); fDebugConnection.stepOver(); } - + /** * Single step the interpreter. - * + * * @throws DebugException if the request fails */ protected void step_into() throws DebugException { @@ -463,10 +514,10 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE resumed(DebugEvent.STEP_INTO); fDebugConnection.stepInto(); } - + /** * Single step the interpreter. - * + * * @throws DebugException if the request fails */ protected void step_out() throws DebugException { @@ -474,18 +525,18 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE resumed(DebugEvent.STEP_RETURN); fDebugConnection.stepOut(); } - + public boolean setVarValue(String name, String value) { int id = fDebugConnection.setVarValue(name,value); XDebugResponse response = getResponse(id); - + if ((response.getAttributeValue("success")).equals("1")) { return true; } else { return false; } } - + public Node eval(String expression) throws DebugException { Node evalProperty = null; if (fDebugConnection != null) { @@ -493,17 +544,17 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE //Node evalProperty = new Node(""); //if (id > 0) { XDebugResponse response = getResponse(id); - + Node evalResponse = response.getParentNode(); /*Node*/ evalProperty = evalResponse.getFirstChild(); //} /*else { - + //}*/ } else { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = null; Document doc = null; - + try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { @@ -518,14 +569,14 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE e.printStackTrace(); }*/ } - + return evalProperty; } - + public void handleDebugEvents(DebugEvent[] events) { for (int i = 0; i < events.length; i++) { DebugEvent event = events[i]; - + if (fResponseListener != null) { Object s = null; s = event.getSource(); @@ -537,7 +588,7 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE } else { return; } - + if (event.getKind() == DebugEvent.MODEL_SPECIFIC) { switch (event.getDetail()) { case IPHPDebugEvent.BREAKPOINT_HIT: @@ -545,7 +596,7 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE XDebugResponse lastResponse = getResponse(id); IBreakpoint breakpoint = breakpointHit(lastResponse.getParentNode()); - + if (breakpoint != null) { fThread.setBreakpoints(new IBreakpoint[]{breakpoint}); fThread.incrementStepCounter(); @@ -569,7 +620,7 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE } } } - + public void stopped() { if(fDebugConnection == null) { return; @@ -593,15 +644,15 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE fDebugConnection = null; fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT)); } - + fThread.removeEventListeners(); fThread = null; fThreads = new IThread[0]; } - + public void handleProxyEvent(XDebugConnection connection) { //System.out.println("* New Connection - XDebug.Target: " + fDebugConnection.getSessionID()); - + if (setDebugConnection(connection)) { fThread = new XDebugThread(this); fThreads = new IThread[] {fThread}; @@ -609,7 +660,7 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE try { started(); } catch( DebugException e ){ - e.printStackTrace(); + e.printStackTrace(); } } } @@ -619,22 +670,22 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE fDebugConnection = connection; fResponseListener = new ResponseListener(connection); startListener(); - + return true; } else { connection.close(); - + return false; } } - + /** * @return Returns the fDebugConnection. */ public XDebugConnection getDebugConnection() { return fDebugConnection; - } - + } + public void addProcess(IProcess p) { fProcess = p; @@ -642,47 +693,47 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE public Node getLocalVariables(int level) throws DebugException { int id = fDebugConnection.contextGet(level, 0); XDebugResponse response = getResponse(id); - + return response.getParentNode(); } - + public Node getGlobalVariables(int level) throws DebugException { int id = fDebugConnection.contextGet(level, 1); XDebugResponse response = getResponse(id); - + return response.getParentNode(); } - + public void stop() { fDebugConnection.stop(); } - + protected IBreakpoint breakpointHit(Node node) { Node child = node.getFirstChild(); if (child.getNodeName().equals("stack")) { int lineNumber = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "lineno")); - String filename = PHPDebugUtils.getAttributeValue(child, "filename"); + String filename = PHPDebugUtils.getAttributeValue(child, "filename"); IBreakpoint[] breakpoints = XDebugCorePlugin.getBreakpoints(); for (int i = 0; i < breakpoints.length; i++) { IBreakpoint breakpoint = breakpoints[i]; if (supportsBreakpoint(breakpoint)) { if (breakpoint instanceof ILineBreakpoint) { ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint; - try { + try { if (breakpoint.isEnabled()) { IMarker marker = breakpoint.getMarker(); if (marker != null) { String endfilename; - + if (getProcess() == null) { - endfilename = marker.getResource().getLocation().lastSegment(); + endfilename = marker.getResource().getLocation().lastSegment(); } else { endfilename = marker.getResource().getLocation().toOSString(); } - + int id = fDebugConnection.breakpointGet(marker.getAttribute(XDebugLineBreakpoint.BREAKPOINT_ID,-1)); XDebugResponse dr = getResponse(id); - + Node hitCo = dr.getParentNode().getFirstChild(); int hitCount = 0; if (hitCo.hasAttributes()) { @@ -697,7 +748,7 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE && (lineBreakpoint.getLineNumber() == lineNumber) ) { if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) > 0) { if (marker.getAttribute(XDebugLineBreakpoint.HIT_COUNT, 0) == hitCount) { - return (breakpoint); + return (breakpoint); } } else { return (breakpoint); @@ -711,14 +762,14 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE } } } - + return null; } - + public void startListener() { fResponseListener.schedule(); } - + public void stopListener() { fResponseListener.cancel(); } @@ -727,4 +778,4 @@ public class XDebugTarget extends XDebugElement implements IDebugTarget, IDebugE return response; } -} \ No newline at end of file +} diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugThread.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugThread.java index 2e00970..67aacaf 100644 --- a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugThread.java +++ b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugThread.java @@ -6,6 +6,11 @@ */ package net.sourceforge.phpeclipse.xdebug.php.model; +import java.util.Arrays; +import java.util.Collections; +import java.util.Vector; + +import net.sourceforge.phpeclipse.PHPeclipsePlugin; import net.sourceforge.phpeclipse.xdebug.core.PHPDebugUtils; import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugResponse; @@ -16,6 +21,8 @@ import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IThread; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.model.IWorkbenchAdapter; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -27,19 +34,19 @@ import org.w3c.dom.NodeList; */ public class XDebugThread extends XDebugElement implements IThread, IDebugEventSetListener { private XDebugStackFrame[] fStackFrames; - - private IBreakpoint[] fBreakpoints; - - /* Whether this thread is stepping */ - private boolean fStepping = false; - private boolean fTerminated = false; - - private int fStepCount = 0; - private int fCurrentStepCount = 0; - + private IBreakpoint[] fBreakpoints; + + private boolean fStepping = false; // Whether this thread is stepping + private boolean fTerminated = false; + + private int fStepCount = 0; + private int fCurrentStepCount = 0; + + private Vector stackListOld = new Vector(); // The 'static' list of stack frames + /** * Constructs a new thread for the given target - * + * * @param target VM */ public XDebugThread(XDebugTarget target) { @@ -47,88 +54,271 @@ public class XDebugThread extends XDebugElement implements IThread, IDebugEventS DebugPlugin.getDefault().addDebugEventListener(this); fStackFrames = null; } - + + /** + * + */ + public void setStackFrames (XDebugStackFrame[] frames) { + this.fStackFrames = frames; + } + public void incrementStepCounter() { fStepCount++; } + + /** + * + * @param arg0 + * @return + */ + public Object getAdapter (Class arg0) { + if (IWorkbenchAdapter.class.equals (arg0)) { + return new IWorkbenchAdapter() { + public Object[] getChildren(Object o) { + try { + return getStackFrames (); + } catch (DebugException x) { + PHPeclipsePlugin.log ("Unable to get stack frames.", x); + } + + return new Object[0]; + } + + public ImageDescriptor getImageDescriptor(Object object) { + return null; + } + + public String getLabel(Object o) { + throw new UnsupportedOperationException(); + } + + public Object getParent(Object o) { + return getDebugTarget(); + } + }; + } + return super.getAdapter(arg0); + } + + /** + * Reset the availability flag for all stackframes in the list. + * + * @param list The list of old stackframes + */ + private void resetAvailability (Vector list) { + int i; + + for (i = 0; i < list.size (); i++) { + ((XDebugStackFrame) list.get(i)).setAvailable (false); // + } + } + + /** + * Check whether the new stackframe is in the list of old stackframes. + * Test for identical stackframe (identical means same description and same line number). + * + * @param stackFrameNew The stackframe to check whether he is already within the old stackframe list + * @param list The list of old stackframes + * @return + * - true if we have found the identical stackframe within the list + * - false if we did not find the identical stackframe within the list + */ + private boolean isStackFrameInList (XDebugStackFrame stackFrameNew, Vector list) { + int i; + XDebugStackFrame stackFrameOld; + + for (i = 0; i < list.size (); i++) { + stackFrameOld = (XDebugStackFrame) list.get (i); // + + if (stackFrameNew.getFullName ().equals (stackFrameOld.getFullName ()) && + stackFrameNew.getDescription ().equals (stackFrameOld.getDescription ()) && + stackFrameNew.getLineNumber () == stackFrameOld.getLineNumber ()) { // Did we find the sent stackframe within the list of old stackframes? + stackFrameOld.setAvailable (true); // We found the new stackframe in the list of old stack frames + stackFrameOld.setLevel (stackFrameNew.getLevel ()); + stackFrameOld.setUpToDate (false); // Need to update the variables + + return true; // The stackframe was found in the list + } + } + + return false; + } + + /** + * Check whether the new stackframe is in the list of old stackframes. + * Test for exact stackframe (exact means same description and same line number). + * + * @param stackFrameNew The stackframe to check whether he is already within the old stackframe list + * @param list The list of old stackframes + * @return + * - true if we have exactly this stackframe within the list + * - false if we did not find the exact stackframe within the list + */ + private void markIdenticalStackFrames (Vector oldList, Vector newList) { + int i; + XDebugStackFrame stackFrameNew; + + resetAvailability (oldList); // Reset the availability flag of the old stack frames + resetAvailability (newList); // Reset the availability flag of the old stack frames + + for (i = 0; i < newList.size (); i++) { // For all stackList entries + stackFrameNew = (XDebugStackFrame) newList.get (i); + + if (isStackFrameInList (stackFrameNew, oldList)) { // Is this stackframe in the list + stackFrameNew.setAvailable (true); // + // +// break; + } + } + } + + /** + * + * The stackList contains the currently read stackframes which were sent + * from DBG. The DBG interface holds a list of the active stack frames. + * This method replicates the 'static' stackframe list with the DBG stackframe list + * Replication is done in the following way: + * + * + * Removes the unused stackframes from the list, or adds stackframes which + * are not yet in the list. + * + * + * @param stackList + */ + private void updateStackFrameList (Vector stackList) { + int i; + int n; + XDebugStackFrame stackFrameNew; + XDebugStackFrame stackFrameOld; + XDebugStackFrame[] newStackList; + + markIdenticalStackFrames (stackListOld, stackList); // Check whether the newly send stack frames can be found in the list + // of old stack frames + + for (i = 0; i < stackList.size (); i++) { // For all stackList entries + stackFrameNew = (XDebugStackFrame) stackList.get(i); + + for (n = 0; n < stackListOld.size (); n++) { // For all StackFrames in the StackFrame list + stackFrameOld = (XDebugStackFrame) stackListOld.get (n); // + + if (stackFrameOld.isAvailable ()) { // If this stack frame was already found in the new list skip it + continue; + } + + if (stackFrameNew.getFullName ().equals (stackFrameOld.getFullName ()) && // Did we find the sent stackframe within the list of old stackframes? + stackFrameNew.getDescription ().equals (stackFrameOld.getDescription ())) {// Did we find the sent stackframe within the list of old stackframes? + stackFrameOld.setLineNumber (stackFrameNew.getLineNumber ()); + stackFrameOld.setLevel (stackFrameNew.getLevel ()); + stackFrameOld.setUpToDate (false); // Need to update the variables + + stackFrameOld.setAvailable (true); // And mark this stack frame as available + stackFrameNew.setAvailable (true); // And mark this stack frame as available + + break; // Yes, then break; + } + } + + if (!stackFrameNew.isAvailable ()) { // Did not find the new stackframe within the list? + stackFrameNew.setAvailable (true); // Mark the stack frame as available and + stackListOld.add (stackFrameNew); // then add the new stackframe + } + } + + // And now for removing unused stackframes from list + + for (n = 0; n < stackListOld.size(); n++) { + stackFrameOld = (XDebugStackFrame) stackListOld.get(n); + + if (!stackFrameOld.isAvailable()) { + stackListOld.remove(n--); + } + } + + Collections.sort (stackListOld); // Sort the 'static' stackframe list by the stackframe index numbers. + // + newStackList = new XDebugStackFrame[stackListOld.size ()]; + newStackList = (XDebugStackFrame[]) stackListOld.toArray (newStackList); + +// DBGStackList = newStackList; + fStackFrames = newStackList; + } + + public IStackFrame[] getStackFrames() throws DebugException { if (!isSuspended()) { return new IStackFrame[0]; } - - if (fStepCount > fCurrentStepCount) { - XDebugResponse dr = ((XDebugTarget) getDebugTarget()).getStackFrames(); - XDebugStackFrame[] newStackFrames = _getStackFrames(dr); - - /*if (fStackFrames != null) { - if (newStackFrames.length >= fStackFrames.length) { - int delta = newStackFrames.length - fStackFrames.length + 1; - - for (int i = fStackFrames.length - 1; i >= 0; i--) { - if (fStackFrames[i].equals(newStackFrames[newStackFrames.length - delta])) { - int b = 2; b++; - //((XDebugStackFrame) newStackFrames[newStackFrames.length - delta]).evaluateChange((XDebugStackFrame) fStackFrames[i]); - } else if (fStackFrames[i].isSameStackFrame(newStackFrames[newStackFrames.length - delta])) { - int b = 2; b++; - //((XDebugStackFrame) newStackFrames[newStackFrames.length - delta]).evaluateChange((XDebugStackFrame) fStackFrames[i]); - } - - delta ++; - } - } else { - fStackFrames = newStackFrames; - } - } else { - fStackFrames = newStackFrames; - }*/ - fCurrentStepCount++; + if (fStepCount > fCurrentStepCount) { // Do we need to update the list of stackframes + XDebugResponse dr = ((XDebugTarget) getDebugTarget()).getStackFrames(); // Get the stackframes list from XDebug + XDebugStackFrame[] newStackFrames = _getStackFrames(dr); // Parse the stackframes list + + updateStackFrameList (new Vector (Arrays.asList(newStackFrames))); // update the 'static' list of stackframes - fStackFrames = newStackFrames; + fCurrentStepCount++; // } return fStackFrames; } - - + + private XDebugStackFrame[] _getStackFrames(XDebugResponse lastResponse) { if (lastResponse.isError()) { return new XDebugStackFrame[0]; } - - Node response = lastResponse.getParentNode(); - NodeList frames = response.getChildNodes(); - XDebugStackFrame[] theFrames = new XDebugStackFrame[frames.getLength()]; - + + Node response = lastResponse.getParentNode(); + NodeList frames = response.getChildNodes(); + XDebugStackFrame[] theFrames = new XDebugStackFrame[frames.getLength()]; + for (int i = 0; i < frames.getLength(); i++) { - Node stackNode = frames.item(i); - String fileName=PHPDebugUtils.unescapeString(PHPDebugUtils.getAttributeValue(stackNode,"filename")); - String lineNo = PHPDebugUtils.getAttributeValue(stackNode,"lineno"); - - XDebugStackFrame frame = new XDebugStackFrame(this/*fThread*/, i, /*type*/PHPDebugUtils.getAttributeValue(stackNode,"type"), /*lineno*/Integer.parseInt(lineNo), /*where*/PHPDebugUtils.getAttributeValue(stackNode,"where"), fileName); - + Node stackNode = frames.item(i); + String fileName = PHPDebugUtils.unescapeString(PHPDebugUtils.getAttributeValue(stackNode,"filename")); + String lineNo = PHPDebugUtils.getAttributeValue(stackNode,"lineno"); + + XDebugStackFrame frame = new XDebugStackFrame (this, + i, + PHPDebugUtils.getAttributeValue(stackNode,"type"), + Integer.parseInt(lineNo), + PHPDebugUtils.getAttributeValue(stackNode,"where"), + fileName); + frame.incrementStepCounter(); - + theFrames[i] = frame; } return theFrames; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IThread#hasStackFrames() */ public boolean hasStackFrames() throws DebugException { - return isSuspended(); + if (fStackFrames == null) { + return false; + } + + return fStackFrames.length > 0; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IThread#getPriority() */ public int getPriority() throws DebugException { return 0; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IThread#getTopStackFrame() */ @@ -137,10 +327,10 @@ public class XDebugThread extends XDebugElement implements IThread, IDebugEventS if (frames.length > 0) { return frames[0]; } - - return null; + + return null; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IThread#getName() */ @@ -157,39 +347,39 @@ public class XDebugThread extends XDebugElement implements IThread, IDebugEventS } return fBreakpoints; } - + /** * Sets the breakpoints this thread is suspended at, or null * if none. - * + * * @param breakpoints the breakpoints this thread is suspended at, or null * if none */ protected void setBreakpoints(IBreakpoint[] breakpoints) { fBreakpoints = breakpoints; } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#canResume() */ public boolean canResume() { return isSuspended(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend() */ public boolean canSuspend() { return !isTerminated() && !isSuspended(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended() */ public boolean isSuspended() { return getDebugTarget().isSuspended(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#resume() */ @@ -197,28 +387,28 @@ public class XDebugThread extends XDebugElement implements IThread, IDebugEventS fBreakpoints = null; getDebugTarget().resume(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#suspend() */ public void suspend() throws DebugException { getDebugTarget().suspend(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#canStepInto() */ public boolean canStepInto() { return isSuspended(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#canStepOver() */ public boolean canStepOver() { return isSuspended(); } - + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#canStepReturn() */ @@ -282,14 +472,14 @@ public class XDebugThread extends XDebugElement implements IThread, IDebugEventS ((XDebugTarget) getDebugTarget()).getDebugConnection().stop(); fTerminated = true; } - + public void terminated() throws DebugException { fTerminated = true; } /** * Sets whether this thread is stepping - * + * * @param stepping whether stepping */ protected void setStepping(boolean stepping) { @@ -298,17 +488,17 @@ public class XDebugThread extends XDebugElement implements IThread, IDebugEventS public void handleDebugEvents(DebugEvent[] events) { DebugEvent de = events[0]; - System.out.println(de.toString()); + System.out.println(de.toString()); } public void removeEventListeners() { DebugPlugin.getDefault().removeDebugEventListener(this); } - + /** * Fires a RESUME event for this element with * the given detail. - * + * * @param detail event detail code */ public void fireResumeEvent(int detail) { @@ -318,10 +508,10 @@ public class XDebugThread extends XDebugElement implements IThread, IDebugEventS /** * Fires a SUSPEND event for this element with * the given detail. - * + * * @param detail event detail code */ - public void fireSuspendEvent(int detail) { - fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail)); + public void fireSuspendEvent (int detail) { + fireEvent (new DebugEvent (this, DebugEvent.SUSPEND, detail)); } -} \ No newline at end of file +} diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugVariable.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugVariable.java index 240ec9c..35b04a0 100644 --- a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugVariable.java +++ b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugVariable.java @@ -20,10 +20,12 @@ import org.w3c.dom.Node; * Window - Preferences - Java - Code Style - Code Templates */ public class XDebugVariable extends XDebugElement implements IVariable { - private String fName; - private XDebugStackFrame fFrame; + private String fName; + private XDebugStackFrame fFrame; private XDebugAbstractValue fValue; - private String fFacet; + private String fFacet; + private XDebugVariable fParent; // The parent variable (a back link) + /** * Constructs a variable contained in the given stack frame @@ -68,14 +70,14 @@ public class XDebugVariable extends XDebugElement implements IVariable { /* (non-Javadoc) * @see org.eclipse.debug.core.model.IVariable#getValue() */ - public IValue getValue() throws DebugException { + public IValue getValue() { return fValue; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IVariable#getName() */ - public String getName() throws DebugException { + public String getName() { return fName; } @@ -108,6 +110,17 @@ public class XDebugVariable extends XDebugElement implements IVariable { public void setValue(IValue value) throws DebugException { } + /** + * + * @param changed This method is called after a suspend when the list of + * variables is updated, to mark that this variable has a changed + * value. The variable view will show this variable in + * a different color. + */ + public void setValueChanged(boolean changed) { + fValue.setChanged(changed); + } + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IValueModification#supportsValueModification() */ @@ -136,4 +149,18 @@ public class XDebugVariable extends XDebugElement implements IVariable { public String getVisibility() { return fFacet; } + + /** + * + */ + public XDebugVariable getParent() { + return fParent; + } + + /** + * + */ + public void setParent(XDebugVariable parent) { + this.fParent = parent; + } } \ No newline at end of file diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugVariableComparator.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugVariableComparator.java new file mode 100644 index 0000000..9bae593 --- /dev/null +++ b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugVariableComparator.java @@ -0,0 +1,14 @@ +package net.sourceforge.phpeclipse.xdebug.php.model; + +import java.util.Comparator; + +public class XDebugVariableComparator implements Comparator { + + public int compare (Object arg0, Object arg1) { + XDebugVariable left = (XDebugVariable) arg0; + XDebugVariable right = (XDebugVariable) arg1; + + return left.getName ().compareTo (right.getName ()); + } + +}