Merge remote-tracking branch 'origin/master'
[idea/community.git] / plugins / gradle / src / org / jetbrains / plugins / gradle / service / project / wizard / GradleModuleBuilder.java
1 /*
2  * Copyright 2000-2013 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.plugins.gradle.service.project.wizard;
17
18 import com.intellij.ide.fileTemplates.FileTemplate;
19 import com.intellij.ide.fileTemplates.FileTemplateManager;
20 import com.intellij.ide.util.projectWizard.JavaModuleBuilder;
21 import com.intellij.ide.util.projectWizard.ModuleWizardStep;
22 import com.intellij.ide.util.projectWizard.WizardContext;
23 import com.intellij.openapi.Disposable;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
26 import com.intellij.openapi.externalSystem.service.project.wizard.AbstractExternalModuleBuilder;
27 import com.intellij.openapi.externalSystem.service.project.wizard.ExternalModuleSettingsStep;
28 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
29 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
30 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
31 import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
32 import com.intellij.openapi.fileEditor.FileDocumentManager;
33 import com.intellij.openapi.module.*;
34 import com.intellij.openapi.options.ConfigurationException;
35 import com.intellij.openapi.project.Project;
36 import com.intellij.openapi.projectRoots.JavaSdkType;
37 import com.intellij.openapi.projectRoots.SdkTypeId;
38 import com.intellij.openapi.roots.ContentEntry;
39 import com.intellij.openapi.roots.ModifiableRootModel;
40 import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
41 import com.intellij.openapi.util.io.FileUtil;
42 import com.intellij.openapi.util.io.FileUtilRt;
43 import com.intellij.openapi.util.text.StringUtil;
44 import com.intellij.openapi.vfs.LocalFileSystem;
45 import com.intellij.openapi.vfs.VfsUtil;
46 import com.intellij.openapi.vfs.VfsUtilCore;
47 import com.intellij.openapi.vfs.VirtualFile;
48 import com.intellij.util.containers.ContainerUtil;
49 import org.jetbrains.annotations.NotNull;
50 import org.jetbrains.annotations.Nullable;
51 import org.jetbrains.plugins.gradle.service.settings.GradleProjectSettingsControl;
52 import org.jetbrains.plugins.gradle.settings.DistributionType;
53 import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
54 import org.jetbrains.plugins.gradle.util.GradleConstants;
55
56 import javax.swing.*;
57 import java.io.File;
58 import java.io.IOException;
59 import java.util.Map;
60
61 /**
62  * @author Denis Zhdanov
63  * @since 6/26/13 11:10 AM
64  */
65 public class GradleModuleBuilder extends AbstractExternalModuleBuilder<GradleProjectSettings> {
66
67   private static final Logger LOG = Logger.getInstance(GradleModuleBuilder.class);
68
69   private static final String TEMPLATE_GRADLE_SETTINGS = "Gradle Settings.gradle";
70   private static final String TEMPLATE_GRADLE_SETTINGS_MERGE = "Gradle Settings merge.gradle";
71   private static final String TEMPLATE_GRADLE_BUILD_WITH_WRAPPER = "Gradle Build Script with wrapper.gradle";
72   private static final String DEFAULT_TEMPLATE_GRADLE_BUILD = "Gradle Build Script.gradle";
73
74   private static final String TEMPLATE_ATTRIBUTE_PROJECT_NAME = "PROJECT_NAME";
75   private static final String TEMPLATE_ATTRIBUTE_MODULE_DIR_NAME = "MODULE_DIR_NAME";
76   private static final String TEMPLATE_ATTRIBUTE_MODULE_NAME = "MODULE_NAME";
77
78   private @NotNull WizardContext myWizardContext;
79
80   public GradleModuleBuilder() {
81     super(GradleConstants.SYSTEM_ID, new GradleProjectSettings());
82   }
83
84   @Override
85   public void setupRootModel(final ModifiableRootModel modifiableRootModel) throws ConfigurationException {
86     String contentEntryPath = getContentEntryPath();
87     if (StringUtil.isEmpty(contentEntryPath)) {
88       return;
89     }
90     File contentRootDir = new File(contentEntryPath);
91     FileUtilRt.createDirectory(contentRootDir);
92     LocalFileSystem fileSystem = LocalFileSystem.getInstance();
93     VirtualFile modelContentRootDir = fileSystem.refreshAndFindFileByIoFile(contentRootDir);
94     if (modelContentRootDir == null) {
95       return;
96     }
97
98     modifiableRootModel.addContentEntry(modelContentRootDir);
99     // todo this should be moved to generic ModuleBuilder
100     if (myJdk != null){
101       modifiableRootModel.setSdk(myJdk);
102     } else {
103       modifiableRootModel.inheritSdk();
104     }
105
106     final Project project = modifiableRootModel.getProject();
107
108     setupGradleBuildFile(modelContentRootDir);
109     setupGradleSettingsFile(modelContentRootDir, modifiableRootModel);
110
111     if (myWizardContext.isCreatingNewProject()) {
112       String externalProjectPath = FileUtil.toCanonicalPath(project.getBasePath());
113       getExternalProjectSettings().setExternalProjectPath(externalProjectPath);
114       AbstractExternalSystemSettings settings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID);
115       project.putUserData(ExternalSystemDataKeys.NEWLY_CREATED_PROJECT, Boolean.TRUE);
116       //noinspection unchecked
117       settings.linkProject(getExternalProjectSettings());
118     }
119     else {
120       FileDocumentManager.getInstance().saveAllDocuments();
121       ExternalSystemUtil.refreshProjects(project, GradleConstants.SYSTEM_ID, false);
122     }
123   }
124
125   @Override
126   public ModuleWizardStep[] createWizardSteps(@NotNull WizardContext wizardContext, @NotNull ModulesProvider modulesProvider) {
127     myWizardContext = wizardContext;
128     return super.createWizardSteps(wizardContext, modulesProvider);
129   }
130
131   @Nullable
132   @Override
133   public ModuleWizardStep getCustomOptionsStep(WizardContext context, Disposable parentDisposable) {
134     if (!myWizardContext.isCreatingNewProject()) return new ModuleWizardStep() {
135       @Override
136       public JComponent getComponent() {
137         return new JPanel();
138       }
139
140       @Override
141       public void updateDataModel() {
142
143       }
144     };
145     final GradleProjectSettingsControl settingsControl = new GradleProjectSettingsControl(getExternalProjectSettings(), myWizardContext.getProject());
146     return new ExternalModuleSettingsStep<GradleProjectSettings>(this, settingsControl);
147   }
148
149   @Override
150   public boolean isSuitableSdkType(SdkTypeId sdk) {
151     return sdk instanceof JavaSdkType;
152   }
153
154   @Override
155   public String getParentGroup() {
156     return JavaModuleType.BUILD_TOOLS_GROUP;
157   }
158
159   @Override
160   public int getWeight() {
161     return JavaModuleBuilder.BUILD_SYSTEM_WEIGHT;
162   }
163
164   @Override
165   public ModuleType getModuleType() {
166     return StdModuleTypes.JAVA;
167   }
168
169   @Nullable
170   private VirtualFile setupGradleBuildFile(@NotNull VirtualFile modelContentRootDir) throws ConfigurationException {
171     final VirtualFile file = getExternalProjectConfigFile(modelContentRootDir.getPath(), GradleConstants.DEFAULT_SCRIPT_NAME);
172     final String templateName = getExternalProjectSettings().getDistributionType() == DistributionType.WRAPPED
173                                 ? TEMPLATE_GRADLE_BUILD_WITH_WRAPPER
174                                 : DEFAULT_TEMPLATE_GRADLE_BUILD;
175
176     Map attributes = ContainerUtil.newHashMap();
177     if (file != null) {
178       saveFile(file, templateName, attributes);
179     }
180     return file;
181   }
182
183   @Nullable
184   private VirtualFile setupGradleSettingsFile(@NotNull VirtualFile modelContentRootDir, @NotNull ModifiableRootModel model)
185     throws ConfigurationException {
186     VirtualFile file = null;
187     if (myWizardContext.isCreatingNewProject()) {
188       final String moduleDirName = VfsUtilCore.getRelativePath(modelContentRootDir, model.getProject().getBaseDir(), '/');
189       file = getExternalProjectConfigFile(model.getProject().getBasePath(), GradleConstants.SETTINGS_FILE_NAME);
190       if (file == null) return null;
191
192       Map<String, String> attributes = ContainerUtil.newHashMap();
193       final String projectName = model.getProject().getName();
194       attributes.put(TEMPLATE_ATTRIBUTE_PROJECT_NAME, projectName);
195       attributes.put(TEMPLATE_ATTRIBUTE_MODULE_DIR_NAME, moduleDirName);
196       attributes.put(TEMPLATE_ATTRIBUTE_MODULE_NAME, model.getModule().getName());
197       saveFile(file, TEMPLATE_GRADLE_SETTINGS, attributes);
198     }
199     else {
200       Map<String, Module> moduleMap = ContainerUtil.newHashMap();
201       for (Module module : ModuleManager.getInstance(model.getProject()).getModules()) {
202         for (ContentEntry contentEntry : model.getContentEntries()) {
203           if (contentEntry.getFile() != null) {
204             moduleMap.put(contentEntry.getFile().getPath(), module);
205           }
206         }
207       }
208
209       VirtualFile virtualFile = modelContentRootDir;
210       Module module = null;
211       while (virtualFile != null && module == null) {
212         module = moduleMap.get(virtualFile.getPath());
213         virtualFile = virtualFile.getParent();
214       }
215
216       if (module != null) {
217         String rootProjectPath = module.getOptionValue(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY);
218
219         if (!StringUtil.isEmpty(rootProjectPath)) {
220           VirtualFile rootProjectFile = VfsUtil.findFileByIoFile(new File(rootProjectPath), true);
221           if (rootProjectFile == null) return null;
222
223           final String moduleDirName = VfsUtilCore.getRelativePath(modelContentRootDir, rootProjectFile, '/');
224           file = getExternalProjectConfigFile(rootProjectPath, GradleConstants.SETTINGS_FILE_NAME);
225           if (file == null) return null;
226
227           Map<String, String> attributes = ContainerUtil.newHashMap();
228           attributes.put(TEMPLATE_ATTRIBUTE_MODULE_DIR_NAME, moduleDirName);
229           attributes.put(TEMPLATE_ATTRIBUTE_MODULE_NAME, model.getModule().getName());
230           appendToFile(file, TEMPLATE_GRADLE_SETTINGS_MERGE, attributes);
231         }
232       }
233     }
234     return file;
235   }
236
237   private static void saveFile(@NotNull VirtualFile file, @NotNull String templateName, @Nullable Map templateAttributes)
238     throws ConfigurationException {
239     FileTemplateManager manager = FileTemplateManager.getDefaultInstance();
240     FileTemplate template = manager.getInternalTemplate(templateName);
241     try {
242       VfsUtil.saveText(file, templateAttributes != null ? template.getText(templateAttributes) : template.getText());
243     }
244     catch (IOException e) {
245       LOG.warn(String.format("Unexpected exception on applying template %s config", GradleConstants.SYSTEM_ID.getReadableName()), e);
246       throw new ConfigurationException(
247         e.getMessage(), String.format("Can't apply %s template config text", GradleConstants.SYSTEM_ID.getReadableName())
248       );
249     }
250   }
251
252   private static void appendToFile(@NotNull VirtualFile file, @NotNull String templateName, @Nullable Map templateAttributes)
253     throws ConfigurationException {
254     FileTemplateManager manager = FileTemplateManager.getDefaultInstance();
255     FileTemplate template = manager.getInternalTemplate(templateName);
256     try {
257       VfsUtil.saveText(file, VfsUtilCore.loadText(file) +
258                              (templateAttributes != null ? template.getText(templateAttributes) : template.getText()));
259     }
260     catch (IOException e) {
261       LOG.warn(String.format("Unexpected exception on appending template %s config", GradleConstants.SYSTEM_ID.getReadableName()), e);
262       throw new ConfigurationException(
263         e.getMessage(), String.format("Can't append %s template config text", GradleConstants.SYSTEM_ID.getReadableName())
264       );
265     }
266   }
267
268
269   @Nullable
270   private static VirtualFile getExternalProjectConfigFile(@NotNull String parent, @NotNull String fileName) {
271     File file = new File(parent, fileName);
272     FileUtilRt.createIfNotExists(file);
273     return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
274   }
275 }