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