Package org.eclipse.php.internal.debug.core.zend.debugger

Source Code of org.eclipse.php.internal.debug.core.zend.debugger.RemoteDebugger

/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*     Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.debug.core.zend.debugger;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IBuildpathContainer;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.environment.EnvironmentPathUtils;
import org.eclipse.php.debug.core.debugger.IDebugHandler;
import org.eclipse.php.debug.core.debugger.messages.IDebugMessage;
import org.eclipse.php.debug.core.debugger.messages.IDebugNotificationMessage;
import org.eclipse.php.debug.core.debugger.messages.IDebugRequestMessage;
import org.eclipse.php.debug.core.debugger.messages.IDebugResponseMessage;
import org.eclipse.php.debug.core.debugger.parameters.IDebugParametersKeys;
import org.eclipse.php.internal.core.includepath.IncludePath;
import org.eclipse.php.internal.core.includepath.IncludePathManager;
import org.eclipse.php.internal.core.util.*;
import org.eclipse.php.internal.core.util.PHPSearchEngine.ExternalFileResult;
import org.eclipse.php.internal.core.util.PHPSearchEngine.IncludedFileResult;
import org.eclipse.php.internal.core.util.PHPSearchEngine.ResourceResult;
import org.eclipse.php.internal.core.util.PHPSearchEngine.Result;
import org.eclipse.php.internal.debug.core.PHPDebugPlugin;
import org.eclipse.php.internal.debug.core.pathmapper.*;
import org.eclipse.php.internal.debug.core.preferences.PHPDebugCorePreferenceNames;
import org.eclipse.php.internal.debug.core.preferences.PHPDebuggersRegistry;
import org.eclipse.php.internal.debug.core.preferences.PHPProjectPreferences;
import org.eclipse.php.internal.debug.core.zend.communication.DebugConnectionThread;
import org.eclipse.php.internal.debug.core.zend.communication.DebuggerCommunicationDaemon;
import org.eclipse.php.internal.debug.core.zend.communication.ResponseHandler;
import org.eclipse.php.internal.debug.core.zend.debugger.messages.*;
import org.eclipse.php.internal.debug.core.zend.model.PHPDebugTarget;
import org.eclipse.php.internal.ui.Logger;
import org.eclipse.swt.widgets.Display;

/**
* An IRemoteDebugger implementation.
*/
public class RemoteDebugger implements IRemoteDebugger {

  /**
   * Original PDT protocol ID from 04/2006
   */
  public static final int PROTOCOL_ID_2006040701 = 2006040701;

  /**
   * Improved protocol ID from 06/2007 which provides new message type (
   * {@link StartProcessFileNotification}) that allows to control debug state
   * when debugger is preparing processing new file. We use this state for
   * doing on-demand path mapping, and for sending breakpoints for the next
   * file.
   */
  public static final int PROTOCOL_ID_2006040703 = 2006040703;

  /**
   * New protocol ID from 04/2008 which provides two new message types:
   * {@link GetCWDRequest} allows to ask Debugger to return current working
   * directory,
   */
  public static final int PROTOCOL_ID_2006040705 = 2006040705;

  /**
   * New protocol ID from 12/2012 which provides new message type:
   * {@link AddFilesRequest} allows send initial list of files which contain
   * at least one breakpoint.
   */
  public static final int PROTOCOL_ID_2012121702 = 2012121702;

  /**
   * Latest protocol ID
   */
  public static final int PROTOCOL_ID_LATEST = PROTOCOL_ID_2012121702;

  private static final String EVAL_ERROR = "[Error]"; //$NON-NLS-1$

  protected boolean isDebugMode = System.getProperty("loggingDebug") != null; //$NON-NLS-1$
  private DebugConnectionThread connection;
  private IDebugHandler debugHandler;
  private Map<String, String> resolvedFiles;
  private Map<String, List<IPath>> resolvedIncludePaths;
  private int currentProtocolId = 0;
  private int previousSuspendCount;
  private String cachedCWD;
  private PHPstack cachedStack;

  /**
   * Creates new RemoteDebugSession
   */
  public RemoteDebugger(IDebugHandler debugHandler,
      DebugConnectionThread connectionThread) {
    // this.kit = createCommunicationKit();
    connection = connectionThread;
    this.debugHandler = debugHandler;
    connection.setCommunicationAdministrator(this);
    connection.setCommunicationClient(this);
    resolvedFiles = new HashMap<String, String>();
    resolvedIncludePaths = new HashMap<String, List<IPath>>();
  }

  public IDebugHandler getDebugHandler() {
    return debugHandler;
  }

  public DebugConnectionThread getConnectionThread() {
    return connection;
  }

  public void closeConnection() {
    connection.closeConnection();
  }

  public void connectionEstablished() {
    debugHandler.connectionEstablished();
  }

  public void connectionClosed() {
    debugHandler.connectionClosed();
  }

  public void closeDebugSession() {
    if (connection.isConnected()) {
      connection.sendNotification(new DebugSessionClosedNotification());
    }
  }

