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.
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
;
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)));
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
);
88 libraryRoots
.addAll(Arrays
.asList(LibraryUtil
.getLibraryRoots(modules
.toArray(new Module
[modules
.size()]), false, false)));
90 GlobalSearchScope searchScope
= null;
92 @NonNls final String libsName
= "libs";
93 searchScope
= GlobalSearchScope
.filterScope(project
, new NamedScope(libsName
, PackageSetFactory
.getInstance().compile("lib:*..*")));
95 catch (ParsingException e
) {
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(){
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)) {
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
);
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();
155 addProblemElement(refModule
, manager
.createProblemDescriptor(InspectionsBundle
.message(
156 "unused.library.roots.problem.descriptor", unusedLibraryRoots
, orderEntry
.getPresentableName()), new RemoveUnusedLibrary(refModule
, orderEntry
, files
)));
158 addProblemElement(refModule
, manager
.createProblemDescriptor(InspectionsBundle
.message("unused.library.problem.descriptor",
159 orderEntry
.getPresentableName()), new RemoveUnusedLibrary(refModule
, orderEntry
, null)));
166 public boolean isGraphNeeded() {
171 public JobDescriptor
[] getJobDescriptors() {
172 return new JobDescriptor
[] {BACKWARD_ANALYSIS
};
175 public boolean isEnabledByDefault() {
181 public String
getGroupDisplayName() {
182 return GroupNames
.DECLARATION_REDUNDANCY
;
186 public String
getDisplayName() {
187 return InspectionsBundle
.message("unused.library.display.name");
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
;
208 public String
getName() {
209 return myFiles
== null ? InspectionsBundle
.message("detach.library.quickfix.name") : InspectionsBundle
.message("detach.library.roots.quickfix.name");
213 public String
getFamilyName() {
217 public void applyFix(@NotNull final Project project
, @NotNull final CommonProblemDescriptor descriptor
) {
218 final Module module
= myRefModule
.getModule();
220 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
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
);
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();