/* * Created on 23.11.2004 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ 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.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; /** * @author Axel * * 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 { 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; } /** * */ public void setStackFrames (XDebugStackFrame[] frames) { this.fStackFrames = frames; } public void incrementStepCounter() { fStepCount++; } /** * * @param arg0 * @return */ public Object getAdapter (Class arg0) { if (IWorkbenchAdapter.class.equals (arg0)) { return new IWorkbenchAdapter() { public Object[] getChildren(Object o) { try { return getStackFrames (); } catch (DebugException x) { PHPeclipsePlugin.log ("Unable to get stack frames.", x); } return new Object[0]; } public ImageDescriptor getImageDescriptor(Object object) { return null; } public String getLabel(Object o) { throw new UnsupportedOperationException(); } public Object getParent(Object o) { return getDebugTarget(); } }; } return super.getAdapter(arg0); } /** * Reset the availability flag for all stackframes in the list. * * @param list The list of old stackframes */ private void resetAvailability (Vector list) { int i; for (i = 0; i < list.size (); i++) { ((XDebugStackFrame) list.get(i)).setAvailable (false); // } } /** * Check whether the new stackframe is in the list of old stackframes. * Test for identical stackframe (identical means same description and same line number). * * @param stackFrameNew The stackframe to check whether he is already within the old stackframe list * @param list The list of old stackframes * @return * - true if we have found the identical stackframe within the list * - false if we did not find the identical stackframe within the list */ private boolean isStackFrameInList (XDebugStackFrame stackFrameNew, Vector list) { int i; XDebugStackFrame stackFrameOld; for (i = 0; i < list.size (); i++) { stackFrameOld = (XDebugStackFrame) list.get (i); // if (stackFrameNew.getFullName ().equals (stackFrameOld.getFullName ()) && stackFrameNew.getDescription ().equals (stackFrameOld.getDescription ()) && stackFrameNew.getLineNumber () == stackFrameOld.getLineNumber ()) { // Did we find the sent stackframe within the list of old stackframes? stackFrameOld.setAvailable (true); // We found the new stackframe in the list of old stack frames stackFrameOld.setLevel (stackFrameNew.getLevel ()); stackFrameOld.setUpToDate (false); // Need to update the variables return true; // The stackframe was found in the list } } return false; } /** * Check whether the new stackframe is in the list of old stackframes. * Test for exact stackframe (exact means same description and same line number). * * @param stackFrameNew The stackframe to check whether he is already within the old stackframe list * @param list The list of old stackframes * @return * - true if we have exactly this stackframe within the list * - false if we did not find the exact stackframe within the list */ private void markIdenticalStackFrames (Vector oldList, Vector newList) { int i; XDebugStackFrame stackFrameNew; resetAvailability (oldList); // Reset the availability flag of the old stack frames resetAvailability (newList); // Reset the availability flag of the old stack frames for (i = 0; i < newList.size (); i++) { // For all stackList entries stackFrameNew = (XDebugStackFrame) newList.get (i); if (isStackFrameInList (stackFrameNew, oldList)) { // Is this stackframe in the list stackFrameNew.setAvailable (true); // // // break; } } } /** * * The stackList contains the currently read stackframes which were sent * from DBG. The DBG interface holds a list of the active stack frames. * This method replicates the 'static' stackframe list with the DBG stackframe list * Replication is done in the following way: * * * Removes the unused stackframes from the list, or adds stackframes which * are not yet in the list. * * * @param stackList */ private void updateStackFrameList (Vector stackList) { int i; int n; XDebugStackFrame stackFrameNew; XDebugStackFrame stackFrameOld; XDebugStackFrame[] newStackList; markIdenticalStackFrames (stackListOld, stackList); // Check whether the newly send stack frames can be found in the list // of old stack frames for (i = 0; i < stackList.size (); i++) { // For all stackList entries stackFrameNew = (XDebugStackFrame) stackList.get(i); for (n = 0; n < stackListOld.size (); n++) { // For all StackFrames in the StackFrame list stackFrameOld = (XDebugStackFrame) stackListOld.get (n); // if (stackFrameOld.isAvailable ()) { // If this stack frame was already found in the new list skip it continue; } if (stackFrameNew.getFullName ().equals (stackFrameOld.getFullName ()) && // Did we find the sent stackframe within the list of old stackframes? stackFrameNew.getDescription ().equals (stackFrameOld.getDescription ())) {// Did we find the sent stackframe within the list of old stackframes? stackFrameOld.setLineNumber (stackFrameNew.getLineNumber ()); stackFrameOld.setLevel (stackFrameNew.getLevel ()); stackFrameOld.setUpToDate (false); // Need to update the variables stackFrameOld.setAvailable (true); // And mark this stack frame as available stackFrameNew.setAvailable (true); // And mark this stack frame as available break; // Yes, then break; } } if (!stackFrameNew.isAvailable ()) { // Did not find the new stackframe within the list? stackFrameNew.setAvailable (true); // Mark the stack frame as available and stackListOld.add (stackFrameNew); // then add the new stackframe } } // And now for removing unused stackframes from list for (n = 0; n < stackListOld.size(); n++) { stackFrameOld = (XDebugStackFrame) stackListOld.get(n); if (!stackFrameOld.isAvailable()) { stackListOld.remove(n--); } } Collections.sort (stackListOld); // Sort the 'static' stackframe list by the stackframe index numbers. // newStackList = new XDebugStackFrame[stackListOld.size ()]; newStackList = (XDebugStackFrame[]) stackListOld.toArray (newStackList); // DBGStackList = newStackList; fStackFrames = newStackList; } public IStackFrame[] getStackFrames() throws DebugException { if (!isSuspended()) { return new IStackFrame[0]; } if (fStepCount > fCurrentStepCount) { // 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; } 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 { 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() */ public IStackFrame getTopStackFrame() throws DebugException { IStackFrame[] frames = getStackFrames(); if (frames.length > 0) { return frames[0]; } return null; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IThread#getName() */ public String getName() throws DebugException { return "Thread[1]"; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IThread#getBreakpoints() */ public IBreakpoint[] getBreakpoints() { if (fBreakpoints == null) { return new IBreakpoint[0]; } return fBreakpoints; } /** * Sets the breakpoints this thread is suspended at, or null * if none. * * @param breakpoints the breakpoints this thread is suspended at, or null * if none */ protected void setBreakpoints(IBreakpoint[] breakpoints) { fBreakpoints = breakpoints; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#canResume() */ public boolean canResume() { return isSuspended(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend() */ public boolean canSuspend() { return !isTerminated() && !isSuspended(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended() */ public boolean isSuspended() { return getDebugTarget().isSuspended(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ISuspendResume#resume() */ public void resume() throws DebugException { 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() */ public boolean canStepReturn() { if (fStackFrames != null) { return (fStackFrames.length > 1); } else { return false; } } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#isStepping() */ public boolean isStepping() { return fStepping; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#stepInto() */ public void stepInto() throws DebugException { fBreakpoints = null; ((XDebugTarget) getDebugTarget()).step_into(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#stepOver() */ public void stepOver() throws DebugException { fBreakpoints = null; ((XDebugTarget) getDebugTarget()).step_over(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStep#stepReturn() */ public void stepReturn() throws DebugException { fBreakpoints = null; ((XDebugTarget) getDebugTarget()).step_out(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ITerminate#canTerminate() */ public boolean canTerminate() { return !isTerminated(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ITerminate#isTerminated() */ public boolean isTerminated() { return fTerminated; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ITerminate#terminate() */ public void terminate() throws DebugException { ((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) { 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 RESUME 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 SUSPEND event for this element with * the given detail. * * @param detail event detail code */ public void fireSuspendEvent (int detail) { fireEvent (new DebugEvent (this, DebugEvent.SUSPEND, detail)); } }