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.
17 package org
.jetbrains
.idea
.devkit
.inspections
;
19 import com
.intellij
.codeInspection
.InspectionManager
;
20 import com
.intellij
.codeInspection
.LocalQuickFix
;
21 import com
.intellij
.codeInspection
.ProblemDescriptor
;
22 import com
.intellij
.codeInspection
.ProblemHighlightType
;
23 import com
.intellij
.openapi
.module
.Module
;
24 import com
.intellij
.openapi
.module
.ModuleUtil
;
25 import com
.intellij
.openapi
.project
.Project
;
26 import com
.intellij
.openapi
.roots
.ModuleRootManager
;
27 import com
.intellij
.openapi
.vfs
.VirtualFile
;
28 import com
.intellij
.psi
.*;
29 import com
.intellij
.psi
.search
.GlobalSearchScope
;
30 import org
.jetbrains
.annotations
.Nls
;
31 import org
.jetbrains
.annotations
.NonNls
;
32 import org
.jetbrains
.annotations
.NotNull
;
33 import org
.jetbrains
.annotations
.Nullable
;
34 import org
.jetbrains
.idea
.devkit
.inspections
.quickfix
.CreateHtmlDescriptionFix
;
35 import org
.jetbrains
.idea
.devkit
.util
.PsiUtil
;
37 import java
.util
.ArrayList
;
38 import java
.util
.Arrays
;
39 import java
.util
.List
;
42 * @author Konstantin Bulenkov
44 public class DescriptionNotFoundInspection
extends DevKitInspectionBase
{
45 @NonNls private static final String INSPECTION_PROFILE_ENTRY
= "com.intellij.codeInspection.InspectionProfileEntry";
46 @NonNls private static final String INSPECTION_DESCRIPTIONS
= "inspectionDescriptions";
49 public ProblemDescriptor
[] checkClass(@NotNull PsiClass aClass
, @NotNull InspectionManager manager
, boolean isOnTheFly
) {
50 final Project project
= aClass
.getProject();
51 final PsiIdentifier nameIdentifier
= aClass
.getNameIdentifier();
52 final Module module
= ModuleUtil
.findModuleForPsiElement(aClass
);
54 if (nameIdentifier
== null || module
== null || !PsiUtil
.isInstanciatable(aClass
)) return null;
56 final PsiClass base
= JavaPsiFacade
.getInstance(project
).findClass(INSPECTION_PROFILE_ENTRY
, GlobalSearchScope
.allScope(project
));
58 if (base
== null || ! aClass
.isInheritor(base
, true) || isPathMethodsAreOverriden(aClass
)) return null;
60 final PsiMethod method
= findNearestMethod("getShortName", aClass
);
61 if (method
== null) return null;
62 final String filename
= PsiUtil
.getReturnedLiteral(method
, aClass
);
63 if (filename
== null) return null;
66 for (PsiDirectory description
: getInspectionDescriptionsDirs(module
)) {
67 final PsiFile file
= description
.findFile(filename
+ ".html");
68 if (file
== null) continue;
69 final VirtualFile vf
= file
.getVirtualFile();
70 if (vf
== null) continue;
71 if (vf
.getNameWithoutExtension().equals(filename
)) {
77 final PsiElement problem
= getProblemElement(aClass
, method
);
78 final ProblemDescriptor problemDescriptor
= manager
79 .createProblemDescriptor(problem
== null ? nameIdentifier
: problem
,
80 "Inspection does not have a description", isOnTheFly
, new LocalQuickFix
[]{new CreateHtmlDescriptionFix(filename
, module
)},
81 ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
);
82 return new ProblemDescriptor
[]{problemDescriptor
};
86 private static PsiElement
getProblemElement(PsiClass aClass
, PsiMethod method
) {
87 if (method
.getContainingClass() == aClass
) {
88 return PsiUtil
.getReturnedExpression(method
);
90 return aClass
.getNameIdentifier();
94 private static boolean isPathMethodsAreOverriden(PsiClass aClass
) {
95 return! ( isLastMethodDefinitionIn("getStaticDescription", INSPECTION_PROFILE_ENTRY
, aClass
)
96 && isLastMethodDefinitionIn("getDescriptionUrl", INSPECTION_PROFILE_ENTRY
, aClass
)
97 && isLastMethodDefinitionIn("getDescriptionContextClass", INSPECTION_PROFILE_ENTRY
, aClass
)
98 && isLastMethodDefinitionIn("getDescriptionFileName", INSPECTION_PROFILE_ENTRY
, aClass
));
101 private static boolean isLastMethodDefinitionIn(@NotNull String methodName
, @NotNull String classFQN
, PsiClass cls
) {
102 if (cls
== null) return false;
103 for (PsiMethod method
: cls
.getMethods()) {
104 if (method
.getName().equals(methodName
)) {
105 final PsiClass containingClass
= method
.getContainingClass();
106 if (containingClass
== null) return false;
107 return classFQN
.equals(containingClass
.getQualifiedName());
110 return isLastMethodDefinitionIn(methodName
, classFQN
, cls
.getSuperClass());
113 public static List
<VirtualFile
> getPotentialRoots(Module module
) {
114 final PsiDirectory
[] dirs
= getInspectionDescriptionsDirs(module
);
115 final List
<VirtualFile
> result
= new ArrayList
<VirtualFile
>();
116 if (dirs
.length
!= 0) {
117 for (PsiDirectory dir
: dirs
) {
118 final PsiDirectory parent
= dir
.getParentDirectory();
119 if (parent
!= null) result
.add(parent
.getVirtualFile());
122 result
.addAll(Arrays
.asList(ModuleRootManager
.getInstance(module
).getSourceRoots()));
127 public static PsiDirectory
[] getInspectionDescriptionsDirs(Module module
) {
128 final PsiPackage aPackage
= JavaPsiFacade
.getInstance(module
.getProject()).findPackage(INSPECTION_DESCRIPTIONS
);
129 if (aPackage
!= null) {
130 return aPackage
.getDirectories(GlobalSearchScope
.moduleWithDependenciesScope(module
));
132 return PsiDirectory
.EMPTY_ARRAY
;
137 private static PsiMethod
findNearestMethod(String name
, @Nullable PsiClass cls
) {
138 if (cls
== null) return null;
139 for (PsiMethod method
: cls
.getMethods()) {
140 if (method
.getParameterList().getParametersCount() == 0 && method
.getName().equals(name
)) {
141 return method
.getModifierList().hasModifierProperty(PsiModifier
.ABSTRACT
) ?
null : method
;
144 return findNearestMethod(name
, cls
.getSuperClass());
149 public String
getDisplayName() {
150 return "Inspection Description Checker";
154 public String
getShortName() {
155 return "DescriptionNotFoundInspection";
159 public boolean isEnabledByDefault() {