2 * Copyright 2000-2015 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 package com.intellij.codeInspection.ex;
23 import com.intellij.codeInspection.CommonProblemDescriptor;
24 import com.intellij.codeInspection.QuickFix;
25 import com.intellij.codeInspection.offlineViewer.OfflineRefElementNode;
26 import com.intellij.codeInspection.reference.RefEntity;
27 import com.intellij.codeInspection.ui.*;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.module.Module;
30 import com.intellij.openapi.module.ModuleManager;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.util.Ref;
33 import com.intellij.util.Function;
34 import com.intellij.util.containers.MultiMap;
35 import com.intellij.util.ui.tree.TreeUtil;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
39 import javax.swing.tree.TreeNode;
40 import javax.swing.tree.TreePath;
42 import java.util.function.Consumer;
44 public abstract class InspectionRVContentProvider {
45 private static final Logger LOG = Logger.getInstance("#" + InspectionRVContentProvider.class.getName());
46 private final Project myProject;
48 public InspectionRVContentProvider(@NotNull Project project) {
52 protected interface UserObjectContainer<T> {
54 UserObjectContainer<T> getOwner();
57 RefElementNode createNode(@NotNull InspectionToolPresentation presentation);
65 boolean areEqual(final T o1, final T o2);
67 boolean supportStructure();
70 public abstract boolean checkReportedProblems(@NotNull GlobalInspectionContextImpl context, @NotNull InspectionToolWrapper toolWrapper);
72 public Iterable<? extends ScopeToolState> getTools(Tools tools) {
73 return tools.getTools();
76 public boolean hasQuickFixes(InspectionTree tree) {
77 final TreePath[] treePaths = tree.getSelectionPaths();
78 if (treePaths == null) return false;
79 for (TreePath selectionPath : treePaths) {
80 if (!TreeUtil.traverseDepth((TreeNode)selectionPath.getLastPathComponent(), node -> {
81 if (!((InspectionTreeNode) node).isValid()) return true;
82 if (node instanceof ProblemDescriptionNode) {
83 ProblemDescriptionNode problemDescriptionNode = (ProblemDescriptionNode)node;
84 if (!problemDescriptionNode.isQuickFixAppliedFromView()) {
85 final CommonProblemDescriptor descriptor = problemDescriptionNode.getDescriptor();
86 final QuickFix[] fixes = descriptor != null ? descriptor.getFixes() : null;
87 return fixes == null || fixes.length == 0;
99 public abstract QuickFixAction[] getQuickFixes(@NotNull InspectionToolWrapper toolWrapper, @NotNull InspectionTree tree);
102 public void appendToolNodeContent(@NotNull GlobalInspectionContextImpl context,
103 @NotNull InspectionNode toolNode,
104 @NotNull InspectionTreeNode parentNode,
105 final boolean showStructure) {
106 InspectionToolWrapper wrapper = toolNode.getToolWrapper();
107 InspectionToolPresentation presentation = context.getPresentation(wrapper);
108 Map<String, Set<RefEntity>> content = presentation.getContent();
109 Map<RefEntity, CommonProblemDescriptor[]> problems = presentation.getProblemElements();
110 appendToolNodeContent(context, toolNode, parentNode, showStructure, content, problems);
113 public abstract void appendToolNodeContent(@NotNull GlobalInspectionContextImpl context,
114 @NotNull InspectionNode toolNode,
115 @NotNull InspectionTreeNode parentNode,
116 final boolean showStructure,
117 @NotNull Map<String, Set<RefEntity>> contents,
118 @NotNull Map<RefEntity, CommonProblemDescriptor[]> problems);
120 protected abstract void appendDescriptor(@NotNull GlobalInspectionContextImpl context,
121 @NotNull InspectionToolWrapper toolWrapper,
122 @NotNull UserObjectContainer container,
123 @NotNull InspectionTreeNode pNode,
124 final boolean canPackageRepeat);
126 public boolean isContentLoaded() {
130 protected <T> void buildTree(@NotNull GlobalInspectionContextImpl context,
131 @NotNull Map<String, Set<T>> packageContents,
132 final boolean canPackageRepeat,
133 @NotNull InspectionToolWrapper toolWrapper,
134 @NotNull Function<T, UserObjectContainer<T>> computeContainer,
135 final boolean showStructure,
136 final Consumer<InspectionTreeNode> createdNodesConsumer) {
137 final Map<String, Map<String, InspectionPackageNode>> module2PackageMap = new HashMap<String, Map<String, InspectionPackageNode>>();
138 boolean supportStructure = showStructure;
139 final MultiMap<InspectionPackageNode, UserObjectContainer<T>> packageDescriptors = new MultiMap<>();
140 for (String packageName : packageContents.keySet()) {
141 final Set<T> elements = packageContents.get(packageName);
142 for (T userObject : elements) {
143 final UserObjectContainer<T> container = computeContainer.fun(userObject);
144 supportStructure &= container.supportStructure();
145 final String moduleName = showStructure ? container.getModule() : null;
146 Map<String, InspectionPackageNode> packageNodes = module2PackageMap.get(moduleName);
147 if (packageNodes == null) {
148 packageNodes = new HashMap<String, InspectionPackageNode>();
149 module2PackageMap.put(moduleName, packageNodes);
151 InspectionPackageNode pNode = packageNodes.get(packageName);
153 pNode = new InspectionPackageNode(packageName);
154 packageNodes.put(packageName, pNode);
157 packageDescriptors.putValue(pNode, container);
161 if (supportStructure) {
162 final HashMap<String, InspectionModuleNode> moduleNodes = new HashMap<String, InspectionModuleNode>();
163 for (final String moduleName : module2PackageMap.keySet()) {
164 final Map<String, InspectionPackageNode> packageNodes = module2PackageMap.get(moduleName);
165 InspectionModuleNode moduleNode = moduleNodes.get(moduleName);
167 if (moduleNode == null) {
168 if (moduleName != null) {
169 final Module module = ModuleManager.getInstance(myProject).findModuleByName(moduleName);
170 if (module != null) {
171 moduleNode = new InspectionModuleNode(module);
172 moduleNodes.put(moduleName, moduleNode);
174 else { //module content was removed ?
179 for (InspectionPackageNode packageNode : packageNodes.values()) {
180 createdNodesConsumer.accept(packageNode);
181 for (UserObjectContainer<T> container : packageDescriptors.get(packageNode)) {
182 appendDescriptor(context, toolWrapper, container, packageNode, canPackageRepeat);
188 for (InspectionPackageNode packageNode : packageNodes.values()) {
189 if (packageNode.getPackageName() != null) {
190 moduleNode.insertByOrder(packageNode, false);
191 for (UserObjectContainer<T> container : packageDescriptors.get(packageNode)) {
192 appendDescriptor(context, toolWrapper, container, packageNode, canPackageRepeat);
196 for (UserObjectContainer<T> container : packageDescriptors.get(packageNode)) {
197 appendDescriptor(context, toolWrapper, container, moduleNode, canPackageRepeat);
201 createdNodesConsumer.accept(moduleNode);
205 for (Map<String, InspectionPackageNode> packageNodes : module2PackageMap.values()) {
206 for (InspectionPackageNode pNode : packageNodes.values()) {
207 for (UserObjectContainer<T> container : packageDescriptors.get(pNode)) {
208 appendDescriptor(context, toolWrapper, container, pNode, canPackageRepeat);
210 final int count = pNode.getChildCount();
211 final ArrayList<TreeNode> childNodes = new ArrayList<>(count);
212 for (int i = 0; i < count; i++) {
213 childNodes.add(pNode.getChildAt(i));
215 for (TreeNode childNode: childNodes) {
216 if (childNode instanceof ProblemDescriptionNode) {
217 createdNodesConsumer.accept(pNode);
220 LOG.assertTrue(childNode instanceof RefElementNode, childNode.getClass().getName());
221 final RefElementNode elementNode = (RefElementNode)childNode;
222 final Set<RefElementNode> parentNodes = new LinkedHashSet<RefElementNode>();
223 if (pNode.getPackageName() != null) {
224 parentNodes.add(elementNode);
226 boolean hasElementNodeUnder = true;
227 for(int e = 0; e < elementNode.getChildCount(); e++) {
228 final TreeNode grandChildNode = elementNode.getChildAt(e);
229 if (grandChildNode instanceof ProblemDescriptionNode) {
230 hasElementNodeUnder = false;
233 LOG.assertTrue(grandChildNode instanceof RefElementNode);
234 parentNodes.add((RefElementNode)grandChildNode);
236 if (!hasElementNodeUnder) {
237 createdNodesConsumer.accept(elementNode);
241 for (RefElementNode parentNode : parentNodes) {
242 final List<ProblemDescriptionNode> nodes = new ArrayList<ProblemDescriptionNode>();
243 TreeUtil.traverse(parentNode, new TreeUtil.Traverse() {
245 public boolean accept(final Object node) {
246 if (node instanceof ProblemDescriptionNode) {
247 nodes.add((ProblemDescriptionNode)node);
252 if (nodes.isEmpty()) continue; //FilteringInspectionTool == DeadCode
253 parentNode.removeAllChildren();
254 for (ProblemDescriptionNode node : nodes) {
255 parentNode.add(node);
258 for (RefElementNode node : parentNodes) {
259 createdNodesConsumer.accept(node);
268 protected static RefElementNode addNodeToParent(@NotNull UserObjectContainer container,
269 @NotNull InspectionToolPresentation presentation,
270 final InspectionTreeNode parentNode) {
271 final RefElementNode nodeToBeAdded = container.createNode(presentation);
272 final Ref<Boolean> firstLevel = new Ref<Boolean>(true);
273 RefElementNode prevNode = null;
274 final Ref<RefElementNode> result = new Ref<RefElementNode>();
276 final RefElementNode currentNode = firstLevel.get() ? nodeToBeAdded : container.createNode(presentation);
277 final UserObjectContainer finalContainer = container;
278 final RefElementNode finalPrevNode = prevNode;
279 TreeUtil.traverseDepth(parentNode, new TreeUtil.Traverse() {
281 public boolean accept(Object node) {
282 if (node instanceof RefElementNode) {
283 final RefElementNode refElementNode = (RefElementNode)node;
284 final Object userObject = finalContainer.getUserObject();
285 final Object object = node instanceof OfflineRefElementNode ? ((OfflineRefElementNode) refElementNode).getOfflineDescriptor() : refElementNode.getUserObject();
286 if ((object == null || userObject.getClass().equals(object.getClass())) && finalContainer.areEqual(object, userObject)) {
287 if (firstLevel.get()) {
288 result.set(refElementNode);
292 refElementNode.insertByOrder(finalPrevNode, false);
293 result.set(nodeToBeAdded);
301 if(!result.isNull()) return result.get();
303 if (!firstLevel.get()) {
304 currentNode.insertByOrder(prevNode, false);
306 final UserObjectContainer owner = container.getOwner();
308 parentNode.insertByOrder(currentNode, false);
309 return nodeToBeAdded;
312 prevNode = currentNode;
313 firstLevel.set(false);
317 @SuppressWarnings({"ConstantConditions"}) //class cast suppression
318 protected static void merge(InspectionTreeNode child, InspectionTreeNode parent, boolean merge) {
320 for (int i = 0; i < parent.getChildCount(); i++) {
321 InspectionTreeNode current = (InspectionTreeNode)parent.getChildAt(i);
322 if (child.getClass() != current.getClass()) {
325 if (current instanceof InspectionPackageNode) {
326 if (((InspectionPackageNode)current).getPackageName().compareTo(((InspectionPackageNode)child).getPackageName()) == 0) {
327 processDepth(child, current);
331 else if (current instanceof RefElementNode) {
332 if (((RefElementNode)current).getElement().getName().compareTo(((RefElementNode)child).getElement().getName()) == 0 &&
333 ((RefElementNode)current).getElement().getQualifiedName().compareTo(((RefElementNode)child).getElement().getQualifiedName()) == 0) {
334 processDepth(child, current);
338 else if (current instanceof InspectionNode) {
339 if (((InspectionNode)current).getToolWrapper().getShortName().compareTo(((InspectionNode)child).getToolWrapper().getShortName()) == 0) {
340 processDepth(child, current);
344 else if (current instanceof InspectionModuleNode) {
345 if (((InspectionModuleNode)current).getName().compareTo(((InspectionModuleNode)child).getName()) == 0) {
346 processDepth(child, current);
352 parent.insertByOrder(child, false);
355 private static void processDepth(final InspectionTreeNode child, final InspectionTreeNode current) {
356 InspectionTreeNode[] children = new InspectionTreeNode[child.getChildCount()];
357 for (int i = 0; i < children.length; i++) {
358 children[i] = (InspectionTreeNode)child.getChildAt(i);
360 for (InspectionTreeNode node : children) {
361 merge(node, current, true);