/*******************************************************************************
* 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.ide.ext.git.client.branch;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.core.ErrorCodes;
import org.eclipse.che.api.git.shared.Branch;
import org.eclipse.che.api.git.shared.CheckoutRequest;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.dialogs.ConfirmCallback;
import org.eclipse.che.ide.api.dialogs.DialogFactory;
import org.eclipse.che.ide.api.dialogs.InputCallback;
import org.eclipse.che.ide.api.git.GitServiceClient;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.ext.git.client.GitLocalizationConstant;
import org.eclipse.che.ide.ext.git.client.outputconsole.GitOutputConsole;
import org.eclipse.che.ide.ext.git.client.outputconsole.GitOutputConsoleFactory;
import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter;
import javax.validation.constraints.NotNull;
import java.util.List;
import static org.eclipse.che.api.git.shared.BranchListMode.LIST_ALL;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
import static org.eclipse.che.ide.util.ExceptionUtils.getErrorCode;
/**
* Presenter for displaying and work with branches.
*
* @author Ann Zhuleva
* @author Vlad Zhukovskyi
*/
@Singleton
public class BranchPresenter implements BranchView.ActionDelegate {
public static final String BRANCH_RENAME_COMMAND_NAME = "Git rename branch";
public static final String BRANCH_DELETE_COMMAND_NAME = "Git delete branch";
public static final String BRANCH_CHECKOUT_COMMAND_NAME = "Git checkout branch";
public static final String BRANCH_CREATE_COMMAND_NAME = "Git create branch";
public static final String BRANCH_LIST_COMMAND_NAME = "Git list of branches";
private final DtoFactory dtoFactory;
private final BranchView view;
private final GitOutputConsoleFactory gitOutputConsoleFactory;
private final ProcessesPanelPresenter consolesPanelPresenter;
private final DialogFactory dialogFactory;
private final GitServiceClient service;
private final GitLocalizationConstant constant;
private final AppContext appContext;
private final NotificationManager notificationManager;
private Branch selectedBranch;
private Project project;
/** Create presenter. */
@Inject
public BranchPresenter(BranchView view,
DtoFactory dtoFactory,
GitServiceClient service,
GitLocalizationConstant constant,
AppContext appContext,
NotificationManager notificationManager,
GitOutputConsoleFactory gitOutputConsoleFactory,
ProcessesPanelPresenter processesPanelPresenter,
DialogFactory dialogFactory) {
this.view = view;
this.dtoFactory = dtoFactory;
this.gitOutputConsoleFactory = gitOutputConsoleFactory;
this.consolesPanelPresenter = processesPanelPresenter;
this.dialogFactory = dialogFactory;
this.view.setDelegate(this);
this.service = service;
this.constant = constant;
this.appContext = appContext;
this.notificationManager = notificationManager;
}
/** Open dialog if closed and shows branches. */
public void showBranches(Project project) {
this.project = project;
getBranches();
}
/** {@inheritDoc} */
@Override
public void onCloseClicked() {
view.close();
}
/** {@inheritDoc} */
@Override
public void onRenameClicked() {
if (selectedBranch.isRemote()) {
dialogFactory.createConfirmDialog(constant.branchConfirmRenameTitle(),
constant.branchConfirmRenameMessage(),
new ConfirmCallback() {
@Override
public void accepted() {
renameBranch();
}
},
null).show();
} else {
renameBranch();
}
}
private void renameBranch() {
final String selectedBranchName = getSelectedBranchName();
dialogFactory.createInputDialog(constant.branchTitleRename(),
constant.branchTypeRename(),
selectedBranchName,
0,
selectedBranchName.length(),
new InputCallback() {
@Override
public void accepted(String newBranchName) {
renameBranch(newBranchName);
}
},
null).show();
}
private void renameBranch(String newName) {
service.branchRename(appContext.getDevMachine(), project.getLocation(), selectedBranch.getDisplayName(), newName)
.then(new Operation<Void>() {
@Override
public void apply(Void ignored) throws OperationException {
getBranches();
}
})
.catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError error) throws OperationException {
handleError(error.getCause(), BRANCH_RENAME_COMMAND_NAME);
getBranches();//rename of remote branch occurs in three stages, so needs update list of branches on view
}
});
}
/** @return name of branch, e.g. 'origin/master' -> 'master' */
private String getSelectedBranchName() {
String selectedBranchName = selectedBranch.getDisplayName();
String[] tokens = selectedBranchName.split("/");
return tokens.length > 0 ? tokens[tokens.length - 1] : selectedBranchName;
}
/** {@inheritDoc} */
@Override
public void onDeleteClicked() {
service.branchDelete(appContext.getDevMachine(), project.getLocation(), selectedBranch.getName(), true).then(new Operation<Void>() {
@Override
public void apply(Void ignored) throws OperationException {
getBranches();
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError error) throws OperationException {
handleError(error.getCause(), BRANCH_DELETE_COMMAND_NAME);
}
});
}
/** {@inheritDoc} */
@Override
public void onCheckoutClicked() {
final CheckoutRequest checkoutRequest = dtoFactory.createDto(CheckoutRequest.class);
if (selectedBranch.isRemote()) {
checkoutRequest.setTrackBranch(selectedBranch.getDisplayName());
} else {
checkoutRequest.setName(selectedBranch.getDisplayName());
}
service.checkout(appContext.getDevMachine(), project.getLocation(), checkoutRequest).then(new Operation<Void>() {
@Override
public void apply(Void ignored) throws OperationException {
getBranches();
project.synchronize();
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError error) throws OperationException {
handleError(error.getCause(), BRANCH_CHECKOUT_COMMAND_NAME);
}
});
}
/** Get the list of branches. */
private void getBranches() {
service.branchList(appContext.getDevMachine(), project.getLocation(), LIST_ALL).then(new Operation<List<Branch>>() {
@Override
public void apply(List<Branch> branches) throws OperationException {
if (branches.isEmpty()) {
dialogFactory.createMessageDialog(constant.branchTitle(),
constant.initCommitWasNotPerformed(),
null).show();
} else {
view.setBranches(branches);
view.showDialogIfClosed();
}
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError error) throws OperationException {
handleError(error.getCause(), BRANCH_LIST_COMMAND_NAME);
}
});
}
/** {@inheritDoc} */
@Override
public void onCreateClicked() {
dialogFactory.createInputDialog(constant.branchCreateNew(), constant.branchTypeNew(), new InputCallback() {
@Override
public void accepted(String value) {
if (value.isEmpty()) {
return;
}
service.branchCreate(appContext.getDevMachine(), project.getLocation(), value, null).then(new Operation<Branch>() {
@Override
public void apply(Branch branch) throws OperationException {
getBranches();
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError error) throws OperationException {
handleError(error.getCause(), BRANCH_CREATE_COMMAND_NAME);
}
});
}
}, null).show();
}
@Override
public void onBranchUnselected() {
selectedBranch = null;
view.setEnableCheckoutButton(false);
view.setEnableRenameButton(false);
view.setEnableDeleteButton(false);
}
/** {@inheritDoc} */
@Override
public void onBranchSelected(@NotNull Branch branch) {
selectedBranch = branch;
boolean isActive = selectedBranch.isActive();
view.setEnableCheckoutButton(!isActive);
view.setEnableDeleteButton(!isActive);
view.setEnableRenameButton(true);
}
/**
* Handler some action whether some exception happened.
*
* @param exception
* exception what happened
* @param commandName
* name of the executed command
*/
void handleError(@NotNull Throwable exception, String commandName) {
if (getErrorCode(exception) == ErrorCodes.UNABLE_GET_PRIVATE_SSH_KEY) {
dialogFactory.createMessageDialog(commandName, constant.messagesUnableGetSshKey(), null).show();
return;
}
String errorMessage = exception.getMessage();
if (errorMessage == null) {
switch (commandName) {
case BRANCH_CREATE_COMMAND_NAME:
errorMessage = constant.branchCreateFailed();
break;
case BRANCH_DELETE_COMMAND_NAME:
errorMessage = constant.branchDeleteFailed();
break;
case BRANCH_LIST_COMMAND_NAME:
errorMessage = constant.branchesListFailed();
break;
case BRANCH_RENAME_COMMAND_NAME:
errorMessage = constant.branchRenameFailed();
break;
case BRANCH_CHECKOUT_COMMAND_NAME:
errorMessage = constant.branchCheckoutFailed();
break;
}
}
GitOutputConsole console = gitOutputConsoleFactory.create(commandName);
printGitMessage(errorMessage, console);
consolesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), console);
notificationManager.notify(errorMessage, FAIL, FLOAT_MODE);
}
private void printGitMessage(String messageText, GitOutputConsole console) {
console.print("");
String[] lines = messageText.split("\n");
for (String line : lines) {
console.printError(line);
}
}
}