Java tutorial
/* * Copyright 2010-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.jet.plugin.configuration; import com.intellij.codeInsight.CodeInsightUtilCore; import com.intellij.ide.actions.OpenFileAction; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.WritingAccessProvider; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.plugin.framework.ui.ConfigureDialogWithModulesAndVersion; import org.jetbrains.jet.plugin.project.ProjectStructureUtil; import org.jetbrains.plugins.gradle.util.GradleConstants; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; import java.io.File; import java.util.List; import static org.jetbrains.jet.plugin.configuration.ConfigureKotlinInProjectUtils.showInfoNotification; public abstract class KotlinWithGradleConfigurator implements KotlinProjectConfigurator { private static final String[] KOTLIN_VERSIONS = { "0.6.+" }; protected static final String VERSION_TEMPLATE = "$VERSION$"; protected static final String CLASSPATH = "classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$ext.kotlin_version\""; protected static final String SNAPSHOT_REPOSITORY = "maven {\nurl 'http://oss.sonatype.org/content/repositories/snapshots'\n}"; protected static final String REPOSITORY = "mavenCentral()\n"; protected static final String LIBRARY = "compile \"org.jetbrains.kotlin:kotlin-stdlib:$ext.kotlin_version\""; protected static final String SOURCE_SET = "main.java.srcDirs += 'src/main/kotlin'\n"; protected static final String VERSION = String.format("ext.kotlin_version = '%s'", VERSION_TEMPLATE); @Override public boolean isConfigured(@NotNull Module module) { if (ProjectStructureUtil.isJavaKotlinModule(module)) { return true; } GroovyFile moduleGradleFile = getBuildGradleFile(module.getProject(), getDefaultPathToBuildGradleFile(module)); if (moduleGradleFile != null && isFileConfigured(moduleGradleFile)) { return true; } GroovyFile projectGradleFile = getBuildGradleFile(module.getProject(), getDefaultPathToBuildGradleFile(module.getProject())); return projectGradleFile != null && isFileConfigured(projectGradleFile); } private boolean isFileConfigured(GroovyFile projectGradleFile) { String fileText = projectGradleFile.getText(); return fileText.contains(getApplyPluginDirective()) && fileText.contains(LIBRARY); } @Override public void configure(@NotNull Project project) { List<Module> nonConfiguredModules = ConfigureKotlinInProjectUtils.getNonConfiguredModules(project, this); ConfigureDialogWithModulesAndVersion dialog = new ConfigureDialogWithModulesAndVersion(project, nonConfiguredModules, KOTLIN_VERSIONS); dialog.show(); if (!dialog.isOK()) return; for (Module module : dialog.getModulesToConfigure()) { String gradleFilePath = getDefaultPathToBuildGradleFile(module); GroovyFile file = getBuildGradleFile(project, gradleFilePath); if (file != null && canConfigureFile(file)) { changeGradleFile(file, dialog.getKotlinVersion()); OpenFileAction.openFile(gradleFilePath, project); } else { showErrorMessage(project, "Cannot find build.gradle file for module " + module.getName()); } } } private void addElements(@NotNull GroovyFile file, @NotNull String version) { GrClosableBlock buildScriptBlock = getBuildScriptBlock(file); addFirstExpressionInBlockIfNeeded(VERSION.replace(VERSION_TEMPLATE, version), buildScriptBlock); GrClosableBlock buildScriptRepositoriesBlock = getBuildScriptRepositoriesBlock(file); if (isSnapshot(version)) { addLastExpressionInBlockIfNeeded(SNAPSHOT_REPOSITORY, buildScriptRepositoriesBlock); } else if (!buildScriptRepositoriesBlock.getText().contains(REPOSITORY)) { addLastExpressionInBlockIfNeeded(REPOSITORY, buildScriptRepositoriesBlock); } GrClosableBlock buildScriptDependenciesBlock = getBuildScriptDependenciesBlock(file); addLastExpressionInBlockIfNeeded(CLASSPATH, buildScriptDependenciesBlock); if (!file.getText().contains(getApplyPluginDirective())) { GrExpression apply = GroovyPsiElementFactory.getInstance(file.getProject()) .createExpressionFromText(getApplyPluginDirective()); GrApplicationStatement applyStatement = getApplyStatement(file); if (applyStatement != null) { file.addAfter(apply, applyStatement); } else { file.addAfter(apply, buildScriptBlock.getParent()); } } GrClosableBlock repositoriesBlock = getRepositoriesBlock(file); if (isSnapshot(version)) { addLastExpressionInBlockIfNeeded(SNAPSHOT_REPOSITORY, repositoriesBlock); } else if (!repositoriesBlock.getText().contains(REPOSITORY)) { addLastExpressionInBlockIfNeeded(REPOSITORY, repositoriesBlock); } GrClosableBlock dependenciesBlock = getDependenciesBlock(file); addLastExpressionInBlockIfNeeded(LIBRARY, dependenciesBlock); addSourceSetsBlock(file); } protected abstract String getApplyPluginDirective(); protected abstract void addSourceSetsBlock(@NotNull GroovyFile file); protected static boolean canConfigureFile(@NotNull GroovyFile file) { return WritingAccessProvider.isPotentiallyWritable(file.getVirtualFile(), null); } @Nullable protected static GroovyFile getBuildGradleFile(Project project, String path) { VirtualFile file = VfsUtil.findFileByIoFile(new File(path), true); if (file == null) { return null; } PsiFile psiFile = PsiManager.getInstance(project).findFile(file); if (!(psiFile instanceof GroovyFile)) return null; return (GroovyFile) psiFile; } @NotNull protected static String getDefaultPathToBuildGradleFile(@NotNull Project project) { return project.getBasePath() + "/" + GradleConstants.DEFAULT_SCRIPT_NAME; } @NotNull protected static String getDefaultPathToBuildGradleFile(@NotNull Module module) { return new File(module.getModuleFilePath()).getParent() + "/" + GradleConstants.DEFAULT_SCRIPT_NAME; } protected static boolean isSnapshot(@NotNull String version) { return version.contains("SNAPSHOT"); } protected void changeGradleFile(@NotNull final GroovyFile groovyFile, @NotNull final String version) { new WriteCommandAction(groovyFile.getProject()) { @Override protected void run(Result result) { addElements(groovyFile, version); CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(groovyFile); } }.execute(); VirtualFile virtualFile = groovyFile.getVirtualFile(); if (virtualFile != null) { showInfoNotification(virtualFile.getPath() + " was modified"); } } @NotNull protected static GrClosableBlock getDependenciesBlock(@NotNull GrStatementOwner file) { return getBlockOrCreate(file, "dependencies"); } @NotNull protected static GrClosableBlock getSourceSetsBlock(@NotNull GrStatementOwner parent) { return getBlockOrCreate(parent, "sourceSets"); } @NotNull protected static GrClosableBlock getBuildScriptBlock(@NotNull GrStatementOwner file) { return getBlockOrCreate(file, "buildscript"); } @NotNull protected static GrClosableBlock getBuildScriptDependenciesBlock(@NotNull GrStatementOwner file) { GrClosableBlock buildScript = getBuildScriptBlock(file); return getBlockOrCreate(buildScript, "dependencies"); } @NotNull protected static GrClosableBlock getBuildScriptRepositoriesBlock(@NotNull GrStatementOwner file) { GrClosableBlock buildScript = getBuildScriptBlock(file); return getBlockOrCreate(buildScript, "repositories"); } @NotNull protected static GrClosableBlock getRepositoriesBlock(@NotNull GrStatementOwner file) { return getBlockOrCreate(file, "repositories"); } @NotNull protected static GrClosableBlock getBlockOrCreate(@NotNull GrStatementOwner parent, @NotNull String name) { GrClosableBlock block = getBlockByName(parent, name); if (block == null) { GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(parent.getProject()); GrExpression newBlock = factory.createExpressionFromText(name + "{\n}\n"); GrStatement[] statements = parent.getStatements(); if (statements.length > 0) { parent.addAfter(newBlock, statements[statements.length - 1]); } else { parent.addAfter(newBlock, parent.getFirstChild()); } block = getBlockByName(parent, name); assert block != null : "Block should be non-null because it is created"; } return block; } protected static void addLastExpressionInBlockIfNeeded(@NotNull String text, @NotNull GrClosableBlock block) { addExpressionInBlockIfNeeded(text, block, false); } protected static void addFirstExpressionInBlockIfNeeded(@NotNull String text, @NotNull GrClosableBlock block) { addExpressionInBlockIfNeeded(text, block, true); } @Nullable private static GrClosableBlock getBlockByName(@NotNull PsiElement parent, @NotNull String name) { GrMethodCallExpression[] allExpressions = PsiTreeUtil.getChildrenOfType(parent, GrMethodCallExpression.class); if (allExpressions != null) { for (GrMethodCallExpression expression : allExpressions) { GrExpression invokedExpression = expression.getInvokedExpression(); if (invokedExpression == null) continue; if (expression.getClosureArguments().length == 0) continue; String expressionText = invokedExpression.getText(); if (expressionText.equals(name)) return expression.getClosureArguments()[0]; } } return null; } private static void addExpressionInBlockIfNeeded(@NotNull String text, @NotNull GrClosableBlock block, boolean isFirst) { if (block.getText().contains(text)) return; GrExpression newStatement = GroovyPsiElementFactory.getInstance(block.getProject()) .createExpressionFromText(text); CodeStyleManager.getInstance(block.getProject()).reformat(newStatement); GrStatement[] statements = block.getStatements(); if (!isFirst && statements.length > 0) { GrStatement lastStatement = statements[statements.length - 1]; if (lastStatement != null) { block.addAfter(newStatement, lastStatement); } } else { PsiElement firstChild = block.getFirstChild(); if (firstChild != null) { block.addAfter(newStatement, firstChild); } } } @Nullable private static GrApplicationStatement getApplyStatement(@NotNull GroovyFile file) { GrApplicationStatement[] applyStatement = PsiTreeUtil.getChildrenOfType(file, GrApplicationStatement.class); if (applyStatement == null) return null; for (GrApplicationStatement callExpression : applyStatement) { GrExpression invokedExpression = callExpression.getInvokedExpression(); if (invokedExpression != null && invokedExpression.getText().equals("apply")) { return callExpression; } } return null; } protected static void showErrorMessage(@NotNull Project project, @Nullable String message) { Messages.showErrorDialog(project, "<html>Couldn't configure kotlin-gradle plugin automatically.<br/>" + (message != null ? message : "") + "See manual installation instructions <a href=\"http://confluence.jetbrains.com/display/Kotlin/Kotlin+Build+Tools#KotlinBuildTools-Gradle\">here</a></html>", "Configure Kotlin-Gradle Plugin"); } }