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