001/*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.jetbrains.jet.lang.resolve.java.resolver;
018
019import com.google.common.collect.Lists;
020import com.intellij.codeInsight.ExternalAnnotationsManager;
021import com.intellij.psi.*;
022import org.jetbrains.annotations.NotNull;
023import org.jetbrains.annotations.Nullable;
024import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
025import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
026import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
028import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils;
029import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule;
030import org.jetbrains.jet.lang.resolve.java.JavaToKotlinClassMap;
031import org.jetbrains.jet.lang.resolve.java.JvmAbi;
032import org.jetbrains.jet.lang.resolve.name.FqName;
033import org.jetbrains.jet.lang.resolve.name.Name;
034
035import javax.inject.Inject;
036import java.util.ArrayList;
037import java.util.Arrays;
038import java.util.List;
039
040public final class JavaAnnotationResolver {
041    private JavaClassResolver classResolver;
042    private JavaCompileTimeConstResolver compileTimeConstResolver;
043
044    public JavaAnnotationResolver() {
045    }
046
047    @Inject
048    public void setClassResolver(JavaClassResolver classResolver) {
049        this.classResolver = classResolver;
050    }
051
052    @Inject
053    public void setCompileTimeConstResolver(JavaCompileTimeConstResolver compileTimeConstResolver) {
054        this.compileTimeConstResolver = compileTimeConstResolver;
055    }
056
057    @NotNull
058    public List<AnnotationDescriptor> resolveAnnotations(@NotNull PsiModifierListOwner owner, @NotNull PostponedTasks tasks) {
059        PsiAnnotation[] psiAnnotations = getAllAnnotations(owner);
060        List<AnnotationDescriptor> r = Lists.newArrayListWithCapacity(psiAnnotations.length);
061        for (PsiAnnotation psiAnnotation : psiAnnotations) {
062            AnnotationDescriptor annotation = resolveAnnotation(psiAnnotation, tasks);
063            if (annotation != null) {
064                r.add(annotation);
065            }
066        }
067        return r;
068    }
069
070    @NotNull
071    public List<AnnotationDescriptor> resolveAnnotations(@NotNull PsiModifierListOwner owner) {
072        PostponedTasks postponedTasks = new PostponedTasks();
073        List<AnnotationDescriptor> annotations = resolveAnnotations(owner, postponedTasks);
074        postponedTasks.performTasks();
075        return annotations;
076    }
077
078    @Nullable
079    public AnnotationDescriptor resolveAnnotation(PsiAnnotation psiAnnotation, @NotNull PostponedTasks postponedTasks) {
080        final AnnotationDescriptor annotation = new AnnotationDescriptor();
081        String qname = psiAnnotation.getQualifiedName();
082        if (qname == null) {
083            return null;
084        }
085
086        // Don't process internal jet annotations and jetbrains NotNull annotations
087        if (qname.startsWith("jet.runtime.typeinfo.") || qname.equals(JvmAbi.JETBRAINS_NOT_NULL_ANNOTATION.getFqName().asString())) {
088            return null;
089        }
090
091        FqName annotationFqName = new FqName(qname);
092
093        AnnotationDescriptor mappedClassDescriptor = JavaToKotlinClassMap.getInstance().mapToAnnotationClass(annotationFqName);
094        if (mappedClassDescriptor != null) {
095            return mappedClassDescriptor;
096        }
097
098        final ClassDescriptor annotationClass =
099                classResolver.resolveClass(annotationFqName, DescriptorSearchRule.INCLUDE_KOTLIN, postponedTasks);
100        if (annotationClass == null) {
101            return null;
102        }
103
104        postponedTasks.addTask(new Runnable() {
105            @Override
106            public void run() {
107                annotation.setAnnotationType(annotationClass.getDefaultType());
108            }
109        });
110
111
112        PsiAnnotationParameterList parameterList = psiAnnotation.getParameterList();
113        for (PsiNameValuePair psiNameValuePair : parameterList.getAttributes()) {
114            PsiAnnotationMemberValue value = psiNameValuePair.getValue();
115            String name = psiNameValuePair.getName();
116            if (name == null) name = "value";
117            Name identifier = Name.identifier(name);
118
119            if (value == null) return null;
120            CompileTimeConstant compileTimeConst =
121                    compileTimeConstResolver.getCompileTimeConstFromExpression(annotationFqName, identifier, value, postponedTasks);
122            if (compileTimeConst != null) {
123                ValueParameterDescriptor valueParameterDescriptor =
124                        DescriptorResolverUtils.getValueParameterDescriptorForAnnotationParameter(identifier, annotationClass);
125                if (valueParameterDescriptor != null) {
126                    annotation.setValueArgument(valueParameterDescriptor, compileTimeConst);
127                }
128            }
129        }
130
131        return annotation;
132    }
133
134    @NotNull
135    private static PsiAnnotation[] getAllAnnotations(@NotNull PsiModifierListOwner owner) {
136        List<PsiAnnotation> result = new ArrayList<PsiAnnotation>();
137
138        PsiModifierList list = owner.getModifierList();
139        if (list != null) {
140            result.addAll(Arrays.asList(list.getAnnotations()));
141        }
142
143        PsiAnnotation[] externalAnnotations = ExternalAnnotationsManager.getInstance(owner.getProject()).findExternalAnnotations(owner);
144        if (externalAnnotations != null) {
145            result.addAll(Arrays.asList(externalAnnotations));
146        }
147
148        return result.toArray(new PsiAnnotation[result.size()]);
149    }
150
151    @Nullable
152    public static PsiAnnotation findOwnAnnotation(@NotNull PsiModifierListOwner owner, @NotNull String fqName) {
153        PsiModifierList list = owner.getModifierList();
154        if (list != null) {
155            PsiAnnotation found = list.findAnnotation(fqName);
156            if (found != null) {
157                return found;
158            }
159        }
160
161        return null;
162    }
163
164    @Nullable
165    public static PsiAnnotation findAnnotationWithExternal(@NotNull PsiModifierListOwner owner, @NotNull String fqName) {
166        PsiAnnotation annotation = findOwnAnnotation(owner, fqName);
167        if (annotation != null) {
168            return annotation;
169        }
170
171        return ExternalAnnotationsManager.getInstance(owner.getProject()).findExternalAnnotation(owner, fqName);
172    }
173}