  public void handleMultipleBindings() {
    debugHandler.multipleBindOccured();
  }

  public void handlePeerResponseTimeout() {
    debugHandler.connectionTimedout();
  }

  public boolean canDo(int feature) {
    switch (feature) {
    case START_PROCESS_FILE_NOTIFICATION:
      return getCurrentProtocolID() >= PROTOCOL_ID_2006040703;
    case GET_CWD:
    case GET_CALL_STACK_LITE:
      return getCurrentProtocolID() >= PROTOCOL_ID_2006040705;
    }
    return false;
  }

  /**
   * Asks Debug server for a current working directory (old way)
   *
   * @return current working directory, or <code>null</code> in case of error
   */
  public String getCWDOld() {
    EvalRequest request = new EvalRequest();
    request.setCommand("getcwd()"); //$NON-NLS-1$
    IDebugResponseMessage response = sendCustomRequest(request);
    if (response != null && response instanceof EvalResponse) {
      String result = ((EvalResponse) response).getResult();
      if (!EVAL_ERROR.equals(result)) {
        return result;
      }
    }
    return null;
  }

  /**
   * Asks Debug server for a current working directory (new way)
   *
   * @return current working directory, or <code>null</code> in case of error
   */
  public String getCWDNew() {
    GetCWDRequest request = new GetCWDRequest();
    IDebugResponseMessage response = sendCustomRequest(request);
    if (response != null && response.getStatus() == 0) {
      return ((GetCWDResponse) response).getCWD();
    }
    return null;
  }

  public String getCWD() {
    if (!this.isActive()) {
      return null;
    }

    PHPDebugTarget debugTarget = getDebugHandler().getDebugTarget();
    int suspendCount = debugTarget.getSuspendCount();
    if (suspendCount == previousSuspendCount && cachedCWD != null) {
      return cachedCWD;
    }

    if (canDo(GET_CWD)) {
      cachedCWD = getCWDNew();
    } else {
      cachedCWD = getCWDOld();
    }
    return cachedCWD;
  }

  /**
   * Returns local path corresponding to the current working directory of the
   * PHP script, which is currently running.
   *
   * @return current working directory
   */
  public String getCurrentWorkingDirectory() {
    PHPDebugTarget debugTarget = debugHandler.getDebugTarget();

    String cwd = getCWD();
    if (cwd != null) {
      PathMapper pathMapper = PathMapperRegistry
          .getByLaunchConfiguration(debugTarget.getLaunch()
              .getLaunchConfiguration());
      if (pathMapper != null) {
        PathEntry cwdEntry = pathMapper.getLocalFile(cwd);
        if (cwdEntry != null) {
          cwd = cwdEntry.getResolvedPath();
        }
      }
    }
    return cwd;
  }

  /**
   * Sets current working directory on the debugger side
   *
   * @param cwd
   *            Current working directory to set
   * @return <code>true</code> if success, <code>false</code> - otherwise
   */
  public boolean setCurrentWorkingDirectory(String cwd) {
    try {
      EvalRequest request = new EvalRequest();
      request.setCommand(String.format("chdir('%1$s')", cwd)); //$NON-NLS-1$
      IDebugResponseMessage response = sendCustomRequest(request);
      if (response != null && response instanceof EvalResponse) {
        String result = ((EvalResponse) response).getResult();
        if (!EVAL_ERROR.equals(result)) {
          return true;
        }
      }
    } catch (Exception e) {
      Logger.logException(e);
    }
    return false;
  }

  /**
   * Returns local file name corresponding to the given remote path. This
   * method asks debugger for the current working directory before resolving.
   *
   * @param remoteFile
   *            File to resolve
   * @return local file, or <code>null</code> in case of resolving failure
   */
  public String convertToLocalFilename(String remoteFile) {
    String currentScript = null;
    PHPstack callStack = getCallStack();
    if (callStack.getSize() > 0) {
      currentScript = callStack.getLayer(callStack.getSize() - 1)
          .getResolvedCalledFileName();
    }
    return convertToLocalFilename(remoteFile, getCurrentWorkingDirectory(),
        currentScript);
  }

