update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInspection / unusedLibraries / UnusedLibrariesInspection.java
blob0c87b4ce8013be03ca480638a677746389fbea1a
1 /*
2 * Copyright 2000-2009 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.
18 * User: anna
19 * Date: 18-Apr-2007
21 package com.intellij.codeInspection.unusedLibraries;
23 import com.intellij.analysis.AnalysisScope;
24 import com.intellij.codeInsight.daemon.GroupNames;
25 import com.intellij.codeInspection.CommonProblemDescriptor;
26 import com.intellij.codeInspection.InspectionManager;
27 import com.intellij.codeInspection.InspectionsBundle;
28 import com.intellij.codeInspection.QuickFix;
29 import com.intellij.codeInspection.ex.DescriptorProviderInspection;
30 import com.intellij.codeInspection.ex.JobDescriptor;
31 import com.intellij.codeInspection.reference.RefManager;
32 import com.intellij.codeInspection.reference.RefModule;
33 import com.intellij.openapi.application.ApplicationManager;
34 import com.intellij.openapi.diagnostic.Logger;
35 import com.intellij.openapi.module.Module;
36 import com.intellij.openapi.module.ModuleUtil;
37 import com.intellij.openapi.progress.ProgressIndicator;
38 import com.intellij.openapi.progress.ProgressManager;
39 import com.intellij.openapi.progress.impl.ProgressManagerImpl;
40 import com.intellij.openapi.progress.util.ProgressIndicatorBase;
41 import com.intellij.openapi.project.Project;
42 import com.intellij.openapi.roots.*;
43 import com.intellij.openapi.roots.libraries.Library;
44 import com.intellij.openapi.roots.libraries.LibraryUtil;
45 import com.intellij.openapi.util.Comparing;
46 import com.intellij.openapi.util.text.StringUtil;
47 import com.intellij.openapi.vfs.VfsUtil;
48 import com.intellij.openapi.vfs.VirtualFile;
49 import com.intellij.packageDependencies.BackwardDependenciesBuilder;
50 import com.intellij.psi.PsiCompiledElement;
51 import com.intellij.psi.PsiFile;
52 import com.intellij.psi.PsiRecursiveElementVisitor;
53 import com.intellij.psi.search.GlobalSearchScope;
54 import com.intellij.psi.search.scope.packageSet.NamedScope;
55 import com.intellij.psi.search.scope.packageSet.PackageSetFactory;
56 import com.intellij.psi.search.scope.packageSet.ParsingException;
57 import com.intellij.util.Function;
58 import org.jetbrains.annotations.Nls;
59 import org.jetbrains.annotations.NonNls;
60 import org.jetbrains.annotations.NotNull;
62 import java.util.*;
64 public class UnusedLibrariesInspection extends DescriptorProviderInspection {
65 private static final Logger LOG = Logger.getInstance("#" + UnusedLibrariesInspection.class.getName());
66 private static final JobDescriptor BACKWARD_ANALYSIS = new JobDescriptor(InspectionsBundle.message("unused.library.backward.analysis.job.description"));
68 public void runInspection(final AnalysisScope scope, final InspectionManager manager) {
69 final Project project = getContext().getProject();
70 final ArrayList<VirtualFile> libraryRoots = new ArrayList<VirtualFile>();
71 if (scope.getScopeType() == AnalysisScope.PROJECT) {
72 libraryRoots.addAll(Arrays.asList(LibraryUtil.getLibraryRoots(project, false, false)));
73 } else {
74 final Set<Module> modules = new HashSet<Module>();
75 scope.accept(new PsiRecursiveElementVisitor() {
76 @Override public void visitFile(PsiFile file) {
77 if (!(file instanceof PsiCompiledElement)) {
78 final VirtualFile virtualFile = file.getVirtualFile();
79 if (virtualFile != null) {
80 final Module module = ModuleUtil.findModuleForFile(virtualFile, project);
81 if (module != null) {
82 modules.add(module);
87 });
88 libraryRoots.addAll(Arrays.asList(LibraryUtil.getLibraryRoots(modules.toArray(new Module[modules.size()]), false, false)));
90 GlobalSearchScope searchScope = null;
91 try {
92 @NonNls final String libsName = "libs";
93 searchScope = GlobalSearchScope.filterScope(project, new NamedScope(libsName, PackageSetFactory.getInstance().compile("lib:*..*")));
95 catch (ParsingException e) {
96 //can't be
98 final AnalysisScope analysisScope = new AnalysisScope(searchScope, project);
99 analysisScope.setSearchInLibraries(true);
100 final BackwardDependenciesBuilder builder = new BackwardDependenciesBuilder(project, analysisScope);
102 final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
104 BACKWARD_ANALYSIS.setTotalAmount(builder.getTotalFileCount());
105 ((ProgressManagerImpl)ProgressManager.getInstance()).executeProcessUnderProgress(new Runnable(){
106 public void run() {
107 builder.analyze();
109 }, new ProgressIndicatorBase() {
110 public void setFraction(final double fraction) {
111 super.setFraction(fraction);
112 BACKWARD_ANALYSIS.setDoneAmount(((int)fraction * BACKWARD_ANALYSIS.getTotalAmount()));
113 getContext().incrementJobDoneAmount(BACKWARD_ANALYSIS, getText2());
116 public boolean isCanceled() {
117 return progressIndicator != null && progressIndicator.isCanceled() || super.isCanceled();
120 final Map<PsiFile, Set<PsiFile>> dependencies = builder.getDependencies();
121 for (PsiFile file : dependencies.keySet()) {
122 final VirtualFile virtualFile = file.getVirtualFile();
123 LOG.assertTrue(virtualFile != null);
124 for (Iterator<VirtualFile> i = libraryRoots.iterator(); i.hasNext();) {
125 if (VfsUtil.isAncestor(i.next(), virtualFile, false)) {
126 i.remove();
130 if (libraryRoots.size() > 0) {
131 ProjectFileIndex projectIndex = ProjectRootManager.getInstance(project).getFileIndex();
132 Map<OrderEntry, Set<VirtualFile>> unusedLibs = new HashMap<OrderEntry, Set<VirtualFile>>();
133 for (VirtualFile libraryRoot : libraryRoots) {
134 final List<OrderEntry> orderEntries = projectIndex.getOrderEntriesForFile(libraryRoot);
135 for (OrderEntry orderEntry : orderEntries) {
136 Set<VirtualFile> files = unusedLibs.get(orderEntry);
137 if (files == null) {
138 files = new HashSet<VirtualFile>();
139 unusedLibs.put(orderEntry, files);
141 files.add(libraryRoot);
144 final RefManager refManager = getRefManager();
145 for (OrderEntry orderEntry : unusedLibs.keySet()) {
146 if (orderEntry instanceof LibraryOrderEntry) {
147 final RefModule refModule = refManager.getRefModule(orderEntry.getOwnerModule());
148 final Set<VirtualFile> files = unusedLibs.get(orderEntry);
149 if (files.size() < orderEntry.getFiles(OrderRootType.CLASSES).length) {
150 final String unusedLibraryRoots = StringUtil.join(files, new Function<VirtualFile, String>() {
151 public String fun(final VirtualFile file) {
152 return file.getPresentableName();
154 }, ",");
155 addProblemElement(refModule, manager.createProblemDescriptor(InspectionsBundle.message(
156 "unused.library.roots.problem.descriptor", unusedLibraryRoots, orderEntry.getPresentableName()), new RemoveUnusedLibrary(refModule, orderEntry, files)));
157 } else {
158 addProblemElement(refModule, manager.createProblemDescriptor(InspectionsBundle.message("unused.library.problem.descriptor",
159 orderEntry.getPresentableName()), new RemoveUnusedLibrary(refModule, orderEntry, null)));
166 public boolean isGraphNeeded() {
167 return false;
170 @NotNull
171 public JobDescriptor[] getJobDescriptors() {
172 return new JobDescriptor[] {BACKWARD_ANALYSIS};
175 public boolean isEnabledByDefault() {
176 return false;
179 @Nls
180 @NotNull
181 public String getGroupDisplayName() {
182 return GroupNames.DECLARATION_REDUNDANCY;
185 @NotNull
186 public String getDisplayName() {
187 return InspectionsBundle.message("unused.library.display.name");
190 @NonNls
191 @NotNull
192 public String getShortName() {
193 return "UnusedLibrary";
196 private static class RemoveUnusedLibrary implements QuickFix {
197 private final RefModule myRefModule;
198 private final OrderEntry myOrderEntry;
199 private final Set<VirtualFile> myFiles;
201 public RemoveUnusedLibrary(final RefModule refModule, final OrderEntry orderEntry, final Set<VirtualFile> files) {
202 myRefModule = refModule;
203 myOrderEntry = orderEntry;
204 myFiles = files;
207 @NotNull
208 public String getName() {
209 return myFiles == null ? InspectionsBundle.message("detach.library.quickfix.name") : InspectionsBundle.message("detach.library.roots.quickfix.name");
212 @NotNull
213 public String getFamilyName() {
214 return getName();
217 public void applyFix(@NotNull final Project project, @NotNull final CommonProblemDescriptor descriptor) {
218 final Module module = myRefModule.getModule();
220 ApplicationManager.getApplication().runWriteAction(new Runnable() {
221 public void run() {
222 final ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel();
223 for (OrderEntry entry : model.getOrderEntries()) {
224 if (entry instanceof LibraryOrderEntry && Comparing.strEqual(entry.getPresentableName(), myOrderEntry.getPresentableName())) {
225 if (myFiles == null) {
226 model.removeOrderEntry(entry);
228 else {
229 final Library library = ((LibraryOrderEntry)entry).getLibrary();
230 if (library != null) {
231 final Library.ModifiableModel modifiableModel = library.getModifiableModel();
232 for (VirtualFile file : myFiles) {
233 modifiableModel.removeRoot(file.getUrl(), OrderRootType.CLASSES);
235 modifiableModel.commit();
240 model.commit();