package org.jetbrains.plugins.cucumber.java.steps; import com.intellij.codeInsight.CodeInsightUtilCore; import com.intellij.lang.Language; 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.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex; import com.intellij.psi.impl.source.tree.Factory; import com.intellij.psi.impl.source.tree.LeafElement; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; import cucumber.runtime.snippets.CamelCaseConcatenator; import cucumber.runtime.snippets.FunctionNameGenerator; import cucumber.runtime.snippets.SnippetGenerator; import gherkin.formatter.model.Step; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.cucumber.java.CucumberJavaUtil; import org.jetbrains.plugins.cucumber.psi.GherkinStep; import java.util.ArrayList; import java.util.Collection; public class Java8StepDefinitionCreator extends JavaStepDefinitionCreator { public static final String CUCUMBER_API_JAVA8_EN = "cucumber.api.java8.En"; @NotNull @Override public PsiFile createStepDefinitionContainer(@NotNull PsiDirectory dir, @NotNull String name) { final PsiFile result = super.createStepDefinitionContainer(dir, name); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(dir.getProject()).getFileIndex(); final Module module = fileIndex.getModuleForFile(result.getVirtualFile()); assert module != null; final GlobalSearchScope dependenciesScope = module.getModuleWithDependenciesAndLibrariesScope(true); Collection<PsiClass> stepDefContainerInterfaces = JavaFullClassNameIndex.getInstance().get( CUCUMBER_API_JAVA8_EN.hashCode(), module.getProject(), dependenciesScope); if (stepDefContainerInterfaces.size() > 0) { final PsiClass stepDefContainerInterface = stepDefContainerInterfaces.iterator().next(); final PsiClass createPsiClass = PsiTreeUtil.getChildOfType(result, PsiClass.class); assert createPsiClass != null; final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(dir.getProject()).getElementFactory(); PsiJavaCodeReferenceElement ref = elementFactory.createClassReferenceElement(stepDefContainerInterface); if (stepDefContainerInterface.isInterface()) { PsiReferenceList implementsList = createPsiClass.getImplementsList(); if (implementsList != null) { WriteAction.run(() -> implementsList.add(ref)); } } } return result; } @NotNull @Override public String getStepDefinitionFilePath(@NotNull PsiFile file) { return super.getStepDefinitionFilePath(file) + " (Java 8 style)"; } private static PsiMethod getConstructor(PsiClass clazz) { if (clazz.getConstructors().length == 0) { JVMElementFactory factory = JVMElementFactories.requireFactory(clazz.getLanguage(), clazz.getProject()); PsiMethod constructor = factory.createConstructor(clazz.getName()); return (PsiMethod)clazz.add(constructor); } return clazz.getConstructors()[0]; } @Override public boolean createStepDefinition(@NotNull GherkinStep step, @NotNull PsiFile file) { if (!(file instanceof PsiClassOwner)) return false; final PsiClass clazz = PsiTreeUtil.getChildOfType(file, PsiClass.class); if (clazz == null) { return false; } final Project project = file.getProject(); closeActiveTemplateBuilders(file); PsiDocumentManager.getInstance(project).commitAllDocuments(); final PsiElement stepDef = buildStepDefinitionByStep(step, file.getLanguage()); final PsiMethod constructor = getConstructor(clazz); final PsiCodeBlock constructorBody = constructor.getBody(); if (constructorBody == null) { return false; } PsiElement anchor = constructorBody.getFirstChild(); if (constructorBody.getStatements().length > 0) { anchor = constructorBody.getStatements()[constructorBody.getStatements().length - 1]; } PsiElement addedStepDef = constructorBody.addAfter(stepDef, anchor); wrapStepDefWithLineBreakAndSemicolon(addedStepDef); addedStepDef = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(addedStepDef); JavaCodeStyleManager.getInstance(project).shortenClassReferences(addedStepDef); Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); assert editor != null; if (!(addedStepDef instanceof PsiMethodCallExpression)) { return false; } PsiMethodCallExpression stepDefCall = (PsiMethodCallExpression)addedStepDef; if (stepDefCall.getArgumentList().getExpressions().length < 2) { return false; } final PsiExpression regexpElement = stepDefCall.getArgumentList().getExpressions()[0]; final PsiExpression secondArgument = stepDefCall.getArgumentList().getExpressions()[1]; if (!(secondArgument instanceof PsiLambdaExpression)) { return false; } PsiLambdaExpression lambda = (PsiLambdaExpression)secondArgument; final PsiParameterList blockVars = lambda.getParameterList(); PsiElement lambdaBody = lambda.getBody(); if (!(lambdaBody instanceof PsiCodeBlock)) { return false; } final PsiCodeBlock body = (PsiCodeBlock)lambdaBody; runTemplateBuilderOnAddedStep(editor, addedStepDef, regexpElement, blockVars, body); return true; } protected void wrapStepDefWithLineBreakAndSemicolon(PsiElement addedStepDef) { LeafElement linebreak = Factory.createSingleLeafElement(TokenType.WHITE_SPACE, "\n", 0, 1, null, addedStepDef.getManager()); addedStepDef.getParent().addBefore(linebreak.getPsi(), addedStepDef); LeafElement semicolon = Factory.createSingleLeafElement(JavaTokenType.SEMICOLON, ";", 0, 1, null, addedStepDef.getManager()); addedStepDef.getParent().addAfter(semicolon.getPsi(), addedStepDef); } private static PsiElement buildStepDefinitionByStep(@NotNull final GherkinStep step, Language language) { final Step cucumberStep = new Step(new ArrayList<>(), step.getKeyword().getText(), step.getStepName(), 0, null, null); final SnippetGenerator generator = new SnippetGenerator(new Java8Snippet()); final String snippet = generator.getSnippet(cucumberStep, new FunctionNameGenerator(new CamelCaseConcatenator())) .replace("PendingException", CucumberJavaUtil.getCucumberPendingExceptionFqn(step)) .replaceAll("\\\\\\\\", "\\\\") .replaceAll("\\\\d", "\\\\\\\\d"); JVMElementFactory factory = JVMElementFactories.requireFactory(language, step.getProject()); return factory.createExpressionFromText(snippet, step); } }