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    
017    package org.jetbrains.jet.lang.resolve.java.resolver;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.codeInsight.ExternalAnnotationsManager;
021    import com.intellij.psi.*;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
025    import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
026    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
028    import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils;
029    import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule;
030    import org.jetbrains.jet.lang.resolve.java.JavaToKotlinClassMap;
031    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
032    import org.jetbrains.jet.lang.resolve.name.FqName;
033    import org.jetbrains.jet.lang.resolve.name.Name;
034    
035    import javax.inject.Inject;
036    import java.util.ArrayList;
037    import java.util.Arrays;
038    import java.util.List;
039    
040    public 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    }