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