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