  /**
   * Returns local file name corresponding to the given remote path
   *
   * @param remoteFile
   *            File to resolve
   * @param cwd
   *            Current working directory received from the debugger
   * @param currentScript
   *            Script that is on the top of the debug stack currently
   * @return local file, or <code>null</code> in case of resolving failure
   */
  public String convertToLocalFilename(String remoteFile, String cwd,
      String currentScript) {
    PHPDebugTarget debugTarget = debugHandler.getDebugTarget();
    if (debugTarget.getContextManager().isResolveBlacklisted(remoteFile)) {
      return remoteFile;
    }

    IWorkspace workspace = ResourcesPlugin.getWorkspace();

    // check if this file is already local
    if (workspace.getRoot().findMember(remoteFile) != null) {
      return remoteFile;
    }

    // If we are running local debugger, check if "remote" file exists and
    // return it if it does
    if (debugTarget.isPHPCGI() && new File(remoteFile).exists()) {

      IFile wsFile = null;
      IPath location = new Path(remoteFile);
      IProject[] projects = workspace.getRoot().getProjects();
      IProject currentProject = debugTarget.getProject();
      // set current project to higher priority:
      for (int i = 0; i < projects.length; i++) {
        IProject project = projects[i];
        if (project.equals(currentProject)) {
          IProject tmp = projects[0];
          projects[0] = project;
          projects[i] = tmp;
          break;
        }
      }
      for (int i = 0; i < projects.length; i++) {
        IProject project = projects[i];
        if (!project.isOpen() || !project.isAccessible()) {
          continue;
        }
        IPath projectLocation = project.getLocation();
        if (projectLocation != null
            && projectLocation.isPrefixOf(location)
            && !projectLocation.equals(location)) {
          try {
            wsFile = workspace
                .getRoot()
                .getFile(
                    project.getFullPath()
                        .append(location
                            .makeRelativeTo(projectLocation)));
          } catch (Exception ex) {
            Logger.logException(ex);
          }
          break;
        }
      }

      if (wsFile != null) {
        return wsFile.getFullPath().toString();
      } else {
        return remoteFile;
      }
    }

    String resolvedFileKey = new StringBuilder(remoteFile).append(cwd)
        .append(currentScript).toString();
    if (!resolvedFiles.containsKey(resolvedFileKey)) {
      String currentScriptDir = null;
      if (currentScript != null) {
        currentScriptDir = new Path(currentScript)
            .removeLastSegments(1).toString();
      }

      String resolvedFile = null;
      PathEntry pathEntry = DebugSearchEngine.find(remoteFile,
          debugTarget, cwd, currentScriptDir);
      if (pathEntry != null) {
        resolvedFile = pathEntry.getResolvedPath();
      } else {
        try {
          resolvedFile = tryGuessMapping(remoteFile, debugTarget);
        } catch (ModelException e) {
          resolvedFile = remoteFile;
        }
      }
      resolvedFiles.put(resolvedFileKey, resolvedFile);
    }
    String localFile = resolvedFiles.get(resolvedFileKey);
    if (localFile == null) {
      return remoteFile;
    }
    return localFile;
  }

  /**
   * Returns remote file name corresponding to the given local path
   *
   * @param localFile
   * @return remote file path, or localFile in case it couldn't be resolved
   */
  public static String convertToRemoteFilename(String localFile,
      PHPDebugTarget debugTarget) {
    IPath path = Path.fromPortableString(localFile);

    // check if this is valid workspace path to avoid IAE
    // e.g. when using debug URL, localFile can be "c:\Program Files\..."
    // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=306834
    if (path.segmentCount() >= 2) {

      IFile wsFile = ResourcesPlugin.getWorkspace().getRoot()
          .getFile(path);
      if (debugTarget.isPHPCGI() && wsFile.exists()
          && wsFile.getLocation() != null) {
        File fsFile = wsFile.getLocation().toFile();
        if (fsFile.exists()) {
          return fsFile.getAbsolutePath();
        }
      }
    }
    if (VirtualPath.isAbsolute(localFile)) {
      PathMapper pathMapper = PathMapperRegistry
          .getByLaunchConfiguration(debugTarget.getLaunch()
              .getLaunchConfiguration());
      if (pathMapper != null) {
        String remoteFile = pathMapper.getRemoteFile(localFile);
        if (remoteFile != null) {
          return remoteFile;
        }
      }
    }
    return localFile;
  }

  // ---------------------------------------------------------------------------

