IDEADEV-35332
[fedora-idea.git] / plugins / IntelliLang / src / org / intellij / plugins / intelliLang / util / AnnotationUtilEx.java
blobcde648c449db5c7d9ad833a5127a6a595b390305
1 /*
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;
32 import java.util.Set;
34 /**
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() {
43 /**
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
51 /**
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").
56 @Nullable
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) {
67 return null;
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);
85 if (m != null) {
86 return m;
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);
102 else {
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;
117 return null;
120 @Nullable
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;
134 return 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.
140 * <p/>
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]
144 * <p/>
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.
149 @NotNull
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};
162 if (allowIndirect) {
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
188 * it finds.
190 @Nullable
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;
196 return null;
199 @Nullable
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) {
205 return (String)o;
208 return null;
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;
221 if (inHierarchy) {
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()));
233 return true;
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()));
250 return true;
253 return all.toArray(new PsiAnnotation[all.size()]);
256 return modifierList.getAnnotations();