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