package org.jnario.ui.handler;
import static org.jnario.util.Strings.PENDING_FLAG;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil;
import org.eclipse.jdt.internal.junit.BasicElementLabels;
import org.eclipse.jdt.internal.junit.Messages;
import org.eclipse.jdt.internal.junit.model.TestCaseElement;
import org.eclipse.jdt.internal.junit.model.TestElement;
import org.eclipse.jdt.internal.junit.model.TestRoot;
import org.eclipse.jdt.internal.junit.model.TestRunSession;
import org.eclipse.jdt.internal.junit.model.TestSuiteElement;
import org.eclipse.jdt.internal.junit.ui.JUnitMessages;
import org.eclipse.jdt.internal.junit.ui.OpenEditorAction;
import org.eclipse.jdt.internal.junit.ui.OpenTestAction;
import org.eclipse.jdt.internal.junit.ui.TestRunnerViewPart;
import org.eclipse.jdt.junit.model.ITestRunSession;
import org.eclipse.jdt.junit.runners.ReflectionUtil;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ui.texteditor.ITextEditor;
import org.jnario.util.Strings;
@SuppressWarnings("restriction")
public abstract class AbstractJnarioOpenTestAction extends OpenTestAction {
private final static Logger logger = Logger.getLogger(AbstractJnarioOpenTestAction.class);
private IType fSuiteType;
public AbstractJnarioOpenTestAction(TestRunnerViewPart testRunnerPart,
TestCaseElement testCase) {
super(testRunnerPart, testCase);
String factname = extractTestName(testCase);
String methodName = Strings.toMethodName(factname.replace(PENDING_FLAG,
""));
ReflectionUtil.writeField(OpenTestAction.class, this, "fMethodName",
methodName);
}
static String extractTestName(TestElement testCase) {
String testname = testCase.getTestName();
String classname = testCase.getClassName();
String factname = testname.replace("(" + classname + ")", "");
return factname;
}
public AbstractJnarioOpenTestAction(TestRunnerViewPart testRunnerPart, TestSuiteElement testSuite) {
super(testRunnerPart, testSuite.getClassName());
setText("Go to Test");
String suiteType = findSuiteType(testSuite);
ReflectionUtil.writeField(OpenEditorAction.class, this, "fClassName", suiteType);
}
private String getSuiteClassName(TestSuiteElement testSuite) {
String mainType= null;
try {
ITestRunSession runSession = testSuite.getRoot().getTestRunSession();
if (runSession instanceof TestRunSession) {
ILaunch launch = ((TestRunSession)runSession).getLaunch();
if (launch != null && launch.getLaunchConfiguration() != null) {
mainType = launch.getLaunchConfiguration().getAttribute("org.eclipse.jdt.launching.MAIN_TYPE", (String)null);
}
}
} catch (Exception e) {
logger.debug("Unable to find launch classname for suite", e);
}
if (mainType == null) {
mainType = testSuite.getRoot().getClassName();
}
return mainType;
}
private String findSuiteType(TestSuiteElement suite) {
String mainType = getSuiteClassName(suite);
Iterable<String> path = getSuitePath(suite);
IType type = findType(suite.getRoot().getTestRunSession().getLaunchedProject(), mainType);
if (type == null) {
return null;
}
logger.debug("Main type of suite execution is " + type.getFullyQualifiedName());
logger.debug("Path from root to suite " + suite.getTestName() + " is " + path);
Iterator<String> pathSteps = path.iterator();
while(type != null && pathSteps.hasNext()) {
String suiteName = pathSteps.next();
type = findContainedType(type, suiteName);
}
if (pathSteps.hasNext() || type == null) {
return suite.getSuiteTypeName();
}
logger.debug("Suite path resolves to type " + type.getFullyQualifiedName());
return type.getFullyQualifiedName();
}
private IType findContainedType(IType type, String suiteName) {
try {
Object containsValue = getAnnotationMemberValue(type, "Contains", "value");
if (containsValue == null) {
return null;
}
Object[] containedTypes = (containsValue instanceof Object[]) ? (Object[]) containsValue : new Object[] {containsValue};
for (int j = 0; j < containedTypes.length; j++) {
Object containedType = containedTypes[j];
IType subType = findType(getLaunchedProject(), String.valueOf(containedType));
if (subType != null) {
Object name = getAnnotationMemberValue(subType, "Named", "value");
if (name != null && suiteName.equals(name.toString())) {
return subType;
}
}
}
} catch (JavaModelException e) {
logger.debug("Model error finding Jnario contained types", e);
}
return null;
}
private static Object getAnnotationMemberValue(IType type, String annotationName, String memberName) throws JavaModelException {
IAnnotation[] annotations = type.getAnnotations();
for (int i = 0; i < annotations.length; i++) {
IAnnotation annotation = annotations[i];
if (annotation.getElementName().equals(annotationName)) {
IMemberValuePair[] values = annotation.getMemberValuePairs();
for (int j = 0; j < values.length; j++) {
IMemberValuePair member = values[j];
if (member.getMemberName().equals(memberName)) {
return member.getValue();
}
}
}
}
return null;
}
private static Iterable<String> getSuitePath(TestSuiteElement testSuite) {
LinkedList<String> path = new LinkedList<String>();
TestElement current = testSuite;
while(current != null && !(current.getParent() instanceof TestRoot)) {
path.addFirst(extractTestName(current));
current = current.getParent();
}
return path;
}
@Override
protected IJavaElement findElement(IJavaProject project, String className)
throws JavaModelException {
IType type = findType(project, className);
if (type == null)
return null;
String fMethodName = ReflectionUtil.readField(OpenTestAction.class,
this, "fMethodName", String.class);
if (fMethodName == null) {
// Suite level selection. Flag so reveal() can handle.
fSuiteType = type;
return type;
}
fSuiteType = null;
IMethod method = findMethod(type);
if (method == null) {
ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(null);
IType[] supertypes = typeHierarchy.getAllSuperclasses(type);
for (IType supertype : supertypes) {
method = findMethod(supertype);
if (method != null)
break;
}
}
if (method == null) {
String title = errorTitleMessage();
String message = Messages.format(
JUnitMessages.OpenTestAction_error_methodNoFound,
BasicElementLabels.getJavaElementName(fMethodName));
MessageDialog.openInformation(getShell(), title, message);
return type;
}
ReflectionUtil
.writeField(OpenTestAction.class, this, "fMethod", method);
return method;
}
private static String errorTitleMessage() {
try {
// Luna + Mars
return (String) JUnitMessages.class.getField("OpenTestAction_dialog_title").get(null);
} catch(Throwable t) {
}
try {
// Kepler
return (String) JUnitMessages.class.getField("OpenTestAction_error_title").get(null);
} catch(Throwable t) {
}
return "Go to Test";
}
private IMethod findMethod(IType type) {
String fMethodName = ReflectionUtil.readField(OpenTestAction.class,
this, "fMethodName", String.class);
IStatus status = JavaConventionsUtil.validateMethodName(fMethodName,
type);
// FIX: also find methods with parameters
if (!status.isOK() || fMethodName == null)
return null;
try {
for (IMethod m : type.getMethods()) {
if (fMethodName.equals(m.getElementName()) && m.exists())
return m;
}
} catch (JavaModelException e) {
return null;
}
return null;
}
@Override
protected void reveal(ITextEditor textEditor) {
// Handle suite only selection
if (fSuiteType != null) {
try {
ISourceRange range= fSuiteType.getNameRange();
if (range != null && range.getOffset() >= 0)
textEditor.selectAndReveal(range.getOffset(), range.getLength());
} catch (JavaModelException e) {
// not a problem
}
return;
}
super.reveal(textEditor);
}
}