| /*=============================================================================# |
| # Copyright (c) 2015, 2020 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.element.SourceStructElement; |
| import org.eclipse.statet.ltk.model.core.element.SourceUnit; |
| 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 SourceUnit> |
| 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 SourceUnit sourceUnit= sourceEditor.getSourceUnit(); |
| setBaseEnabled(sourceUnit != null && |
| (!isEditTask() || sourceEditor.isEditable(false) )); |
| return; |
| } |
| |
| if (selection instanceof IStructuredSelection) { |
| final SourceStructElement[] 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 SourceUnit 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 SourceStructElement[] 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 SourceStructElement[] selectedElements) { |
| SourceUnit lastSourceUnit= null; |
| List<SourceUnit> sourceUnits= null; |
| for (int i= 0; i < selectedElements.length; i++) { |
| final SourceUnit 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 SourceStructElement[] selectedElements, |
| final IProgressMonitor monitor) { |
| TSourceUnit lastSourceUnit= null; |
| List<TSourceUnit> sourceUnits= null; |
| for (int i= 0; i < selectedElements.length; i++) { |
| final SourceUnit 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 SourceUnit 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 SourceStructElement[] 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; |
| |
| } |