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.
16 package com
.intellij
.codeInspection
.java15api
;
18 import com
.intellij
.ExtensionPoints
;
19 import com
.intellij
.codeHighlighting
.HighlightDisplayLevel
;
20 import com
.intellij
.codeInsight
.daemon
.GroupNames
;
21 import com
.intellij
.codeInspection
.*;
22 import com
.intellij
.openapi
.extensions
.ExtensionPoint
;
23 import com
.intellij
.openapi
.extensions
.Extensions
;
24 import com
.intellij
.openapi
.module
.LanguageLevelUtil
;
25 import com
.intellij
.openapi
.module
.Module
;
26 import com
.intellij
.openapi
.module
.ModuleUtil
;
27 import com
.intellij
.openapi
.vfs
.CharsetToolkit
;
28 import com
.intellij
.pom
.java
.LanguageLevel
;
29 import com
.intellij
.psi
.*;
30 import com
.intellij
.psi
.javadoc
.PsiDocComment
;
31 import com
.intellij
.psi
.util
.PsiTreeUtil
;
32 import com
.intellij
.psi
.util
.PsiUtil
;
33 import com
.intellij
.util
.containers
.HashMap
;
34 import gnu
.trove
.THashSet
;
35 import org
.jetbrains
.annotations
.NonNls
;
36 import org
.jetbrains
.annotations
.NotNull
;
37 import org
.jetbrains
.annotations
.Nullable
;
44 public class Java15APIUsageInspection
extends BaseJavaLocalInspectionTool
{
45 @NonNls public static final String SHORT_NAME
= "Since15";
47 private static final HashMap
<LanguageLevel
, THashSet
<String
>> ourForbiddenAPI
= new HashMap
<LanguageLevel
, THashSet
<String
>>(5);
48 private static final THashSet
<String
> ourIgnored16ClassesAPI
= new THashSet
<String
>(10);
49 private static final HashMap
<LanguageLevel
, String
> ourAPIPresentationMap
= new HashMap
<LanguageLevel
, String
>(5);
52 final THashSet
<String
> ourForbidden14API
= new THashSet
<String
>(1000);
53 initForbiddenApi("api14List.txt", ourForbidden14API
);
54 ourForbiddenAPI
.put(LanguageLevel
.JDK_1_3
, ourForbidden14API
);
55 ourAPIPresentationMap
.put(LanguageLevel
.JDK_1_3
, "1.4");
56 final THashSet
<String
> ourForbidden15API
= new THashSet
<String
>(1000);
57 initForbiddenApi("apiList.txt", ourForbidden15API
);
58 ourForbiddenAPI
.put(LanguageLevel
.JDK_1_4
, ourForbidden15API
);
59 ourAPIPresentationMap
.put(LanguageLevel
.JDK_1_4
, "1.5");
60 final THashSet
<String
> ourForbidden16API
= new THashSet
<String
>(1000);
61 initForbiddenApi("api16List.txt", ourForbidden16API
);
62 ourForbiddenAPI
.put(LanguageLevel
.JDK_1_5
, ourForbidden16API
);
63 ourAPIPresentationMap
.put(LanguageLevel
.JDK_1_5
, "1.6");
64 initForbiddenApi("ignore16List.txt", ourIgnored16ClassesAPI
);
67 private static void initForbiddenApi(@NonNls String list
, THashSet
<String
> set
) {
68 BufferedReader reader
= null;
70 final InputStream stream
= Java15APIUsageInspection
.class.getResourceAsStream(list
);
71 reader
= new BufferedReader(new InputStreamReader(stream
, CharsetToolkit
.UTF8_CHARSET
));
74 String line
= reader
.readLine();
75 if (line
== null) break;
80 catch (UnsupportedEncodingException e
) {
83 catch (IOException e
) {
91 catch (IOException e
) {
99 public String
getGroupDisplayName() {
100 return GroupNames
.JDK15_SPECIFIC_GROUP_NAME
;
104 public String
getDisplayName() {
105 return InspectionsBundle
.message("inspection.1.5.display.name", "@since 1.5(1.6)");
109 public String
getShortName() {
116 public HighlightDisplayLevel
getDefaultLevel() {
117 return HighlightDisplayLevel
.ERROR
;
121 public boolean isEnabledByDefault() {
126 public PsiElementVisitor
buildVisitor(@NotNull ProblemsHolder holder
, boolean isOnTheFly
) {
127 return new MyVisitor(holder
);
130 private static boolean isInProject(final PsiElement elt
) {
131 return elt
.getManager().isInProject(elt
);
135 public ProblemDescriptor
[] checkFile(@NotNull PsiFile file
, @NotNull InspectionManager manager
, boolean isOnTheFly
) {
136 ExtensionPoint
<FileCheckingInspection
> point
= Extensions
.getRootArea().getExtensionPoint(ExtensionPoints
.JAVA15_INSPECTION_TOOL
);
137 final FileCheckingInspection
[] fileCheckingInspections
= point
.getExtensions();
138 for(FileCheckingInspection obj
: fileCheckingInspections
) {
139 ProblemDescriptor
[] descriptors
= obj
.checkFile(file
, manager
, isOnTheFly
);
140 if (descriptors
!= null) {
148 public static String
getPresentable(LanguageLevel languageLevel
) {
149 return ourAPIPresentationMap
.get(languageLevel
);
152 private class MyVisitor
extends JavaElementVisitor
{
153 private final ProblemsHolder myHolder
;
155 public MyVisitor(final ProblemsHolder holder
) {
159 @Override public void visitDocComment(PsiDocComment comment
) {
160 // No references inside doc comment are of interest.
163 @Override public void visitClass(PsiClass aClass
) {
164 // Don't go into classes (anonymous, locals).
167 @Override public void visitReferenceExpression(PsiReferenceExpression expression
) {
168 visitReferenceElement(expression
);
171 @Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference
) {
172 super.visitReferenceElement(reference
);
173 final PsiElement resolved
= reference
.resolve();
175 if (resolved
instanceof PsiCompiledElement
&& resolved
instanceof PsiMember
) {
176 final Module module
= ModuleUtil
.findModuleForPsiElement(reference
.getElement());
177 if (module
!= null) {
178 final LanguageLevel languageLevel
= LanguageLevelUtil
.getEffectiveLanguageLevel(module
);
179 if (isForbiddenApiUsage((PsiMember
)resolved
, languageLevel
)) {
180 PsiClass psiClass
= null;
181 final PsiElement qualifier
= reference
.getQualifier();
182 if (qualifier
!= null) {
183 if (qualifier
instanceof PsiExpression
) {
184 psiClass
= PsiUtil
.resolveClassInType(((PsiExpression
)qualifier
).getType());
188 psiClass
= PsiTreeUtil
.getParentOfType(reference
, PsiClass
.class);
190 if (psiClass
!= null) {
191 if (isIgnored(psiClass
)) return;
192 for (PsiClass superClass
: psiClass
.getSupers()) {
193 if (isIgnored(superClass
)) return;
196 registerError(reference
, languageLevel
);
202 private boolean isIgnored(PsiClass psiClass
) {
203 final String qualifiedName
= psiClass
.getQualifiedName();
204 return qualifiedName
!= null && ourIgnored16ClassesAPI
.contains(qualifiedName
);
207 @Override public void visitNewExpression(final PsiNewExpression expression
) {
208 super.visitNewExpression(expression
);
209 final PsiMethod constructor
= expression
.resolveConstructor();
210 final Module module
= ModuleUtil
.findModuleForPsiElement(expression
);
211 if (module
!= null) {
212 final LanguageLevel languageLevel
= LanguageLevelUtil
.getEffectiveLanguageLevel(module
);
213 if (constructor
instanceof PsiCompiledElement
) {
214 if (isForbiddenApiUsage(constructor
, languageLevel
)) {
215 registerError(expression
.getClassReference(), languageLevel
);
221 private void registerError(PsiJavaCodeReferenceElement reference
, LanguageLevel api
) {
222 if (isInProject(reference
)) {
223 myHolder
.registerProblem(reference
, InspectionsBundle
.message("inspection.1.5.problem.descriptor", getPresentable(api
)));
228 public static boolean isForbiddenApiUsage(final PsiMember member
, LanguageLevel languageLevel
) {
229 if (member
== null) return false;
231 // Annotations caught by special inspection if necessary
232 if (member
instanceof PsiClass
&& ((PsiClass
)member
).isAnnotationType()) return false;
234 if (member
instanceof PsiAnonymousClass
) return false;
235 if (member
.getContainingClass() instanceof PsiAnonymousClass
) return false;
236 if (member
instanceof PsiClass
&& !(member
.getParent() instanceof PsiClass
|| member
.getParent() instanceof PsiFile
)) return false;
238 return isForbiddenSignature(member
, languageLevel
) ||
239 isForbiddenApiUsage(member
.getContainingClass(), languageLevel
);
243 private static boolean isForbiddenSignature(PsiMember member
, LanguageLevel languageLevel
) {
245 final THashSet
<String
> forbiddenApi
= ourForbiddenAPI
.get(languageLevel
);
246 if (forbiddenApi
== null) return false;
247 return forbiddenApi
.contains(getSignature(member
)) ||
248 (languageLevel
.compareTo(LanguageLevel
.HIGHEST
) != 0 && isForbiddenSignature(member
, LanguageLevel
.values()[languageLevel
.ordinal() + 1]));
251 public static String
getSignature(PsiMember member
) {
252 if (member
instanceof PsiClass
) {
253 return ((PsiClass
)member
).getQualifiedName();
255 if (member
instanceof PsiField
) {
256 return getSignature(member
.getContainingClass()) + "#" + member
.getName();
258 if (member
instanceof PsiMethod
) {
259 final PsiMethod method
= (PsiMethod
)member
;
260 StringBuffer buf
= new StringBuffer();
261 buf
.append(getSignature(method
.getContainingClass()));
263 buf
.append(method
.getName());
265 final PsiType
[] params
= method
.getSignature(PsiSubstitutor
.EMPTY
).getParameterTypes();
266 for (PsiType type
: params
) {
267 buf
.append(type
.getCanonicalText());
271 return buf
.toString();