| /* |
| * Copyright 2000-2013 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. |
| */ |
| |
| /* |
| * User: anna |
| * Date: 10-Jan-2007 |
| */ |
| package com.intellij.codeInspection.ex; |
| |
| import com.intellij.codeInspection.CommonProblemDescriptor; |
| import com.intellij.codeInspection.reference.RefEntity; |
| import com.intellij.codeInspection.ui.*; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.util.Function; |
| import com.intellij.util.ui.tree.TreeUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.tree.DefaultTreeModel; |
| import javax.swing.tree.MutableTreeNode; |
| import javax.swing.tree.TreeNode; |
| import java.util.*; |
| |
| public abstract class InspectionRVContentProvider { |
| private static final Logger LOG = Logger.getInstance("#" + InspectionRVContentProvider.class.getName()); |
| private final Project myProject; |
| |
| public InspectionRVContentProvider(@NotNull Project project) { |
| myProject = project; |
| } |
| |
| protected interface UserObjectContainer<T> { |
| @Nullable |
| UserObjectContainer<T> getOwner(); |
| |
| @NotNull |
| RefElementNode createNode(@NotNull InspectionToolPresentation presentation); |
| |
| @NotNull |
| T getUserObject(); |
| |
| @Nullable |
| String getModule(); |
| |
| boolean areEqual(final T o1, final T o2); |
| |
| boolean supportStructure(); |
| } |
| |
| public abstract boolean checkReportedProblems(@NotNull GlobalInspectionContextImpl context, @NotNull InspectionToolWrapper toolWrapper); |
| |
| @Nullable |
| public abstract QuickFixAction[] getQuickFixes(@NotNull InspectionToolWrapper toolWrapper, @NotNull InspectionTree tree); |
| |
| |
| public void appendToolNodeContent(@NotNull GlobalInspectionContextImpl context, |
| @NotNull InspectionNode toolNode, |
| @NotNull InspectionTreeNode parentNode, |
| final boolean showStructure) { |
| InspectionToolWrapper wrapper = toolNode.getToolWrapper(); |
| InspectionToolPresentation presentation = context.getPresentation(wrapper); |
| Map<String, Set<RefEntity>> content = presentation.getContent(); |
| Map<RefEntity, CommonProblemDescriptor[]> problems = presentation.getProblemElements(); |
| Map<String, Set<RefEntity>> contents = content == null ? new HashMap<String, Set<RefEntity>>() : content; |
| appendToolNodeContent(context, toolNode, parentNode, showStructure, contents, problems, null); |
| } |
| |
| public abstract void appendToolNodeContent(@NotNull GlobalInspectionContextImpl context, |
| @NotNull InspectionNode toolNode, |
| @NotNull InspectionTreeNode parentNode, |
| final boolean showStructure, |
| @NotNull Map<String, Set<RefEntity>> contents, |
| @NotNull Map<RefEntity, CommonProblemDescriptor[]> problems, |
| @Nullable final DefaultTreeModel model); |
| |
| protected abstract void appendDescriptor(@NotNull GlobalInspectionContextImpl context, |
| @NotNull InspectionToolWrapper toolWrapper, |
| @NotNull UserObjectContainer container, |
| @NotNull InspectionPackageNode pNode, |
| final boolean canPackageRepeat); |
| |
| public boolean isContentLoaded() { |
| return true; |
| } |
| |
| protected <T> List<InspectionTreeNode> buildTree(@NotNull GlobalInspectionContextImpl context, |
| @NotNull Map<String, Set<T>> packageContents, |
| final boolean canPackageRepeat, |
| @NotNull InspectionToolWrapper toolWrapper, |
| @NotNull Function<T, UserObjectContainer<T>> computeContainer, |
| final boolean showStructure) { |
| final List<InspectionTreeNode> content = new ArrayList<InspectionTreeNode>(); |
| final Map<String, Map<String, InspectionPackageNode>> module2PackageMap = new HashMap<String, Map<String, InspectionPackageNode>>(); |
| boolean supportStructure = showStructure; |
| for (String packageName : packageContents.keySet()) { |
| final Set<T> elements = packageContents.get(packageName); |
| for (T userObject : elements) { |
| final UserObjectContainer<T> container = computeContainer.fun(userObject); |
| supportStructure &= container.supportStructure(); |
| final String moduleName = showStructure ? container.getModule() : null; |
| Map<String, InspectionPackageNode> packageNodes = module2PackageMap.get(moduleName); |
| if (packageNodes == null) { |
| packageNodes = new HashMap<String, InspectionPackageNode>(); |
| module2PackageMap.put(moduleName, packageNodes); |
| } |
| InspectionPackageNode pNode = packageNodes.get(packageName); |
| if (pNode == null) { |
| pNode = new InspectionPackageNode(packageName); |
| packageNodes.put(packageName, pNode); |
| } |
| appendDescriptor(context, toolWrapper, container, pNode, canPackageRepeat); |
| } |
| } |
| if (supportStructure) { |
| final HashMap<String, InspectionModuleNode> moduleNodes = new HashMap<String, InspectionModuleNode>(); |
| for (final String moduleName : module2PackageMap.keySet()) { |
| final Map<String, InspectionPackageNode> packageNodes = module2PackageMap.get(moduleName); |
| for (InspectionPackageNode packageNode : packageNodes.values()) { |
| if (packageNode.getChildCount() > 0) { |
| InspectionModuleNode moduleNode = moduleNodes.get(moduleName); |
| if (moduleNode == null) { |
| if (moduleName != null) { |
| final Module module = ModuleManager.getInstance(myProject).findModuleByName(moduleName); |
| if (module != null) { |
| moduleNode = new InspectionModuleNode(module); |
| moduleNodes.put(moduleName, moduleNode); |
| } |
| else { //module content was removed ? |
| continue; |
| } |
| } else { |
| content.addAll(packageNodes.values()); |
| break; |
| } |
| } |
| if (packageNode.getPackageName() != null) { |
| moduleNode.add(packageNode); |
| } else { |
| for(int i = packageNode.getChildCount() - 1; i >= 0; i--) { |
| moduleNode.add((MutableTreeNode)packageNode.getChildAt(i)); |
| } |
| } |
| } |
| } |
| } |
| content.addAll(moduleNodes.values()); |
| } |
| else { |
| for (Map<String, InspectionPackageNode> packageNodes : module2PackageMap.values()) { |
| for (InspectionPackageNode pNode : packageNodes.values()) { |
| for (int i = 0; i < pNode.getChildCount(); i++) { |
| final TreeNode childNode = pNode.getChildAt(i); |
| if (childNode instanceof ProblemDescriptionNode) { |
| content.add(pNode); |
| break; |
| } |
| LOG.assertTrue(childNode instanceof RefElementNode, childNode.getClass().getName()); |
| final RefElementNode elementNode = (RefElementNode)childNode; |
| final Set<RefElementNode> parentNodes = new LinkedHashSet<RefElementNode>(); |
| if (pNode.getPackageName() != null) { |
| parentNodes.add(elementNode); |
| } else { |
| boolean hasElementNodeUnder = true; |
| for(int e = 0; e < elementNode.getChildCount(); e++) { |
| final TreeNode grandChildNode = elementNode.getChildAt(e); |
| if (grandChildNode instanceof ProblemDescriptionNode) { |
| hasElementNodeUnder = false; |
| break; |
| } |
| LOG.assertTrue(grandChildNode instanceof RefElementNode); |
| parentNodes.add((RefElementNode)grandChildNode); |
| } |
| if (!hasElementNodeUnder) { |
| content.add(elementNode); |
| continue; |
| } |
| } |
| for (RefElementNode parentNode : parentNodes) { |
| final List<ProblemDescriptionNode> nodes = new ArrayList<ProblemDescriptionNode>(); |
| TreeUtil.traverse(parentNode, new TreeUtil.Traverse() { |
| @Override |
| public boolean accept(final Object node) { |
| if (node instanceof ProblemDescriptionNode) { |
| nodes.add((ProblemDescriptionNode)node); |
| } |
| return true; |
| } |
| }); |
| if (nodes.isEmpty()) continue; //FilteringInspectionTool == DeadCode |
| parentNode.removeAllChildren(); |
| for (ProblemDescriptionNode node : nodes) { |
| parentNode.add(node); |
| } |
| } |
| content.addAll(parentNodes); |
| } |
| } |
| } |
| } |
| return content; |
| } |
| |
| @NotNull |
| protected static RefElementNode addNodeToParent(@NotNull UserObjectContainer container, |
| @NotNull InspectionToolPresentation presentation, |
| final InspectionTreeNode parentNode) { |
| final RefElementNode nodeToBeAdded = container.createNode(presentation); |
| final Ref<Boolean> firstLevel = new Ref<Boolean>(true); |
| RefElementNode prevNode = null; |
| final Ref<RefElementNode> result = new Ref<RefElementNode>(); |
| while (true) { |
| final RefElementNode currentNode = firstLevel.get() ? nodeToBeAdded : container.createNode(presentation); |
| final UserObjectContainer finalContainer = container; |
| final RefElementNode finalPrevNode = prevNode; |
| TreeUtil.traverseDepth(parentNode, new TreeUtil.Traverse() { |
| @Override |
| public boolean accept(Object node) { |
| if (node instanceof RefElementNode) { |
| final RefElementNode refElementNode = (RefElementNode)node; |
| if (finalContainer.areEqual(refElementNode.getUserObject(), finalContainer.getUserObject())) { |
| if (firstLevel.get()) { |
| result.set(refElementNode); |
| return false; |
| } |
| else { |
| insertByIndex(finalPrevNode, refElementNode); |
| result.set(nodeToBeAdded); |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| }); |
| if(!result.isNull()) return result.get(); |
| |
| if (!firstLevel.get()) { |
| insertByIndex(prevNode, currentNode); |
| } |
| final UserObjectContainer owner = container.getOwner(); |
| if (owner == null) { |
| insertByIndex(currentNode, parentNode); |
| return nodeToBeAdded; |
| } |
| container = owner; |
| prevNode = currentNode; |
| firstLevel.set(false); |
| } |
| } |
| |
| @SuppressWarnings({"ConstantConditions"}) //class cast suppression |
| protected static void merge(@Nullable DefaultTreeModel model, InspectionTreeNode child, InspectionTreeNode parent, boolean merge) { |
| if (merge) { |
| for (int i = 0; i < parent.getChildCount(); i++) { |
| InspectionTreeNode current = (InspectionTreeNode)parent.getChildAt(i); |
| if (child.getClass() != current.getClass()) { |
| continue; |
| } |
| if (current instanceof InspectionPackageNode) { |
| if (((InspectionPackageNode)current).getPackageName().compareTo(((InspectionPackageNode)child).getPackageName()) == 0) { |
| processDepth(model, child, current); |
| return; |
| } |
| } |
| else if (current instanceof RefElementNode) { |
| if (((RefElementNode)current).getElement().getName().compareTo(((RefElementNode)child).getElement().getName()) == 0) { |
| processDepth(model, child, current); |
| return; |
| } |
| } |
| else if (current instanceof InspectionNode) { |
| if (((InspectionNode)current).getToolWrapper().getShortName().compareTo(((InspectionNode)child).getToolWrapper().getShortName()) == 0) { |
| processDepth(model, child, current); |
| return; |
| } |
| } |
| else if (current instanceof InspectionModuleNode) { |
| if (((InspectionModuleNode)current).getName().compareTo(((InspectionModuleNode)child).getName()) == 0) { |
| processDepth(model, child, current); |
| return; |
| } |
| } |
| } |
| } |
| add(model, child, parent); |
| } |
| |
| protected static void add(@Nullable final DefaultTreeModel model, final InspectionTreeNode child, final InspectionTreeNode parent) { |
| if (model == null) { |
| insertByIndex(child, parent); |
| } |
| else { |
| if (parent.getIndex(child) < 0) { |
| model.insertNodeInto(child, parent, child.getParent() == parent ? parent.getChildCount() - 1 : parent.getChildCount()); |
| } |
| } |
| } |
| |
| private static void insertByIndex(InspectionTreeNode child, InspectionTreeNode parent) { |
| if (ApplicationManager.getApplication().isUnitTestMode()) { |
| parent.add(child); |
| return; |
| } |
| final int i = TreeUtil.indexedBinarySearch(parent, child, InspectionResultsViewComparator.getInstance()); |
| if (i >= 0){ |
| parent.add(child); |
| return; |
| } |
| parent.insert(child, -i -1); |
| } |
| |
| private static void processDepth(@Nullable DefaultTreeModel model, final InspectionTreeNode child, final InspectionTreeNode current) { |
| InspectionTreeNode[] children = new InspectionTreeNode[child.getChildCount()]; |
| for (int i = 0; i < children.length; i++) { |
| children[i] = (InspectionTreeNode)child.getChildAt(i); |
| } |
| for (InspectionTreeNode node : children) { |
| merge(model, node, current, true); |
| } |
| } |
| } |