blob: d19aff4c91fed3da79d763551cb43690e5d7894a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 University of Illinois at Urbana-Champaign 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:
* UIUC - Initial API and implementation
*******************************************************************************/
package org.eclipse.photran.internal.ui.actions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
import org.eclipse.photran.internal.core.refactoring.IRefactoring;
import org.eclipse.photran.internal.core.refactoring.UserInputBoolean;
import org.eclipse.photran.internal.core.refactoring.UserInputString;
import org.eclipse.photran.internal.ui.UIUtil;
import org.eclipse.photran.internal.ui.menus.RefactorMenu;
import org.eclipse.photran.internal.ui.refactoring.CustomUserInputPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;
/**
* A default action for refactorings, which provides a refactoring wizard and optionally generates
* a simple user input page for that wizard based on {@link UserInputString} and
* {@link UserInputBoolean} method annotations..
*
* @author Jeff Overbey
*
* @see RefactorMenu
* @see UserInputString
* @see UserInputBoolean
*/
public class RefactoringAction<T extends Refactoring>
implements IWorkbenchWindowActionDelegate,
//IEditorActionDelegate,
IRunnableWithProgress
{
private IWorkbenchWindow activeWindow = null;
private Shell activeShell = null;
protected IRefactoring refactoring;
protected CustomUserInputPage<T> customUserInputPage = null;
public RefactoringAction(IRefactoring refactoring)
{
this(refactoring, null);
}
public RefactoringAction(IRefactoring refactoring,
CustomUserInputPage<T> customUserInputPage)
{
this.refactoring = refactoring;
this.customUserInputPage = customUserInputPage;
}
public void init(IWorkbenchWindow window)
{
activeWindow = window;
if (activeWindow != null)
activeShell = activeWindow.getShell();
}
public void dispose() {;}
public void selectionChanged(IAction action, ISelection selection) {;}
public void run()
{
run((IAction)null);
}
public void run(IAction action)
{
if (!(refactoring instanceof Refactoring))
{
UIUtil.displayErrorDialog("ERROR: " + refactoring.getClass().getName() + //$NON-NLS-1$
" is not a subclass of org.eclipse.ltk.core.refactoring.Refactoring." + //$NON-NLS-1$
" A custom Action must be provided."); //$NON-NLS-1$
return;
}
IProgressService context = PlatformUI.getWorkbench().getProgressService();
ISchedulingRule lockEntireWorkspace = ResourcesPlugin.getWorkspace().getRoot();
try
{
context.runInUI(context, this, lockEntireWorkspace);
}
catch (InvocationTargetException e)
{
e.printStackTrace();
MessageDialog.openError(
activeShell,
Messages.RefactoringAction_UnhandledExceptionTitle,
e.getMessage());
}
catch (InterruptedException e)
{
// Do nothing
}
}
public void run(IProgressMonitor progressMonitor) throws InvocationTargetException, InterruptedException
{
if (filesSavedAsNecessary())
{
String name = refactoring.getName();
DefaultRefactoringWizard wizard = new DefaultRefactoringWizard();
RefactoringWizardOpenOperation wiz = new RefactoringWizardOpenOperation(wizard);
if (activeShell == null) activeShell = UIUtil.determineActiveShell();
wiz.run(activeShell, name);
}
}
private boolean filesSavedAsNecessary()
{
return UIUtil.askUserToSaveModifiedFiles();
}
public class DefaultRefactoringWizard extends RefactoringWizard
{
@SuppressWarnings("unchecked")
public DefaultRefactoringWizard()
{
// CHECK_INITIAL_CONDITIONS_ON_OPEN causes the initial conditions to be checked
// twice, which may lead to duplicate or missing error messages
// (missing if, say, an INCLUDE could not be found, but the AST was already
// loaded into the workspace on the second invocation)
super((Refactoring)refactoring, DIALOG_BASED_USER_INTERFACE /*| CHECK_INITIAL_CONDITIONS_ON_OPEN*/);
setNeedsProgressMonitor(true);
setChangeCreationCancelable(false);
setWindowTitle(getRefactoring().getName());
if (customUserInputPage != null)
customUserInputPage.setRefactoring((T)getRefactoring());
}
@Override
protected final void addUserInputPages()
{
setDefaultPageTitle(refactoring.getName());
if (customUserInputPage != null)
addPage(customUserInputPage);
else if (hasAnnotatedMethods())
addPage(new SimpleUserInputWizardPage(refactoring.getName()));
else
addPage(new NoUserInputWizardPage(refactoring.getName()));
}
private boolean hasAnnotatedMethods()
{
for (Method method : refactoring.getClass().getMethods())
{
if (method.getAnnotation(UserInputString.class) != null
|| method.getAnnotation(UserInputBoolean.class) != null)
{
return true;
}
}
return false;
}
}
private final class NoUserInputWizardPage extends UserInputWizardPage
{
private NoUserInputWizardPage(String name)
{
super(name);
}
public void createControl(Composite parent)
{
Composite top = new Composite(parent, SWT.NONE);
initializeDialogUnits(top);
setControl(top);
top.setLayout(new GridLayout(1, false));
Label lbl = new Label(top, SWT.NONE);
lbl.setText(
Messages.bind(
Messages.RefactoringAction_ClickOKToRunTheRefactoring,
refactoring.getName()));
}
}
private final class SimpleUserInputWizardPage extends UserInputWizardPage
{
private Control firstField = null;
private SimpleUserInputWizardPage(String name)
{
super(name);
}
public void createControl(Composite parent)
{
Composite top = new Composite(parent, SWT.NONE);
initializeDialogUnits(top);
setControl(top);
top.setLayout(new GridLayout(2, false));
addContolsFromAnnotatedFields(top);
}
private void addContolsFromAnnotatedFields(Composite group)
{
for (Method method : refactoring.getClass().getMethods())
{
UserInputString stringAnnotation = method.getAnnotation(UserInputString.class);
if (stringAnnotation != null)
{
checkMethodSignature(method, String.class);
String defaultValue = getDefaultValue(refactoring, stringAnnotation.defaultValueMethod());
addTextField(group, stringAnnotation.label(), defaultValue, method);
}
UserInputBoolean annotation = method.getAnnotation(UserInputBoolean.class);
if (annotation != null)
{
checkMethodSignature(method, Boolean.TYPE);
addCheckBoxField(group, annotation.label(), annotation.defaultValue(), method);
}
}
}
private void checkMethodSignature(Method method, Class<?> parameterType)
{
if (!method.getReturnType().equals(Void.TYPE)
|| method.getParameterTypes().length != 1
|| !method.getParameterTypes()[0].equals(parameterType))
{
throw new IllegalArgumentException(
"The method " + method.getName() + " may not have the @UserInput " + //$NON-NLS-1$ //$NON-NLS-2$
"annotation unless it has the following signature:\n" + //$NON-NLS-1$
" void " + method.getName() + "(" + parameterType.getSimpleName() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
private String getDefaultValue(IRefactoring refactoring, String defaultValueMethod)
{
try
{
if (defaultValueMethod != null && !defaultValueMethod.equals("")) //$NON-NLS-1$
{
Method getter = refactoring.getClass().getMethod(defaultValueMethod);
String result = getter.invoke(refactoring).toString();
return result == null ? "" : result; //$NON-NLS-1$
}
else return ""; //$NON-NLS-1$
}
catch (Throwable e)
{
e.printStackTrace();
return ""; //$NON-NLS-1$
}
}
private void addTextField(Composite group, String label, String defaultValue, final Method method)
{
Label lbl = new Label(group, SWT.NONE);
lbl.setText(label);
final Text newNameField = new Text(group, SWT.BORDER);
newNameField.setText(defaultValue);
setString(method, defaultValue);
newNameField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
newNameField.selectAll();
newNameField.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
setString(method, newNameField.getText());
}
});
if (firstField == null) firstField = newNameField;
}
private void setString(Method method, String value)
{
try
{
method.invoke(refactoring, value);
}
catch (Exception x)
{
throw new Error(x);
}
}
private void addCheckBoxField(Composite group, String label, boolean defaultValue, final Method method)
{
new Label(group, SWT.NONE).setText(""); //$NON-NLS-1$
final Button button = new Button(group, SWT.CHECK);
button.setText(label);
button.setSelection(defaultValue);
setBoolean(method, defaultValue);
button.addSelectionListener(new SelectionListener()
{
public void widgetDefaultSelected(SelectionEvent e)
{
widgetSelected(e);
}
public void widgetSelected(SelectionEvent e)
{
setBoolean(method, button.getSelection());
}
});
if (firstField == null) firstField = button;
}
private void setBoolean(Method method, boolean isChecked)
{
try
{
method.invoke(refactoring, isChecked);
}
catch (Exception x)
{
throw new Error(x);
}
}
}
}