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 if (description
.getVirtualFile().getNameWithoutExtension().equals(filename
)) {
73 final PsiElement problem
= getProblemElement(aClass
, method
);
74 final ProblemDescriptor problemDescriptor
= manager
75 .createProblemDescriptor(problem
== null ? nameIdentifier
: problem
,
76 "Inspection does not have a description",
77 new LocalQuickFix
[]{new CreateHtmlDescriptionFix(filename
, module
)},
78 ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
);
79 return new ProblemDescriptor
[]{problemDescriptor
};
83 private static PsiElement
getProblemElement(PsiClass aClass
, PsiMethod method
) {
84 if (method
.getContainingClass() == aClass
) {
85 return PsiUtil
.getReturnedExpression(method
);
87 return aClass
.getNameIdentifier();
91 private static boolean isPathMethodsAreOverriden(PsiClass aClass
) {
92 return! ( isLastMethodDefinitionIn("getStaticDescription", INSPECTION_PROFILE_ENTRY
, aClass
)
93 && isLastMethodDefinitionIn("getDescriptionUrl", INSPECTION_PROFILE_ENTRY
, aClass
)
94 && isLastMethodDefinitionIn("getDescriptionContextClass", INSPECTION_PROFILE_ENTRY
, aClass
)
95 && isLastMethodDefinitionIn("getDescriptionFileName", INSPECTION_PROFILE_ENTRY
, aClass
));
98 private static boolean isLastMethodDefinitionIn(@NotNull String methodName
, @NotNull String classFQN
, PsiClass cls
) {
99 if (cls
== null) return false;
100 for (PsiMethod method
: cls
.getMethods()) {
101 if (method
.getName().equals(methodName
)) {
102 final PsiClass containingClass
= method
.getContainingClass();
103 if (containingClass
== null) return false;
104 return classFQN
.equals(containingClass
.getQualifiedName());
107 return isLastMethodDefinitionIn(methodName
, classFQN
, cls
.getSuperClass());
110 public static List
<VirtualFile
> getPotentialRoots(Module module
) {
111 final PsiDirectory
[] dirs
= getInspectionDescriptionsDirs(module
);
112 final List
<VirtualFile
> result
= new ArrayList
<VirtualFile
>();
113 if (dirs
.length
!= 0) {
114 for (PsiDirectory dir
: dirs
) {
115 final PsiDirectory parent
= dir
.getParentDirectory();
116 if (parent
!= null) result
.add(parent
.getVirtualFile());
119 result
.addAll(Arrays
.asList(ModuleRootManager
.getInstance(module
).getSourceRoots()));
124 public static PsiDirectory
[] getInspectionDescriptionsDirs(Module module
) {
125 final PsiPackage aPackage
= JavaPsiFacade
.getInstance(module
.getProject()).findPackage(INSPECTION_DESCRIPTIONS
);
126 if (aPackage
!= null) {
127 return aPackage
.getDirectories(GlobalSearchScope
.moduleWithDependenciesScope(module
));
129 return PsiDirectory
.EMPTY_ARRAY
;
134 private static PsiMethod
findNearestMethod(String name
, @Nullable PsiClass cls
) {
135 if (cls
== null) return null;
136 for (PsiMethod method
: cls
.getMethods()) {
137 if (method
.getParameterList().getParametersCount() == 0 && method
.getName().equals(name
)) {
138 return method
.getModifierList().hasModifierProperty(PsiModifier
.ABSTRACT
) ?
null : method
;
141 return findNearestMethod(name
, cls
.getSuperClass());
146 public String
getDisplayName() {
147 return "Inspection Description Checker";
151 public String
getShortName() {
152 return "DescriptionNotFoundInspection";
156 public boolean isEnabledByDefault() {