  /**
   * Sends the request through the communication connection and returns
   * response
   *
   * @param message
   *            request that will be sent to the debugger
   * @return message response recieved from the debugger
   */
  public IDebugResponseMessage sendCustomRequest(IDebugRequestMessage request) {
    IDebugResponseMessage response = null;
    if (this.isActive()) {
      try {
        Object obj = connection.sendRequest(request);
        if (obj instanceof IDebugResponseMessage) {
          response = (IDebugResponseMessage) obj;
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    return response;
  }

  /**
   * Sends custom notification through the communication connection
   *
   * @param message
   *            notification that will be delivered to the debugger
   * @return <code>true</code> if succeeded sending the message,
   *         <code>false</code> - otherwise
   */
  public boolean sendCustomNotification(IDebugNotificationMessage notification) {
    if (this.isActive()) {
      try {
        connection.sendNotification(notification);
        return true;
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    return false;
  }

  /**
   * Asynchronic addBreakpoint Returns true if succeeded sending the request,
   * false otherwise.
   */
  public boolean addBreakpoint(Breakpoint bp,
      BreakpointAddedResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    try {
      AddBreakpointRequest request = new AddBreakpointRequest();
      Breakpoint tmpBreakpoint = (Breakpoint) bp.clone();
      String fileName = tmpBreakpoint.getFileName();
      tmpBreakpoint.setFileName(fileName);
      request.setBreakpoint(tmpBreakpoint);
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronic addBreakpoint Returns true if succeeded adding the Breakpoint.
   */
  public void addBreakpoint(Breakpoint breakpoint) {
    if (!this.isActive()) {
      return;
    }
    try {
      AddBreakpointRequest request = new AddBreakpointRequest();
      Breakpoint tmpBreakpoint = (Breakpoint) breakpoint.clone();
      String fileName = tmpBreakpoint.getFileName();
      tmpBreakpoint.setFileName(fileName);
      request.setBreakpoint(tmpBreakpoint);
      AddBreakpointResponse response = (AddBreakpointResponse) connection
          .sendRequest(request);
      if (response != null && response.getStatus() == 0) {
        // Log.writeLog("addBreakpoint");
        breakpoint.setID(response.getBreakpointID());
      }
    } catch (Exception exc) {
      exc.printStackTrace();
    }
  }

  /**
   * Asynchronic removeBreakpoint Returns true if succeeded sending the
   * request, false otherwise.
   */
  public boolean removeBreakpoint(int id,
      BreakpointRemovedResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    CancelBreakpointRequest request = new CancelBreakpointRequest();
    request.setBreakpointID(id);
    connection
        .sendRequest(request, new ThisHandleResponse(responseHandler));
    return true;
  }

  /**
   * Synchronic removeBreakpoint Returns true if succeeded removing the
   * Breakpoint.
   */
  public boolean removeBreakpoint(int id) {
    if (!this.isActive()) {
      return false;
    }
    try {
      CancelBreakpointRequest request = new CancelBreakpointRequest();
      request.setBreakpointID(id);
      CancelBreakpointResponse response = (CancelBreakpointResponse) connection
          .sendRequest(request);
      return response != null && response.getStatus() == 0;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Asynchronic removeBreakpoint Returns true if succeeded sending the
   * request, false otherwise.
   */
  public boolean removeBreakpoint(Breakpoint breakpoint,
      BreakpointRemovedResponseHandler responseHandler) {
    return removeBreakpoint(breakpoint.getID(), responseHandler);
  }

  /**
   * Synchronic removeBreakpoint Returns true if succeeded removing the
   * Breakpoint.
   */
  public boolean removeBreakpoint(Breakpoint breakpoint) {
    return removeBreakpoint(breakpoint.getID());
  }

  /**
   * Asynchronic removeAllBreakpoints Returns true if succeeded sending the
   * request, false otherwise.
   */
  public boolean removeAllBreakpoints(
      AllBreakpointRemovedResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    CancelAllBreakpointsRequest request = new CancelAllBreakpointsRequest();
    connection
        .sendRequest(request, new ThisHandleResponse(responseHandler));
    return true;
  }

  /**
   * Synchronic removeAllBreakpoints Returns true if succeeded removing all
   * the Breakpoint.
   */
  public boolean removeAllBreakpoints() {
    if (!this.isActive()) {
      return false;
    }
    try {
      CancelAllBreakpointsRequest request = new CancelAllBreakpointsRequest();
      CancelAllBreakpointsResponse response = (CancelAllBreakpointsResponse) connection
          .sendRequest(request);
      return response != null && response.getStatus() == 0;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Asynchronic stepInto Returns true if succeeded sending the request, false
   * otherwise.
   */
  public boolean stepInto(StepIntoResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    StepIntoRequest request = new StepIntoRequest();
    connection
        .sendRequest(request, new ThisHandleResponse(responseHandler));
    return true;
  }

  /**
   * Synchronic stepInto Returns true if succeeded stepInto.
   */
  public boolean stepInto() {
    if (!this.isActive()) {
      return false;
    }
    try {
      StepIntoRequest request = new StepIntoRequest();
      StepIntoResponse response = (StepIntoResponse) connection
          .sendRequest(request);
      return response != null && response.getStatus() == 0;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Asynchronic stepOver Returns true if succeeded sending the request, false
   * otherwise.
   */
  public boolean stepOver(StepOverResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    try {
      StepOverRequest request = new StepOverRequest();
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronic stepOver Returns true if succeeded stepOver.
   */
  public boolean stepOver() {
    if (!this.isActive()) {
      return false;
    }
    try {
      StepOverRequest request = new StepOverRequest();
      StepOverResponse response = (StepOverResponse) connection
          .sendRequest(request);
      return response != null && response.getStatus() == 0;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Asynchronic stepOut Returns true if succeeded sending the request, false
   * otherwise.
   */
  public boolean stepOut(StepOutResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    try {
      StepOutRequest request = new StepOutRequest();
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronic stepOut Returns true if succeeded stepOut.
   */
  public boolean stepOut() {
    if (!this.isActive()) {
      return false;
    }
    try {
      StepOutRequest request = new StepOutRequest();
      StepOutResponse response = (StepOutResponse) connection
          .sendRequest(request);
      return response != null && response.getStatus() == 0;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Asynchronic go Returns true if succeeded sending the request, false
   * otherwise.
   */
  public boolean go(GoResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    try {
      GoRequest request = new GoRequest();
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronic go Returns true if succeeded go.
   */
  public boolean go() {
    if (!this.isActive()) {
      return false;
    }
    try {
      GoRequest request = new GoRequest();
      GoResponse response = (GoResponse) connection.sendRequest(request);
      return response != null && response.getStatus() == 0;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Asynchronic start Returns true if succeeded sending the request, false
   * otherwise.
   */
  public boolean start(StartResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }

    if (!detectProtocolID()) {
      return false;
    }

    try {
      debugHandler.getDebugTarget().installDeferredBreakpoints();
    } catch (CoreException ce) {
      return false;
    }

    StartRequest request = new StartRequest();
    try {
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronic start Returns true if succeeded start.
   */
  public boolean start() {
    if (!this.isActive()) {
      return false;
    }

    if (!detectProtocolID()) {
      return false;
    }

    StartRequest request = new StartRequest();
    try {
      StartResponse response = (StartResponse) connection
          .sendRequest(request);
      return response != null && response.getStatus() == 0;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Asynchronous addFiles Returns true if succeeded sending the request,
   * false otherwise.
   */
  public boolean addFiles(String[] paths,
      AddFilesResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    try {
      AddFilesRequest request = new AddFilesRequest();
      request.setPaths(paths);
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronous addFiles Returns true if succeeded adding the Breakpoint.
   */
  public boolean addFiles(String[] paths) {
    if (!this.isActive()) {
      return false;
    }
    try {
      AddFilesRequest request = new AddFilesRequest();
      request.setPaths(paths);
      AddFilesResponse response = (AddFilesResponse) connection
          .sendRequest(request);
      return response != null && response.getStatus() == 0;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * This method is used for detecting protocol version of Debugger
   *
   * @return <code>true</code> if succeeded to detect, otherwise
   *         <code>false</code>
   */
  protected boolean detectProtocolID() {
    boolean isUseNewProtocol = false;
    try {
      ILaunchConfiguration config = getDebugHandler().getDebugTarget()
          .getLaunch().getLaunchConfiguration();
      String debuggerId = config.getAttribute(
          PHPDebugCorePreferenceNames.PHP_DEBUGGER_ID,
          DebuggerCommunicationDaemon.ZEND_DEBUGGER_ID);
      if (DebuggerCommunicationDaemon.ZEND_DEBUGGER_ID.equals(debuggerId)) {
        ZendDebuggerConfiguration debuggerConfiguration = (ZendDebuggerConfiguration) PHPDebuggersRegistry
            .getDebuggerConfiguration(debuggerId);
        isUseNewProtocol = debuggerConfiguration.isUseNewProtocol();
      }
    } catch (CoreException e) {
      PHPDebugPlugin.log(e);
    }
    // check whether debugger is using the latest protocol ID:
    if (isUseNewProtocol && setProtocol(PROTOCOL_ID_LATEST)) {
      return true;
    }
    // check whether debugger is using one of older protocol ID:
    if (setProtocol(PROTOCOL_ID_2006040705)) {
      return true;
    }
    // check whether debugger is using one of older protocol ID:
    if (setProtocol(PROTOCOL_ID_2006040703)) {
      // warn user that he is using an old debugger
      warnOlderDebugVersion();
      return true;
    }
    // check whether debugger is using one of older protocol ID:
    if (setProtocol(PROTOCOL_ID_2006040701)) {
      // warn user that he is using an old debugger
      warnOlderDebugVersion();
      return true;
    }
    // user is using an incompatible version of debugger:
    getDebugHandler().wrongDebugServer();
    return false;
  }

  public static void warnOlderDebugVersion() {
    boolean dontShowWarning = PHPDebugPlugin.getDefault()
        .getPluginPreferences()
        .getBoolean("DontShowOlderDebuggerWarning"); //$NON-NLS-1$
    if (!dontShowWarning) {
      Display.getDefault().asyncExec(new Runnable() {
        public void run() {
          OldDebuggerWarningDialog dialog = new OldDebuggerWarningDialog(
              Display.getDefault().getActiveShell());
          dialog.open();
        }
      });
    }
  }

  public boolean setProtocol(int protocolID) {
    SetProtocolRequest request = new SetProtocolRequest();
    request.setProtocolID(protocolID);
    IDebugResponseMessage response = sendCustomRequest(request);
    if (response != null && response instanceof SetProtocolResponse) {
      int responceProtocolID = ((SetProtocolResponse) response)
          .getProtocolID();
      if (responceProtocolID == protocolID) {
        currentProtocolId = protocolID;
        return true;
      }
    }
    return false;
  }

  public int getCurrentProtocolID() {
    return currentProtocolId;
  }

  /**
   * Asynchronic pause Returns true if succeeded sending the request, false
   * otherwise.
   */
  public boolean pause(PauseResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    PauseDebuggerRequest request = new PauseDebuggerRequest();
    try {
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronic pause Returns true if succeeded pause.
   */
  public boolean pause() {
    if (!this.isActive()) {
      return false;
    }
    PauseDebuggerRequest request = new PauseDebuggerRequest();
    try {
      PauseDebuggerResponse response = (PauseDebuggerResponse) connection
          .sendRequest(request);
      return response != null && response.getStatus() == 0;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Asynchronic pause Returns true if succeeded sending the request, false
   * otherwise.
   */
  public boolean eval(String commandString,
      EvalResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    EvalRequest request = new EvalRequest();
    request.setCommand(commandString);
    try {
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  public boolean assignValue(String var, String value, int depth,
      String[] path, AssignValueResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    AssignValueRequest request = new AssignValueRequest();
    request.setVar(var);
    request.setValue(value);
    request.setDepth(depth);
    request.setPath(path);
    request.setTransferEncoding(getTransferEncoding());
    try {
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /*
   * Returns the transfer encoding for the current project.
   */
  private String getTransferEncoding() {
    IProject project = debugHandler.getDebugTarget().getProject();
    return project == null ? null : PHPProjectPreferences
        .getTransferEncoding(project);
  }

  /**
   * aSynchronic assigned value
   */
  public boolean assignValue(String var, String value, int depth,
      String[] path) {
    if (!this.isActive()) {
      return false;
    }
    AssignValueRequest request = new AssignValueRequest();
    request.setVar(var);
    request.setValue(value);
    request.setDepth(depth);
    request.setPath(path);
    request.setTransferEncoding(getTransferEncoding());
    try {
      connection.sendRequest(request);
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronic pause Returns true if succeeded pause.
   */
  public String eval(String commandString) {
    if (!this.isActive()) {
      return null;
    }
    EvalRequest request = new EvalRequest();
    request.setCommand(commandString);
    try {
      EvalResponse response = (EvalResponse) connection
          .sendRequest(request);
      String result = null;
      if (response != null) {
        if (response.getStatus() == 0) {
          result = response.getResult();
        } else {
          result = "---ERROR---"; //$NON-NLS-1$
        }
      }
      return result;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return null;
  }

  /**
   * Finish the debugger running.
   */
  public void finish() {
    connection.closeConnection();
  }

  /**
   * Checks if there is a connection.
   */
  public boolean isActive() {
    return connection != null && connection.isConnected();
  }

  public boolean getVariableValue(String var, int depth, String[] path,
      VariableValueResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    GetVariableValueRequest request = new GetVariableValueRequest();
    request.setVar(var);
    request.setDepth(depth);
    request.setPath(path);
    try {
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronic getVariableValue Returns the variable var.
   */
  public byte[] getVariableValue(String var, int depth, String[] path)
      throws IllegalArgumentException {
    if (!this.isActive()) {
      return null;
    }
    GetVariableValueRequest request = new GetVariableValueRequest();
    request.setVar(var);
    request.setDepth(depth);
    request.setPath(path);
    GetVariableValueResponse response = null;
    try {
      response = (GetVariableValueResponse) connection
          .sendRequest(request);
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    if (response == null || response.getStatus() != 0) {
      return null;
    }
    return response.getVarResult();
  }

  public boolean getCallStack(GetCallStackResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    GetCallStackRequest request = new GetCallStackRequest();
    try {
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronic getCallStack Returns the Stack layer with no variables
   * information.
   */
  public PHPstack getCallStack() {
    if (!this.isActive()) {
      return null;
    }

    PHPDebugTarget debugTarget = getDebugHandler().getDebugTarget();
    int suspendCount = debugTarget.getSuspendCount();
    if (suspendCount == previousSuspendCount && cachedStack != null) {
      return cachedStack;
    }

    GetCallStackRequest request = new GetCallStackRequest();
    PHPstack remoteStack = null;
    try {
      GetCallStackResponse response = (GetCallStackResponse) connection
          .sendRequest(request);
      if (response != null) {
        remoteStack = response.getPHPstack();
      }
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    convertToSystem(remoteStack);

    previousSuspendCount = suspendCount;
    cachedStack = remoteStack;

    return remoteStack;
  }

  private void convertToSystem(PHPstack remoteStack) {
    if (remoteStack != null) {
      String currentWorkingDir = getCurrentWorkingDirectory();

      for (int i = 0; i < remoteStack.getSize(); i++) {
        StackLayer layer = remoteStack.getLayer(i);
        layer.setCallerLineNumber(layer.getCallerLineNumber() - 1);
        layer.setCalledLineNumber(layer.getCalledLineNumber() - 1);

        layer.setResolvedCalledFileName(layer.getCalledFileName());
        if (i > 0) {
          String previousScript = remoteStack.getLayer(i - 1)
              .getResolvedCalledFileName();
          String previousScriptDir = "."; //$NON-NLS-1$
          int idx = Math.max(previousScript.lastIndexOf('/'),
              previousScript.lastIndexOf('\\'));
          if (idx != -1) {
            previousScriptDir = previousScript.substring(0, idx);
          }

          IProject project = null;
          IResource resource = ResourcesPlugin.getWorkspace()
              .getRoot().findMember(previousScript);
          if (resource != null) {
            project = resource.getProject();
          } else {
            project = debugHandler.getDebugTarget().getProject();
          }

          if (layer.getCalledFileName() != null
              && currentWorkingDir != null && project != null) {
            Result<?, ?> result = PHPSearchEngine.find(
                layer.getCalledFileName(), currentWorkingDir,
                previousScriptDir, project);
            if (result instanceof ResourceResult) {
              layer.setResolvedCalledFileName(((ResourceResult) result)
                  .getFile().getFullPath().toString());
            } else if (result instanceof IncludedFileResult) {
              layer.setResolvedCalledFileName(((IncludedFileResult) result)
                  .getFile().getAbsolutePath());
            } else if (result instanceof ExternalFileResult) {
              layer.setResolvedCalledFileName(((ExternalFileResult) result)
                  .getFile().getAbsolutePath());
            }
          }
        }
      }
    }
  }

  public boolean getStackVariableValue(int stackDepth, String value,
      int depth, String[] path,
      GetStackVariableValueResponseHandler responseHandler) {
    if (!this.isActive()) {
      return false;
    }
    GetStackVariableValueRequest request = new GetStackVariableValueRequest();
    request.setVar(value);
    request.setDepth(depth);
    request.setLayerDepth(stackDepth);
    request.setPath(path);
    try {
      connection.sendRequest(request, new ThisHandleResponse(
          responseHandler));
      return true;
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    return false;
  }

  /**
   * Synchronic getStackVariableValue Returns the variable value.
   */
  public byte[] getStackVariableValue(int stackDepth, String value,
      int depth, String[] path) {
    if (!this.isActive()) {
      return null;
    }
    GetStackVariableValueRequest request = new GetStackVariableValueRequest();
    request.setVar(value);
    request.setDepth(depth);
    request.setLayerDepth(stackDepth);
    request.setPath(path);
    GetStackVariableValueResponse response = null;
    try {
      response = (GetStackVariableValueResponse) connection
          .sendRequest(request);
    } catch (Exception exc) {
      exc.printStackTrace();
    }
    if (response == null || response.getStatus() != 0) {
      return null;
    }
    return response.getVarResult();
  }

  private String tryGuessMapping(String remoteFile, PHPDebugTarget debugTarget)
      throws ModelException {
    IProject project = debugTarget.getProject();
    if (project == null) {
      String orginalURL = debugTarget.getLaunch().getAttribute(
          IDebugParametersKeys.ORIGINAL_URL);
      String projectName = new Path(orginalURL).segment(0);
      IWorkspace workspace = ResourcesPlugin.getWorkspace();
      project = workspace.getRoot().getProject(projectName);
    }
    if (project != null) {
      String projectName = project.getName();
      IPath remotePath = new Path(remoteFile);
      int size = remotePath.segmentCount();
      for (int j = 0; j < size; j++) {
        String segment = remotePath.segment(j);
        if (segment.equals(projectName)) {
          remotePath = remotePath.removeFirstSegments(j);
          size = remotePath.segmentCount();
          for (int i = 0; i < size; i++) {
            remotePath = remotePath.removeFirstSegments(1);
            if (remotePath.segmentCount() > 0) {
              IResource res = project.getFile(remotePath);
              if (res != null && res.exists()) {
                return project.getFullPath().append(remotePath)
                    .toString();
              }
            }
          }
          List<IPath> includePaths = getIncludePaths(project);
          if (includePaths.size() > 0) {
            return checkIncludePaths(remoteFile, includePaths);
          }
          break;
        }
      }
    }
    return remoteFile;
  }

  private String checkIncludePaths(String remoteFile, List<IPath> includePaths) {
    IPath remotePath = new Path(remoteFile);
    int size = remotePath.segmentCount();
    for (int i = 0; i < size; i++) {
      if (remotePath.segmentCount() > 0) {
        for (IPath includePath : includePaths) {
          File file = includePath.append(remotePath).toFile();
          if (file.exists()) {
            return file.toString();
          }
        }
        remotePath = remotePath.removeFirstSegments(1);
      }
    }
    return remoteFile;
  }

  public List<IPath> getIncludePaths(IProject project) throws ModelException {
    List<IPath> includePaths = resolvedIncludePaths.get(project.getName());
    if (includePaths != null) {
      return includePaths;
    } else {
      includePaths = new ArrayList<IPath>();
    }
    IncludePath[] paths = IncludePathManager.getInstance().getIncludePaths(
        project);
    for (IncludePath includePath : paths) {
      if (includePath.getEntry() instanceof IBuildpathEntry) {
        IBuildpathEntry bPath = (IBuildpathEntry) includePath
            .getEntry();
        if (bPath.getEntryKind() == IBuildpathEntry.BPE_CONTAINER
            && !bPath.getPath().toString()
                .equals("org.eclipse.php.core.LANGUAGE")) { //$NON-NLS-1$
          IBuildpathContainer buildpathContainer = DLTKCore
              .getBuildpathContainer(bPath.getPath(),
                  DLTKCore.create(project));
          if (buildpathContainer != null) {
            final IBuildpathEntry[] buildpathEntries = buildpathContainer
                .getBuildpathEntries();
            for (IBuildpathEntry buildpathEntry : buildpathEntries) {
              IPath localPath = EnvironmentPathUtils
                  .getLocalPath(buildpathEntry.getPath());
              includePaths.add(localPath);
            }
          }
        }
      }
    }
    resolvedIncludePaths.put(project.getName(), includePaths);
    return includePaths;
  }

  // ---------------------------------------------------------------------------
  // ---------------------------------------------------------------------------
  // ---------------------------------------------------------------------------

  private class ThisHandleResponse implements ResponseHandler {
    Object responseHandler;

    public ThisHandleResponse(Object responseHandler) {
      this.responseHandler = responseHandler;
    }

    public void handleResponse(Object request, Object response) {
      boolean success = response != null
          && ((IDebugResponseMessage) response).getStatus() == 0;

      if (request instanceof AddBreakpointRequest) {
        AddBreakpointRequest addBreakpointRequest = (AddBreakpointRequest) request;
        Breakpoint bp = addBreakpointRequest.getBreakpoint();
        String fileName = bp.getFileName();
        int lineNumber = bp.getLineNumber();
        int id = -1;
        if (response != null) {
          id = ((AddBreakpointResponse) response).getBreakpointID();
        }
        ((BreakpointAddedResponseHandler) responseHandler)
            .breakpointAdded(fileName, lineNumber, id, success);

      } else if (request instanceof CancelBreakpointRequest) {
        ((BreakpointRemovedResponseHandler) responseHandler)
            .breakpointRemoved(((CancelBreakpointRequest) request)
                .getBreakpointID(), success);

      } else if (request instanceof CancelAllBreakpointsRequest) {
        ((AllBreakpointRemovedResponseHandler) responseHandler)
            .allBreakpointRemoved(success);

      } else if (request instanceof StartRequest) {
        ((StartResponseHandler) responseHandler).started(success);

      } else if (request instanceof EvalRequest) {
        ((EvalResponseHandler) responseHandler).evaled(
            ((EvalRequest) request).getCommand(),
            success ? ((EvalResponse) response).getResult() : null,
            success);

      } else if (request instanceof StepIntoRequest) {
        ((StepIntoResponseHandler) responseHandler).stepInto(success);

      } else if (request instanceof StepOverRequest) {
        ((StepOverResponseHandler) responseHandler).stepOver(success);

      } else if (request instanceof StepOutRequest) {
        ((StepOutResponseHandler) responseHandler).stepOut(success);

      } else if (request instanceof GoRequest) {
        ((GoResponseHandler) responseHandler).go(success);

      } else if (request instanceof PauseDebuggerRequest) {
        ((PauseResponseHandler) responseHandler).pause(success);

      } else if (request instanceof AssignValueRequest) {
        AssignValueRequest assignValueRequest = (AssignValueRequest) request;
        String var = assignValueRequest.getVar();
        String value = assignValueRequest.getValue();
        int depth = assignValueRequest.getDepth();
        String[] path = assignValueRequest.getPath();
        ((AssignValueResponseHandler) responseHandler).valueAssigned(
            var, value, depth, path, success);

      } else if (request instanceof GetVariableValueRequest) {
        GetVariableValueRequest getVariableValueRequest = (GetVariableValueRequest) request;
        String value = getVariableValueRequest.getVar();
        int depth = getVariableValueRequest.getDepth();
        String[] path = getVariableValueRequest.getPath();

        String result = null;
        if (response != null) {
          try {
            result = new String(
                ((GetVariableValueResponse) response)
                    .getVarResult(),
                ((IDebugMessage) response)
                    .getTransferEncoding());
          } catch (UnsupportedEncodingException e) {
          }
        }
        ((VariableValueResponseHandler) responseHandler).variableValue(
            value, depth, path, result, success);

      } else if (request instanceof GetCallStackRequest) {
        PHPstack remoteStack = null;
        if (response != null) {
          remoteStack = ((GetCallStackResponse) response)
              .getPHPstack();
        }
        convertToSystem(remoteStack);
        ((GetCallStackResponseHandler) responseHandler).callStack(
            remoteStack, success);

      } else if (request instanceof GetStackVariableValueRequest) {
        GetStackVariableValueRequest getStackVariableValueRequest = (GetStackVariableValueRequest) request;

        int stackDepth = getStackVariableValueRequest.getLayerDepth();
        String value = getStackVariableValueRequest.getVar();
        int depth = getStackVariableValueRequest.getDepth();
        String[] path = getStackVariableValueRequest.getPath();

        String result = null;
        if (response != null) {
          try {
            result = new String(
                ((GetStackVariableValueResponse) response)
                    .getVarResult(),
                ((IDebugMessage) response)
                    .getTransferEncoding());
          } catch (UnsupportedEncodingException e) {
          }
        }
        ((GetStackVariableValueResponseHandler) responseHandler)
            .stackVariableValue(stackDepth, value, depth, path,
                result, success);
      }
    }
  }
}
TOP

Related Classes of org.eclipse.php.internal.debug.core.zend.debugger.RemoteDebugger

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.