*/
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.ResponseListener.XDebugResponse;
+import net.sourceforge.phpeclipse.xdebug.core.xdebug.XDebugResponse;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
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;
*/
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) {
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()
*/
if (frames.length > 0) {
return frames[0];
}
-
- return null;
+
+ return null;
}
-
+
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#getName()
*/
}
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()
*/
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()
*/
((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) {
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) {
/**
* 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
+}