ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / plugins / IntelliLang / src / org / intellij / plugins / intelliLang / util / AnnotationUtilEx.java
blobcbb98ac79f8622a3b3419c87ffcbf15e31aa5db8
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.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;
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.PsiElement, LookupType)
46 public enum LookupType {
47 PREFER_CONTEXT, PREFER_DECLARATION, CONTEXT_ONLY, DECLRARATION_ONLY
50 /**
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").
55 @Nullable
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) {
65 return null;
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();
74 continue;
76 else if (parent instanceof PsiAssignmentExpression) {
77 final PsiAssignmentExpression p = (PsiAssignmentExpression)parent;
78 if (p.getRExpression() == element) {
79 element = p.getLExpression();
80 continue;
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;
116 return null;
118 return null;
121 @Nullable
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;
135 return 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.
141 * <p/>
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]
145 * <p/>
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.
150 @NotNull
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};
163 if (allowIndirect) {
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
189 * it finds.
191 @Nullable
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;
197 return null;
200 @Nullable
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) {
206 return (String)o;
209 return null;
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;
222 if (!inHierarchy) {
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()));
236 return true;
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()));
256 return true;
259 return all.toArray(new PsiAnnotation[all.size()]);
262 return modifierList.getAnnotations();