package org.testng.eclipse.wizards;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWizard;
import org.testng.eclipse.ui.util.Utils;
import org.testng.eclipse.util.StringUtils;
import org.testng.eclipse.util.SuiteGenerator;
import org.testng.eclipse.util.Utils.JavaElement;
/**
* The wizard that creates a new TestNG class. This wizard looks at the current
* selected class and prefills some of its pages based on the information found
* in these classes.
*
* @author Cedric Beust <cedric@beust.com>
*/
public class NewTestNGClassWizard extends Wizard implements INewWizard {
private NewTestNGClassWizardPage m_page;
private TestNGMethodWizardPage m_methodPage;
/**
* Constructor for NewTestNGClassWizard.
*/
public NewTestNGClassWizard() {
super();
setNeedsProgressMonitor(true);
}
private boolean hasAtLeastOneMethod(List<JavaElement> elements) {
for (JavaElement je : elements) {
if (je.compilationUnit != null) return true;
}
return false;
}
/**
* Adding the pages to the wizard.
*/
@Override
public void addPages() {
List<JavaElement> elements = org.testng.eclipse.util.Utils.getSelectedJavaElements();
if (hasAtLeastOneMethod(elements)) {
m_methodPage = new TestNGMethodWizardPage(elements);
addPage(m_methodPage);
}
m_page = new NewTestNGClassWizardPage();
addPage(m_page);
}
/**
* This method is called when 'Finish' button is pressed in
* the wizard. We will create an operation and run it
* using wizard as execution context.
*/
@Override
public boolean performFinish() {
String containerName = m_page.getSourceFolder();
String className = m_page.getClassName();
String packageName = m_page.getPackageName();
List<IMethod> methods = m_methodPage != null
? m_methodPage.getSelectedMethods() : Collections.<IMethod>emptyList();
try {
return doFinish(containerName, packageName, className, m_page.getXmlFile(), methods,
new NullProgressMonitor());
} catch (CoreException e) {
e.printStackTrace();
}
// IRunnableWithProgress op = new IRunnableWithProgress() {
// public void run(IProgressMonitor monitor) throws InvocationTargetException {
// try {
// doFinish(containerName, fileName, monitor);
// } catch (CoreException e) {
// throw new InvocationTargetException(e);
// } finally {
// monitor.done();
// }
// }
// };
// try {
// getContainer().run(true /* fork */, false /* cancelable */, op);
// } catch (InterruptedException e) {
// return false;
// } catch (InvocationTargetException e) {
// Throwable realException = e.getTargetException();
// MessageDialog.openError(getShell(), "Error", realException.getMessage());
// return false;
// }
return true;
}
/**
* The worker method. It will find the container, create the
* file(s) if missing or just replace its contents, and open
* the editor on the newly created file.
*
* @return true if the operation succeeded, false otherwise.
*/
private boolean doFinish(String containerName, String packageName, String className,
String xmlPath, List<IMethod> methods, IProgressMonitor monitor) throws CoreException {
boolean result = true;
//
// Create XML file at the root directory, if applicable
//
if (!StringUtils.isEmptyString(xmlPath)) {
IFile file = createFile(containerName, "", xmlPath, createXmlContentStream(), monitor);
if (file != null) org.testng.eclipse.util.Utils.openFile(getShell(), file, monitor);
else result = false;
}
//
// Create Java file
//
if (result) {
IFile file = createFile(containerName, packageName, className + ".java",
createJavaContentStream(className, methods), monitor);
if (file != null) org.testng.eclipse.util.Utils.openFile(getShell(), file, monitor);
else result = false;
}
return result;
}
private IFile createFile(String containerName, String packageName, String fileName,
InputStream contentStream, IProgressMonitor monitor) throws CoreException {
monitor.beginTask("Creating " + fileName, 2);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
String fullPath = fileName;
if (packageName != null && ! "".equals(packageName)) {
fullPath = packageName.replace(".", File.separator) + File.separatorChar + fileName;
}
Path absolutePath = new Path(containerName + File.separatorChar + fullPath);
final IFile result = root.getFile(absolutePath);
Utils.createFileWithDialog(getShell(), result, contentStream);
return result;
}
/**
* Create the content for the Java file.
* @param testMethods
*/
private InputStream createJavaContentStream(String className, List<IMethod> testMethods) {
StringBuilder imports = new StringBuilder("import org.testng.annotations.Test;\n");
StringBuilder methods = new StringBuilder();
String dataProvider = "";
String signature = "()";
//
// Configuration methods
//
for (String a : NewTestNGClassWizardPage.ANNOTATIONS) {
if (!"".equals(a) && m_page.containsAnnotation(a)) {
imports.append("import org.testng.annotations." + a + ";\n");
if ("DataProvider".equals(a)) {
dataProvider = "(dataProvider = \"dp\")";
methods.append("\n @DataProvider\n"
+ " public Object[][] dp() {\n"
+ " return new Object[][] {\n"
+ " new Object[] { 1, \"a\" },\n"
+ " new Object[] { 2, \"b\" },\n"
+ " };\n"
+ " }\n"
);
;
signature = "(Integer n, String s)";
} else {
methods.append(" @" + a + "\n"
+ " public void " + toMethod(a) + "() {\n"
+ " }\n\n"
);
}
}
}
//
// Test methods
//
Set<String> overloadedMethods = new HashSet<>();
Set<String> temp = new HashSet<>();
for (IMethod m : testMethods) {
String name = m.getElementName();
if (temp.contains(name)) overloadedMethods.add(name);
temp.add(name);
}
for (IMethod m : testMethods) {
methods.append("\n"
+ " @Test\n"
+ " public void " + createSignature(m, overloadedMethods) + " {\n"
+ " throw new RuntimeException(\"Test not implemented\");\n"
+ " }\n");
}
StringBuilder contents = new StringBuilder();
if (!StringUtils.isEmptyString(m_page.getPackageName())) {
contents.append("package " + m_page.getPackageName() + ";\n\n");
}
contents.append(imports).append("\n");
contents.append("public class " + className + " {\n");
if (testMethods.size() == 0 || ! StringUtils.isEmptyString(dataProvider)) {
contents.append(
" @Test" + dataProvider + "\n"
+ " public void f" + signature + " {\n"
+ " }\n");
}
contents.append(methods + "}\n");
return new ByteArrayInputStream(contents.toString().getBytes());
}
/**
* @return a suitable signature, possible with its name mangled if it's
* overloaded in the class (e.g foo() -> foo(), foo(Integer) -> fooInteger()).
*/
private String createSignature(IMethod m, Set<String> overloadedMethods) {
String elementName = m.getElementName();
StringBuilder result = new StringBuilder(elementName);
if (overloadedMethods.contains(elementName)) {
for (String type : m.getParameterTypes()) {
result.append(sanitizeSignature(Signature.toString(type)));
}
}
result.append("()");
return result.toString();
}
/**
* @return a string that can be used as a method name.
*/
private String sanitizeSignature(String string) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
if (Character.isJavaIdentifierPart(c)) {
result.append(c);
}
}
return result.toString();
}
/**
* Create the content for the XML file.
*/
private InputStream createXmlContentStream() {
String cls = m_page.getClassName();
String pkg = m_page.getPackageName();
String className = StringUtils.isEmptyString(pkg) ? cls : pkg + "." + cls;
return new ByteArrayInputStream(
SuiteGenerator.createSingleClassSuite(className).getBytes());
}
private String toMethod(String a) {
return Character.toLowerCase(a.charAt(0)) + a.substring(1);
}
/**
* We will accept the selection in the workbench to see if
* we can initialize from it.
* @see IWorkbenchWizard#init(IWorkbench, IStructuredSelection)
*/
public void init(IWorkbench workbench, IStructuredSelection selection) {
}
}