Java: Use template when generating module-info files (IDEA-184148)

This commit is contained in:
Pavel Dolgov
2018-02-15 15:56:45 +03:00
parent 9293ec30af
commit 0590c5ddd4
2 changed files with 72 additions and 45 deletions
@@ -3,7 +3,9 @@ package com.intellij.codeInspection.java19api;
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil;
import com.intellij.codeInspection.AbstractDependencyVisitor;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
@@ -45,6 +47,9 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
import static com.intellij.ide.fileTemplates.JavaTemplateUtil.INTERNAL_MODULE_INFO_TEMPLATE_NAME;
import static com.intellij.psi.PsiJavaModule.*;
/**
* @author Pavel.Dolgov
*/
@@ -207,7 +212,7 @@ public class Java9GenerateModuleDescriptorsAction extends AnAction {
}
void generate(THashMap<Module, List<File>> classFiles, int totalFiles) {
List<GeneratedCode> generatedCode;
List<ModuleInfo> moduleInfos;
try {
myProgressTracker.startPhase(RefactoringBundle.message("generate.module.descriptors.collecting.message"), totalFiles);
Map<String, Set<ModuleNode>> packagesDeclaredInModules = collectDependencies(classFiles);
@@ -218,22 +223,22 @@ public class Java9GenerateModuleDescriptorsAction extends AnAction {
myProgressTracker.nextPhase();
myProgressTracker.startPhase(RefactoringBundle.message("generate.module.descriptors.preparing.message"), myModuleNodes.size());
generatedCode = generateCode();
moduleInfos = prepareModuleInfos();
myProgressTracker.nextPhase();
}
finally {
myProgressTracker.dispose();
}
createFilesLater(generatedCode);
createFilesLater(moduleInfos);
}
private void createFilesLater(List<GeneratedCode> generatedCode) {
private void createFilesLater(List<ModuleInfo> moduleInfos) {
ApplicationManager.getApplication().invokeLater(() -> {
if (!myProject.isDisposed()) {
CommandProcessor.getInstance().executeCommand(myProject, () ->
((ApplicationImpl)ApplicationManager.getApplication()).runWriteActionWithCancellableProgressInDispatchThread(
COMMAND_TITLE, myProject, null,
indicator -> createFiles(myProject, generatedCode, indicator)), COMMAND_TITLE, null);
indicator -> createFiles(myProject, moduleInfos, indicator)), COMMAND_TITLE, null);
}
});
}
@@ -246,7 +251,12 @@ public class Java9GenerateModuleDescriptorsAction extends AnAction {
Module module = entry.getKey();
ModuleVisitor visitor = new ModuleVisitor(packageNamesCache);
for (File file : entry.getValue()) {
List<File> files = entry.getValue();
if (files.isEmpty()) {
LOG.info("Output directory for module " + module.getName() + " doesn't contain .class files");
continue;
}
for (File file : files) {
visitor.processFile(file);
myProgressTracker.increment();
}
@@ -285,7 +295,7 @@ public class Java9GenerateModuleDescriptorsAction extends AnAction {
}
else {
if (set.size() != 1) {
LOG.debug("Split package " + packageName + " in " + set);
LOG.info("Split package " + packageName + " found in " + set);
}
moduleNode.getDependencies().addAll(set);
}
@@ -294,11 +304,11 @@ public class Java9GenerateModuleDescriptorsAction extends AnAction {
}
}
private List<GeneratedCode> generateCode() {
List<GeneratedCode> generatedCode = new ArrayList<>();
private List<ModuleInfo> prepareModuleInfos() {
List<ModuleInfo> moduleInfo = new ArrayList<>();
for (ModuleNode moduleNode : myModuleNodes) {
if (moduleNode.getDescriptor() != null) {
LOG.debug("Descriptor already exists in " + moduleNode);
LOG.info("Module descriptor already exists in " + moduleNode);
continue;
}
for (String packageName : moduleNode.getDeclaredPackages()) {
@@ -307,50 +317,60 @@ public class Java9GenerateModuleDescriptorsAction extends AnAction {
}
}
StringBuilder text = new StringBuilder();
text.append("module ").append(moduleNode.getName()).append(" {");
List<ModuleNode> sortedDependencies = moduleNode.getSortedDependencies();
List<String> sortedExports = moduleNode.getSortedExports();
for (ModuleNode dependencyNode : sortedDependencies) {
if (!"java.base".equals(dependencyNode.getName())) {
text.append("\n requires ").append(dependencyNode.getName()).append(";");
}
}
if (!sortedDependencies.isEmpty() && !sortedExports.isEmpty()) {
text.append('\n');
}
for (String packageName : sortedExports) {
text.append("\n exports ").append(packageName).append(";");
}
text.append("\n}");
PsiDirectory rootDir = moduleNode.getRootDir();
if (rootDir != null) {
generatedCode.add(new GeneratedCode(rootDir, text.toString()));
List<String> dependencies = StreamEx.of(moduleNode.getSortedDependencies())
.map(ModuleNode::getName)
.filter(name -> !JAVA_BASE.equals(name))
.toList();
List<String> exports = moduleNode.getSortedExports();
moduleInfo.add(new ModuleInfo(rootDir, moduleNode.getName(), dependencies, exports));
}
else {
LOG.debug("Skipped module " + moduleNode);
LOG.info("Skipped module " + moduleNode + " because it doesn't have production source root");
}
myProgressTracker.increment();
}
return generatedCode;
return moduleInfo;
}
private static void createFiles(Project project, List<GeneratedCode> generatedCode, ProgressIndicator indicator) {
private static void createFiles(Project project, List<ModuleInfo> moduleInfos, ProgressIndicator indicator) {
indicator.setIndeterminate(false);
int count = 0;
double total = generatedCode.size();
PsiFileFactory factory = PsiFileFactory.getInstance(project);
for (GeneratedCode code : generatedCode) {
ProgressManager.getInstance().executeNonCancelableSection(() -> {
PsiFile file = factory.createFileFromText(PsiJavaModule.MODULE_INFO_FILE, JavaLanguage.INSTANCE, code.myText);
PsiElement added = code.myRootDir.add(file);
CodeStyleManager.getInstance(project).reformat(added);
});
double total = moduleInfos.size();
FileTemplate template = FileTemplateManager.getInstance(project).getInternalTemplate(INTERNAL_MODULE_INFO_TEMPLATE_NAME);
for (ModuleInfo moduleInfo : moduleInfos) {
ProgressManager.getInstance().executeNonCancelableSection(() -> createFile(template, moduleInfo));
indicator.setFraction(++count / total);
}
}
private static void createFile(FileTemplate template, ModuleInfo moduleInfo) {
Project project = moduleInfo.myRootDir.getProject();
Properties properties = FileTemplateManager.getInstance(project).getDefaultProperties();
FileTemplateUtil.fillDefaultProperties(properties, moduleInfo.myRootDir);
properties.setProperty(FileTemplate.ATTRIBUTE_NAME, MODULE_INFO_CLASS);
try {
PsiJavaFile moduleInfoFile =
(PsiJavaFile)FileTemplateUtil.createFromTemplate(template, MODULE_INFO_FILE, properties, moduleInfo.myRootDir);
PsiJavaModule javaModule = moduleInfoFile.getModuleDeclaration();
LOG.assertTrue(javaModule != null, "module-info file should contain module declaration");
javaModule.setName(moduleInfo.myName);
for (String export : moduleInfo.myExports) {
PsiUtil.addModuleStatement(javaModule, PsiKeyword.EXPORTS + ' ' + export);
}
for (String dependency : moduleInfo.myRequires) {
PsiUtil.addModuleStatement(javaModule, PsiKeyword.REQUIRES + ' ' + dependency);
}
CodeStyleManager.getInstance(project).reformat(moduleInfoFile);
}
catch (Exception e) {
LOG.info("Failed to create module-info.java in " + moduleInfo.myRootDir.getVirtualFile().getPath() + ": " + e.getMessage());
}
}
@Nullable
private static PsiJavaModule findDescriptor(PsiPackage psiPackage) {
PsiManager psiManager = psiPackage.getManager();
@@ -543,13 +563,20 @@ public class Java9GenerateModuleDescriptorsAction extends AnAction {
}
}
private static class GeneratedCode {
private static class ModuleInfo {
final PsiDirectory myRootDir;
final String myText;
final String myName;
final List<String> myRequires;
final List<String> myExports;
private GeneratedCode(@NotNull PsiDirectory rootDir, @NotNull String text) {
private ModuleInfo(@NotNull PsiDirectory rootDir,
@NotNull String name,
@NotNull List<String> requires,
@NotNull List<String> exports) {
myRootDir = rootDir;
myText = text;
myName = name;
myRequires = requires;
myExports = exports;
}
}
}
@@ -834,4 +834,4 @@ generate.module.descriptors.build.required.message=Couldn't generate module desc
generate.module.descriptors.scanning.message=Scanning Compiler Output
generate.module.descriptors.collecting.message=Collecting Dependencies
generate.module.descriptors.analysing.message=Analysing Dependencies
generate.module.descriptors.preparing.message=Analysing Dependencies
generate.module.descriptors.preparing.message=Preparing Code