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
.ex
;
23 import com
.intellij
.CommonBundle
;
24 import com
.intellij
.analysis
.AnalysisScope
;
25 import com
.intellij
.codeInspection
.GlobalInspectionContext
;
26 import com
.intellij
.codeInspection
.GlobalJavaInspectionContext
;
27 import com
.intellij
.codeInspection
.InspectionProfileEntry
;
28 import com
.intellij
.codeInspection
.InspectionsBundle
;
29 import com
.intellij
.codeInspection
.deadCode
.DeadCodeInspection
;
30 import com
.intellij
.codeInspection
.reference
.*;
31 import com
.intellij
.lang
.StdLanguages
;
32 import com
.intellij
.openapi
.application
.ApplicationManager
;
33 import com
.intellij
.openapi
.diagnostic
.Logger
;
34 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
35 import com
.intellij
.openapi
.module
.Module
;
36 import com
.intellij
.openapi
.module
.ModuleManager
;
37 import com
.intellij
.openapi
.project
.Project
;
38 import com
.intellij
.openapi
.projectRoots
.Sdk
;
39 import com
.intellij
.openapi
.roots
.*;
40 import com
.intellij
.openapi
.roots
.libraries
.Library
;
41 import com
.intellij
.openapi
.roots
.ui
.configuration
.ProjectSettingsService
;
42 import com
.intellij
.openapi
.ui
.Messages
;
43 import com
.intellij
.openapi
.util
.Computable
;
44 import com
.intellij
.openapi
.vfs
.VirtualFile
;
45 import com
.intellij
.psi
.*;
46 import com
.intellij
.psi
.javadoc
.PsiDocComment
;
47 import com
.intellij
.psi
.search
.*;
48 import com
.intellij
.psi
.search
.searches
.ClassInheritorsSearch
;
49 import com
.intellij
.psi
.search
.searches
.MethodReferencesSearch
;
50 import com
.intellij
.psi
.search
.searches
.OverridingMethodsSearch
;
51 import com
.intellij
.psi
.search
.searches
.ReferencesSearch
;
52 import com
.intellij
.psi
.util
.PsiTreeUtil
;
53 import com
.intellij
.util
.Processor
;
54 import com
.intellij
.util
.containers
.ContainerUtil
;
55 import gnu
.trove
.THashMap
;
56 import org
.jetbrains
.annotations
.NotNull
;
60 public class GlobalJavaInspectionContextImpl
extends GlobalJavaInspectionContext
{
61 private static final Logger LOG
= Logger
.getInstance("#" + GlobalJavaInspectionContextImpl
.class.getName());
63 private THashMap
<SmartPsiElementPointer
, List
<DerivedMethodsProcessor
>> myDerivedMethodsRequests
;
64 private THashMap
<SmartPsiElementPointer
, List
<DerivedClassesProcessor
>> myDerivedClassesRequests
;
65 private THashMap
<SmartPsiElementPointer
, List
<UsagesProcessor
>> myMethodUsagesRequests
;
66 private THashMap
<SmartPsiElementPointer
, List
<UsagesProcessor
>> myFieldUsagesRequests
;
67 private THashMap
<SmartPsiElementPointer
, List
<UsagesProcessor
>> myClassUsagesRequests
;
70 public void enqueueClassUsagesProcessor(RefClass refClass
, UsagesProcessor p
) {
71 if (myClassUsagesRequests
== null) myClassUsagesRequests
= new THashMap
<SmartPsiElementPointer
, List
<UsagesProcessor
>>();
72 enqueueRequestImpl(refClass
, myClassUsagesRequests
, p
);
75 public void enqueueDerivedClassesProcessor(RefClass refClass
, DerivedClassesProcessor p
) {
76 if (myDerivedClassesRequests
== null) myDerivedClassesRequests
= new THashMap
<SmartPsiElementPointer
, List
<DerivedClassesProcessor
>>();
77 enqueueRequestImpl(refClass
, myDerivedClassesRequests
, p
);
80 public void enqueueDerivedMethodsProcessor(RefMethod refMethod
, DerivedMethodsProcessor p
) {
81 if (refMethod
.isConstructor() || refMethod
.isStatic()) return;
82 if (myDerivedMethodsRequests
== null) myDerivedMethodsRequests
= new THashMap
<SmartPsiElementPointer
, List
<DerivedMethodsProcessor
>>();
83 enqueueRequestImpl(refMethod
, myDerivedMethodsRequests
, p
);
86 public void enqueueFieldUsagesProcessor(RefField refField
, UsagesProcessor p
) {
87 if (myFieldUsagesRequests
== null) myFieldUsagesRequests
= new THashMap
<SmartPsiElementPointer
, List
<UsagesProcessor
>>();
88 enqueueRequestImpl(refField
, myFieldUsagesRequests
, p
);
91 public void enqueueMethodUsagesProcessor(RefMethod refMethod
, UsagesProcessor p
) {
92 if (myMethodUsagesRequests
== null) myMethodUsagesRequests
= new THashMap
<SmartPsiElementPointer
, List
<UsagesProcessor
>>();
93 enqueueRequestImpl(refMethod
, myMethodUsagesRequests
, p
);
96 public EntryPointsManager
getEntryPointsManager(final RefManager manager
) {
97 return manager
.getExtension(RefJavaManager
.MANAGER
).getEntryPointsManager();
100 @SuppressWarnings({"UseOfSystemOutOrSystemErr"})
101 public static boolean isInspectionsEnabled(final boolean online
, Project project
) {
102 final Module
[] modules
= ModuleManager
.getInstance(project
).getModules();
104 if (modules
.length
== 0) {
105 Messages
.showMessageDialog(project
, InspectionsBundle
.message("inspection.no.modules.error.message"),
106 CommonBundle
.message("title.error"), Messages
.getErrorIcon());
109 while (isBadSdk(project
, modules
)) {
110 Messages
.showMessageDialog(project
, InspectionsBundle
.message("inspection.no.jdk.error.message"),
111 CommonBundle
.message("title.error"), Messages
.getErrorIcon());
112 final Sdk projectJdk
= ProjectSettingsService
.getInstance(project
).chooseAndSetSdk();
113 if (projectJdk
== null) return false;
117 if (modules
.length
== 0) {
118 System
.err
.println(InspectionsBundle
.message("inspection.no.modules.error.message"));
121 if (isBadSdk(project
, modules
)) {
122 System
.err
.println(InspectionsBundle
.message("inspection.no.jdk.error.message"));
124 InspectionsBundle
.message("offline.inspections.jdk.not.found", ProjectRootManager
.getInstance(project
).getProjectJdkName()));
127 for (Module module
: modules
) {
128 final ModuleRootManager rootManager
= ModuleRootManager
.getInstance(module
);
129 final Sdk jdk
= rootManager
.getSdk();
130 final OrderEntry
[] entries
= rootManager
.getOrderEntries();
131 for (OrderEntry entry
: entries
) {
132 if (entry
instanceof JdkOrderEntry
) {
134 System
.err
.println(InspectionsBundle
.message("offline.inspections.module.jdk.not.found", ((JdkOrderEntry
)entry
).getJdkName(),
139 else if (entry
instanceof LibraryOrderEntry
) {
140 final LibraryOrderEntry libraryOrderEntry
= (LibraryOrderEntry
)entry
;
141 final Library library
= libraryOrderEntry
.getLibrary();
142 if (library
== null || library
.getFiles(OrderRootType
.CLASSES
).length
!= library
.getUrls(OrderRootType
.CLASSES
).length
) {
143 System
.err
.println(InspectionsBundle
.message("offline.inspections.library.was.not.resolved",
144 libraryOrderEntry
.getPresentableName(), module
.getName()));
153 private static boolean isBadSdk(final Project project
, final Module
[] modules
) {
154 boolean anyModuleAcceptsSdk
= false;
155 boolean anyModuleUsesProjectSdk
= false;
156 Sdk projectSdk
= ProjectRootManager
.getInstance(project
).getProjectJdk();
157 for (Module module
: modules
) {
158 if (ModuleRootManager
.getInstance(module
).isSdkInherited()) {
159 anyModuleUsesProjectSdk
= true;
160 if (module
.getModuleType().isValidSdk(module
, projectSdk
)) {
161 anyModuleAcceptsSdk
= true;
165 return anyModuleUsesProjectSdk
&& !anyModuleAcceptsSdk
;
168 private static <T
extends Processor
> void enqueueRequestImpl(RefElement refElement
, Map
<SmartPsiElementPointer
, List
<T
>> requestMap
, T processor
) {
169 List
<T
> requests
= requestMap
.get(refElement
.getPointer());
170 if (requests
== null) {
171 requests
= new ArrayList
<T
>();
172 requestMap
.put(refElement
.getPointer(), requests
);
174 requests
.add(processor
);
177 public void cleanup() {
178 myDerivedMethodsRequests
= null;
179 myDerivedClassesRequests
= null;
180 myMethodUsagesRequests
= null;
181 myFieldUsagesRequests
= null;
182 myClassUsagesRequests
= null;
186 public void processSearchRequests(final GlobalInspectionContext context
) {
187 final RefManager refManager
= context
.getRefManager();
188 final AnalysisScope scope
= refManager
.getScope();
190 final SearchScope searchScope
= new GlobalSearchScope(refManager
.getProject()) {
191 public boolean contains(VirtualFile file
) {
192 return !scope
.contains(file
) || file
.getFileType() != StdFileTypes
.JAVA
;
195 public int compare(VirtualFile file1
, VirtualFile file2
) {
199 public boolean isSearchInModuleContent(@NotNull Module aModule
) {
203 public boolean isSearchInLibraries() {
208 if (myDerivedClassesRequests
!= null) {
209 final List
<SmartPsiElementPointer
> sortedIDs
= getSortedIDs(myDerivedClassesRequests
);
210 for (SmartPsiElementPointer sortedID
: sortedIDs
) {
211 final PsiClass psiClass
= (PsiClass
)sortedID
.getElement();
212 if (psiClass
== null) continue;
213 ((GlobalInspectionContextImpl
)context
).incrementJobDoneAmount(GlobalInspectionContextImpl
.FIND_EXTERNAL_USAGES
, ApplicationManager
.getApplication().runReadAction(
214 new Computable
<String
>() {
215 public String
compute() {
216 return psiClass
.getQualifiedName();
221 final List
<DerivedClassesProcessor
> processors
= myDerivedClassesRequests
.get(sortedID
);
222 ClassInheritorsSearch
.search(psiClass
, searchScope
, false)
223 .forEach(createMembersProcessor(processors
, scope
));
226 myDerivedClassesRequests
= null;
229 if (myDerivedMethodsRequests
!= null) {
230 final List
<SmartPsiElementPointer
> sortedIDs
= getSortedIDs(myDerivedMethodsRequests
);
231 for (SmartPsiElementPointer sortedID
: sortedIDs
) {
232 final PsiMethod psiMethod
= (PsiMethod
)sortedID
.getElement();
233 final RefMethod refMethod
= (RefMethod
)refManager
.getReference(psiMethod
);
235 ((GlobalInspectionContextImpl
)context
)
236 .incrementJobDoneAmount(GlobalInspectionContextImpl
.FIND_EXTERNAL_USAGES
, refManager
.getQualifiedName(refMethod
));
238 final List
<DerivedMethodsProcessor
> processors
= myDerivedMethodsRequests
.get(sortedID
);
239 OverridingMethodsSearch
.search(psiMethod
, searchScope
, true)
240 .forEach(createMembersProcessor(processors
, scope
));
243 myDerivedMethodsRequests
= null;
246 if (myFieldUsagesRequests
!= null) {
247 final List
<SmartPsiElementPointer
> sortedIDs
= getSortedIDs(myFieldUsagesRequests
);
248 for (SmartPsiElementPointer sortedID
: sortedIDs
) {
249 final PsiField psiField
= (PsiField
)sortedID
.getElement();
250 if (psiField
== null) continue;
251 final List
<UsagesProcessor
> processors
= myFieldUsagesRequests
.get(sortedID
);
253 ((GlobalInspectionContextImpl
)context
)
254 .incrementJobDoneAmount(GlobalInspectionContextImpl
.FIND_EXTERNAL_USAGES
, refManager
.getQualifiedName(refManager
.getReference(psiField
)));
256 ReferencesSearch
.search(psiField
, searchScope
, false)
257 .forEach(new PsiReferenceProcessorAdapter(createReferenceProcessor(processors
, context
)));
260 myFieldUsagesRequests
= null;
263 if (myClassUsagesRequests
!= null) {
264 final List
<SmartPsiElementPointer
> sortedIDs
= getSortedIDs(myClassUsagesRequests
);
265 for (SmartPsiElementPointer sortedID
: sortedIDs
) {
266 final PsiClass psiClass
= (PsiClass
)sortedID
.getElement();
267 if (psiClass
== null) continue;
268 final List
<UsagesProcessor
> processors
= myClassUsagesRequests
.get(sortedID
);
270 ((GlobalInspectionContextImpl
)context
).incrementJobDoneAmount(GlobalInspectionContextImpl
.FIND_EXTERNAL_USAGES
, ApplicationManager
.getApplication().runReadAction(
271 new Computable
<String
>() {
272 public String
compute() {
273 return psiClass
.getQualifiedName();
278 ReferencesSearch
.search(psiClass
, searchScope
, false)
279 .forEach(new PsiReferenceProcessorAdapter(createReferenceProcessor(processors
, context
)));
282 myClassUsagesRequests
= null;
285 if (myMethodUsagesRequests
!= null) {
286 List
<SmartPsiElementPointer
> sortedIDs
= getSortedIDs(myMethodUsagesRequests
);
287 for (SmartPsiElementPointer sortedID
: sortedIDs
) {
288 final PsiMethod psiMethod
= (PsiMethod
)sortedID
.getElement();
289 if (psiMethod
== null) continue;
290 final List
<UsagesProcessor
> processors
= myMethodUsagesRequests
.get(sortedID
);
292 ((GlobalInspectionContextImpl
)context
)
293 .incrementJobDoneAmount(GlobalInspectionContextImpl
.FIND_EXTERNAL_USAGES
, refManager
.getQualifiedName(refManager
.getReference(psiMethod
)));
295 MethodReferencesSearch
.search(psiMethod
, searchScope
, true)
296 .forEach(new PsiReferenceProcessorAdapter(createReferenceProcessor(processors
, context
)));
299 myMethodUsagesRequests
= null;
304 private static <Member
extends PsiMember
, P
extends Processor
<Member
>> PsiElementProcessorAdapter
<Member
> createMembersProcessor(final List
<P
> processors
,
305 final AnalysisScope scope
) {
306 return new PsiElementProcessorAdapter
<Member
>(new PsiElementProcessor
<Member
>() {
307 public boolean execute(Member member
) {
308 if (scope
.contains(member
)) return true;
309 final List
<P
> processorsArrayed
= new ArrayList
<P
>(processors
);
310 for (P processor
: processorsArrayed
) {
311 if (!processor
.process(member
)) {
312 processors
.remove(processor
);
315 return !processors
.isEmpty();
320 private int getRequestCount() {
323 sum
+= getRequestListSize(myClassUsagesRequests
);
324 sum
+= getRequestListSize(myDerivedClassesRequests
);
325 sum
+= getRequestListSize(myDerivedMethodsRequests
);
326 sum
+= getRequestListSize(myFieldUsagesRequests
);
327 sum
+= getRequestListSize(myMethodUsagesRequests
);
332 private static int getRequestListSize(THashMap list
) {
333 if (list
== null) return 0;
337 private static List
<SmartPsiElementPointer
> getSortedIDs(final Map
<SmartPsiElementPointer
, ?
> requests
) {
338 final List
<SmartPsiElementPointer
> result
= new ArrayList
<SmartPsiElementPointer
>();
340 ApplicationManager
.getApplication().runReadAction(new Runnable() {
342 for (SmartPsiElementPointer id
: requests
.keySet()) {
344 final PsiElement psi
= id
.getElement();
350 Collections
.sort(result
, new Comparator
<SmartPsiElementPointer
>() {
351 public int compare(final SmartPsiElementPointer o1
, final SmartPsiElementPointer o2
) {
352 PsiElement p1
= o1
.getElement();
353 PsiElement p2
= o2
.getElement();
354 final PsiFile psiFile1
= p1
!= null ? p1
.getContainingFile() : null;
355 LOG
.assertTrue(psiFile1
!= null);
356 final PsiFile psiFile2
= p2
!= null ? p2
.getContainingFile() : null;
357 LOG
.assertTrue(psiFile2
!= null);
358 return psiFile1
.getName().compareTo(psiFile2
.getName());
367 private static PsiReferenceProcessor
createReferenceProcessor(@NotNull final List
<UsagesProcessor
> processors
,
368 final GlobalInspectionContext context
) {
369 return new PsiReferenceProcessor() {
370 public boolean execute(PsiReference reference
) {
371 AnalysisScope scope
= context
.getRefManager().getScope();
372 if ((scope
.contains(reference
.getElement()) && reference
.getElement().getLanguage() == StdLanguages
.JAVA
) ||
373 PsiTreeUtil
.getParentOfType(reference
.getElement(), PsiDocComment
.class) != null) {
377 synchronized (processors
) {
378 UsagesProcessor
[] processorsArrayed
= processors
.toArray(new UsagesProcessor
[processors
.size()]);
379 for (UsagesProcessor processor
: processorsArrayed
) {
380 if (!processor
.process(reference
)) {
381 processors
.remove(processor
);
386 return !processors
.isEmpty();
391 public void performPreRunActivities(final List
<Tools
> globalTools
, final List
<Tools
> localTools
,
392 final GlobalInspectionContext context
) {
393 getEntryPointsManager(context
.getRefManager()).resolveEntryPoints(context
.getRefManager());
394 ContainerUtil
.quickSort(globalTools
, new Comparator
<Tools
>() {
395 public int compare(Tools o1
, Tools o2
) {
396 if (o1
.getTool() instanceof DeadCodeInspection
) return -1;
397 if (o2
.getTool() instanceof DeadCodeInspection
) return 1;
405 public void performPostRunActivities(List
<InspectionProfileEntry
> needRepeatSearchRequest
, final GlobalInspectionContext context
) {
406 GlobalInspectionContextImpl
.FIND_EXTERNAL_USAGES
.setTotalAmount(getRequestCount() * 2);
409 processSearchRequests(context
);
410 InspectionProfileEntry
[] requestors
= needRepeatSearchRequest
.toArray(new InspectionProfileEntry
[needRepeatSearchRequest
.size()]);
411 for (InspectionProfileEntry requestor
: requestors
) {
412 if (requestor
instanceof InspectionTool
&&
413 !((InspectionTool
)requestor
).queryExternalUsagesRequests(InspectionManagerEx
.getInstance(context
.getProject()))) {
414 needRepeatSearchRequest
.remove(requestor
);
417 int oldSearchRequestCount
= GlobalInspectionContextImpl
.FIND_EXTERNAL_USAGES
.getTotalAmount();
418 float proportion
= GlobalInspectionContextImpl
.FIND_EXTERNAL_USAGES
.getProgress();
419 int totalAmount
= oldSearchRequestCount
+ getRequestCount() * 2;
420 GlobalInspectionContextImpl
.FIND_EXTERNAL_USAGES
.setTotalAmount(totalAmount
);
421 GlobalInspectionContextImpl
.FIND_EXTERNAL_USAGES
.setDoneAmount((int)(totalAmount
* proportion
));
423 while (!needRepeatSearchRequest
.isEmpty());