/*******************************************************************************
* 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.common.base.Optional;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;
import org.eclipse.che.api.debug.shared.model.Location;
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.editor.EditorAgent;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.text.TextPosition;
import org.eclipse.che.ide.api.editor.texteditor.TextEditor;
import org.eclipse.che.ide.api.resources.File;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.api.resources.Resource;
import org.eclipse.che.ide.api.resources.VirtualFile;
/**
* @author Anatoliy Bazko
*/
public class BasicActiveFileHandler implements ActiveFileHandler {
private final EditorAgent editorAgent;
private final AppContext appContext;
@Inject
public BasicActiveFileHandler(EditorAgent editorAgent, AppContext appContext) {
this.editorAgent = editorAgent;
this.appContext = appContext;
}
/**
* Tries to open file in the editor.
* To perform the operation the following sequence of methods invocation are processed:
*
* {@link BasicActiveFileHandler#tryFindFileInProject(Location, AsyncCallback)}
* {@link BasicActiveFileHandler#tryFindFileInWorkspace(Location, AsyncCallback)}
* {@link BasicActiveFileHandler#trySearchSource(Location, AsyncCallback)}
*
* @see ActiveFileHandler#openFile(Location, AsyncCallback)
*/
@Override
public void openFile(final Location location, final AsyncCallback<VirtualFile> callback) {
tryFindFileInProject(location, new AsyncCallback<VirtualFile>() {
@Override
public void onSuccess(VirtualFile virtualFile) {
callback.onSuccess(virtualFile);
}
@Override
public void onFailure(Throwable caught) {
tryFindFileInWorkspace(location, new AsyncCallback<VirtualFile>() {
@Override
public void onSuccess(VirtualFile virtualFile) {
callback.onSuccess(virtualFile);
}
@Override
public void onFailure(Throwable caught) {
trySearchSource(location, new AsyncCallback<VirtualFile>() {
@Override
public void onFailure(Throwable caught) {
callback.onFailure(caught);
}
@Override
public void onSuccess(VirtualFile result) {
callback.onSuccess(result);
}
});
}
});
}
});
}
protected void tryFindFileInProject(final Location location,
final AsyncCallback<VirtualFile> callback) {
Resource resource = appContext.getResource();
if (resource == null) {
callback.onFailure(new IllegalStateException("Resource is undefined"));
return;
}
Optional<Project> project = resource.getRelatedProject();
if (!project.isPresent()) {
callback.onFailure(new IllegalStateException("Project is undefined"));
return;
}
project.get().getFile(location.getTarget()).then(new Operation<Optional<File>>() {
@Override
public void apply(Optional<File> file) throws OperationException {
if (file.isPresent()) {
openFileAndScrollToLine(file.get(), location.getLineNumber(), callback);
} else {
callback.onFailure(new IllegalArgumentException(location.getTarget() + " not found."));
}
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError error) throws OperationException {
callback.onFailure(new IllegalArgumentException(location.getTarget() + " not found."));
}
});
}
protected void tryFindFileInWorkspace(final Location location,
final AsyncCallback<VirtualFile> callback) {
try {
appContext.getWorkspaceRoot().getFile(location.getTarget()).then(new Operation<Optional<File>>() {
@Override
public void apply(Optional<File> file) throws OperationException {
if (file.isPresent()) {
openFileAndScrollToLine(file.get(), location.getLineNumber(), callback);
} else {
callback.onFailure(new IllegalArgumentException(location.getTarget() + " not found."));
}
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
callback.onFailure(new IllegalArgumentException(location.getTarget() + " not found."));
}
});
} catch (IllegalStateException ignored) {
callback.onFailure(new IllegalArgumentException(location.getTarget() + " not found."));
}
}
protected void trySearchSource(final Location location, final AsyncCallback<VirtualFile> callback) {
appContext.getWorkspaceRoot().search(location.getTarget(), "").then(new Operation<Resource[]>() {
@Override
public void apply(Resource[] resources) throws OperationException {
if (resources.length == 0) {
callback.onFailure(new IllegalArgumentException(location.getTarget() + " not found."));
return;
}
appContext.getWorkspaceRoot().getFile(resources[0].getLocation()).then(new Operation<Optional<File>>() {
@Override
public void apply(Optional<File> file) throws OperationException {
if (file.isPresent()) {
openFileAndScrollToLine(file.get(), location.getLineNumber(), callback);
} else {
callback.onFailure(new IllegalArgumentException(location.getTarget() + " not found."));
}
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
callback.onFailure(new IllegalArgumentException(location.getTarget() + " not found."));
}
});
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
callback.onFailure(new IllegalArgumentException(location.getTarget() + " not found."));
}
});
}
protected void openFileAndScrollToLine(final VirtualFile virtualFile,
final int scrollToLine,
final AsyncCallback<VirtualFile> callback) {
editorAgent.openEditor(virtualFile, new EditorAgent.OpenEditorCallback() {
@Override
public void onEditorOpened(EditorPartPresenter editor) {
new Timer() {
@Override
public void run() {
scrollToLine(editorAgent.getActiveEditor(), scrollToLine);
callback.onSuccess(virtualFile);
}
}.schedule(300);
}
@Override
public void onEditorActivated(EditorPartPresenter editor) {
new Timer() {
@Override
public void run() {
scrollToLine(editorAgent.getActiveEditor(), scrollToLine);
callback.onSuccess(virtualFile);
}
}.schedule(300);
}
@Override
public void onInitializationFailed() {
callback.onFailure(new IllegalStateException("Initialization " + virtualFile.getName() + " in the editor failed"));
}
});
}
protected void scrollToLine(EditorPartPresenter editor, int lineNumber) {
if (editor instanceof TextEditor) {
TextEditor textEditor = (TextEditor)editor;
Document document = textEditor.getDocument();
if (document != null) {
TextPosition newPosition = new TextPosition(lineNumber, 0);
document.setCursorPosition(newPosition);
}
}
}
}