/*******************************************************************************
* Copyright (c) 2017 Rogue Wave Software Inc. and others.
* 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:
* Rogue Wave Software Inc. - initial implementation
*******************************************************************************/
package org.eclipse.php.internal.ui.handlers;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.IType;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.internal.core.ExternalSourceModule;
import org.eclipse.dltk.ti.types.IEvaluatedType;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRewriteTarget;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.php.core.ast.nodes.IMethodBinding;
import org.eclipse.php.core.ast.nodes.ITypeBinding;
import org.eclipse.php.core.ast.nodes.Program;
import org.eclipse.php.core.ast.nodes.TypeBinding;
import org.eclipse.php.core.compiler.PHPFlags;
import org.eclipse.php.internal.core.typeinference.PHPClassType;
import org.eclipse.php.internal.ui.PHPUiPlugin;
import org.eclipse.php.internal.ui.actions.AddUnimplementedMethodsOperation;
import org.eclipse.php.internal.ui.actions.WorkbenchRunnableAdapter;
import org.eclipse.php.internal.ui.dialogs.OverrideMethodDialog;
import org.eclipse.php.internal.ui.editor.PHPStructuredEditor;
import org.eclipse.php.internal.ui.util.BusyIndicatorRunnableContext;
import org.eclipse.php.ui.util.CodeGenerationUtils;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.handlers.HandlerUtil;
/**
* This is a handler for OverrideImplementAction class. Allows contributing to
* extension 'org.eclipse.ui.menus'.
*
*/
public class OverrideImplementHandler extends AbstractHandler {
/** The dialog title */
private static final String DIALOG_TITLE = Messages.OverrideImplementHandler_0;
private IWorkbenchWindow fWindow;
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
fWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
Object selection = HandlerUtil.getCurrentSelectionChecked(event);
// Selection are on view.
if (selection instanceof IStructuredSelection) {
execute(event, ((IStructuredSelection) selection));
}
// Selection are on editor.
if (selection instanceof ITextSelection) {
execute(event, (ITextSelection) selection);
}
return null;
}
private void execute(ExecutionEvent event, IStructuredSelection selection) throws ExecutionException {
Object object = selection.getFirstElement();
if (object instanceof IModelElement) {
IModelElement element = (IModelElement) object;
if (element.getOpenable() instanceof ExternalSourceModule) {
MessageDialog.openError(fWindow.getShell(), DIALOG_TITLE, Messages.OverrideImplementHandler_1);
return;
}
try {
IEditorPart editor = CodeGenerationUtils.openInEditor(element, true);
if (editor instanceof PHPStructuredEditor) {
run((ISourceModule) ((PHPStructuredEditor) editor).getModelElement(), element,
(PHPStructuredEditor) editor);
}
} catch (PartInitException e) {
MessageDialog.openError(fWindow.getShell(), DIALOG_TITLE, Messages.OverrideImplementHandler_1);
throw new ExecutionException(Messages.OverrideImplementHandler_2, e);
} catch (ModelException e) {
PHPUiPlugin.log(e);
}
}
}
private void execute(ExecutionEvent event, ITextSelection selection) throws ExecutionException {
PHPStructuredEditor editor = CodeGenerationUtils.getPHPEditor(event);
if (editor == null) {
return;
}
IModelElement source = editor.getModelElement();
if (!(source instanceof ISourceModule)) {
MessageDialog.openError(fWindow.getShell(), DIALOG_TITLE, Messages.OverrideImplementHandler_3);
return;
}
if (source instanceof ExternalSourceModule) {
MessageDialog.openError(fWindow.getShell(), DIALOG_TITLE, Messages.OverrideImplementHandler_1);
return;
}
IModelElement element = CodeGenerationUtils.getCurrentModelElement(source, editor, selection);
// This is a bug of HandlerUtils. When open the file first time, the
// current selection returns the offset as 0.
// try to get the element via the editor.
if (element == null) {
element = CodeGenerationUtils.getCurrentModelElement(event);
}
try {
run((ISourceModule) source, element, editor);
} catch (ModelException e) {
PHPUiPlugin.log(e);
}
}
private void run(ISourceModule source, IModelElement element, TextEditor editor) throws ModelException {
IType type = CodeGenerationUtils.getType(element);
if (type == null) {
MessageDialog.openError(fWindow.getShell(), DIALOG_TITLE, Messages.OverrideImplementHandler_4);
return;
}
int flags = type.getFlags();
if (PHPFlags.isInterface(flags)) {
MessageDialog.openInformation(fWindow.getShell(), DIALOG_TITLE, Messages.OverrideImplementHandler_5);
return;
}
OverrideMethodDialog dialog = new OverrideMethodDialog(fWindow.getShell(), editor, type, false);
dialog.setProject(source.getScriptProject().getProject());
if (!dialog.hasMethodsToOverride()) {
MessageDialog.openInformation(fWindow.getShell(), Messages.OverrideImplementHandler_6,
Messages.OverrideImplementHandler_7);
return;
}
if (dialog.open() != Window.OK || dialog.getResult() == null) {
return;
}
final Object[] selected = dialog.getResult();
ArrayList<Object> methods = new ArrayList<>();
for (Object elem : selected) {
if (elem instanceof IMethodBinding) {
methods.add(elem);
}
}
IMethodBinding[] methodToOverride = methods.toArray(new IMethodBinding[methods.size()]);
final IRewriteTarget target = editor.getAdapter(IRewriteTarget.class);
if (target != null) {
target.setRedraw(false);
target.beginCompoundChange();
}
try {
Program astRoot = dialog.getCompilationUnit();
IEvaluatedType evaluatedType = PHPClassType.fromIType(type);
final ITypeBinding typeBinding = new TypeBinding(astRoot.getAST().getBindingResolver(), evaluatedType,
type);
int insertPos = dialog.getInsertOffset();
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
AddUnimplementedMethodsOperation operation = createRunnable(astRoot, type, typeBinding, methodToOverride,
insertPos, dialog.getGenerateComment(), document);
IRunnableContext context = PHPUiPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow();
if (context == null) {
context = new BusyIndicatorRunnableContext();
}
PlatformUI.getWorkbench().getProgressService().runInUI(context,
new WorkbenchRunnableAdapter(operation, operation.getSchedulingRule()),
operation.getSchedulingRule());
final String[] created = operation.getCreatedMethods();
if (created == null || created.length == 0) {
MessageDialog.openInformation(fWindow.getShell(), DIALOG_TITLE, Messages.OverrideImplementHandler_8);
}
} catch (InvocationTargetException exception) {
MessageDialog.openInformation(fWindow.getShell(), DIALOG_TITLE, Messages.OverrideImplementHandler_9);
PHPUiPlugin.log(exception);
} catch (InterruptedException exception) {
// Do nothing. Operation has been canceled by user.
} finally {
if (target != null) {
target.setRedraw(true);
target.endCompoundChange();
}
}
}
private AddUnimplementedMethodsOperation createRunnable(Program astRoot, IType element, ITypeBinding typeBinding,
IMethodBinding[] methodToOverride, int insertPos, boolean generateComment, IDocument doc) {
AddUnimplementedMethodsOperation operation = new AddUnimplementedMethodsOperation(astRoot, element, typeBinding,
methodToOverride, insertPos, true, doc);
operation.setCreateComments(generateComment);
return operation;
}
}