blob: 6a5227d9f3b79c9470203bf0a26b77773d070297 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2015, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.ui.sourceediting.actions;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
import org.eclipse.statet.internal.ltk.ui.EditingMessages;
import org.eclipse.statet.ltk.model.core.ElementSet;
import org.eclipse.statet.ltk.model.core.elements.ISourceStructElement;
import org.eclipse.statet.ltk.model.core.elements.ISourceUnit;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditorAssociated;
import org.eclipse.statet.ltk.ui.util.LTKSelectionUtils;
public abstract class AbstractSourceDocumentHandler<TSourceUnit extends ISourceUnit>
extends AbstractHandler {
protected class ExecData {
private final IWorkbenchPart activePart;
private final ISourceEditor sourceEditor;
private final ImList<? extends TSourceUnit> sourceUnits;
final ITextSelection textSelection;
final ElementSet elementSelection;
public ExecData(final IWorkbenchPart activePart, final ISourceEditor sourceEditor,
final ImList<? extends TSourceUnit> sourceUnits,
final ITextSelection textSelection, final ElementSet elementSet) {
this.activePart= activePart;
this.sourceEditor= sourceEditor;
this.sourceUnits= sourceUnits;
this.textSelection= textSelection;
this.elementSelection= elementSet;
}
public IWorkbenchPart getActivePart() {
return this.activePart;
}
public ISourceEditor getSourceEditor() {
return this.sourceEditor;
}
public ImList<? extends TSourceUnit> getSourceUnits() {
return this.sourceUnits;
}
public ITextSelection getTextSelection() {
return this.textSelection;
}
public ElementSet getElementSelection() {
return this.elementSelection;
}
}
protected static final byte DIRECT= 0;
protected static final byte SHOW_BUSY= 1;
protected static final byte BACKGROUND= 2;
public AbstractSourceDocumentHandler() {
}
protected abstract String getTaskLabel();
protected abstract boolean isEditTask();
@Override
public void setEnabled(final Object evaluationContext) {
final IWorkbenchPart activePart= WorkbenchUIUtils.getActivePart(evaluationContext);
final ISelection selection= WorkbenchUIUtils.getCurrentSelection(evaluationContext);
final ISourceEditor sourceEditor= getSourceEditor(activePart);
if (sourceEditor != null && selection instanceof ITextSelection) {
final ISourceUnit sourceUnit= sourceEditor.getSourceUnit();
setBaseEnabled(sourceUnit != null &&
(!isEditTask() || sourceEditor.isEditable(false) ));
return;
}
if (selection instanceof IStructuredSelection) {
final ISourceStructElement[] selectedElements= LTKSelectionUtils
.getSelectedSourceStructElements((IStructuredSelection) selection);
setBaseEnabled(selectedElements != null && selectedElements.length > 0
&& checkSourceUnits(selectedElements) );
return;
}
setBaseEnabled(false);
}
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
final IWorkbenchPart activePart= WorkbenchUIUtils.getActivePart(event.getApplicationContext());
final ISelection selection= WorkbenchUIUtils.getCurrentSelection(event.getApplicationContext());
final ISourceEditor sourceEditor= getSourceEditor(activePart);
try {
final IProgressMonitor monitor= new NullProgressMonitor();
if (sourceEditor != null && selection instanceof ITextSelection) {
final ISourceUnit sourceUnit= sourceEditor.getSourceUnit();
if (sourceUnit != null && isSourceUnitSupported(sourceUnit)) {
if (!isEditTask() || sourceEditor.isEditable(true)) {
final ExecData data= createExecData(event, activePart, sourceEditor,
ImCollections.newList((TSourceUnit) sourceUnit),
(ITextSelection) selection, null, monitor );
if (data != null) {
execute(data);
return null;
}
}
}
return null;
}
if (selection instanceof IStructuredSelection) {
final ISourceStructElement[] selectedElements= LTKSelectionUtils
.getSelectedSourceStructElements((IStructuredSelection) selection);
if (selectedElements != null && selectedElements.length > 0) {
final ImList<TSourceUnit> sourceUnits= createSourceUnits(selectedElements, monitor);
if (sourceUnits != null) {
final ExecData data= createExecData(event, activePart, sourceEditor,
sourceUnits, null, selectedElements, monitor );
if (data != null) {
execute(data);
return null;
}
}
}
return null;
}
return null;
}
catch (final Exception e) {
throw new ExecutionException(
NLS.bind(EditingMessages.GenericAction_error_message, getTaskLabel()),
e );
}
}
private ISourceEditor getSourceEditor(final IWorkbenchPart part) {
if (part instanceof ISourceEditor) {
return (ISourceEditor) part;
}
{ final ISourceEditor editor= part.getAdapter(ISourceEditor.class);
if (editor != null) {
return editor;
}
}
{ final ISourceEditorAssociated editorAssociated= part
.getAdapter(ISourceEditorAssociated.class);
if (editorAssociated != null) {
return editorAssociated.getSourceEditor();
}
}
return null;
}
private boolean checkSourceUnits(final ISourceStructElement[] selectedElements) {
ISourceUnit lastSourceUnit= null;
List<ISourceUnit> sourceUnits= null;
for (int i= 0; i < selectedElements.length; i++) {
final ISourceUnit sourceUnit= selectedElements[i].getSourceUnit();
if (sourceUnit == null) {
return false;
}
if (sourceUnit.equals(lastSourceUnit)
|| (sourceUnits != null && sourceUnits.contains(sourceUnit)) ) {
continue;
}
if (!isSourceUnitSupported(sourceUnit)
|| (isEditTask() && !sourceUnit.checkState(false, null)) ) {
return false;
}
if (lastSourceUnit == null) {
lastSourceUnit= sourceUnit;
}
else {
if (sourceUnits == null) {
if (!isMultiSourceUnitsSupported()) {
return false;
}
sourceUnits= new ArrayList<>();
sourceUnits.add(lastSourceUnit);
}
sourceUnits.add(sourceUnit);
lastSourceUnit= sourceUnit;
}
}
return true;
}
private ImList<TSourceUnit> createSourceUnits(final ISourceStructElement[] selectedElements,
final IProgressMonitor monitor) {
TSourceUnit lastSourceUnit= null;
List<TSourceUnit> sourceUnits= null;
for (int i= 0; i < selectedElements.length; i++) {
final ISourceUnit sourceUnit= selectedElements[i].getSourceUnit();
if (sourceUnit == null) {
return null;
}
if (sourceUnit.equals(lastSourceUnit)
|| (sourceUnits != null && sourceUnits.contains(sourceUnit)) ) {
continue;
}
if (!isSourceUnitSupported(sourceUnit)
|| (isEditTask() && !sourceUnit.checkState(true, monitor)) ) {
return null;
}
if (lastSourceUnit == null) {
lastSourceUnit= (TSourceUnit) sourceUnit;
}
else {
if (sourceUnits == null) {
if (!isMultiSourceUnitsSupported()) {
return null;
}
sourceUnits= new ArrayList<>();
sourceUnits.add(lastSourceUnit);
}
sourceUnits.add((TSourceUnit) sourceUnit);
lastSourceUnit= (TSourceUnit) sourceUnit;
}
}
return (sourceUnits == null) ?
ImCollections.newList(lastSourceUnit) :
ImCollections.toList(sourceUnits);
}
private void execute(final ExecData data) throws Exception {
final AtomicReference<Exception> error= new AtomicReference<>();
switch (getExecMode(data)) {
case BACKGROUND:
try {
PlatformUI.getWorkbench().getProgressService().busyCursorWhile(new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
try {
AbstractSourceDocumentHandler.this.doExecute(data, monitor);
}
catch (final Exception e) {
throw new InvocationTargetException(e);
}
}
});
}
catch (final InvocationTargetException e) {
error.set((Exception) e.getTargetException());
}
catch (final InterruptedException e) {}
break;
case SHOW_BUSY:
BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
@Override
public void run() {
try {
AbstractSourceDocumentHandler.this.doExecute(data, new NullProgressMonitor());
}
catch (final Exception e) {
error.set(e);
}
}
});
break;
default:
doExecute(data, new NullProgressMonitor());
return;
}
if (error.get() != null) {
throw error.get();
}
}
protected abstract boolean isSourceUnitSupported(final ISourceUnit sourceUnit);
protected boolean isMultiSourceUnitsSupported() {
return false;
}
protected ExecData createExecData(final ExecutionEvent event, final IWorkbenchPart activePart,
final ISourceEditor sourceEditor, final ImList<? extends TSourceUnit> sourceUnits,
final ITextSelection textSelection, final ISourceStructElement[] selectedElements,
final IProgressMonitor monitor) throws Exception {
return new ExecData(activePart, sourceEditor, sourceUnits, textSelection,
(selectedElements != null) ? new ElementSet(selectedElements) : null );
}
protected byte getExecMode(final ExecData data) {
return (isEditTask()) ? SHOW_BUSY : DIRECT;
}
protected abstract void doExecute(ExecData data,
IProgressMonitor monitor) throws Exception;
}