blob: 5ddafe07443e67f0dcb8b06cf75b5959c9fdd42b [file] [log] [blame]
/*
* Copyright 2000-2012 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 com.intellij.platform.templates;
import com.intellij.execution.RunManager;
import com.intellij.execution.configurations.ModuleBasedConfiguration;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
import com.intellij.ide.util.newProjectWizard.modes.ImportImlMode;
import com.intellij.ide.util.projectWizard.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.module.*;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.NullableComputable;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.RefreshQueue;
import com.intellij.platform.templates.github.ZipUtil;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
import org.jdom.JDOMException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipInputStream;
/**
* @author Dmitry Avdeev
* Date: 10/19/12
*/
public class TemplateModuleBuilder extends ModuleBuilder {
public static final String UTF_8 = "UTF-8";
private final ModuleType myType;
private List<WizardInputField> myAdditionalFields;
private ArchivedProjectTemplate myTemplate;
private boolean myProjectMode;
public TemplateModuleBuilder(ArchivedProjectTemplate template, ModuleType moduleType, List<WizardInputField> additionalFields) {
myTemplate = template;
myType = moduleType;
myAdditionalFields = additionalFields;
}
@Override
public void setupRootModel(ModifiableRootModel modifiableRootModel) throws ConfigurationException {
}
@Override
public ModuleWizardStep[] createWizardSteps(@NotNull WizardContext wizardContext, @NotNull ModulesProvider modulesProvider) {
ModuleBuilder builder = myType.createModuleBuilder();
return builder.createWizardSteps(wizardContext, modulesProvider);
}
@Override
protected List<WizardInputField> getAdditionalFields() {
return myAdditionalFields;
}
@Override
public Module commitModule(@NotNull final Project project, ModifiableModuleModel model) {
if (myProjectMode) {
final Module[] modules = ModuleManager.getInstance(project).getModules();
if (modules.length > 0) {
final Module module = modules[0];
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
try {
setupModule(module);
}
catch (ConfigurationException e) {
LOG.error(e);
}
}
});
StartupManager.getInstance(project).registerPostStartupActivity(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
try {
ModifiableModuleModel modifiableModuleModel = ModuleManager.getInstance(project).getModifiableModel();
modifiableModuleModel.renameModule(module, module.getProject().getName());
modifiableModuleModel.commit();
fixModuleName(module);
}
catch (ModuleWithNameAlreadyExists exists) {
// do nothing
}
}
});
}
});
return module;
}
return null;
}
else {
return super.commitModule(project, model);
}
}
@Nullable
@Override
public String getBuilderId() {
return myTemplate.getName();
}
@Override
public ModuleType getModuleType() {
return myType;
}
@Override
public Icon getNodeIcon() {
return myTemplate.getIcon();
}
@Override
public boolean isTemplateBased() {
return true;
}
@NotNull
@Override
public Module createModule(@NotNull ModifiableModuleModel moduleModel)
throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException {
final String path = getContentEntryPath();
final ExistingModuleLoader loader = ImportImlMode.setUpLoader(getModuleFilePath());
unzip(loader.getName(), path, true);
Module module = loader.createModule(moduleModel);
if (myProjectMode) {
moduleModel.renameModule(module, module.getProject().getName());
}
fixModuleName(module);
return module;
}
private void fixModuleName(Module module) {
List<RunConfiguration> configurations = RunManager.getInstance(module.getProject()).getAllConfigurationsList();
for (RunConfiguration configuration : configurations) {
if (configuration instanceof ModuleBasedConfiguration) {
((ModuleBasedConfiguration)configuration).getConfigurationModule().setModule(module);
}
}
ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel();
for (WizardInputField field : myAdditionalFields) {
ProjectTemplateParameterFactory factory = WizardInputField.getFactoryById(field.getId());
factory.applyResult(field.getValue(), model);
}
model.commit();
}
private WizardInputField getBasePackageField() {
for (WizardInputField field : getAdditionalFields()) {
if (ProjectTemplateParameterFactory.IJ_BASE_PACKAGE.equals(field.getId())) {
return field;
}
}
return null;
}
private void unzip(final @Nullable String projectName, String path, final boolean moduleMode) {
File dir = new File(path);
ZipInputStream zipInputStream = null;
final WizardInputField basePackage = getBasePackageField();
try {
zipInputStream = myTemplate.getStream();
NullableFunction<String, String> pathConvertor = new NullableFunction<String, String>() {
@Nullable
@Override
public String fun(String path) {
if (moduleMode && path.contains(Project.DIRECTORY_STORE_FOLDER)) return null;
if (basePackage != null) {
return path.replace(getPathFragment(basePackage.getDefaultValue()), getPathFragment(basePackage.getValue()));
}
return path;
}
};
ZipUtil.unzip(ProgressManager.getInstance().getProgressIndicator(), dir, zipInputStream, pathConvertor, new ZipUtil.ContentProcessor() {
@Override
public byte[] processContent(byte[] content, File file) throws IOException {
FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension(FileUtilRt.getExtension(file.getName()));
return fileType.isBinary() ? content : processTemplates(projectName, new String(content), file);
}
}, true);
String iml = ContainerUtil.find(dir.list(), new Condition<String>() {
@Override
public boolean value(String s) {
return s.endsWith(".iml");
}
});
if (moduleMode) {
File from = new File(path, iml);
File to = new File(getModuleFilePath());
if (!from.renameTo(to)) {
throw new IOException("Can't rename " + from + " to " + to);
}
}
VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(dir);
if (virtualFile == null) {
throw new IOException("Can't find " + dir);
}
RefreshQueue.getInstance().refresh(false, true, null, virtualFile);
}
catch (IOException e) {
throw new RuntimeException(e);
}
finally {
StreamUtil.closeStream(zipInputStream);
}
}
private static String getPathFragment(String value) {
return "/" + value.replace('.', '/') + "/";
}
@SuppressWarnings("UseOfPropertiesAsHashtable")
@Nullable
private byte[] processTemplates(@Nullable String projectName, String content, File file) throws IOException {
for (WizardInputField field : myAdditionalFields) {
if (!field.acceptFile(file)) {
return null;
}
}
Properties properties = FileTemplateManager.getInstance().getDefaultProperties();
for (WizardInputField field : myAdditionalFields) {
properties.putAll(field.getValues());
}
if (projectName != null) {
properties.put(ProjectTemplateParameterFactory.IJ_PROJECT_NAME, projectName);
}
String merged = FileTemplateUtil.mergeTemplate(properties, content, true);
return merged.replace("\\$", "$").replace("\\#", "#").getBytes(UTF_8);
}
@Nullable
@Override
public Project createProject(String name, final String path) {
myProjectMode = true;
unzip(name, path, false);
return ApplicationManager.getApplication().runWriteAction(new NullableComputable<Project>() {
@Nullable
@Override
public Project compute() {
try {
return ProjectManagerEx.getInstanceEx().convertAndLoadProject(path);
}
catch (IOException e) {
LOG.error(e);
return null;
}
}
});
}
private final static Logger LOG = Logger.getInstance(TemplateModuleBuilder.class);
}