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
.search
.searches
.SuperMethodsSearch
;
22 import com
.intellij
.psi
.impl
.PsiConstantEvaluationHelperImpl
;
23 import com
.intellij
.psi
.util
.PsiTreeUtil
;
24 import com
.intellij
.psi
.util
.MethodSignatureBackedByPsiMethod
;
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.PsiExpression,
45 * org.intellij.plugins.intelliLang.util.AnnotationUtilEx.LookupType)
47 public enum LookupType
{
48 PREFER_CONTEXT
, PREFER_DECLARATION
, CONTEXT_ONLY
, DECLRARATION_ONLY
52 * Determines the PsiModifierListOwner for the passed element depending of the specified LookupType. The LookupType
53 * decides whether to prefer the element a reference expressions resolves to, or the element that is implied by the
54 * usage context ("expected type").
57 public static PsiModifierListOwner
getAnnotatedElementFor(@Nullable PsiElement element
, LookupType type
) {
58 if (element
== null) return null;
60 if (type
== LookupType
.PREFER_DECLARATION
|| type
== LookupType
.DECLRARATION_ONLY
) {
61 if (element
instanceof PsiReferenceExpression
) {
62 final PsiElement e
= ((PsiReferenceExpression
)element
).resolve();
63 if (e
instanceof PsiModifierListOwner
) {
64 return (PsiModifierListOwner
)e
;
66 if (type
== LookupType
.DECLRARATION_ONLY
) {
72 final PsiElement parent
= element
.getParent();
74 if (parent
instanceof PsiAssignmentExpression
) {
75 final PsiAssignmentExpression p
= (PsiAssignmentExpression
)parent
;
76 if (p
.getRExpression() == element
) {
77 return getAnnotatedElementFor(p
.getLExpression(), type
);
80 else if (parent
instanceof PsiExpression
) {
81 return getAnnotatedElementFor((PsiExpression
)parent
, type
);
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
;
121 private static PsiModifierListOwner
getAnnotationMethod(PsiNameValuePair pair
, PsiElement element
) {
122 final PsiAnnotation annotation
= PsiTreeUtil
.getParentOfType(pair
.getParent(), PsiAnnotation
.class);
123 assert annotation
!= null;
125 final String fqn
= annotation
.getQualifiedName();
126 if (fqn
== null) return null;
128 final PsiClass psiClass
= JavaPsiFacade
.getInstance(element
.getProject()).findClass(fqn
, element
.getResolveScope());
129 if (psiClass
!= null && psiClass
.isAnnotationType()) {
130 final String name
= pair
.getName();
131 final PsiMethod
[] methods
= psiClass
.findMethodsByName(name
!= null ? name
: "value", false);
132 return methods
.length
> 0 ? methods
[0] : null;
138 * Utility method to obtain annotations of a specific type from the supplied PsiModifierListOwner.
139 * For optimization reasons, this method only looks at elements of type java.lang.String.
141 * The parameter <code>allowIndirect</code> determines if the method should look for indirect annotations, i.e.
142 * annotations which have themselves been annotated by the supplied annotation name. Currently, this only allows
143 * one level of indirection and returns an array of [base-annotation, indirect annotation]
145 * The <code>annotationName</code> parameter is a pair of the target annotation class' fully qualified name as a
146 * String and as a Set. This is done for performance reasons because the Set is required by the
147 * {@link com.intellij.codeInsight.AnnotationUtil} utility class and allows to avoid unecessary object constructions.
150 public static PsiAnnotation
[] getAnnotationFrom(PsiModifierListOwner owner
,
151 Pair
<String
, ?
extends Set
<String
>> annotationName
,
152 boolean allowIndirect
,
153 boolean inHierarchy
) {
154 if (!PsiUtilEx
.isLanguageAnnotationTarget(owner
)) return PsiAnnotation
.EMPTY_ARRAY
;
156 final PsiAnnotation directAnnotation
= inHierarchy?
157 AnnotationUtil
.findAnnotationInHierarchy(owner
, annotationName
.second
) :
158 AnnotationUtil
.findAnnotation(owner
, annotationName
.second
);
159 if (directAnnotation
!= null) {
160 return new PsiAnnotation
[]{directAnnotation
};
163 final PsiAnnotation
[] annotations
= getAnnotations(owner
, inHierarchy
);
164 for (PsiAnnotation annotation
: annotations
) {
165 PsiJavaCodeReferenceElement nameReference
= annotation
.getNameReferenceElement();
166 if (nameReference
== null) continue;
167 PsiElement resolved
= nameReference
.resolve();
168 if (resolved
instanceof PsiClass
) {
169 final PsiAnnotation psiAnnotation
= AnnotationUtil
.findAnnotationInHierarchy((PsiModifierListOwner
)resolved
, annotationName
.second
);
170 if (psiAnnotation
!= null) {
171 return new PsiAnnotation
[]{psiAnnotation
, annotation
};
176 return PsiAnnotation
.EMPTY_ARRAY
;
179 public static PsiAnnotation
[] getAnnotationFrom(@NotNull PsiModifierListOwner owner
,
180 @NotNull Pair
<String
, ?
extends Set
<String
>> annotationName
,
181 boolean allowIndirect
) {
182 return getAnnotationFrom(owner
, annotationName
, allowIndirect
, true);
186 * Calculates the value of the annotation's attribute referenced by the <code>attr</code> parameter by trying to
187 * find the attribute in the supplied list of annotations and calculating the constant value for the first attribute
191 public static String
calcAnnotationValue(PsiAnnotation
[] annotation
, @NonNls String attr
) {
192 for (PsiAnnotation psiAnnotation
: annotation
) {
193 final String value
= calcAnnotationValue(psiAnnotation
, attr
);
194 if (value
!= null) return value
;
200 public static String
calcAnnotationValue(@NotNull PsiAnnotation annotation
, @NonNls String attr
) {
201 PsiElement value
= annotation
.findAttributeValue(attr
);
202 if (value
instanceof PsiExpression
) {
203 Object o
= CONSTANT_EVALUATION_HELPER
.computeConstantExpression((PsiExpression
)value
);
204 if (o
instanceof String
) {
212 * Returns all annotations for <code>listOwner</code>, possibly walking up the method hierarchy.
214 * @see com.intellij.codeInsight.AnnotationUtil#isAnnotated(com.intellij.psi.PsiModifierListOwner, java.lang.String, boolean)
216 private static PsiAnnotation
[] getAnnotations(@NotNull PsiModifierListOwner listOwner
, boolean inHierarchy
) {
217 final PsiModifierList modifierList
= listOwner
.getModifierList();
218 if (modifierList
== null) {
219 return PsiAnnotation
.EMPTY_ARRAY
;
222 final Set
<PsiAnnotation
> all
= new HashSet
<PsiAnnotation
>() {
223 public boolean add(PsiAnnotation o
) {
224 // don't overwrite "higher level" annotations
225 return !contains(o
) && super.add(o
);
228 if (listOwner
instanceof PsiMethod
) {
229 all
.addAll(Arrays
.asList(modifierList
.getAnnotations()));
230 SuperMethodsSearch
.search((PsiMethod
)listOwner
, null, true, true).forEach(new Processor
<MethodSignatureBackedByPsiMethod
>() {
231 public boolean process(final MethodSignatureBackedByPsiMethod superMethod
) {
232 all
.addAll(Arrays
.asList(superMethod
.getMethod().getModifierList().getAnnotations()));
236 return all
.toArray(new PsiAnnotation
[all
.size()]);
238 if (listOwner
instanceof PsiParameter
&& ((PsiParameter
)listOwner
).getDeclarationScope() instanceof PsiMethod
) {
239 PsiParameter parameter
= (PsiParameter
)listOwner
;
240 PsiMethod method
= (PsiMethod
)parameter
.getDeclarationScope();
241 final int parameterIndex
= method
.getParameterList().getParameterIndex(parameter
);
242 all
.addAll(Arrays
.asList(modifierList
.getAnnotations()));
243 SuperMethodsSearch
.search(method
, null, true, true).forEach(new Processor
<MethodSignatureBackedByPsiMethod
>() {
244 public boolean process(final MethodSignatureBackedByPsiMethod superMethod
) {
245 PsiParameter superParameter
= superMethod
.getMethod().getParameterList().getParameters()[parameterIndex
];
246 PsiModifierList modifierList
= superParameter
.getModifierList();
247 if (modifierList
!= null) {
248 all
.addAll(Arrays
.asList(modifierList
.getAnnotations()));
253 return all
.toArray(new PsiAnnotation
[all
.size()]);
256 return modifierList
.getAnnotations();