2 * Copyright 2006 Sascha Weinreuter
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 org
.intellij
.plugins
.intelliLang
.util
;
18 import com
.intellij
.codeInsight
.AnnotationUtil
;
19 import com
.intellij
.openapi
.util
.Pair
;
20 import com
.intellij
.psi
.*;
21 import com
.intellij
.psi
.impl
.PsiConstantEvaluationHelperImpl
;
22 import com
.intellij
.psi
.search
.searches
.SuperMethodsSearch
;
23 import com
.intellij
.psi
.util
.MethodSignatureBackedByPsiMethod
;
24 import com
.intellij
.psi
.util
.PsiTreeUtil
;
25 import com
.intellij
.util
.Processor
;
26 import org
.jetbrains
.annotations
.NonNls
;
27 import org
.jetbrains
.annotations
.NotNull
;
28 import org
.jetbrains
.annotations
.Nullable
;
30 import java
.util
.Arrays
;
31 import java
.util
.HashSet
;
35 * Contains some extended utility functions for dealing with annotations.
37 public class AnnotationUtilEx
{
38 private static final PsiConstantEvaluationHelperImpl CONSTANT_EVALUATION_HELPER
= new PsiConstantEvaluationHelperImpl();
40 private AnnotationUtilEx() {
44 * @see AnnotationUtilEx#getAnnotatedElementFor(com.intellij.psi.PsiElement, LookupType)
46 public enum LookupType
{
47 PREFER_CONTEXT
, PREFER_DECLARATION
, CONTEXT_ONLY
, DECLRARATION_ONLY
51 * Determines the PsiModifierListOwner for the passed element depending of the specified LookupType. The LookupType
52 * decides whether to prefer the element a reference expressions resolves to, or the element that is implied by the
53 * usage context ("expected type").
56 public static PsiModifierListOwner
getAnnotatedElementFor(@Nullable PsiElement element
, LookupType type
) {
57 while (element
!= null) {
58 if (type
== LookupType
.PREFER_DECLARATION
|| type
== LookupType
.DECLRARATION_ONLY
) {
59 if (element
instanceof PsiReferenceExpression
) {
60 final PsiElement e
= ((PsiReferenceExpression
)element
).resolve();
61 if (e
instanceof PsiModifierListOwner
) {
62 return (PsiModifierListOwner
)e
;
64 if (type
== LookupType
.DECLRARATION_ONLY
) {
69 element
= ContextComputationProcessor
.getTopLevelInjectionTarget(element
);
70 final PsiElement parent
= element
.getParent();
72 if (element
instanceof PsiAssignmentExpression
&& ((PsiAssignmentExpression
)element
).getOperationSign().getTokenType() == JavaTokenType
.PLUSEQ
) {
73 element
= ((PsiAssignmentExpression
)element
).getLExpression();
76 else if (parent
instanceof PsiAssignmentExpression
) {
77 final PsiAssignmentExpression p
= (PsiAssignmentExpression
)parent
;
78 if (p
.getRExpression() == element
) {
79 element
= p
.getLExpression();
83 else if (parent
instanceof PsiReturnStatement
) {
84 final PsiMethod m
= PsiTreeUtil
.getParentOfType(parent
, PsiMethod
.class);
89 else if (parent
instanceof PsiModifierListOwner
) {
90 return (PsiModifierListOwner
)parent
;
92 else if (parent
instanceof PsiArrayInitializerMemberValue
) {
93 final PsiArrayInitializerMemberValue value
= (PsiArrayInitializerMemberValue
)parent
;
94 final PsiElement pair
= value
.getParent();
95 if (pair
instanceof PsiNameValuePair
) {
96 return getAnnotationMethod((PsiNameValuePair
)pair
, element
);
99 else if (parent
instanceof PsiNameValuePair
) {
100 return getAnnotationMethod((PsiNameValuePair
)parent
, element
);
103 return PsiUtilEx
.getParameterForArgument(element
);
106 // If no annotation has been found through the usage context, check if the element
107 // (i.e. the element the reference refers to) is annotated itself
108 if (type
!= LookupType
.DECLRARATION_ONLY
) {
109 if (element
instanceof PsiReferenceExpression
) {
110 final PsiElement e
= ((PsiReferenceExpression
)element
).resolve();
111 if (e
instanceof PsiModifierListOwner
) {
112 return (PsiModifierListOwner
)e
;
122 private static PsiModifierListOwner
getAnnotationMethod(PsiNameValuePair pair
, PsiElement element
) {
123 final PsiAnnotation annotation
= PsiTreeUtil
.getParentOfType(pair
.getParent(), PsiAnnotation
.class);
124 assert annotation
!= null;
126 final String fqn
= annotation
.getQualifiedName();
127 if (fqn
== null) return null;
129 final PsiClass psiClass
= JavaPsiFacade
.getInstance(element
.getProject()).findClass(fqn
, element
.getResolveScope());
130 if (psiClass
!= null && psiClass
.isAnnotationType()) {
131 final String name
= pair
.getName();
132 final PsiMethod
[] methods
= psiClass
.findMethodsByName(name
!= null ? name
: "value", false);
133 return methods
.length
> 0 ? methods
[0] : null;
139 * Utility method to obtain annotations of a specific type from the supplied PsiModifierListOwner.
140 * For optimization reasons, this method only looks at elements of type java.lang.String.
142 * The parameter <code>allowIndirect</code> determines if the method should look for indirect annotations, i.e.
143 * annotations which have themselves been annotated by the supplied annotation name. Currently, this only allows
144 * one level of indirection and returns an array of [base-annotation, indirect annotation]
146 * The <code>annotationName</code> parameter is a pair of the target annotation class' fully qualified name as a
147 * String and as a Set. This is done for performance reasons because the Set is required by the
148 * {@link com.intellij.codeInsight.AnnotationUtil} utility class and allows to avoid unecessary object constructions.
151 public static PsiAnnotation
[] getAnnotationFrom(PsiModifierListOwner owner
,
152 Pair
<String
, ?
extends Set
<String
>> annotationName
,
153 boolean allowIndirect
,
154 boolean inHierarchy
) {
155 if (!PsiUtilEx
.isLanguageAnnotationTarget(owner
)) return PsiAnnotation
.EMPTY_ARRAY
;
157 final PsiAnnotation directAnnotation
= inHierarchy?
158 AnnotationUtil
.findAnnotationInHierarchy(owner
, annotationName
.second
) :
159 AnnotationUtil
.findAnnotation(owner
, annotationName
.second
);
160 if (directAnnotation
!= null) {
161 return new PsiAnnotation
[]{directAnnotation
};
164 final PsiAnnotation
[] annotations
= getAnnotations(owner
, inHierarchy
);
165 for (PsiAnnotation annotation
: annotations
) {
166 PsiJavaCodeReferenceElement nameReference
= annotation
.getNameReferenceElement();
167 if (nameReference
== null) continue;
168 PsiElement resolved
= nameReference
.resolve();
169 if (resolved
instanceof PsiClass
) {
170 final PsiAnnotation psiAnnotation
= AnnotationUtil
.findAnnotationInHierarchy((PsiModifierListOwner
)resolved
, annotationName
.second
);
171 if (psiAnnotation
!= null) {
172 return new PsiAnnotation
[]{psiAnnotation
, annotation
};
177 return PsiAnnotation
.EMPTY_ARRAY
;
180 public static PsiAnnotation
[] getAnnotationFrom(@NotNull PsiModifierListOwner owner
,
181 @NotNull Pair
<String
, ?
extends Set
<String
>> annotationName
,
182 boolean allowIndirect
) {
183 return getAnnotationFrom(owner
, annotationName
, allowIndirect
, true);
187 * Calculates the value of the annotation's attribute referenced by the <code>attr</code> parameter by trying to
188 * find the attribute in the supplied list of annotations and calculating the constant value for the first attribute
192 public static String
calcAnnotationValue(PsiAnnotation
[] annotation
, @NonNls String attr
) {
193 for (PsiAnnotation psiAnnotation
: annotation
) {
194 final String value
= calcAnnotationValue(psiAnnotation
, attr
);
195 if (value
!= null) return value
;
201 public static String
calcAnnotationValue(@NotNull PsiAnnotation annotation
, @NonNls String attr
) {
202 PsiElement value
= annotation
.findAttributeValue(attr
);
203 if (value
instanceof PsiExpression
) {
204 Object o
= CONSTANT_EVALUATION_HELPER
.computeConstantExpression(value
);
205 if (o
instanceof String
) {
213 * Returns all annotations for <code>listOwner</code>, possibly walking up the method hierarchy.
215 * @see com.intellij.codeInsight.AnnotationUtil#isAnnotated(com.intellij.psi.PsiModifierListOwner, java.lang.String, boolean)
217 private static PsiAnnotation
[] getAnnotations(@NotNull PsiModifierListOwner listOwner
, boolean inHierarchy
) {
218 final PsiModifierList modifierList
= listOwner
.getModifierList();
219 if (modifierList
== null) {
220 return PsiAnnotation
.EMPTY_ARRAY
;
223 return modifierList
.getAnnotations();
225 final Set
<PsiAnnotation
> all
= new HashSet
<PsiAnnotation
>() {
226 public boolean add(PsiAnnotation o
) {
227 // don't overwrite "higher level" annotations
228 return !contains(o
) && super.add(o
);
231 if (listOwner
instanceof PsiMethod
) {
232 all
.addAll(Arrays
.asList(modifierList
.getAnnotations()));
233 SuperMethodsSearch
.search((PsiMethod
)listOwner
, null, true, true).forEach(new Processor
<MethodSignatureBackedByPsiMethod
>() {
234 public boolean process(final MethodSignatureBackedByPsiMethod superMethod
) {
235 all
.addAll(Arrays
.asList(superMethod
.getMethod().getModifierList().getAnnotations()));
239 return all
.toArray(new PsiAnnotation
[all
.size()]);
241 if (listOwner
instanceof PsiParameter
) {
242 PsiParameter parameter
= (PsiParameter
)listOwner
;
243 PsiElement declarationScope
= parameter
.getDeclarationScope();
244 PsiParameterList parameterList
;
245 if (declarationScope
instanceof PsiMethod
&& parameter
.getParent() == (parameterList
= ((PsiMethod
)declarationScope
).getParameterList())) {
246 PsiMethod method
= (PsiMethod
)declarationScope
;
247 final int parameterIndex
= parameterList
.getParameterIndex(parameter
);
248 all
.addAll(Arrays
.asList(modifierList
.getAnnotations()));
249 SuperMethodsSearch
.search(method
, null, true, true).forEach(new Processor
<MethodSignatureBackedByPsiMethod
>() {
250 public boolean process(final MethodSignatureBackedByPsiMethod superMethod
) {
251 PsiParameter superParameter
= superMethod
.getMethod().getParameterList().getParameters()[parameterIndex
];
252 PsiModifierList modifierList
= superParameter
.getModifierList();
253 if (modifierList
!= null) {
254 all
.addAll(Arrays
.asList(modifierList
.getAnnotations()));
259 return all
.toArray(new PsiAnnotation
[all
.size()]);
262 return modifierList
.getAnnotations();