/*******************************************************************************
* Copyright Technophobia Ltd 2012
*
* This file is part of the Substeps Eclipse Plugin.
*
* The Substeps Eclipse Plugin is free software: you can redistribute it and/or modify
* it under the terms of the Eclipse Public License v1.0.
*
* The Substeps Eclipse Plugin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Eclipse Public License for more details.
*
* You should have received a copy of the Eclipse Public License
* along with the Substeps Eclipse Plugin. If not, see <http://www.eclipse.org/legal/epl-v10.html>.
******************************************************************************/
package com.technophobia.substeps.junit.action;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.TypeNameMatch;
import org.eclipse.jdt.core.search.TypeNameMatchRequestor;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.texteditor.ITextEditor;
import com.technophobia.eclipse.ui.NotifyingUiUpdater;
import com.technophobia.substeps.FeatureRunnerPlugin;
import com.technophobia.substeps.junit.ui.SubstepsFeatureMessages;
import com.technophobia.substeps.junit.ui.SubstepsRunSession;
import com.technophobia.substeps.supplier.Supplier;
public abstract class OpenEditorAction extends Action {
protected String className;
private final boolean activate;
private final Supplier<SubstepsRunSession> substepsRunSessionSupplier;
private final NotifyingUiUpdater<String> infoMessageUpdater;
private final Shell shell;
protected OpenEditorAction(final Supplier<SubstepsRunSession> substepsRunSessionSupplier,
final NotifyingUiUpdater<String> infoMessageUpdater, final Shell shell, final String testClassName) {
this(substepsRunSessionSupplier, infoMessageUpdater, shell, testClassName, true);
}
public OpenEditorAction(final Supplier<SubstepsRunSession> substepsRunSessionSupplier,
final NotifyingUiUpdater<String> infoMessageUpdater, final Shell shell, final String className,
final boolean activate) {
super(SubstepsFeatureMessages.OpenEditorAction_action_label);
this.substepsRunSessionSupplier = substepsRunSessionSupplier;
this.infoMessageUpdater = infoMessageUpdater;
this.shell = shell;
this.className = className;
this.activate = activate;
}
/*
* @see IAction#run()
*/
@Override
public void run() {
IEditorPart editor = null;
try {
final IJavaElement element = findElement(getLaunchedProject(), className);
if (element == null) {
MessageDialog.openError(getShell(), SubstepsFeatureMessages.OpenEditorAction_error_cannotopen_title,
SubstepsFeatureMessages.OpenEditorAction_error_cannotopen_message);
return;
}
editor = JavaUI.openInEditor(element, activate, false);
} catch (final CoreException e) {
ErrorDialog.openError(getShell(), SubstepsFeatureMessages.OpenEditorAction_error_dialog_title,
SubstepsFeatureMessages.OpenEditorAction_error_dialog_message, e.getStatus());
return;
}
if (!(editor instanceof ITextEditor)) {
infoMessageUpdater.notify(SubstepsFeatureMessages.OpenEditorAction_message_cannotopen);
return;
}
reveal((ITextEditor) editor);
}
protected Shell getShell() {
return shell;
}
/**
* @return the Java project, or <code>null</code>
*/
protected IJavaProject getLaunchedProject() {
return substepsRunSessionSupplier.get().getLaunchedProject();
}
protected String getClassName() {
return className;
}
protected abstract IJavaElement findElement(IJavaProject project, String testClassName) throws CoreException;
protected abstract void reveal(ITextEditor editor);
protected final IType findType(final IJavaProject project, final String testClassName) {
final IType[] result = { null };
final String dottedName = testClassName.replace('$', '.'); // for nested
// classes...
try {
PlatformUI.getWorkbench().getProgressService().busyCursorWhile(new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
if (project != null) {
result[0] = internalFindType(project, dottedName, new HashSet<IJavaProject>(), monitor);
}
if (result[0] == null) {
final int lastDot = dottedName.lastIndexOf('.');
final TypeNameMatchRequestor nameMatchRequestor = new TypeNameMatchRequestor() {
@Override
public void acceptTypeNameMatch(final TypeNameMatch match) {
result[0] = match.getType();
}
};
new SearchEngine().searchAllTypeNames(lastDot >= 0 ? dottedName.substring(0, lastDot)
.toCharArray() : null,
SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
(lastDot >= 0 ? dottedName.substring(lastDot + 1) : dottedName).toCharArray(),
SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
IJavaSearchConstants.TYPE, SearchEngine.createWorkspaceScope(), nameMatchRequestor,
IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor);
}
} catch (final JavaModelException e) {
throw new InvocationTargetException(e);
}
}
});
} catch (final InvocationTargetException e) {
FeatureRunnerPlugin.log(e);
} catch (final InterruptedException e) {
// user cancelled
}
return result[0];
}
private IType internalFindType(final IJavaProject project, final String testClassName,
final Set<IJavaProject> visitedProjects, final IProgressMonitor monitor) throws JavaModelException {
try {
if (visitedProjects.contains(project))
return null;
monitor.beginTask("", 2); //$NON-NLS-1$
IType type = project.findType(testClassName, new SubProgressMonitor(monitor, 1));
if (type != null)
return type;
// fix for bug 87492: visit required projects explicitly to also
// find not exported types
visitedProjects.add(project);
final IJavaModel javaModel = project.getJavaModel();
final String[] requiredProjectNames = project.getRequiredProjectNames();
final IProgressMonitor reqMonitor = new SubProgressMonitor(monitor, 1);
reqMonitor.beginTask("", requiredProjectNames.length); //$NON-NLS-1$
for (final String requiredProjectName : requiredProjectNames) {
final IJavaProject requiredProject = javaModel.getJavaProject(requiredProjectName);
if (requiredProject.exists()) {
type = internalFindType(requiredProject, testClassName, visitedProjects, new SubProgressMonitor(
reqMonitor, 1));
if (type != null)
return type;
}
}
return null;
} finally {
monitor.done();
}
}
}