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
 
   9  IBM Corporation - Initial implementation
 
  10  Vicente Fernando - www.alfersoft.com.ar
 
  11  **********************************************************************/
 
  12 package net.sourceforge.phpdt.internal.debug.core.model;
 
  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;
 
  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;
 
  38  * Debug target for PHP debug model.
 
  40 public class PHPDebugTarget extends PHPDebugElement implements IPHPDebugTarget, ILaunchListener,
 
  41                 IDebugEventSetListener {
 
  43         private IProcess process;
 
  45         private ILaunch launch;
 
  47         private PHPThread[] threads = new PHPThread[0];
 
  49         private PHPDBGProxy phpDBGProxy;
 
  52                 private boolean isTerminated = false;
 
  54                 private boolean isSuspended = false;
 
  56                 boolean isTerminated() {
 
  60                 boolean isSuspended() {
 
  64                 void setTerminated(boolean terminated) {
 
  65                         this.isTerminated = terminated;
 
  68                 void setSuspended(boolean suspended) {
 
  70                                 throw new IllegalStateException();
 
  71                         this.isSuspended = suspended;
 
  75         private final State state = new State();
 
  77         public PHPDebugTarget(ILaunch launch, IProcess process) {
 
  79                 if (null == launch && null == process)
 
  80                         throw new IllegalArgumentException();
 
  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);
 
  91         protected synchronized void initialize() {
 
  92                 DebugEvent ev = new DebugEvent(this, DebugEvent.CREATE);
 
  93                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
 
  96         public void addThread(PHPThread phpThread) {
 
  98                 PHPThread[] updatedThreads = new PHPThread[threads.length + 1];
 
 100                 for (i = 0; i < threads.length; i++) {
 
 101                         updatedThreads[i] = threads[i];
 
 103                 updatedThreads[i] = phpThread;
 
 104                 threads = updatedThreads;
 
 107                 fireThreadCreateEvent(phpThread);
 
 110 //      public void updateThreads(PHPThread phpThread) {
 
 111 //              fireChangeEvent();
 
 112 //              fireThreadCreateEvent(phpThread);
 
 115         private void fireChangeEvent() {
 
 116                 DebugEvent ev = new DebugEvent(this, DebugEvent.CHANGE);
 
 117                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
 
 120         private void fireThreadCreateEvent(PHPThread phpThread) {
 
 121                 DebugEvent ev = new DebugEvent(phpThread, DebugEvent.CREATE);
 
 122                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
 
 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];
 
 134         public IThread[] getThreads() {
 
 138         public boolean hasThreads() throws DebugException {
 
 139                 return threads.length > 0;
 
 142         public String getName() throws DebugException {
 
 143                 return "PHP Debugger at localhost:" + getPHPDBGProxy().getPort();
 
 146         public boolean supportsBreakpoint(IBreakpoint arg0) {
 
 147                 if (arg0.getModelIdentifier().equals(PHPDebugCorePlugin.PLUGIN_ID)) {
 
 153         public String getModelIdentifier() {
 
 154                 return PHPDebugCorePlugin.PLUGIN_ID;
 
 157         public IStackFrame[] getStackFrames () throws DebugException {
 
 158                 return (IStackFrame[]) this.phpDBGProxy.getDBGInterface ().getStackList ();
 
 161         public IDebugTarget getDebugTarget() {
 
 165         public ILaunch getLaunch() {
 
 169         public synchronized boolean canTerminate() {
 
 170                 return !isTerminated();
 
 173         public synchronized boolean isTerminated() {
 
 174                 return state.isTerminated();
 
 177         private synchronized void terminateThreads () {
 
 181                         for (i = 0; i < threads.length; i++) {
 
 182                                 threads[i].terminate ();
 
 184                 } catch (DebugException e) {
 
 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
 
 193                         // Avoid terminating twice...
 
 195                 state.setTerminated(true);
 
 198                 this.threads = new PHPThread[0];
 
 200                 IBreakpointManager manager = DebugPlugin.getDefault()
 
 201                                 .getBreakpointManager();
 
 202                 manager.removeBreakpointListener(this);
 
 203                 DebugPlugin.getDefault().removeDebugEventListener(this);
 
 206         public synchronized boolean canResume() {
 
 209                 return isSuspended();
 
 212         public synchronized boolean canSuspend() {
 
 215                 return !isSuspended();
 
 218         public synchronized boolean isSuspended() {
 
 219                 return state.isSuspended();
 
 222         public synchronized void resume() throws DebugException {
 
 225                 state.setSuspended(false);
 
 226                 this.getPHPDBGProxy().resume();
 
 227                 IThread[] threads = getThreads();
 
 228                 for (int i = 0; i < threads.length; ++i)
 
 232         public synchronized void suspend() throws DebugException {
 
 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();
 
 242         public void breakpointAdded(IBreakpoint breakpoint) {
 
 243                 this.getPHPDBGProxy().addBreakpoint(breakpoint);
 
 246         public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta arg1) {
 
 247                 this.getPHPDBGProxy().removeBreakpoint(breakpoint);
 
 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.
 
 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?
 
 261         public void breakpointChanged (IBreakpoint breakpoint, IMarkerDelta arg1) {
 
 262                 PHPLineBreakpoint bp;
 
 263                 bp = (PHPLineBreakpoint) breakpoint;
 
 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);
 
 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);
 
 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!).
 
 280                                         this.getPHPDBGProxy().removeBreakpoint(breakpoint);
 
 283                         else {                                                                                                                  // All other cases will terminate the debugger
 
 286                 } catch (CoreException e) {
 
 291         public boolean canDisconnect() {
 
 295         public void disconnect() throws DebugException {
 
 298         public boolean isDisconnected() {
 
 302         public boolean supportsStorageRetrieval() {
 
 306         public IMemoryBlock getMemoryBlock(long arg0, long arg1)
 
 307                         throws DebugException {
 
 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];
 
 325                                 public ImageDescriptor getImageDescriptor(Object object) {
 
 329                                 public String getLabel(Object o) {
 
 330                                         String label = "(Unable to look up name... check error log)";
 
 333                                         } catch (DebugException x) {
 
 334                                                 PHPeclipsePlugin.log(label, x);
 
 339                                 public Object getParent(Object o) {
 
 340                                         return PHPDebugTarget.this.getLaunch();
 
 345                     if (arg0 == PHPDebugElement.class) {
 
 349                     return super.getAdapter(arg0);
 
 353         public IProcess getProcess() {
 
 357         public void setProcess(IProcess process) {
 
 358                 this.process = process;
 
 361         public PHPDBGProxy getPHPDBGProxy() {
 
 365         public void setPHPDBGProxy(PHPDBGProxy phpDBGProxy) {
 
 366                 this.phpDBGProxy = phpDBGProxy;
 
 370          * @see ILaunchListener#launchRemoved(ILaunch)
 
 372         public void launchRemoved(ILaunch launch) {
 
 373                 if (!isTerminated()) {
 
 376                 if (launch.equals(getLaunch())) {
 
 377                         // This target has been deregistered, but it hasn't successfully
 
 379                         // Update internal state to reflect that it is disconnected
 
 385          * @see ILaunchListener#launchAdded(ILaunch)
 
 387         public void launchAdded(ILaunch launch) {
 
 391          * @see ILaunchListener#launchChanged(ILaunch)
 
 393         public void launchChanged(ILaunch launch) {
 
 397          * When a debug target or process terminates, terminate DBG Proxy.
 
 399          * @see IDebugEventSetListener#handleDebugEvents(DebugEvent[])
 
 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();
 
 414                         } else if (event.getKind() == DebugEvent.SUSPEND) {
 
 415                                 getPHPDBGProxy().pause();