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 org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
022    import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
023    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
024    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptorImpl;
025    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
026    import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
027    import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
028    import org.jetbrains.jet.lang.resolve.java.structure.JavaAnnotation;
029    import org.jetbrains.jet.lang.resolve.java.structure.JavaAnnotationArgument;
030    import org.jetbrains.jet.lang.resolve.java.structure.JavaAnnotationOwner;
031    import org.jetbrains.jet.lang.resolve.name.FqName;
032    import org.jetbrains.jet.lang.resolve.name.Name;
033    
034    import javax.inject.Inject;
035    import java.util.ArrayList;
036    import java.util.Collection;
037    import java.util.List;
038    
039    import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.INCLUDE_KOTLIN_SOURCES;
040    import static org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils.fqNameByClass;
041    
042    public final class JavaAnnotationResolver {
043        public static final Name DEFAULT_ANNOTATION_MEMBER_NAME = Name.identifier("value");
044        public static final FqName JETBRAINS_NOT_NULL_ANNOTATION = fqNameByClass(NotNull.class);
045        public static final FqName JETBRAINS_NULLABLE_ANNOTATION = fqNameByClass(Nullable.class);
046        public static final FqName JETBRAINS_MUTABLE_ANNOTATION = new FqName("org.jetbrains.annotations.Mutable");
047        public static final FqName JETBRAINS_READONLY_ANNOTATION = new FqName("org.jetbrains.annotations.ReadOnly");
048    
049        private JavaClassResolver classResolver;
050        private JavaAnnotationArgumentResolver argumentResolver;
051        private ExternalAnnotationResolver externalAnnotationResolver;
052    
053        public JavaAnnotationResolver() {
054        }
055    
056        @Inject
057        public void setClassResolver(JavaClassResolver classResolver) {
058            this.classResolver = classResolver;
059        }
060    
061        @Inject
062        public void setArgumentResolver(JavaAnnotationArgumentResolver argumentResolver) {
063            this.argumentResolver = argumentResolver;
064        }
065    
066        @Inject
067        public void setExternalAnnotationResolver(ExternalAnnotationResolver externalAnnotationResolver) {
068            this.externalAnnotationResolver = externalAnnotationResolver;
069        }
070    
071        private void resolveAnnotations(
072                @NotNull Collection<JavaAnnotation> annotations,
073                @NotNull PostponedTasks tasks,
074                @NotNull List<AnnotationDescriptor> result
075        ) {
076            for (JavaAnnotation javaAnnotation : annotations) {
077                AnnotationDescriptor annotation = resolveAnnotation(javaAnnotation, tasks);
078                if (annotation != null) {
079                    result.add(annotation);
080                }
081            }
082        }
083    
084        @NotNull
085        public List<AnnotationDescriptor> resolveAnnotations(@NotNull JavaAnnotationOwner owner, @NotNull PostponedTasks tasks) {
086            List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>();
087            resolveAnnotations(owner.getAnnotations(), tasks, result);
088            resolveAnnotations(externalAnnotationResolver.findExternalAnnotations(owner), tasks, result);
089            return result;
090        }
091    
092        @NotNull
093        public List<AnnotationDescriptor> resolveAnnotations(@NotNull JavaAnnotationOwner owner) {
094            PostponedTasks postponedTasks = new PostponedTasks();
095            List<AnnotationDescriptor> annotations = resolveAnnotations(owner, postponedTasks);
096            postponedTasks.performTasks();
097            return annotations;
098        }
099    
100        @Nullable
101        public AnnotationDescriptor resolveAnnotation(@NotNull JavaAnnotation javaAnnotation, @NotNull PostponedTasks postponedTasks) {
102            final AnnotationDescriptorImpl annotation = new AnnotationDescriptorImpl();
103            FqName fqName = javaAnnotation.getFqName();
104            if (fqName == null) {
105                return null;
106            }
107    
108            // Don't process internal jet annotations and jetbrains NotNull annotations
109            if (isSpecialAnnotation(fqName)) {
110                return null;
111            }
112    
113            AnnotationDescriptor mappedClassDescriptor = JavaToKotlinClassMap.getInstance().mapToAnnotationClass(fqName);
114            if (mappedClassDescriptor != null) {
115                return mappedClassDescriptor;
116            }
117    
118            final ClassDescriptor annotationClass = classResolver.resolveClass(fqName, INCLUDE_KOTLIN_SOURCES, postponedTasks);
119            if (annotationClass == null) {
120                return null;
121            }
122    
123            postponedTasks.addTask(new Runnable() {
124                @Override
125                public void run() {
126                    annotation.setAnnotationType(annotationClass.getDefaultType());
127                }
128            });
129    
130    
131            for (JavaAnnotationArgument argument : javaAnnotation.getArguments()) {
132                CompileTimeConstant value = argumentResolver.resolveAnnotationArgument(fqName, argument, postponedTasks);
133                if (value == null) continue;
134    
135                Name name = argument.getName();
136                ValueParameterDescriptor descriptor = DescriptorResolverUtils.getAnnotationParameterByName(
137                        name == null ? DEFAULT_ANNOTATION_MEMBER_NAME : name, annotationClass);
138                if (descriptor != null) {
139                    annotation.setValueArgument(descriptor, value);
140                }
141            }
142    
143            return annotation;
144        }
145    
146        public static boolean isSpecialAnnotation(@NotNull FqName fqName) {
147            return fqName.asString().startsWith("jet.runtime.typeinfo.")
148                || fqName.equals(JETBRAINS_NOT_NULL_ANNOTATION)
149                || fqName.equals(JvmAnnotationNames.KOTLIN_CLASS)
150                || fqName.equals(JvmAnnotationNames.KOTLIN_PACKAGE);
151        }
152    
153        @Nullable
154        public JavaAnnotation findAnnotationWithExternal(@NotNull JavaAnnotationOwner owner, @NotNull FqName name) {
155            JavaAnnotation annotation = owner.findAnnotation(name);
156            if (annotation != null) {
157                return annotation;
158            }
159    
160            return externalAnnotationResolver.findExternalAnnotation(owner, name);
161        }
162    
163        public boolean hasNotNullAnnotation(@NotNull JavaAnnotationOwner owner) {
164            return findAnnotationWithExternal(owner, JETBRAINS_NOT_NULL_ANNOTATION) != null;
165        }
166    
167        public boolean hasMutableAnnotation(@NotNull JavaAnnotationOwner owner) {
168            return findAnnotationWithExternal(owner, JETBRAINS_MUTABLE_ANNOTATION) != null;
169        }
170    
171        public boolean hasReadonlyAnnotation(@NotNull JavaAnnotationOwner owner) {
172            return findAnnotationWithExternal(owner, JETBRAINS_READONLY_ANNOTATION) != null;
173        }
174    }