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
.codeInsight
;
18 import com
.intellij
.psi
.*;
19 import com
.intellij
.psi
.util
.PsiUtil
;
20 import com
.intellij
.util
.ArrayUtil
;
21 import gnu
.trove
.THashSet
;
22 import org
.jetbrains
.annotations
.NonNls
;
23 import org
.jetbrains
.annotations
.NotNull
;
24 import org
.jetbrains
.annotations
.Nullable
;
31 public class AnnotationUtil
{
33 * The full qualified name of the standard Nullable annotation.
35 public static final String NULLABLE
= "org.jetbrains.annotations.Nullable";
38 * The full qualified name of the standard NotNull annotation.
40 public static final String NOT_NULL
= "org.jetbrains.annotations.NotNull";
42 @NonNls public static final String NOT_NULL_SIMPLE_NAME
= "NotNull";
44 @NonNls public static final String NULLABLE_SIMPLE_NAME
= "Nullable";
47 * The full qualified name of the standard NonNls annotation.
51 public static final String NON_NLS
= "org.jetbrains.annotations.NonNls";
52 public static final String NLS
= "org.jetbrains.annotations.Nls";
53 public static final String PROPERTY_KEY
= "org.jetbrains.annotations.PropertyKey";
54 @NonNls public static final String PROPERTY_KEY_RESOURCE_BUNDLE_PARAMETER
= "resourceBundle";
56 @NonNls public static final String NON_NLS_SIMPLE_NAME
= "NonNls";
57 @NonNls public static final String PROPERTY_KEY_SIMPLE_NAME
= "PropertyKey";
59 public static final String TEST_ONLY
= "org.jetbrains.annotations.TestOnly";
60 @NonNls public static final String TEST_ONLY_SIMPLE_NAME
= "TestOnly";
62 public static final String LANGUAGE
= "org.intellij.lang.annotations.Language";
64 public static final Set
<String
> ALL_ANNOTATIONS
;
66 @NonNls private static final String
[] SIMPLE_NAMES
=
67 {NOT_NULL_SIMPLE_NAME
, NULLABLE_SIMPLE_NAME
, NON_NLS_SIMPLE_NAME
, PROPERTY_KEY_SIMPLE_NAME
, TEST_ONLY_SIMPLE_NAME
,
68 "Language", "Identifier", "Pattern", "PrintFormat", "RegExp", "Subst"};
69 public static final String TARGET_ANNOTATION_FQ_NAME
= "java.lang.annotation.Target";
72 ALL_ANNOTATIONS
= new HashSet
<String
>(2);
73 ALL_ANNOTATIONS
.add(NULLABLE
);
74 ALL_ANNOTATIONS
.add(NOT_NULL
);
77 public static boolean isNullable(@NotNull PsiModifierListOwner owner
) {
78 return !isNotNull(owner
) && isAnnotated(owner
, NULLABLE
, true);
81 public static boolean isNotNull(@NotNull PsiModifierListOwner owner
) {
82 return isAnnotated(owner
, NOT_NULL
, true);
86 public static PsiAnnotation
findAnnotation(PsiModifierListOwner listOwner
, @NotNull String
... annotationNames
) {
87 return findAnnotation(listOwner
, false, annotationNames
);
91 public static PsiAnnotation
findAnnotation(PsiModifierListOwner listOwner
, final boolean skipExternal
, @NotNull String
... annotationNames
) {
92 if (annotationNames
.length
== 0) return null;
93 Set
<String
> set
= annotationNames
.length
== 1 ? Collections
.singleton(annotationNames
[0]) : new HashSet
<String
>(Arrays
.asList(annotationNames
));
94 return findAnnotation(listOwner
, set
, skipExternal
);
98 public static PsiAnnotation
findAnnotation(PsiModifierListOwner listOwner
, @NotNull Set
<String
> annotationNames
) {
99 return findAnnotation(listOwner
, (Collection
<String
>)annotationNames
);
103 public static PsiAnnotation
findAnnotation(@Nullable PsiModifierListOwner listOwner
, Collection
<String
> annotationNames
) {
104 return findAnnotation(listOwner
, annotationNames
, false);
108 public static PsiAnnotation
findAnnotation(@Nullable PsiModifierListOwner listOwner
, Collection
<String
> annotationNames
,
109 final boolean skipExternal
) {
110 if (listOwner
== null) return null;
111 final PsiModifierList list
= listOwner
.getModifierList();
112 if (list
== null) return null;
113 final PsiAnnotation
[] allAnnotations
= list
.getAnnotations();
114 for (PsiAnnotation annotation
: allAnnotations
) {
115 String qualifiedName
= annotation
.getQualifiedName();
116 if (annotationNames
.contains(qualifiedName
)) {
121 final ExternalAnnotationsManager annotationsManager
= ExternalAnnotationsManager
.getInstance(listOwner
.getProject());
122 for (String annotationName
: annotationNames
) {
123 final PsiAnnotation annotation
= annotationsManager
.findExternalAnnotation(listOwner
, annotationName
);
124 if (annotation
!= null) {
133 public static PsiAnnotation
[] findAnnotations(final PsiModifierListOwner modifierListOwner
, @NotNull Collection
<String
> annotationNames
) {
134 if (modifierListOwner
== null) return PsiAnnotation
.EMPTY_ARRAY
;
135 final PsiModifierList modifierList
= modifierListOwner
.getModifierList();
136 if (modifierList
== null) return PsiAnnotation
.EMPTY_ARRAY
;
137 final PsiAnnotation
[] annotations
= modifierList
.getAnnotations();
138 ArrayList
<PsiAnnotation
> result
= null;
139 for (final PsiAnnotation psiAnnotation
: annotations
) {
140 if (annotationNames
.contains(psiAnnotation
.getQualifiedName())) {
141 if (result
== null) result
= new ArrayList
<PsiAnnotation
>();
142 result
.add(psiAnnotation
);
145 return result
== null ? PsiAnnotation
.EMPTY_ARRAY
: result
.toArray(new PsiAnnotation
[result
.size()]);
149 public static PsiAnnotation
findAnnotationInHierarchy(PsiModifierListOwner listOwner
, Set
<String
> annotationNames
) {
150 PsiAnnotation directAnnotation
= findAnnotation(listOwner
, annotationNames
);
151 if (directAnnotation
!= null) return directAnnotation
;
152 if (listOwner
instanceof PsiMethod
) {
153 PsiMethod method
= (PsiMethod
)listOwner
;
154 PsiClass aClass
= method
.getContainingClass();
155 if (aClass
== null) return null;
156 HierarchicalMethodSignature methodSignature
= method
.getHierarchicalMethodSignature();
157 return findAnnotationInHierarchy(methodSignature
, annotationNames
, method
, null);
158 } else if (listOwner
instanceof PsiClass
) {
159 return findAnnotationInHierarchy(((PsiClass
)listOwner
), annotationNames
, null);
165 private static PsiAnnotation
findAnnotationInHierarchy(final @NotNull PsiClass psiClass
, final Set
<String
> annotationNames
, Set
<PsiClass
> processed
) {
166 final PsiClass
[] superClasses
= psiClass
.getSupers();
167 for (final PsiClass superClass
: superClasses
) {
168 if (processed
== null) processed
= new THashSet
<PsiClass
>();
169 if (!processed
.add(superClass
)) return null;
170 final PsiAnnotation annotation
= findAnnotation(superClass
, annotationNames
);
171 if (annotation
!= null) return annotation
;
172 final PsiAnnotation annotationInHierarchy
= findAnnotationInHierarchy(superClass
, annotationNames
, processed
);
173 if (annotationInHierarchy
!= null) return annotationInHierarchy
;
179 private static PsiAnnotation
findAnnotationInHierarchy(HierarchicalMethodSignature signature
,
180 Set
<String
> annotationNames
,
182 Set
<PsiMethod
> processed
) {
183 final List
<HierarchicalMethodSignature
> superSignatures
= signature
.getSuperSignatures();
184 final PsiResolveHelper resolveHelper
= JavaPsiFacade
.getInstance(place
.getProject()).getResolveHelper();
185 for (final HierarchicalMethodSignature superSignature
: superSignatures
) {
186 final PsiMethod superMethod
= superSignature
.getMethod();
187 if (processed
== null) processed
= new THashSet
<PsiMethod
>();
188 if (!processed
.add(superMethod
)) continue;
189 if (!resolveHelper
.isAccessible(superMethod
, place
, null)) continue;
190 PsiAnnotation direct
= findAnnotation(superMethod
, annotationNames
);
191 if (direct
!= null) return direct
;
192 PsiAnnotation superResult
= findAnnotationInHierarchy(superSignature
, annotationNames
, place
, processed
);
193 if (superResult
!= null) return superResult
;
199 public static boolean isAnnotated(PsiModifierListOwner listOwner
, Collection
<String
> annotations
) {
200 for (String annotation
: annotations
) {
201 if (isAnnotated(listOwner
, annotation
, false)) return true;
206 public static boolean isAnnotated(@NotNull PsiModifierListOwner listOwner
, @NonNls String annotationFQN
, boolean checkHierarchy
) {
207 return isAnnotated(listOwner
, annotationFQN
, checkHierarchy
, false, null);
210 public static boolean isAnnotated(@NotNull PsiModifierListOwner listOwner
, @NonNls String annotationFQN
, boolean checkHierarchy
,
211 boolean skipExternal
) {
212 return isAnnotated(listOwner
, annotationFQN
, checkHierarchy
, skipExternal
, null);
215 private static boolean isAnnotated(@NotNull PsiModifierListOwner listOwner
,
216 @NonNls String annotationFQN
,
217 boolean checkHierarchy
, final boolean skipExternal
, Set
<PsiMember
> processed
) {
218 if (!listOwner
.isValid()) return false;
219 final PsiModifierList modifierList
= listOwner
.getModifierList();
220 if (modifierList
== null) return false;
221 PsiAnnotation annotation
= modifierList
.findAnnotation(annotationFQN
);
222 if (annotation
!= null) return true;
223 if (!skipExternal
&& ExternalAnnotationsManager
.getInstance(listOwner
.getProject()).findExternalAnnotation(listOwner
, annotationFQN
) != null) {
226 if (checkHierarchy
) {
227 if (listOwner
instanceof PsiMethod
) {
228 PsiMethod method
= (PsiMethod
)listOwner
;
229 if (processed
== null) processed
= new THashSet
<PsiMember
>();
230 if (!processed
.add(method
)) return false;
231 final PsiMethod
[] superMethods
= method
.findSuperMethods();
232 for (PsiMethod superMethod
: superMethods
) {
233 if (isAnnotated(superMethod
, annotationFQN
, checkHierarchy
, skipExternal
, processed
)) return true;
235 } else if (listOwner
instanceof PsiClass
) {
236 final PsiClass clazz
= (PsiClass
)listOwner
;
237 if (processed
== null) processed
= new THashSet
<PsiMember
>();
238 if (!processed
.add(clazz
)) return false;
239 final PsiClass
[] superClasses
= clazz
.getSupers();
240 for (PsiClass superClass
: superClasses
) {
241 if (isAnnotated(superClass
, annotationFQN
, checkHierarchy
, skipExternal
, processed
)) return true;
248 public static boolean isAnnotatingApplicable(PsiElement elt
) {
249 return PsiUtil
.isLanguageLevel5OrHigher(elt
) &&
250 JavaPsiFacade
.getInstance(elt
.getProject()).findClass(NULLABLE
, elt
.getResolveScope()) != null;
253 public static boolean isJetbrainsAnnotation(@NonNls final String simpleName
) {
254 return ArrayUtil
.find(SIMPLE_NAMES
, simpleName
) != -1;
258 * Works similar to #isAnnotated(PsiModifierListOwner, Collection<String>) but supports FQN patters
259 * like "javax.ws.rs.*". Supports ending "*" only.
261 * @param owner modifier list
262 * @param annotations annotations qualified names or patterns. Patterns can have '*' at the end
263 * @return <code>true</code> if annotated of at least one annotation from the annotations list
265 public static boolean checkAnnotatedUsingPatterns(PsiModifierListOwner owner
, Collection
<String
> annotations
) {
266 List
<String
> fqns
= null;
267 final PsiModifierList modList
;
268 if (owner
== null || (modList
= owner
.getModifierList()) == null) return false;
270 for (String fqn
: annotations
) {
271 if (! fqn
.endsWith("*") && isAnnotated(owner
, fqn
, false)) {
275 fqns
= new ArrayList
<String
>();
276 final PsiAnnotation
[] annos
= modList
.getAnnotations();
277 for (PsiAnnotation anno
: annos
) {
278 final String qName
= anno
.getQualifiedName();
283 if (fqns
.isEmpty()) return false;
285 fqn
= fqn
.substring(0, fqn
.length() - 2);
286 for (String annoFQN
: fqns
) {
287 if (annoFQN
.startsWith(fqn
)) {