1 /**********************************************************************
2 Copyright (c) 2000, 2002 IBM Corp. and others.
3 All rights reserved. This program and the accompanying materials
4 are made available under the terms of the Common Public License v1.0
5 which accompanies this distribution, and is available at
6 http://www.eclipse.org/legal/cpl-v10.html
9 IBM Corporation - Initial implementation
10 Vicente Fernando - www.alfersoft.com.ar
11 **********************************************************************/
12 package net.sourceforge.phpdt.internal.debug.core.model;
14 import net.sourceforge.phpdt.internal.debug.core.PHPDBGProxy;
15 import net.sourceforge.phpdt.internal.debug.core.PHPDebugCorePlugin;
16 import net.sourceforge.phpdt.internal.debug.core.breakpoints.PHPLineBreakpoint;
17 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
19 import org.eclipse.core.resources.IMarkerDelta;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.debug.core.DebugEvent;
22 import org.eclipse.debug.core.DebugException;
23 import org.eclipse.debug.core.DebugPlugin;
24 import org.eclipse.debug.core.IBreakpointManager;
25 import org.eclipse.debug.core.IDebugEventSetListener;
26 import org.eclipse.debug.core.ILaunch;
27 import org.eclipse.debug.core.ILaunchListener;
28 import org.eclipse.debug.core.model.IBreakpoint;
29 import org.eclipse.debug.core.model.IDebugTarget;
30 import org.eclipse.debug.core.model.IMemoryBlock;
31 import org.eclipse.debug.core.model.IProcess;
32 import org.eclipse.debug.core.model.IStackFrame;
33 import org.eclipse.debug.core.model.IThread;
34 import org.eclipse.jface.resource.ImageDescriptor;
35 import org.eclipse.ui.model.IWorkbenchAdapter;
38 * Debug target for PHP debug model.
40 public class PHPDebugTarget extends PHPDebugElement implements IPHPDebugTarget, ILaunchListener,
41 IDebugEventSetListener {
43 private IProcess process;
45 private ILaunch launch;
47 private PHPThread[] threads = new PHPThread[0];
49 private PHPDBGProxy phpDBGProxy;
52 private boolean isTerminated = false;
54 private boolean isSuspended = false;
56 boolean isTerminated() {
60 boolean isSuspended() {
64 void setTerminated(boolean terminated) {
65 this.isTerminated = terminated;
68 void setSuspended(boolean suspended) {
70 throw new IllegalStateException();
71 this.isSuspended = suspended;
75 private final State state = new State();
77 public PHPDebugTarget(ILaunch launch, IProcess process) {
79 if (null == launch && null == process)
80 throw new IllegalArgumentException();
82 this.process = process;
83 // TODO XXX remove breakpoint listener at termination to avoid live leak
84 IBreakpointManager manager = DebugPlugin.getDefault()
85 .getBreakpointManager();
86 manager.addBreakpointListener(this);
87 DebugPlugin.getDefault().addDebugEventListener(this);
91 protected synchronized void initialize() {
92 DebugEvent ev = new DebugEvent(this, DebugEvent.CREATE);
93 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
96 public void addThread(PHPThread phpThread) {
98 PHPThread[] updatedThreads = new PHPThread[threads.length + 1];
100 for (i = 0; i < threads.length; i++) {
101 updatedThreads[i] = threads[i];
103 updatedThreads[i] = phpThread;
104 threads = updatedThreads;
107 fireThreadCreateEvent(phpThread);
110 // public void updateThreads(PHPThread phpThread) {
111 // fireChangeEvent();
112 // fireThreadCreateEvent(phpThread);
115 private void fireChangeEvent() {
116 DebugEvent ev = new DebugEvent(this, DebugEvent.CHANGE);
117 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
120 private void fireThreadCreateEvent(PHPThread phpThread) {
121 DebugEvent ev = new DebugEvent(phpThread, DebugEvent.CREATE);
122 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
125 // protected PHPThread getThreadById(int id) {
126 // for (int i = 0; i < threads.length; i++) {
127 // if (threads[i].getId() == id) {
128 // return threads[i];
134 public IThread[] getThreads() {
138 public boolean hasThreads() throws DebugException {
139 return threads.length > 0;
142 public String getName() throws DebugException {
143 return "PHP Debugger at localhost:" + getPHPDBGProxy().getPort();
146 public boolean supportsBreakpoint(IBreakpoint arg0) {
147 if (arg0.getModelIdentifier().equals(PHPDebugCorePlugin.PLUGIN_ID)) {
153 public String getModelIdentifier() {
154 return PHPDebugCorePlugin.PLUGIN_ID;
157 public IStackFrame[] getStackFrames () throws DebugException {
158 return (IStackFrame[]) this.phpDBGProxy.getDBGInterface ().getStackList ();
161 public IDebugTarget getDebugTarget() {
165 public ILaunch getLaunch() {
169 public synchronized boolean canTerminate() {
170 return !isTerminated();
173 public synchronized boolean isTerminated() {
174 return state.isTerminated();
177 private synchronized void terminateThreads () {
181 for (i = 0; i < threads.length; i++) {
182 threads[i].terminate ();
184 } catch (DebugException e) {
188 public synchronized void terminate() {
189 // This method is synchronized to control a race condition between the
190 // UI thread that terminates the debugging session, and the slave
191 // thread that executes PHPLoop.run
193 // Avoid terminating twice...
195 state.setTerminated(true);
198 this.threads = new PHPThread[0];
200 IBreakpointManager manager = DebugPlugin.getDefault()
201 .getBreakpointManager();
202 manager.removeBreakpointListener(this);
203 DebugPlugin.getDefault().removeDebugEventListener(this);
206 public synchronized boolean canResume() {
209 return isSuspended();
212 public synchronized boolean canSuspend() {
215 return !isSuspended();
218 public synchronized boolean isSuspended() {
219 return state.isSuspended();
222 public synchronized void resume() throws DebugException {
225 state.setSuspended(false);
226 this.getPHPDBGProxy().resume();
227 IThread[] threads = getThreads();
228 for (int i = 0; i < threads.length; ++i)
232 public synchronized void suspend() throws DebugException {
235 this.getPHPDBGProxy().pause();
236 state.setSuspended(true);
237 IThread[] threads = getThreads();
238 for (int i = 0; i < threads.length; ++i)
239 threads[i].suspend();
242 public void breakpointAdded(IBreakpoint breakpoint) {
243 this.getPHPDBGProxy().addBreakpoint(breakpoint);
246 public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta arg1) {
247 this.getPHPDBGProxy().removeBreakpoint(breakpoint);
251 * The method will be called when the user enables/disables
252 * breakpoints. In this case we add or remove the breakpoint.
253 * It's also called when leaving the breakpoint properties dialog
254 * (skip count and breakpoint condition) with the OK button.
256 * This method is also called whenever a source file has changed.
257 * In this case we terminate since the source will be out of sync with the debugger.
258 * TODO Is it correct to call this method when a sourcefile is modified?
261 public void breakpointChanged (IBreakpoint breakpoint, IMarkerDelta arg1) {
262 PHPLineBreakpoint bp;
263 bp = (PHPLineBreakpoint) breakpoint;
266 if (breakpoint.isEnabled () && // Check if breakpoint state changed from disabled to enabled
267 !arg1.getAttribute ("org.eclipse.debug.core.enabled", false)) {
268 this.getPHPDBGProxy().addBreakpoint(breakpoint);
270 else if (!breakpoint.isEnabled () && // Check if breakpoint state changed from enabled to disabled
271 arg1.getAttribute ("org.eclipse.debug.core.enabled", true)) {
272 this.getPHPDBGProxy().removeBreakpoint(breakpoint);
274 else if (bp.getChangeID() != arg1.getAttribute ("net.sourceforge.phpeclipse.debug.changeID", 0)) {
275 if (breakpoint.isEnabled()) { // If the breakpoint is already enabled
276 this.getPHPDBGProxy().removeBreakpoint(breakpoint); // we remove this breakpoint first
277 this.getPHPDBGProxy().addBreakpoint(breakpoint); // and then we add again (else DBG would have two breakpoints!).
280 this.getPHPDBGProxy().removeBreakpoint(breakpoint);
283 else { // All other cases will terminate the debugger
286 } catch (CoreException e) {
291 public boolean canDisconnect() {
295 public void disconnect() throws DebugException {
298 public boolean isDisconnected() {
302 public boolean supportsStorageRetrieval() {
306 public IMemoryBlock getMemoryBlock(long arg0, long arg1)
307 throws DebugException {
311 public Object getAdapter(Class arg0) {
312 if (IWorkbenchAdapter.class.equals(arg0)) {
313 return new IWorkbenchAdapter() {
314 public Object[] getChildren(Object o) {
315 Object[] children = null;
316 IThread[] threads = getThreads();
317 if (null != threads) {
318 children = new Object[threads.length];
319 for (int i = 0; i < threads.length; ++i)
320 children[i] = threads[i];
325 public ImageDescriptor getImageDescriptor(Object object) {
329 public String getLabel(Object o) {
330 String label = "(Unable to look up name... check error log)";
333 } catch (DebugException x) {
334 PHPeclipsePlugin.log(label, x);
339 public Object getParent(Object o) {
340 return PHPDebugTarget.this.getLaunch();
345 if (arg0 == PHPDebugElement.class) {
349 return super.getAdapter(arg0);
353 public IProcess getProcess() {
357 public void setProcess(IProcess process) {
358 this.process = process;
361 public PHPDBGProxy getPHPDBGProxy() {
365 public void setPHPDBGProxy(PHPDBGProxy phpDBGProxy) {
366 this.phpDBGProxy = phpDBGProxy;
370 * @see ILaunchListener#launchRemoved(ILaunch)
372 public void launchRemoved(ILaunch launch) {
373 if (!isTerminated()) {
376 if (launch.equals(getLaunch())) {
377 // This target has been deregistered, but it hasn't successfully
379 // Update internal state to reflect that it is disconnected
385 * @see ILaunchListener#launchAdded(ILaunch)
387 public void launchAdded(ILaunch launch) {
391 * @see ILaunchListener#launchChanged(ILaunch)
393 public void launchChanged(ILaunch launch) {
397 * When a debug target or process terminates, terminate DBG Proxy.
399 * @see IDebugEventSetListener#handleDebugEvents(DebugEvent[])
401 public void handleDebugEvents(DebugEvent[] events) {
402 for (int i = 0; i < events.length; i++) {
403 DebugEvent event = events[i];
404 if (event.getKind() == DebugEvent.TERMINATE) {
405 Object source = event.getSource();
406 if (source instanceof PHPDebugTarget
407 || source instanceof IDebugTarget) {
408 getPHPDBGProxy().stop();
409 } else if (source instanceof IProcess) {
410 if (getDebugTarget().getProcess() == (IProcess) source) {
411 getPHPDBGProxy().stop();
414 } else if (event.getKind() == DebugEvent.SUSPEND) {
415 getPHPDBGProxy().pause();