Add Base class for configure modules with gradle

This commit is contained in:
Natalia.Ukhorskaya
2013-09-03 12:30:31 +04:00
parent 230be28884
commit f5c730514b
5 changed files with 434 additions and 7 deletions
@@ -168,4 +168,8 @@ public class ConfigureKotlinInProjectUtils {
private ConfigureKotlinInProjectUtils() {
}
public static void showInfoNotification(@NotNull String message) {
Notifications.Bus.notify(new Notification("Configure Kotlin", "Configure Kotlin", message, NotificationType.INFORMATION));
}
}
@@ -0,0 +1,303 @@
/*
* 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.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.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.+", "0.1-SNAPSHOT"};
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) {
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(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());
}
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");
}
}
@@ -1,7 +1,5 @@
package org.jetbrains.jet.plugin.configuration;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
@@ -24,7 +22,7 @@ import org.jetbrains.jet.plugin.framework.ui.FileUIUtils;
import java.io.File;
import static com.intellij.notification.Notifications.Bus;
import static org.jetbrains.jet.plugin.configuration.ConfigureKotlinInProjectUtils.showInfoNotification;
public abstract class KotlinWithLibraryConfigurator implements KotlinProjectConfigurator {
@@ -268,10 +266,6 @@ public abstract class KotlinWithLibraryConfigurator implements KotlinProjectConf
return copy;
}
private static void showInfoNotification(@NotNull String message) {
Bus.notify(new Notification("Configure Kotlin", "Configure Kotlin", message, NotificationType.INFORMATION));
}
protected boolean needToChooseJarPath(@NotNull Project project) {
String defaultPath = getDefaultPathToJarFile(project);
return !isProjectLibraryPresent(project) && !isJarPresent(defaultPath);
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.jet.plugin.framework.ui.ConfigureDialogWithModulesAndVersion">
<grid id="cbd77" binding="contentPane" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="48" y="54" width="436" height="297"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<vspacer id="bb37c">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="46726" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="10" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="f6ae3" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Choose Kotlin plugin version"/>
</properties>
</component>
<component id="1034b" class="javax.swing.JComboBox" binding="kotlinVersionComboBox">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
<grid id="f8666" binding="chooseModulesPanelPlace" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
</children>
</grid>
<buttonGroups>
<group name="sourceGroup">
<member id="730af"/>
<member id="c709f"/>
</group>
</buttonGroups>
</form>
@@ -0,0 +1,70 @@
/*
* 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.framework.ui;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class ConfigureDialogWithModulesAndVersion extends DialogWrapper {
private final ChooseModulePanel chooseModulePanel;
private JPanel contentPane;
private JPanel chooseModulesPanelPlace;
private JComboBox kotlinVersionComboBox;
public ConfigureDialogWithModulesAndVersion(
@NotNull Project project,
@NotNull List<Module> modules,
@NotNull String[] kotlinVersions
) {
super(project);
setTitle("Configure Kotlin in Project");
init();
chooseModulePanel = new ChooseModulePanel(project, modules);
chooseModulesPanelPlace.add(chooseModulePanel.getContentPane(), BorderLayout.CENTER);
chooseModulePanel.showSeparator();
for (String version : kotlinVersions) {
kotlinVersionComboBox.addItem(version);
}
}
public List<Module> getModulesToConfigure() {
return chooseModulePanel.getModulesToConfigure();
}
public String getKotlinVersion() {
return (String) kotlinVersionComboBox.getSelectedItem();
}
@Nullable
@Override
protected JComponent createCenterPanel() {
return contentPane;
}
}