/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.debugger.ide.debug;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.debug.shared.model.Location;
import org.eclipse.che.api.debug.shared.model.MutableVariable;
import org.eclipse.che.api.debug.shared.model.SimpleValue;
import org.eclipse.che.api.debug.shared.model.StackFrameDump;
import org.eclipse.che.api.debug.shared.model.Variable;
import org.eclipse.che.api.debug.shared.model.impl.LocationImpl;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.api.debug.Breakpoint;
import org.eclipse.che.ide.api.debug.BreakpointManager;
import org.eclipse.che.ide.api.debug.BreakpointManagerObserver;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.notification.StatusNotification;
import org.eclipse.che.ide.api.parts.PartStackType;
import org.eclipse.che.ide.api.parts.WorkspaceAgent;
import org.eclipse.che.ide.api.parts.base.BasePresenter;
import org.eclipse.che.ide.debug.Debugger;
import org.eclipse.che.ide.debug.DebuggerDescriptor;
import org.eclipse.che.ide.debug.DebuggerManager;
import org.eclipse.che.ide.debug.DebuggerManagerObserver;
import org.eclipse.che.ide.ui.toolbar.ToolbarPresenter;
import org.eclipse.che.ide.util.loging.Log;
import org.eclipse.che.ide.workspace.perspectives.project.ProjectPerspective;
import org.eclipse.che.plugin.debugger.ide.DebuggerLocalizationConstant;
import org.eclipse.che.plugin.debugger.ide.DebuggerResources;
import org.vectomatic.dom.svg.ui.SVGResource;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.NOT_EMERGE_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.PROGRESS;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.SUCCESS;
/**
* The presenter provides debugging applications.
*
* @author Vitaly Parfonov
* @author Artem Zatsarynnyi
* @author Valeriy Svydenko
* @author Dmitry Shnurenko
* @author Anatoliy Bazko
* @author Mykola Morhun
*/
@Singleton
public class DebuggerPresenter extends BasePresenter implements DebuggerView.ActionDelegate,
DebuggerManagerObserver,
BreakpointManagerObserver {
private static final String TITLE = "Debug";
private final DebuggerResources debuggerResources;
private final ToolbarPresenter debuggerToolbar;
private final BreakpointManager breakpointManager;
private final NotificationManager notificationManager;
private final DebuggerLocalizationConstant constant;
private final DebuggerView view;
private final DebuggerManager debuggerManager;
private final WorkspaceAgent workspaceAgent;
private MutableVariable selectedVariable;
private List<Variable> variables;
private DebuggerDescriptor debuggerDescriptor;
private Location executionPoint;
@Inject
public DebuggerPresenter(final DebuggerView view,
final DebuggerLocalizationConstant constant,
final BreakpointManager breakpointManager,
final NotificationManager notificationManager,
final DebuggerResources debuggerResources,
final @DebuggerToolbar ToolbarPresenter debuggerToolbar,
final DebuggerManager debuggerManager,
final WorkspaceAgent workspaceAgent) {
this.view = view;
this.debuggerResources = debuggerResources;
this.debuggerToolbar = debuggerToolbar;
this.debuggerManager = debuggerManager;
this.workspaceAgent = workspaceAgent;
this.view.setDelegate(this);
this.view.setTitle(TITLE);
this.constant = constant;
this.breakpointManager = breakpointManager;
this.variables = new ArrayList<>();
this.notificationManager = notificationManager;
this.addRule(ProjectPerspective.PROJECT_PERSPECTIVE_ID);
this.debuggerManager.addObserver(this);
this.breakpointManager.addObserver(this);
if (!breakpointManager.getBreakpointList().isEmpty()) {
updateBreakpoints();
}
}
@Override
public String getTitle() {
return TITLE;
}
@Override
public IsWidget getView() {
return view;
}
@Override
public SVGResource getTitleImage() {
return debuggerResources.debug();
}
@Override
public String getTitleToolTip() {
return TITLE;
}
@Override
public void go(AcceptsOneWidget container) {
view.setBreakpoints(breakpointManager.getBreakpointList());
view.setVariables(variables);
container.setWidget(view);
debuggerToolbar.go(view.getDebuggerToolbarPanel());
}
@Override
public void onExpandVariablesTree() {
List<? extends Variable> rootVariables = selectedVariable.getVariables();
if (rootVariables.isEmpty()) {
Debugger debugger = debuggerManager.getActiveDebugger();
if (debugger != null) {
Promise<SimpleValue> promise = debugger.getValue(selectedVariable);
promise.then(new Operation<SimpleValue>() {
@Override
public void apply(SimpleValue arg) throws OperationException {
selectedVariable.setValue(arg.getValue());
view.setVariablesIntoSelectedVariable(arg.getVariables());
view.updateSelectedVariable();
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
notificationManager.notify(constant.failedToGetVariableValueTitle(), arg.getMessage(), FAIL, FLOAT_MODE);
}
});
}
}
}
@Override
public void onSelectedVariableElement(@NotNull MutableVariable variable) {
this.selectedVariable = variable;
}
public void showDebuggerPanel() {
partStack.setActivePart(this);
}
public void hideDebuggerPanel() {
partStack.minimize();
}
public boolean isDebuggerPanelOpened() {
return partStack.getActivePart() == this;
}
public boolean isDebuggerPanelPresent() {
return partStack != null && partStack.containsPart(this);
}
private void resetStates() {
variables.clear();
view.setVariables(variables);
view.setVMName("");
view.setExecutionPoint(null);
selectedVariable = null;
executionPoint = null;
}
public void showAndUpdateView() {
if (debuggerDescriptor == null) {
view.setVMName("");
} else {
view.setVMName(debuggerDescriptor.getInfo());
}
if (executionPoint != null) {
view.setExecutionPoint(executionPoint);
}
view.setBreakpoints(breakpointManager.getBreakpointList());
updateStackFrameDump();
showView();
}
protected void updateBreakpoints() {
view.setBreakpoints(breakpointManager.getBreakpointList());
if (!isDebuggerPanelPresent()) {
showView();
showDebuggerPanel();
}
}
public void showView() {
if (partStack == null || !partStack.containsPart(this)) {
workspaceAgent.openPart(this, PartStackType.INFORMATION);
}
}
private void updateStackFrameDump() {
Debugger debugger = debuggerManager.getActiveDebugger();
if (debugger != null && executionPoint != null) {
Promise<StackFrameDump> promise = debugger.dumpStackFrame();
promise.then(new Operation<StackFrameDump>() {
@Override
public void apply(StackFrameDump arg) throws OperationException {
variables = new ArrayList<>();
variables.addAll(arg.getFields());
variables.addAll(arg.getVariables());
view.setVariables(variables);
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
Log.error(DebuggerPresenter.class, arg.getCause());
}
});
}
}
/**
* @return selected variable in variables tree or null if no selected variables
*/
public Variable getSelectedVariable() {
return selectedVariable;
}
public ToolbarPresenter getDebuggerToolbar() {
return debuggerToolbar;
}
@Override
public void onDebuggerAttached(final DebuggerDescriptor debuggerDescriptor, Promise<Void> connect) {
final String address = debuggerDescriptor.getAddress();
final StatusNotification notification = notificationManager.notify(constant.debuggerConnectingTitle(address), PROGRESS, FLOAT_MODE);
connect.then(new Operation<Void>() {
@Override
public void apply(Void arg) throws OperationException {
DebuggerPresenter.this.debuggerDescriptor = debuggerDescriptor;
notification.setTitle(constant.debuggerConnectedTitle());
notification.setContent(constant.debuggerConnectedDescription(address));
notification.setStatus(SUCCESS);
showAndUpdateView();
showDebuggerPanel();
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
notification.setTitle(constant.failedToConnectToRemoteDebuggerDescription(address, arg.getMessage()));
notification.setStatus(FAIL);
notification.setDisplayMode(FLOAT_MODE);
}
});
}
@Override
public void onDebuggerDisconnected() {
String address = debuggerDescriptor != null ? debuggerDescriptor.getAddress() : "";
String content = constant.debuggerDisconnectedDescription(address);
notificationManager.notify(constant.debuggerDisconnectedTitle(), content, SUCCESS, NOT_EMERGE_MODE);
executionPoint = null;
debuggerDescriptor = null;
resetStates();
showAndUpdateView();
}
@Override
public void onBreakpointAdded(Breakpoint breakpoint) {
updateBreakpoints();
}
@Override
public void onBreakpointActivated(String filePath, int lineNumber) { }
@Override
public void onBreakpointDeleted(Breakpoint breakpoint) {
updateBreakpoints();
}
@Override
public void onAllBreakpointsDeleted() {
updateBreakpoints();
}
@Override
public void onPreStepInto() {
resetStates();
}
@Override
public void onPreStepOut() {
resetStates();
}
@Override
public void onPreStepOver() {
resetStates();
}
@Override
public void onPreResume() {
resetStates();
}
@Override
public void onBreakpointStopped(String filePath, String className, int lineNumber) {
executionPoint = new LocationImpl(className, lineNumber);
showAndUpdateView();
}
@Override
public void onValueChanged(List<String> path, String newValue) {
updateStackFrameDump();
}
@Override
public void onActiveDebuggerChanged(@Nullable Debugger activeDebugger) { }
}