*/
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;
+
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
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;
/**
* @author Axel
- *
- * TODO To change the template for this generated type comment go to Window -
- * Preferences - Java - Code Style - Code Templates
+ *
+ * TODO To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates
*/
-public class XDebugThread extends XDebugElement implements IThread,
- IDebugEventSetListener {
+public class XDebugThread extends XDebugElement implements IThread, IDebugEventSetListener {
+ private XDebugStackFrame[] fStackFrames;
+ 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) {
+ super(target);
+ DebugPlugin.getDefault().addDebugEventListener(this);
+ fStackFrames = null;
+ }
/**
- * Breakpoints this thread is suspended at or <code>null</code> if none.
+ *
*/
+ public void setStackFrames (XDebugStackFrame[] frames) {
+ this.fStackFrames = frames;
+ }
- private IStackFrame[] fStackFrames = null;
+ public void incrementStepCounter() {
+ fStepCount++;
+ }
- private IBreakpoint[] fBreakpoints;
+ /**
+ *
+ * @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);
+ }
/**
- * Whether this thread is stepping
+ * Reset the availability flag for all stackframes in the list.
+ *
+ * @param list The list of old stackframes
*/
- private boolean fStepping = false;
+ private void resetAvailability (Vector list) {
+ int i;
+
+ for (i = 0; i < list.size (); i++) {
+ ((XDebugStackFrame) list.get(i)).setAvailable (false); //
+ }
+ }
/**
- * Constructs a new thread for the given target
- *
- * @param target
- * VM
+ * 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
*/
- public XDebugThread(XDebugTarget target) {
- super(target);
- DebugPlugin.getDefault().addDebugEventListener(this);
+ 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;
}
- /*
- * (non-Javadoc)
- *
- * @see org.eclipse.debug.core.model.IThread#getStackFrames()
+ /**
+ * 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
*/
- public IStackFrame[] getStackFrames() throws DebugException {
- if (isSuspended()) {
- if (fStackFrames == null) {
- // XDebugCorePlugin.log(IStatus.INFO,"vor getStackFrames");
- fStackFrames = ((XDebugTarget) getDebugTarget())
- .getStackFrames();
- // XDebugCorePlugin.log(IStatus.INFO,"nach getStackFrames");
+ 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;
}
- return fStackFrames;
- } else {
+ }
+ }
+
+ /**
+ *
+ * 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) { // 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
+
+ fCurrentStepCount++; //
+ }
+
+ return fStackFrames;
}
- /*
- * (non-Javadoc)
- *
+
+ 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()];
+
+ 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,
+ 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)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#getPriority()
*/
public int getPriority() throws DebugException {
return 0;
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#getTopStackFrame()
*/
public IStackFrame getTopStackFrame() throws DebugException {
if (frames.length > 0) {
return frames[0];
}
- return null;
+
+ return null;
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#getName()
*/
public String getName() throws DebugException {
- // if (fStackFrames!=null)
- // return fStackFrames[0].getName();
- // else
return "Thread[1]";
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#getBreakpoints()
*/
public IBreakpoint[] getBreakpoints() {
/**
* 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
+ *
+ * @param breakpoints the breakpoints this thread is suspended at, or <code>null</code>
+ * if none
*/
protected void setBreakpoints(IBreakpoint[] breakpoints) {
fBreakpoints = breakpoints;
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#canResume()
*/
public boolean canResume() {
return isSuspended();
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
*/
public boolean canSuspend() {
- return !isSuspended();
+ return !isTerminated() && !isSuspended();
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
*/
public boolean isSuspended() {
return getDebugTarget().isSuspended();
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#resume()
*/
public void resume() throws DebugException {
- fStackFrames = null;
fBreakpoints = null;
getDebugTarget().resume();
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#suspend()
*/
public void suspend() throws DebugException {
getDebugTarget().suspend();
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#canStepInto()
*/
public boolean canStepInto() {
return isSuspended();
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#canStepOver()
*/
public boolean canStepOver() {
return isSuspended();
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#canStepReturn()
*/
public boolean canStepReturn() {
- if (fStackFrames != null)
+ if (fStackFrames != null) {
return (fStackFrames.length > 1);
- else
+ } else {
return false;
+ }
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#isStepping()
*/
public boolean isStepping() {
return fStepping;
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#stepInto()
*/
public void stepInto() throws DebugException {
- fStackFrames = null;
fBreakpoints = null;
((XDebugTarget) getDebugTarget()).step_into();
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#stepOver()
*/
public void stepOver() throws DebugException {
- fStackFrames = null;
fBreakpoints = null;
((XDebugTarget) getDebugTarget()).step_over();
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#stepReturn()
*/
public void stepReturn() throws DebugException {
- fStackFrames = null;
fBreakpoints = null;
((XDebugTarget) getDebugTarget()).step_out();
-
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#canTerminate()
*/
public boolean canTerminate() {
return !isTerminated();
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#isTerminated()
*/
public boolean isTerminated() {
- return getDebugTarget().isTerminated();
+ return fTerminated;
}
- /*
- * (non-Javadoc)
- *
+ /* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#terminate()
*/
public void terminate() throws DebugException {
- getDebugTarget().terminate();
+ ((XDebugTarget) getDebugTarget()).getDebugConnection().stop();
+ fTerminated = true;
+ }
+
+ public void terminated() throws DebugException {
+ fTerminated = true;
}
/**
* Sets whether this thread is stepping
- *
- * @param stepping
- * whether stepping
+ *
+ * @param stepping whether stepping
*/
protected void setStepping(boolean stepping) {
fStepping = stepping;
public void handleDebugEvents(DebugEvent[] events) {
DebugEvent de = events[0];
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) {
+ fireEvent(new DebugEvent(this, DebugEvent.RESUME, 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));
}
}