Patch from Anders Betn�- enable changing breakpoints while debugging.
[phpeclipse.git] / net.sourceforge.phpeclipse.debug.core / src / net / sourceforge / phpdt / internal / debug / core / model / PHPDebugTarget.java
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
7
8  Contributors:
9  IBM Corporation - Initial implementation
10  Vicente Fernando - www.alfersoft.com.ar
11  **********************************************************************/
12 package net.sourceforge.phpdt.internal.debug.core.model;
13
14 import net.sourceforge.phpdt.internal.debug.core.PHPDBGProxy;
15 import net.sourceforge.phpdt.internal.debug.core.PHPDebugCorePlugin;
16 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
17
18 import org.eclipse.core.resources.IMarkerDelta;
19 import org.eclipse.core.runtime.CoreException;
20 import org.eclipse.debug.core.DebugEvent;
21 import org.eclipse.debug.core.DebugException;
22 import org.eclipse.debug.core.DebugPlugin;
23 import org.eclipse.debug.core.IBreakpointManager;
24 import org.eclipse.debug.core.IDebugEventSetListener;
25 import org.eclipse.debug.core.ILaunch;
26 import org.eclipse.debug.core.ILaunchListener;
27 import org.eclipse.debug.core.model.IBreakpoint;
28 import org.eclipse.debug.core.model.IDebugTarget;
29 import org.eclipse.debug.core.model.IMemoryBlock;
30 import org.eclipse.debug.core.model.IProcess;
31 import org.eclipse.debug.core.model.IThread;
32 import org.eclipse.jface.resource.ImageDescriptor;
33 import org.eclipse.ui.model.IWorkbenchAdapter;
34
35 /**
36  * Debug target for PHP debug model.
37  */
38 public class PHPDebugTarget implements IPHPDebugTarget, ILaunchListener,
39                 IDebugEventSetListener {
40
41         private IProcess process;
42
43         private ILaunch launch;
44
45         private PHPThread[] threads = new PHPThread[0];
46
47         private PHPDBGProxy phpDBGProxy;
48
49         private class State {
50                 private boolean isTerminated = false;
51
52                 private boolean isSuspended = false;
53
54                 boolean isTerminated() {
55                         return isTerminated;
56                 }
57
58                 boolean isSuspended() {
59                         return isSuspended;
60                 }
61
62                 void setTerminated(boolean terminated) {
63                         this.isTerminated = terminated;
64                 }
65
66                 void setSuspended(boolean suspended) {
67                         if (isTerminated())
68                                 throw new IllegalStateException();
69                         this.isSuspended = suspended;
70                 }
71         }
72
73         private final State state = new State();
74
75         public PHPDebugTarget(ILaunch launch, IProcess process) {
76                 if (null == launch && null == process)
77                         throw new IllegalArgumentException();
78                 this.launch = launch;
79                 this.process = process;
80                 // TODO XXX remove breakpoint listener at termination to avoid live leak
81                 IBreakpointManager manager = DebugPlugin.getDefault()
82                                 .getBreakpointManager();
83                 manager.addBreakpointListener(this);
84                 DebugPlugin.getDefault().addDebugEventListener(this);
85                 initialize();
86         }
87
88         protected synchronized void initialize() {
89                 DebugEvent ev = new DebugEvent(this, DebugEvent.CREATE);
90                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
91         }
92
93         public void addThread(PHPThread phpThread) {
94                 int i;
95                 PHPThread[] updatedThreads = new PHPThread[threads.length + 1];
96
97                 for (i = 0; i < threads.length; i++) {
98                         updatedThreads[i] = threads[i];
99                 }
100                 updatedThreads[i] = phpThread;
101                 threads = updatedThreads;
102
103                 fireChangeEvent();
104                 fireThreadCreateEvent(phpThread);
105         }
106
107         private void fireChangeEvent() {
108                 DebugEvent ev = new DebugEvent(this, DebugEvent.CHANGE);
109                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
110         }
111
112         private void fireThreadCreateEvent(PHPThread phpThread) {
113                 DebugEvent ev = new DebugEvent(phpThread, DebugEvent.CREATE);
114                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
115         }
116
117         protected PHPThread getThreadById(int id) {
118                 for (int i = 0; i < threads.length; i++) {
119                         if (threads[i].getId() == id) {
120                                 return threads[i];
121                         }
122                 }
123                 return null;
124         }
125
126         public IThread[] getThreads() {
127                 return threads;
128         }
129
130         public boolean hasThreads() throws DebugException {
131                 return threads.length > 0;
132         }
133
134         public String getName() throws DebugException {
135                 return "PHP Debugger at localhost:" + getPHPDBGProxy().getPort();
136         }
137
138         public boolean supportsBreakpoint(IBreakpoint arg0) {
139                 if (arg0.getModelIdentifier().equals(PHPDebugCorePlugin.PLUGIN_ID)) {
140                         return true;
141                 }
142                 return false;
143         }
144
145         public String getModelIdentifier() {
146                 return PHPDebugCorePlugin.PLUGIN_ID;
147         }
148
149         public IDebugTarget getDebugTarget() {
150                 return this;
151         }
152
153         public ILaunch getLaunch() {
154                 return launch;
155         }
156
157         public synchronized boolean canTerminate() {
158                 return !isTerminated();
159         }
160
161         public synchronized boolean isTerminated() {
162                 return state.isTerminated();
163         }
164
165         public synchronized void terminate() {
166                 // This method is synchronized to control a race condition between the
167                 // UI thread that terminates the debugging session, and the slave
168                 // thread that executes PHPLoop.run
169                 if (isTerminated())
170                         // Avoid terminating twice...
171                         return;
172                 state.setTerminated(true);
173                 phpDBGProxy.stop();
174                 this.threads = new PHPThread[0];
175                 fireChangeEvent();
176                 IBreakpointManager manager = DebugPlugin.getDefault()
177                                 .getBreakpointManager();
178                 manager.removeBreakpointListener(this);
179                 DebugPlugin.getDefault().removeDebugEventListener(this);
180         }
181
182         public synchronized boolean canResume() {
183                 if (isTerminated())
184                         return false;
185                 return isSuspended();
186         }
187
188         public synchronized boolean canSuspend() {
189                 if (isTerminated())
190                         return false;
191                 return !isSuspended();
192         }
193
194         public synchronized boolean isSuspended() {
195                 return state.isSuspended();
196         }
197
198         public synchronized void resume() throws DebugException {
199                 if (!isSuspended())
200                         return;
201                 state.setSuspended(false);
202                 this.getPHPDBGProxy().resume();
203                 IThread[] threads = getThreads();
204                 for (int i = 0; i < threads.length; ++i)
205                         threads[i].resume();
206         }
207
208         public synchronized void suspend() throws DebugException {
209                 if (isSuspended())
210                         return;
211                 this.getPHPDBGProxy().pause();
212                 state.setSuspended(true);
213                 IThread[] threads = getThreads();
214                 for (int i = 0; i < threads.length; ++i)
215                         threads[i].suspend();
216         }
217
218         public void breakpointAdded(IBreakpoint breakpoint) {
219                 this.getPHPDBGProxy().addBreakpoint(breakpoint);
220         }
221
222         public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta arg1) {
223                 this.getPHPDBGProxy().removeBreakpoint(breakpoint);
224         }
225
226         public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta arg1) {
227                 // This method is called whenever a source file has changed in which
228                 // case
229                 // we terminate since the source will be out of sync with the debugger
230                 // The method will also be called when the user enables/disables
231                 // breakpoints
232                 // in this case we add or remove the breakpoint
233                 try {
234                         // Check if breakpoint state changed from disabled to enabled
235                         if (breakpoint.isEnabled()
236                                         && !arg1.getAttribute("org.eclipse.debug.core.enabled",
237                                                         false)) {
238                                 this.getPHPDBGProxy().addBreakpoint(breakpoint);
239                                 // Check if breakpoint state changed from enabled to disabled
240                         } else if (!breakpoint.isEnabled()
241                                         && arg1
242                                                         .getAttribute("org.eclipse.debug.core.enabled",
243                                                                         true)) {
244                                 this.getPHPDBGProxy().removeBreakpoint(breakpoint);
245                         } else {
246                                 // All other cases will terminate the debugger
247                                 terminate();
248                         }
249                 } catch (CoreException e) {
250                         // Do nothing
251                 }
252         }       
253
254         public boolean canDisconnect() {
255                 return false;
256         }
257
258         public void disconnect() throws DebugException {
259         }
260
261         public boolean isDisconnected() {
262                 return false;
263         }
264
265         public boolean supportsStorageRetrieval() {
266                 return false;
267         }
268
269         public IMemoryBlock getMemoryBlock(long arg0, long arg1)
270                         throws DebugException {
271                 return null;
272         }
273
274         public Object getAdapter(Class arg0) {
275                 if (IWorkbenchAdapter.class.equals(arg0)) {
276                         return new IWorkbenchAdapter() {
277                                 public Object[] getChildren(Object o) {
278                                         Object[] children = null;
279                                         IThread[] threads = getThreads();
280                                         if (null != threads) {
281                                                 children = new Object[threads.length];
282                                                 for (int i = 0; i < threads.length; ++i)
283                                                         children[i] = threads[i];
284                                         }
285                                         return children;
286                                 }
287
288                                 public ImageDescriptor getImageDescriptor(Object object) {
289                                         return null;
290                                 }
291
292                                 public String getLabel(Object o) {
293                                         String label = "(Unable to look up name... check error log)";
294                                         try {
295                                                 label = getName();
296                                         } catch (DebugException x) {
297                                                 PHPeclipsePlugin.log(label, x);
298                                         }
299                                         return label;
300                                 }
301
302                                 public Object getParent(Object o) {
303                                         return PHPDebugTarget.this.getLaunch();
304                                 }
305                         };
306                 }
307                 return null;
308         }
309
310         public IProcess getProcess() {
311                 return process;
312         }
313
314         public void setProcess(IProcess process) {
315                 this.process = process;
316         }
317
318         public PHPDBGProxy getPHPDBGProxy() {
319                 return phpDBGProxy;
320         }
321
322         public void setPHPDBGProxy(PHPDBGProxy phpDBGProxy) {
323                 this.phpDBGProxy = phpDBGProxy;
324         }
325
326         /**
327          * @see ILaunchListener#launchRemoved(ILaunch)
328          */
329         public void launchRemoved(ILaunch launch) {
330                 if (!isTerminated()) {
331                         return;
332                 }
333                 if (launch.equals(getLaunch())) {
334                         // This target has been deregistered, but it hasn't successfully
335                         // terminated.
336                         // Update internal state to reflect that it is disconnected
337                         terminate();
338                 }
339         }
340
341         /**
342          * @see ILaunchListener#launchAdded(ILaunch)
343          */
344         public void launchAdded(ILaunch launch) {
345         }
346
347         /**
348          * @see ILaunchListener#launchChanged(ILaunch)
349          */
350         public void launchChanged(ILaunch launch) {
351         }
352
353         /**
354          * When a debug target or process terminates, terminate DBG Proxy.
355          * 
356          * @see IDebugEventSetListener#handleDebugEvents(DebugEvent[])
357          */
358         public void handleDebugEvents(DebugEvent[] events) {
359                 for (int i = 0; i < events.length; i++) {
360                         DebugEvent event = events[i];
361                         if (event.getKind() == DebugEvent.TERMINATE) {
362                                 Object source = event.getSource();
363                                 if (source instanceof PHPDebugTarget
364                                                 || source instanceof IDebugTarget) {
365                                         getPHPDBGProxy().stop();
366                                 } else if (source instanceof IProcess) {
367                                         if (getDebugTarget().getProcess() == (IProcess) source) {
368                                                 getPHPDBGProxy().stop();
369                                         }
370                                 }
371                         } else if (event.getKind() == DebugEvent.SUSPEND) {
372                                 getPHPDBGProxy().pause();
373                         }
374                 }
375         }
376 }