3a0d8bcf18d6c2206c8d4f4a871fe1bdb10d8ae0
[idea/community.git] / plugins / devkit / src / inspections / quickfix / CreateHtmlDescriptionFix.java
1 /*
2  * Copyright 2000-2016 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
17 package org.jetbrains.idea.devkit.inspections.quickfix;
18
19 import com.intellij.codeInspection.LocalQuickFix;
20 import com.intellij.codeInspection.ProblemDescriptor;
21 import com.intellij.icons.AllIcons;
22 import com.intellij.ide.fileTemplates.FileTemplate;
23 import com.intellij.ide.fileTemplates.FileTemplateManager;
24 import com.intellij.ide.fileTemplates.FileTemplateUtil;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.editor.Editor;
27 import com.intellij.openapi.fileEditor.FileEditorManager;
28 import com.intellij.openapi.module.Module;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.roots.ModuleRootManager;
31 import com.intellij.openapi.ui.popup.JBPopup;
32 import com.intellij.openapi.ui.popup.JBPopupFactory;
33 import com.intellij.openapi.util.Iconable;
34 import com.intellij.openapi.vfs.VfsUtilCore;
35 import com.intellij.openapi.vfs.VirtualFile;
36 import com.intellij.psi.PsiDirectory;
37 import com.intellij.psi.PsiElement;
38 import com.intellij.psi.PsiFile;
39 import com.intellij.psi.PsiManager;
40 import com.intellij.ui.LayeredIcon;
41 import com.intellij.ui.components.JBList;
42 import com.intellij.util.ArrayUtil;
43 import one.util.streamex.StreamEx;
44 import org.jetbrains.annotations.NonNls;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.idea.devkit.DevKitBundle;
47 import org.jetbrains.idea.devkit.inspections.DescriptionCheckerUtil;
48 import org.jetbrains.idea.devkit.inspections.DescriptionType;
49 import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
50 import org.jetbrains.jps.model.java.JavaResourceRootType;
51
52 import javax.swing.*;
53 import java.io.File;
54 import java.util.Arrays;
55 import java.util.List;
56
57 /**
58  * @author Konstantin Bulenkov
59  */
60 public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable {
61
62   @NonNls
63   private static final String TEMPLATE_NAME = "InspectionDescription.html";
64
65   private final String myFilename;
66   private final Module myModule;
67   private final DescriptionType myDescriptionType;
68
69   public CreateHtmlDescriptionFix(String filename, Module module, DescriptionType descriptionType) {
70     myModule = module;
71     myDescriptionType = descriptionType;
72     myFilename = getNormalizedFileName(filename);
73   }
74
75   private boolean isFixedDescriptionFilename() {
76     return myDescriptionType.isFixedDescriptionFilename();
77   }
78
79   private static List<VirtualFile> getPotentialRoots(Module module, PsiDirectory[] dirs) {
80     if (dirs.length != 0) {
81       return StreamEx.of(dirs).map(PsiDirectory::getParentDirectory).nonNull().map(PsiDirectory::getVirtualFile).toList();
82     }
83     else {
84       ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
85       List<VirtualFile> resourceRoots = rootManager.getSourceRoots(JavaResourceRootType.RESOURCE);
86       if (!resourceRoots.isEmpty()) {
87         return resourceRoots;
88       }
89       return rootManager.getSourceRoots(JavaModuleSourceRootTypes.SOURCES);
90     }
91   }
92
93   private String getNormalizedFileName(String filename) {
94     return myDescriptionType.isFixedDescriptionFilename() ? filename : filename + ".html";
95   }
96
97   @NotNull
98   public String getName() {
99     return DevKitBundle.message("create.description.file", getNewFileName());
100   }
101
102   @NotNull
103   public String getFamilyName() {
104     return "DevKit";
105   }
106
107   public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
108     final PsiDirectory[] dirs = getDirectories();
109     final List<VirtualFile> virtualFiles = getPotentialRoots(myModule, dirs);
110     final VirtualFile[] roots = prepare(VfsUtilCore.toVirtualFileArray(virtualFiles));
111     if (roots.length == 1) {
112       ApplicationManager.getApplication().runWriteAction(() -> createDescription(roots[0]));
113     }
114     else {
115       List<String> options = StreamEx.of(roots).map(this::getPath).toList();
116       final JBList files = new JBList(ArrayUtil.toStringArray(options));
117       files.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
118       final JBPopup popup = JBPopupFactory.getInstance()
119         .createListPopupBuilder(files)
120         .setTitle(DevKitBundle.message("select.target.location.of.description", myFilename))
121         .setItemChoosenCallback(() -> {
122           final int index = files.getSelectedIndex();
123           if (0 <= index && index < roots.length) {
124             ApplicationManager.getApplication().runWriteAction(() -> createDescription(roots[index]));
125           }
126         }).createPopup();
127       final Editor editor = FileEditorManager.getInstance(myModule.getProject()).getSelectedTextEditor();
128       if (editor == null) return;
129       popup.showInBestPositionFor(editor);
130     }
131   }
132
133   private String getPath(VirtualFile file) {
134     String path = file.getPresentableUrl() + File.separator + getDescriptionFolderName() + File.separator + myFilename;
135     if (isFixedDescriptionFilename()) {
136       path += File.separator + "description.html";
137     }
138     return path;
139   }
140
141   private PsiDirectory[] getDirectories() {
142     return DescriptionCheckerUtil.getDescriptionsDirs(myModule, myDescriptionType);
143   }
144
145   private void createDescription(VirtualFile root) {
146     if (!root.isDirectory()) return;
147     final PsiManager psiManager = PsiManager.getInstance(myModule.getProject());
148     final PsiDirectory psiRoot = psiManager.findDirectory(root);
149     if (psiRoot == null) return;
150     PsiDirectory descrRoot =
151       StreamEx.of(psiRoot.getSubdirectories()).findFirst(dir -> getDescriptionFolderName().equals(dir.getName())).orElse(null);
152
153     try {
154       descrRoot = descrRoot == null ? psiRoot.createSubdirectory(getDescriptionFolderName()) : descrRoot;
155       if (isFixedDescriptionFilename()) {
156         PsiDirectory dir = descrRoot.findSubdirectory(myFilename);
157         if (dir == null) {
158           descrRoot = descrRoot.createSubdirectory(myFilename);
159         }
160       }
161       final FileTemplate descrTemplate = FileTemplateManager.getInstance(myModule.getProject()).getJ2eeTemplate(TEMPLATE_NAME);
162       final PsiElement template =
163         FileTemplateUtil.createFromTemplate(descrTemplate, getNewFileName(), null, descrRoot);
164       if (template instanceof PsiFile) {
165         final VirtualFile file = ((PsiFile)template).getVirtualFile();
166         if (file != null) {
167           FileEditorManager.getInstance(myModule.getProject()).openFile(file, true);
168         }
169       }
170     }
171     catch (Exception e) {//
172     }
173   }
174
175   private String getNewFileName() {
176     return isFixedDescriptionFilename() ? "description.html" : myFilename;
177   }
178
179   public Icon getIcon(int flags) {
180     return new LayeredIcon(AllIcons.FileTypes.Html, AllIcons.Actions.New);
181   }
182
183   private VirtualFile[] prepare(VirtualFile[] roots) {
184     VirtualFile[] found = Arrays.stream(roots).filter(this::containsDescriptionDir).toArray(VirtualFile[]::new);
185     return found.length > 0 ? found : roots;
186   }
187
188   private boolean containsDescriptionDir(VirtualFile root) {
189     if (!root.isDirectory()) return false;
190     return Arrays.stream(root.getChildren()).anyMatch(file -> file.isDirectory() && getDescriptionFolderName().equals(file.getName()));
191   }
192
193   private String getDescriptionFolderName() {
194     return myDescriptionType.getDescriptionFolder();
195   }
196 }