41c5a39377f69764fdd2594d1de6985ad0dadec7
[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 //      public void updateThreads(PHPThread phpThread) {
111 //              fireChangeEvent();
112 //              fireThreadCreateEvent(phpThread);
113 //      }
114
115         private void fireChangeEvent() {
116                 DebugEvent ev = new DebugEvent(this, DebugEvent.CHANGE);
117                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
118         }
119
120         private void fireThreadCreateEvent(PHPThread phpThread) {
121                 DebugEvent ev = new DebugEvent(phpThread, DebugEvent.CREATE);
122                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
123         }
124
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];
129 //                      }
130 //              }
131 //              return null;
132 //      }
133
134         public IThread[] getThreads() {
135                 return threads;
136         }
137
138         public boolean hasThreads() throws DebugException {
139                 return threads.length > 0;
140         }
141
142         public String getName() throws DebugException {
143                 return "PHP Debugger at localhost:" + getPHPDBGProxy().getPort();
144         }
145
146         public boolean supportsBreakpoint(IBreakpoint arg0) {
147                 if (arg0.getModelIdentifier().equals(PHPDebugCorePlugin.PLUGIN_ID)) {
148                         return true;
149                 }
150                 return false;
151         }
152
153         public String getModelIdentifier() {
154                 return PHPDebugCorePlugin.PLUGIN_ID;
155         }
156
157         public IStackFrame[] getStackFrames () throws DebugException {
158                 return (IStackFrame[]) this.phpDBGProxy.getDBGInterface ().getStackList ();
159         }
160
161         public IDebugTarget getDebugTarget() {
162                 return this;
163         }
164
165         public ILaunch getLaunch() {
166                 return launch;
167         }
168
169         public synchronized boolean canTerminate() {
170                 return !isTerminated();
171         }
172
173         public synchronized boolean isTerminated() {
174                 return state.isTerminated();
175         }
176         
177         private synchronized void terminateThreads () {
178                 int i;
179                 
180                 try {
181                         for (i = 0; i < threads.length; i++) {
182                                 threads[i].terminate ();
183                         }
184                 } catch (DebugException e) {
185                 }
186         }
187
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
192                 if (isTerminated())
193                         // Avoid terminating twice...
194                         return;
195                 state.setTerminated(true);
196                 phpDBGProxy.stop();
197                 terminateThreads ();
198                 this.threads = new PHPThread[0];
199                 fireChangeEvent();
200                 IBreakpointManager manager = DebugPlugin.getDefault()
201                                 .getBreakpointManager();
202                 manager.removeBreakpointListener(this);
203                 DebugPlugin.getDefault().removeDebugEventListener(this);
204         }
205
206         public synchronized boolean canResume() {
207                 if (isTerminated())
208                         return false;
209                 return isSuspended();
210         }
211
212         public synchronized boolean canSuspend() {
213                 if (isTerminated())
214                         return false;
215                 return !isSuspended();
216         }
217
218         public synchronized boolean isSuspended() {
219                 return state.isSuspended();
220         }
221
222         public synchronized void resume() throws DebugException {
223                 if (!isSuspended())
224                         return;
225                 state.setSuspended(false);
226                 this.getPHPDBGProxy().resume();
227                 IThread[] threads = getThreads();
228                 for (int i = 0; i < threads.length; ++i)
229                         threads[i].resume();
230         }
231
232         public synchronized void suspend() throws DebugException {
233                 if (isSuspended())
234                         return;
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();
240         }
241
242         public void breakpointAdded(IBreakpoint breakpoint) {
243                 this.getPHPDBGProxy().addBreakpoint(breakpoint);
244         }
245
246         public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta arg1) {
247                 this.getPHPDBGProxy().removeBreakpoint(breakpoint);
248         }
249
250         /**
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.
255          *
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?
259          *
260          */
261         public void breakpointChanged (IBreakpoint breakpoint, IMarkerDelta arg1) {
262                 PHPLineBreakpoint bp;
263                 bp = (PHPLineBreakpoint) breakpoint;
264
265                 try {
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);
269                         }
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);
273                         }
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!).
278                                 }
279                                 else {
280                                         this.getPHPDBGProxy().removeBreakpoint(breakpoint);
281                                 }
282                         }
283                         else {                                                                                                                  // All other cases will terminate the debugger
284                                 terminate ();
285                         }
286                 } catch (CoreException e) {
287                         // Do nothing
288                 }
289         }
290
291         public boolean canDisconnect() {
292                 return false;
293         }
294
295         public void disconnect() throws DebugException {
296         }
297
298         public boolean isDisconnected() {
299                 return false;
300         }
301
302         public boolean supportsStorageRetrieval() {
303                 return false;
304         }
305
306         public IMemoryBlock getMemoryBlock(long arg0, long arg1)
307                         throws DebugException {
308                 return null;
309         }
310
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];
321                                         }
322                                         return children;
323                                 }
324
325                                 public ImageDescriptor getImageDescriptor(Object object) {
326                                         return null;
327                                 }
328
329                                 public String getLabel(Object o) {
330                                         String label = "(Unable to look up name... check error log)";
331                                         try {
332                                                 label = getName();
333                                         } catch (DebugException x) {
334                                                 PHPeclipsePlugin.log(label, x);
335                                         }
336                                         return label;
337                                 }
338
339                                 public Object getParent(Object o) {
340                                         return PHPDebugTarget.this.getLaunch();
341                                 }
342                         };
343                 }
344                 else {
345                     if (arg0 == PHPDebugElement.class) {
346                         return this;
347                     }
348
349                     return super.getAdapter(arg0);
350                 }
351         }
352
353         public IProcess getProcess() {
354                 return process;
355         }
356
357         public void setProcess(IProcess process) {
358                 this.process = process;
359         }
360
361         public PHPDBGProxy getPHPDBGProxy() {
362                 return phpDBGProxy;
363         }
364
365         public void setPHPDBGProxy(PHPDBGProxy phpDBGProxy) {
366                 this.phpDBGProxy = phpDBGProxy;
367         }
368
369         /**
370          * @see ILaunchListener#launchRemoved(ILaunch)
371          */
372         public void launchRemoved(ILaunch launch) {
373                 if (!isTerminated()) {
374                         return;
375                 }
376                 if (launch.equals(getLaunch())) {
377                         // This target has been deregistered, but it hasn't successfully
378                         // terminated.
379                         // Update internal state to reflect that it is disconnected
380                         terminate();
381                 }
382         }
383
384         /**
385          * @see ILaunchListener#launchAdded(ILaunch)
386          */
387         public void launchAdded(ILaunch launch) {
388         }
389
390         /**
391          * @see ILaunchListener#launchChanged(ILaunch)
392          */
393         public void launchChanged(ILaunch launch) {
394         }
395
396         /**
397          * When a debug target or process terminates, terminate DBG Proxy.
398          *
399          * @see IDebugEventSetListener#handleDebugEvents(DebugEvent[])
400          */
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();
412                                         }
413                                 }
414                         } else if (event.getKind() == DebugEvent.SUSPEND) {
415                                 getPHPDBGProxy().pause();
416                         }
417                 }
418         }
419 }