package org.jetbrains.plugins.cucumber.java.steps;
import com.intellij.codeInsight.CodeInsightUtilCore;
import com.intellij.codeInsight.template.TemplateBuilder;
import com.intellij.codeInsight.template.TemplateBuilderFactory;
import com.intellij.lang.Language;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.file.PsiDirectoryFactory;
import com.intellij.psi.util.CreateClassUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ObjectUtils;
import cucumber.runtime.java.JavaSnippet;
import cucumber.runtime.snippets.SnippetGenerator;
import gherkin.formatter.model.Comment;
import gherkin.formatter.model.Step;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.cucumber.AbstractStepDefinitionCreator;
import org.jetbrains.plugins.cucumber.java.CucumberJavaUtil;
import org.jetbrains.plugins.cucumber.psi.GherkinStep;
import java.util.ArrayList;
/**
* User: Andrey.Vokin
* Date: 8/1/12
*/
public class JavaStepDefinitionCreator extends AbstractStepDefinitionCreator {
public static final String STEP_DEFINITION_SUFFIX = "MyStepdefs";
@NotNull
@Override
public PsiFile createStepDefinitionContainer(@NotNull PsiDirectory dir, @NotNull String name) {
PsiClass newClass = CreateClassUtil.createClassNamed(name, CreateClassUtil.DEFAULT_CLASS_TEMPLATE, dir);
assert newClass != null;
return newClass.getContainingFile();
}
@Override
public boolean createStepDefinition(@NotNull GherkinStep step, @NotNull PsiFile file) {
if (!(file instanceof PsiClassOwner)) return false;
final Project project = file.getProject();
Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
assert editor != null;
closeActiveTemplateBuilders(file);
final PsiClass clazz = PsiTreeUtil.getChildOfType(file, PsiClass.class);
if (clazz != null) {
PsiDocumentManager.getInstance(project).commitAllDocuments();
// snippet text
final PsiMethod element = buildStepDefinitionByStep(step, file.getLanguage());
PsiMethod addedElement = (PsiMethod)clazz.add(element);
addedElement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(addedElement);
JavaCodeStyleManager.getInstance(project).shortenClassReferences(addedElement);
editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
assert editor != null;
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(addedElement);
final PsiAnnotation annotation = addedElement.getModifierList().getAnnotations()[0];
final PsiNameValuePair regexpElement = annotation.getParameterList().getAttributes()[0];
final TextRange range = new TextRange(1, regexpElement.getTextLength() - 1);
builder.replaceElement(regexpElement, range, regexpElement.getText().substring(range.getStartOffset(), range.getEndOffset()));
final PsiParameterList blockVars = addedElement.getParameterList();
for (PsiParameter var : blockVars.getParameters()) {
final PsiElement nameIdentifier = var.getNameIdentifier();
if (nameIdentifier != null) {
builder.replaceElement(nameIdentifier, nameIdentifier.getText());
}
}
final PsiCodeBlock body = addedElement.getBody();
if (body != null && body.getStatements().length > 0) {
final PsiElement firstStatement = body.getStatements()[0];
final TextRange pendingRange = new TextRange(0, firstStatement.getTextLength() - 1);
builder.replaceElement(firstStatement, pendingRange,
firstStatement.getText().substring(pendingRange.getStartOffset(), pendingRange.getEndOffset()));
}
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
documentManager.doPostponedOperationsAndUnblockDocument(editor.getDocument());
builder.run(editor, false);
}
return true;
}
@Override
public boolean validateNewStepDefinitionFileName(@NotNull final Project project, @NotNull final String name) {
if(name.length() == 0) return false;
if (! Character.isJavaIdentifierStart(name.charAt(0))) return false;
for (int i = 1; i < name.length(); i++) {
if (! Character.isJavaIdentifierPart(name.charAt(i))) return false;
}
return true;
}
@NotNull
@Override
public PsiDirectory getDefaultStepDefinitionFolder(@NotNull final GherkinStep step) {
PsiFile featureFile = step.getContainingFile();
if (featureFile != null) {
PsiDirectory psiDirectory = featureFile.getContainingDirectory();
final Project project = step.getProject();
if (psiDirectory != null) {
ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
VirtualFile directory = psiDirectory.getVirtualFile();
if (projectFileIndex.isInContent(directory)) {
VirtualFile sourceRoot = projectFileIndex.getSourceRootForFile(directory);
//noinspection ConstantConditions
final Module module = projectFileIndex.getModuleForFile(featureFile.getVirtualFile());
if (module != null) {
final VirtualFile[] sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots();
if (sourceRoot != null && sourceRoot.getName().equals("resources")) {
final VirtualFile resourceParent = sourceRoot.getParent();
for (VirtualFile vFile : sourceRoots) {
if (vFile.getPath().startsWith(resourceParent.getPath()) && vFile.getName().equals("java")) {
sourceRoot = vFile;
break;
}
}
}
else {
if (sourceRoots.length > 0) {
sourceRoot = sourceRoots[sourceRoots.length - 1];
}
}
}
String packageName = "";
if (sourceRoot != null) {
packageName = CucumberJavaUtil.getPackageOfStepDef(step);
}
final String packagePath = packageName.replace('.', '/');
final String path = sourceRoot != null ? sourceRoot.getPath() : directory.getPath();
// ToDo: I shouldn't create directories, only create VirtualFile object.
final Ref<PsiDirectory> resultRef = new Ref<PsiDirectory>();
new WriteAction() {
protected void run(@NotNull Result result) throws Throwable {
final VirtualFile packageFile = VfsUtil.createDirectoryIfMissing(path + '/' + packagePath);
if (packageFile != null) {
resultRef.set(PsiDirectoryFactory.getInstance(project).createDirectory(packageFile));
}
}
}.execute();
return resultRef.get();
}
}
}
assert featureFile != null;
return ObjectUtils.assertNotNull(featureFile.getParent());
}
@NotNull
@Override
public String getStepDefinitionFilePath(@NotNull final PsiFile file) {
final VirtualFile vFile = file.getVirtualFile();
if (file instanceof PsiClassOwner && vFile != null) {
String packageName = ((PsiClassOwner)file).getPackageName();
if (StringUtil.isEmptyOrSpaces(packageName)) {
return vFile.getNameWithoutExtension();
}
else {
return vFile.getNameWithoutExtension() + " (" + packageName + ")";
}
}
return file.getName();
}
@NotNull
@Override
public String getDefaultStepFileName(@NotNull final GherkinStep step) {
return STEP_DEFINITION_SUFFIX;
}
private static PsiMethod buildStepDefinitionByStep(@NotNull final GherkinStep step, Language language) {
String annotationPackage = new AnnotationPackageProvider().getAnnotationPackageFor(step);
String methodAnnotation = String.format("@%s.", annotationPackage);
final Step cucumberStep = new Step(new ArrayList<Comment>(), step.getKeyword().getText(), step.getStepName(), 0, null, null);
final String snippet = new SnippetGenerator(new JavaSnippet()).getSnippet(cucumberStep)
.replace("PendingException", CucumberJavaUtil.getCucumberPendingExceptionFqn(step))
.replaceFirst("@", methodAnnotation)
.replaceAll("\\\\\\\\", "\\\\")
.replaceAll("\\\\d", "\\\\\\\\d");
JVMElementFactory factory = JVMElementFactories.requireFactory(language, step.getProject());
return factory.createMethodFromText(snippet, step);
}
}