From d5fda774348734bdbc4bf6fd6902c9ca5e1b38bf Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 25 Nov 2009 16:11:52 +0300 Subject: [PATCH] problem 14824 --- .../intelliLang/util/AnnotationUtilEx.java | 529 +++++++++--------- 1 file changed, 265 insertions(+), 264 deletions(-) diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/util/AnnotationUtilEx.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/util/AnnotationUtilEx.java index 8547922d2174..a1e46807fd75 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/util/AnnotationUtilEx.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/util/AnnotationUtilEx.java @@ -1,264 +1,265 @@ -/* - * Copyright 2006 Sascha Weinreuter - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.intellij.plugins.intelliLang.util; - -import com.intellij.codeInsight.AnnotationUtil; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.*; -import com.intellij.psi.impl.PsiConstantEvaluationHelperImpl; -import com.intellij.psi.search.searches.SuperMethodsSearch; -import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.Processor; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -/** - * Contains some extended utility functions for dealing with annotations. - */ -public class AnnotationUtilEx { - private static final PsiConstantEvaluationHelperImpl CONSTANT_EVALUATION_HELPER = new PsiConstantEvaluationHelperImpl(); - - private AnnotationUtilEx() { - } - - /** - * @see AnnotationUtilEx#getAnnotatedElementFor(com.intellij.psi.PsiElement, LookupType) - */ - public enum LookupType { - PREFER_CONTEXT, PREFER_DECLARATION, CONTEXT_ONLY, DECLRARATION_ONLY - } - - /** - * Determines the PsiModifierListOwner for the passed element depending of the specified LookupType. The LookupType - * decides whether to prefer the element a reference expressions resolves to, or the element that is implied by the - * usage context ("expected type"). - */ - @Nullable - public static PsiModifierListOwner getAnnotatedElementFor(@Nullable PsiElement element, LookupType type) { - while (element != null) { - if (type == LookupType.PREFER_DECLARATION || type == LookupType.DECLRARATION_ONLY) { - if (element instanceof PsiReferenceExpression) { - final PsiElement e = ((PsiReferenceExpression)element).resolve(); - if (e instanceof PsiModifierListOwner) { - return (PsiModifierListOwner)e; - } - if (type == LookupType.DECLRARATION_ONLY) { - return null; - } - } - } - element = ContextComputationProcessor.getTopLevelInjectionTarget(element); - final PsiElement parent = element.getParent(); - - if (element instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)element).getOperationSign().getTokenType() == JavaTokenType.PLUSEQ) { - element = ((PsiAssignmentExpression)element).getLExpression(); - continue; - } - else if (parent instanceof PsiAssignmentExpression) { - final PsiAssignmentExpression p = (PsiAssignmentExpression)parent; - if (p.getRExpression() == element) { - element = p.getLExpression(); - continue; - } - } - else if (parent instanceof PsiReturnStatement) { - final PsiMethod m = PsiTreeUtil.getParentOfType(parent, PsiMethod.class); - if (m != null) { - return m; - } - } - else if (parent instanceof PsiModifierListOwner) { - return (PsiModifierListOwner)parent; - } - else if (parent instanceof PsiArrayInitializerMemberValue) { - final PsiArrayInitializerMemberValue value = (PsiArrayInitializerMemberValue)parent; - final PsiElement pair = value.getParent(); - if (pair instanceof PsiNameValuePair) { - return getAnnotationMethod((PsiNameValuePair)pair, element); - } - } - else if (parent instanceof PsiNameValuePair) { - return getAnnotationMethod((PsiNameValuePair)parent, element); - } - else { - return PsiUtilEx.getParameterForArgument(element); - } - - // If no annotation has been found through the usage context, check if the element - // (i.e. the element the reference refers to) is annotated itself - if (type != LookupType.DECLRARATION_ONLY) { - if (element instanceof PsiReferenceExpression) { - final PsiElement e = ((PsiReferenceExpression)element).resolve(); - if (e instanceof PsiModifierListOwner) { - return (PsiModifierListOwner)e; - } - } - } - return null; - } - return null; - } - - @Nullable - private static PsiModifierListOwner getAnnotationMethod(PsiNameValuePair pair, PsiElement element) { - final PsiAnnotation annotation = PsiTreeUtil.getParentOfType(pair.getParent(), PsiAnnotation.class); - assert annotation != null; - - final String fqn = annotation.getQualifiedName(); - if (fqn == null) return null; - - final PsiClass psiClass = JavaPsiFacade.getInstance(element.getProject()).findClass(fqn, element.getResolveScope()); - if (psiClass != null && psiClass.isAnnotationType()) { - final String name = pair.getName(); - final PsiMethod[] methods = psiClass.findMethodsByName(name != null ? name : "value", false); - return methods.length > 0 ? methods[0] : null; - } - return null; - } - - /** - * Utility method to obtain annotations of a specific type from the supplied PsiModifierListOwner. - * For optimization reasons, this method only looks at elements of type java.lang.String. - *

- * The parameter allowIndirect determines if the method should look for indirect annotations, i.e. - * annotations which have themselves been annotated by the supplied annotation name. Currently, this only allows - * one level of indirection and returns an array of [base-annotation, indirect annotation] - *

- * The annotationName parameter is a pair of the target annotation class' fully qualified name as a - * String and as a Set. This is done for performance reasons because the Set is required by the - * {@link com.intellij.codeInsight.AnnotationUtil} utility class and allows to avoid unecessary object constructions. - */ - @NotNull - public static PsiAnnotation[] getAnnotationFrom(PsiModifierListOwner owner, - Pair> annotationName, - boolean allowIndirect, - boolean inHierarchy) { - if (!PsiUtilEx.isLanguageAnnotationTarget(owner)) return PsiAnnotation.EMPTY_ARRAY; - - final PsiAnnotation directAnnotation = inHierarchy? - AnnotationUtil.findAnnotationInHierarchy(owner, annotationName.second) : - AnnotationUtil.findAnnotation(owner, annotationName.second); - if (directAnnotation != null) { - return new PsiAnnotation[]{directAnnotation}; - } - if (allowIndirect) { - final PsiAnnotation[] annotations = getAnnotations(owner, inHierarchy); - for (PsiAnnotation annotation : annotations) { - PsiJavaCodeReferenceElement nameReference = annotation.getNameReferenceElement(); - if (nameReference == null) continue; - PsiElement resolved = nameReference.resolve(); - if (resolved instanceof PsiClass) { - final PsiAnnotation psiAnnotation = AnnotationUtil.findAnnotationInHierarchy((PsiModifierListOwner)resolved, annotationName.second); - if (psiAnnotation != null) { - return new PsiAnnotation[]{psiAnnotation, annotation}; - } - } - } - } - return PsiAnnotation.EMPTY_ARRAY; - } - - public static PsiAnnotation[] getAnnotationFrom(@NotNull PsiModifierListOwner owner, - @NotNull Pair> annotationName, - boolean allowIndirect) { - return getAnnotationFrom(owner, annotationName, allowIndirect, true); - } - - /** - * Calculates the value of the annotation's attribute referenced by the attr parameter by trying to - * find the attribute in the supplied list of annotations and calculating the constant value for the first attribute - * it finds. - */ - @Nullable - public static String calcAnnotationValue(PsiAnnotation[] annotation, @NonNls String attr) { - for (PsiAnnotation psiAnnotation : annotation) { - final String value = calcAnnotationValue(psiAnnotation, attr); - if (value != null) return value; - } - return null; - } - - @Nullable - public static String calcAnnotationValue(@NotNull PsiAnnotation annotation, @NonNls String attr) { - PsiElement value = annotation.findAttributeValue(attr); - if (value instanceof PsiExpression) { - Object o = CONSTANT_EVALUATION_HELPER.computeConstantExpression(value); - if (o instanceof String) { - return (String)o; - } - } - return null; - } - - /** - * Returns all annotations for listOwner, possibly walking up the method hierarchy. - * - * @see com.intellij.codeInsight.AnnotationUtil#isAnnotated(com.intellij.psi.PsiModifierListOwner, java.lang.String, boolean) - */ - private static PsiAnnotation[] getAnnotations(@NotNull PsiModifierListOwner listOwner, boolean inHierarchy) { - final PsiModifierList modifierList = listOwner.getModifierList(); - if (modifierList == null) { - return PsiAnnotation.EMPTY_ARRAY; - } - if (!inHierarchy) { - return modifierList.getAnnotations(); - } - final Set all = new HashSet() { - public boolean add(PsiAnnotation o) { - // don't overwrite "higher level" annotations - return !contains(o) && super.add(o); - } - }; - if (listOwner instanceof PsiMethod) { - all.addAll(Arrays.asList(modifierList.getAnnotations())); - SuperMethodsSearch.search((PsiMethod)listOwner, null, true, true).forEach(new Processor() { - public boolean process(final MethodSignatureBackedByPsiMethod superMethod) { - all.addAll(Arrays.asList(superMethod.getMethod().getModifierList().getAnnotations())); - return true; - } - }); - return all.toArray(new PsiAnnotation[all.size()]); - } - if (listOwner instanceof PsiParameter) { - PsiParameter parameter = (PsiParameter)listOwner; - PsiElement declarationScope = parameter.getDeclarationScope(); - if (declarationScope instanceof PsiMethod && parameter.getParent() == ((PsiMethod)declarationScope).getParameterList()) { - PsiMethod method = (PsiMethod)declarationScope; - final int parameterIndex = method.getParameterList().getParameterIndex(parameter); - all.addAll(Arrays.asList(modifierList.getAnnotations())); - SuperMethodsSearch.search(method, null, true, true).forEach(new Processor() { - public boolean process(final MethodSignatureBackedByPsiMethod superMethod) { - PsiParameter superParameter = superMethod.getMethod().getParameterList().getParameters()[parameterIndex]; - PsiModifierList modifierList = superParameter.getModifierList(); - if (modifierList != null) { - all.addAll(Arrays.asList(modifierList.getAnnotations())); - } - return true; - } - }); - return all.toArray(new PsiAnnotation[all.size()]); - } - } - return modifierList.getAnnotations(); - } - -} +/* + * Copyright 2006 Sascha Weinreuter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.intellij.plugins.intelliLang.util; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import com.intellij.psi.impl.PsiConstantEvaluationHelperImpl; +import com.intellij.psi.search.searches.SuperMethodsSearch; +import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.Processor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Contains some extended utility functions for dealing with annotations. + */ +public class AnnotationUtilEx { + private static final PsiConstantEvaluationHelperImpl CONSTANT_EVALUATION_HELPER = new PsiConstantEvaluationHelperImpl(); + + private AnnotationUtilEx() { + } + + /** + * @see AnnotationUtilEx#getAnnotatedElementFor(com.intellij.psi.PsiElement, LookupType) + */ + public enum LookupType { + PREFER_CONTEXT, PREFER_DECLARATION, CONTEXT_ONLY, DECLRARATION_ONLY + } + + /** + * Determines the PsiModifierListOwner for the passed element depending of the specified LookupType. The LookupType + * decides whether to prefer the element a reference expressions resolves to, or the element that is implied by the + * usage context ("expected type"). + */ + @Nullable + public static PsiModifierListOwner getAnnotatedElementFor(@Nullable PsiElement element, LookupType type) { + while (element != null) { + if (type == LookupType.PREFER_DECLARATION || type == LookupType.DECLRARATION_ONLY) { + if (element instanceof PsiReferenceExpression) { + final PsiElement e = ((PsiReferenceExpression)element).resolve(); + if (e instanceof PsiModifierListOwner) { + return (PsiModifierListOwner)e; + } + if (type == LookupType.DECLRARATION_ONLY) { + return null; + } + } + } + element = ContextComputationProcessor.getTopLevelInjectionTarget(element); + final PsiElement parent = element.getParent(); + + if (element instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)element).getOperationSign().getTokenType() == JavaTokenType.PLUSEQ) { + element = ((PsiAssignmentExpression)element).getLExpression(); + continue; + } + else if (parent instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression p = (PsiAssignmentExpression)parent; + if (p.getRExpression() == element) { + element = p.getLExpression(); + continue; + } + } + else if (parent instanceof PsiReturnStatement) { + final PsiMethod m = PsiTreeUtil.getParentOfType(parent, PsiMethod.class); + if (m != null) { + return m; + } + } + else if (parent instanceof PsiModifierListOwner) { + return (PsiModifierListOwner)parent; + } + else if (parent instanceof PsiArrayInitializerMemberValue) { + final PsiArrayInitializerMemberValue value = (PsiArrayInitializerMemberValue)parent; + final PsiElement pair = value.getParent(); + if (pair instanceof PsiNameValuePair) { + return getAnnotationMethod((PsiNameValuePair)pair, element); + } + } + else if (parent instanceof PsiNameValuePair) { + return getAnnotationMethod((PsiNameValuePair)parent, element); + } + else { + return PsiUtilEx.getParameterForArgument(element); + } + + // If no annotation has been found through the usage context, check if the element + // (i.e. the element the reference refers to) is annotated itself + if (type != LookupType.DECLRARATION_ONLY) { + if (element instanceof PsiReferenceExpression) { + final PsiElement e = ((PsiReferenceExpression)element).resolve(); + if (e instanceof PsiModifierListOwner) { + return (PsiModifierListOwner)e; + } + } + } + return null; + } + return null; + } + + @Nullable + private static PsiModifierListOwner getAnnotationMethod(PsiNameValuePair pair, PsiElement element) { + final PsiAnnotation annotation = PsiTreeUtil.getParentOfType(pair.getParent(), PsiAnnotation.class); + assert annotation != null; + + final String fqn = annotation.getQualifiedName(); + if (fqn == null) return null; + + final PsiClass psiClass = JavaPsiFacade.getInstance(element.getProject()).findClass(fqn, element.getResolveScope()); + if (psiClass != null && psiClass.isAnnotationType()) { + final String name = pair.getName(); + final PsiMethod[] methods = psiClass.findMethodsByName(name != null ? name : "value", false); + return methods.length > 0 ? methods[0] : null; + } + return null; + } + + /** + * Utility method to obtain annotations of a specific type from the supplied PsiModifierListOwner. + * For optimization reasons, this method only looks at elements of type java.lang.String. + *

+ * The parameter allowIndirect determines if the method should look for indirect annotations, i.e. + * annotations which have themselves been annotated by the supplied annotation name. Currently, this only allows + * one level of indirection and returns an array of [base-annotation, indirect annotation] + *

+ * The annotationName parameter is a pair of the target annotation class' fully qualified name as a + * String and as a Set. This is done for performance reasons because the Set is required by the + * {@link com.intellij.codeInsight.AnnotationUtil} utility class and allows to avoid unecessary object constructions. + */ + @NotNull + public static PsiAnnotation[] getAnnotationFrom(PsiModifierListOwner owner, + Pair> annotationName, + boolean allowIndirect, + boolean inHierarchy) { + if (!PsiUtilEx.isLanguageAnnotationTarget(owner)) return PsiAnnotation.EMPTY_ARRAY; + + final PsiAnnotation directAnnotation = inHierarchy? + AnnotationUtil.findAnnotationInHierarchy(owner, annotationName.second) : + AnnotationUtil.findAnnotation(owner, annotationName.second); + if (directAnnotation != null) { + return new PsiAnnotation[]{directAnnotation}; + } + if (allowIndirect) { + final PsiAnnotation[] annotations = getAnnotations(owner, inHierarchy); + for (PsiAnnotation annotation : annotations) { + PsiJavaCodeReferenceElement nameReference = annotation.getNameReferenceElement(); + if (nameReference == null) continue; + PsiElement resolved = nameReference.resolve(); + if (resolved instanceof PsiClass) { + final PsiAnnotation psiAnnotation = AnnotationUtil.findAnnotationInHierarchy((PsiModifierListOwner)resolved, annotationName.second); + if (psiAnnotation != null) { + return new PsiAnnotation[]{psiAnnotation, annotation}; + } + } + } + } + return PsiAnnotation.EMPTY_ARRAY; + } + + public static PsiAnnotation[] getAnnotationFrom(@NotNull PsiModifierListOwner owner, + @NotNull Pair> annotationName, + boolean allowIndirect) { + return getAnnotationFrom(owner, annotationName, allowIndirect, true); + } + + /** + * Calculates the value of the annotation's attribute referenced by the attr parameter by trying to + * find the attribute in the supplied list of annotations and calculating the constant value for the first attribute + * it finds. + */ + @Nullable + public static String calcAnnotationValue(PsiAnnotation[] annotation, @NonNls String attr) { + for (PsiAnnotation psiAnnotation : annotation) { + final String value = calcAnnotationValue(psiAnnotation, attr); + if (value != null) return value; + } + return null; + } + + @Nullable + public static String calcAnnotationValue(@NotNull PsiAnnotation annotation, @NonNls String attr) { + PsiElement value = annotation.findAttributeValue(attr); + if (value instanceof PsiExpression) { + Object o = CONSTANT_EVALUATION_HELPER.computeConstantExpression(value); + if (o instanceof String) { + return (String)o; + } + } + return null; + } + + /** + * Returns all annotations for listOwner, possibly walking up the method hierarchy. + * + * @see com.intellij.codeInsight.AnnotationUtil#isAnnotated(com.intellij.psi.PsiModifierListOwner, java.lang.String, boolean) + */ + private static PsiAnnotation[] getAnnotations(@NotNull PsiModifierListOwner listOwner, boolean inHierarchy) { + final PsiModifierList modifierList = listOwner.getModifierList(); + if (modifierList == null) { + return PsiAnnotation.EMPTY_ARRAY; + } + if (!inHierarchy) { + return modifierList.getAnnotations(); + } + final Set all = new HashSet() { + public boolean add(PsiAnnotation o) { + // don't overwrite "higher level" annotations + return !contains(o) && super.add(o); + } + }; + if (listOwner instanceof PsiMethod) { + all.addAll(Arrays.asList(modifierList.getAnnotations())); + SuperMethodsSearch.search((PsiMethod)listOwner, null, true, true).forEach(new Processor() { + public boolean process(final MethodSignatureBackedByPsiMethod superMethod) { + all.addAll(Arrays.asList(superMethod.getMethod().getModifierList().getAnnotations())); + return true; + } + }); + return all.toArray(new PsiAnnotation[all.size()]); + } + if (listOwner instanceof PsiParameter) { + PsiParameter parameter = (PsiParameter)listOwner; + PsiElement declarationScope = parameter.getDeclarationScope(); + PsiParameterList parameterList; + if (declarationScope instanceof PsiMethod && parameter.getParent() == (parameterList = ((PsiMethod)declarationScope).getParameterList())) { + PsiMethod method = (PsiMethod)declarationScope; + final int parameterIndex = parameterList.getParameterIndex(parameter); + all.addAll(Arrays.asList(modifierList.getAnnotations())); + SuperMethodsSearch.search(method, null, true, true).forEach(new Processor() { + public boolean process(final MethodSignatureBackedByPsiMethod superMethod) { + PsiParameter superParameter = superMethod.getMethod().getParameterList().getParameters()[parameterIndex]; + PsiModifierList modifierList = superParameter.getModifierList(); + if (modifierList != null) { + all.addAll(Arrays.asList(modifierList.getAnnotations())); + } + return true; + } + }); + return all.toArray(new PsiAnnotation[all.size()]); + } + } + return modifierList.getAnnotations(); + } + +}