/*******************************************************************************
* 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.java.client.organizeimports;
import com.google.common.base.Optional;
import com.google.inject.Inject;
import com.google.inject.Singleton;
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.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.texteditor.HandlesUndoRedo;
import org.eclipse.che.ide.api.editor.texteditor.TextEditor;
import org.eclipse.che.ide.api.editor.texteditor.UndoableEditor;
import org.eclipse.che.ide.api.event.ng.ClientServerEventService;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.resources.Container;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.api.resources.Resource;
import org.eclipse.che.ide.api.resources.VirtualFile;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.ext.java.client.JavaLocalizationConstant;
import org.eclipse.che.ide.ext.java.client.editor.JavaCodeAssistClient;
import org.eclipse.che.ide.ext.java.client.resource.SourceFolderMarker;
import org.eclipse.che.ide.ext.java.client.util.JavaUtil;
import org.eclipse.che.ide.ext.java.shared.dto.Change;
import org.eclipse.che.ide.ext.java.shared.dto.ConflictImportDTO;
import org.eclipse.che.ide.ext.java.shared.dto.OrganizeImportResult;
import org.eclipse.che.ide.util.loging.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
/**
* The class that manages conflicts with organize imports if if they occur.
*
* @author Valeriy Svydenko
*/
@Singleton
public class OrganizeImportsPresenter implements OrganizeImportsView.ActionDelegate {
private final OrganizeImportsView view;
private final JavaCodeAssistClient javaCodeAssistClient;
private final DtoFactory dtoFactory;
private final JavaLocalizationConstant locale;
private final NotificationManager notificationManager;
private final ClientServerEventService clientServerEventService;
private int page;
private List<ConflictImportDTO> choices;
private Map<Integer, String> selected;
private VirtualFile file;
private Document document;
private EditorPartPresenter editor;
@Inject
public OrganizeImportsPresenter(OrganizeImportsView view,
JavaCodeAssistClient javaCodeAssistClient,
DtoFactory dtoFactory,
JavaLocalizationConstant locale,
NotificationManager notificationManager,
ClientServerEventService clientServerEventService) {
this.view = view;
this.javaCodeAssistClient = javaCodeAssistClient;
this.clientServerEventService = clientServerEventService;
this.view.setDelegate(this);
this.dtoFactory = dtoFactory;
this.locale = locale;
this.notificationManager = notificationManager;
}
/**
* Make Organize imports operation. If the operation doesn't have conflicts all imports will be applied
* otherwise a special window will be showed for resolving conflicts.
*
* @param editor
* current active editor
*/
public void organizeImports(EditorPartPresenter editor) {
this.editor = editor;
this.document = ((TextEditor)editor).getDocument();
this.file = editor.getEditorInput().getFile();
if (file instanceof Resource) {
final Optional<Project> project = ((Resource)file).getRelatedProject();
final Optional<Resource> srcFolder = ((Resource)file).getParentWithMarker(SourceFolderMarker.ID);
if (!srcFolder.isPresent()) {
return;
}
final String fqn = JavaUtil.resolveFQN((Container)srcFolder.get(), (Resource)file);
clientServerEventService.sendFileTrackingSuspendEvent().then(arg -> {
doOrganizeImports(fqn, project);
});
}
}
private Promise<OrganizeImportResult> doOrganizeImports(String fqn, Optional<Project> project) {
return javaCodeAssistClient.organizeImports(project.get().getLocation().toString(), fqn)
.then(result -> {
if (result.getConflicts() != null && !result.getConflicts().isEmpty()) {
show(result.getConflicts());
} else {
applyChanges(document, result.getChanges());
}
clientServerEventService.sendFileTrackingResumeEvent();
})
.catchError(error -> {
String title = locale.failedToProcessOrganizeImports();
String message = error.getMessage();
notificationManager.notify(title, message, FAIL, FLOAT_MODE);
clientServerEventService.sendFileTrackingResumeEvent();
});
}
/** {@inheritDoc} */
@Override
public void onNextButtonClicked() {
selected.put(page++, view.getSelectedImport());
if (!selected.containsKey(page)) {
String newSelection = choices.get(page).getTypeMatches().get(0);
selected.put(page, newSelection);
}
view.setSelectedImport(selected.get(page));
view.changePage(choices.get(page));
updateButtonsState();
}
/** {@inheritDoc} */
@Override
public void onBackButtonClicked() {
selected.put(page--, view.getSelectedImport());
view.setSelectedImport(selected.get(page));
view.changePage(choices.get(page));
updateButtonsState();
}
/** {@inheritDoc} */
@Override
public void onFinishButtonClicked() {
selected.put(page, view.getSelectedImport());
ConflictImportDTO result = dtoFactory.createDto(ConflictImportDTO.class).withTypeMatches(new ArrayList<>(selected.values()));
if (file instanceof Resource) {
final Optional<Project> project = ((Resource)file).getRelatedProject();
javaCodeAssistClient.applyChosenImports(project.get().getLocation().toString(), JavaUtil.resolveFQN(file), result)
.then(new Operation<List<Change>>() {
@Override
public void apply(List<Change> result) throws OperationException {
applyChanges(((TextEditor) editor).getDocument(), result);
view.hide();
((TextEditor)editor).setFocus();
}
})
.catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
String title = locale.failedToProcessOrganizeImports();
String message = arg.getMessage();
notificationManager.notify(title, message, FAIL, FLOAT_MODE);
}
});
}
}
/** {@inheritDoc} */
@Override
public void onCancelButtonClicked() {
((TextEditor)editor).setFocus();
}
/** Show Organize Imports panel with the special information. */
private void show(List<ConflictImportDTO> choices) {
if (choices == null || choices.isEmpty()) {
return;
}
this.choices = choices;
page = 0;
selected = new HashMap<>(choices.size());
String selection = choices.get(0).getTypeMatches().get(0);
selected.put(page, selection);
view.setSelectedImport(selection);
updateButtonsState();
view.show(choices.get(page));
}
/**
* Update content of the file.
*
* @param document
* current document
* @param changes
*
*/
private void applyChanges(Document document, List<Change> changes) {
HandlesUndoRedo undoRedo = null;
if (editor instanceof UndoableEditor) {
undoRedo = ((UndoableEditor)editor).getUndoRedo();
}
try {
if (undoRedo != null) {
undoRedo.beginCompoundChange();
}
for (Change change : changes) {
document.replace(change.getOffset(), change.getLength(), change.getText());
}
} catch (final Exception e) {
Log.error(getClass(), e);
} finally {
if (undoRedo != null) {
undoRedo.endCompoundChange();
}
}
}
private void updateButtonsState() {
view.setEnableBackButton(!isFirstPage());
view.setEnableNextButton(!isLastPage());
view.setEnableFinishButton(selected.size() == choices.size());
}
private boolean isFirstPage() {
return page == 0;
}
private boolean isLastPage() {
return (choices.size() - 1) == page;
}
}