1) Improvements for the XDebug plugin.
authorrobekras <robekras>
Sun, 13 Mar 2011 12:33:44 +0000 (12:33 +0000)
committerrobekras <robekras>
Sun, 13 Mar 2011 12:33:44 +0000 (12:33 +0000)
 - Avoids rebuild of stackframe tree
 - Avoids rebuild of varible tree
 - Alphabetic sorting of variables

net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/XDebugCorePlugin.java
net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/xdebug/XDebugConnection.java
net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/launching/IXDebugConstants.java
net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/launching/PHPSourceLookupParticipant.java
net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugAbstractValue.java
net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugStackFrame.java
net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugTarget.java
net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugThread.java
net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugVariable.java
net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/php/model/XDebugVariableComparator.java [new file with mode: 0644]

index 4acc25f..10f7582 100644 (file)
@@ -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() {
index a2b9a03..8fa7006 100644 (file)
@@ -101,7 +101,7 @@ public class XDebugConnection {
                } catch (IOException e) {
                        if (e instanceof EOFException == false) {
                                if (!fIsClosed) {
-                                       e.printStackTrace();
+//                                     e.printStackTrace();
                                }
                        }
                        return null;
index 7d4f892..e2f8ca0 100644 (file)
@@ -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
         * <code>et.sourceforge.phpeclipse.debug.</code>).
         */
        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
+}
index aa6bed2..5397391 100644 (file)
 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
+}
index eb91e5b..df5c92a 100644 (file)
@@ -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
+}
index b0e057f..ece1a54 100644 (file)
@@ -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
+        * <ul>
+        * <li> false if the PHPValue is not a child of itself
+        * <li> true if the PHPValue is
+        * </ul>
+        */
+       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:
+        * <ul>
+        * <li> It looks for new variables within the DBG variables list and
+        *      adds them to the 'static' list.
+        * <li> It looks for changed variables copies the current value to the variable within
+        *              the 'static list' and mark these variables as 'hasChanged' (which uses the UI
+        *              for showing the variable with a different color).
+        * <li> It looks for variables within the 'static' list, and removes them
+        *              from the 'static' list in case the do not appear within the DBG list.
+        * </ul>
+        *
+        * @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
+        * <ul>
+        * <li> -1 if the index of this stackframe is less.
+        * <li> 0 if the index of both stackframes are equal (should no happen).
+        * <li> 1 if the index of this stackframe is greater.
+        * </ul>
+        */
+       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
+}
index 06064eb..79e6366 100644 (file)
@@ -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
+}
index 2e00970..67aacaf 100644 (file)
@@ -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:
+        * <ul>
+        * <li> It looks for new stackframes within the DBG stackframe list and
+        *      adds them to the 'static' list.
+        * <li> It looks for stackframes within the 'static' list, and removes them
+        *              from the 'static' list in case they do not appear within the DBG list.
+        * <li> It looks for stackframes which are already existent and replicates the
+        *              line number and the index number.
+        * <li> At the end, the 'static' stackframe list has to be sorted by the stackframes
+        *              index numbers.
+        * </ul>
+        *
+        * 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 <code>null</code>
         * if none.
-        * 
+        *
         * @param breakpoints the breakpoints this thread is suspended at, or <code>null</code>
         * 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 <code>RESUME</code> 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 <code>SUSPEND</code> 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
+}
index 240ec9c..35b04a0 100644 (file)
@@ -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 (file)
index 0000000..9bae593
--- /dev/null
@@ -0,0 +1,14 @@
+package net.sourceforge.phpeclipse.xdebug.php.model;\r
+\r
+import java.util.Comparator;\r
+\r
+public class XDebugVariableComparator implements Comparator {\r
+\r
+    public int compare (Object arg0, Object arg1) {\r
+        XDebugVariable left  = (XDebugVariable) arg0;\r
+        XDebugVariable right = (XDebugVariable) arg1;\r
+        \r
+        return left.getName ().compareTo (right.getName ());\r
+    }\r
+\r
+}\r