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.ClassifierDescriptor;
023    import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
024    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
025    import org.jetbrains.jet.lang.resolve.constants.*;
026    import org.jetbrains.jet.lang.resolve.constants.StringValue;
027    import org.jetbrains.jet.lang.resolve.java.structure.*;
028    import org.jetbrains.jet.lang.resolve.name.FqName;
029    import org.jetbrains.jet.lang.resolve.name.Name;
030    import org.jetbrains.jet.lang.types.JetType;
031    import org.jetbrains.jet.lang.types.JetTypeImpl;
032    import org.jetbrains.jet.lang.types.TypeProjectionImpl;
033    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
034    
035    import javax.inject.Inject;
036    import java.util.ArrayList;
037    import java.util.Collections;
038    import java.util.List;
039    
040    import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.IGNORE_KOTLIN_SOURCES;
041    import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.INCLUDE_KOTLIN_SOURCES;
042    
043    public final class JavaAnnotationArgumentResolver {
044        public static final FqName JL_CLASS_FQ_NAME = new FqName("java.lang.Class");
045    
046        private JavaAnnotationResolver annotationResolver;
047        private JavaClassResolver classResolver;
048        private JavaTypeTransformer typeTransformer;
049    
050        @Inject
051        public void setAnnotationResolver(JavaAnnotationResolver annotationResolver) {
052            this.annotationResolver = annotationResolver;
053        }
054    
055        @Inject
056        public void setClassResolver(JavaClassResolver classResolver) {
057            this.classResolver = classResolver;
058        }
059    
060        @Inject
061        public void setTypeTransformer(JavaTypeTransformer typeTransformer) {
062            this.typeTransformer = typeTransformer;
063        }
064    
065        @Nullable
066        public CompileTimeConstant<?> resolveAnnotationArgument(
067                @NotNull FqName annotationFqName,
068                @NotNull JavaAnnotationArgument argument,
069                @NotNull PostponedTasks postponedTasks
070        ) {
071            if (argument instanceof JavaLiteralAnnotationArgument) {
072                return resolveCompileTimeConstantValue(((JavaLiteralAnnotationArgument) argument).getValue(), null);
073            }
074            // Enum
075            else if (argument instanceof JavaReferenceAnnotationArgument) {
076                return resolveFromReference(((JavaReferenceAnnotationArgument) argument).resolve(), postponedTasks);
077            }
078            // Array
079            else if (argument instanceof JavaArrayAnnotationArgument) {
080                Name argumentName = argument.getName();
081                return resolveFromArray(
082                        annotationFqName,
083                        argumentName == null ? JavaAnnotationResolver.DEFAULT_ANNOTATION_MEMBER_NAME : argumentName,
084                        ((JavaArrayAnnotationArgument) argument).getElements(),
085                        postponedTasks
086                );
087            }
088            // Annotation
089            else if (argument instanceof JavaAnnotationAsAnnotationArgument) {
090                return resolveFromAnnotation(((JavaAnnotationAsAnnotationArgument) argument).getAnnotation(), postponedTasks);
091            }
092            // Class<?>
093            else if (argument instanceof JavaClassObjectAnnotationArgument) {
094                return resolveFromJavaClassObjectType(((JavaClassObjectAnnotationArgument) argument).getReferencedType());
095            }
096    
097            return null;
098        }
099    
100        @Nullable
101        private CompileTimeConstant<?> resolveFromAnnotation(@NotNull JavaAnnotation value, @NotNull PostponedTasks taskList) {
102            AnnotationDescriptor descriptor = annotationResolver.resolveAnnotation(value, taskList);
103            return descriptor == null ? null : new AnnotationValue(descriptor);
104        }
105    
106        @Nullable
107        private CompileTimeConstant<?> resolveFromArray(
108                @NotNull FqName annotationFqName,
109                @NotNull Name argumentName,
110                @NotNull List<JavaAnnotationArgument> elements,
111                @NotNull PostponedTasks taskList
112        ) {
113            ClassDescriptor annotationClass = classResolver.resolveClass(annotationFqName, INCLUDE_KOTLIN_SOURCES, taskList);
114            if (annotationClass == null) return null;
115    
116            //TODO: nullability issues
117            ValueParameterDescriptor valueParameter = DescriptorResolverUtils.getAnnotationParameterByName(argumentName, annotationClass);
118            if (valueParameter == null) return null;
119    
120            List<CompileTimeConstant<?>> values = new ArrayList<CompileTimeConstant<?>>(elements.size());
121            for (JavaAnnotationArgument argument : elements) {
122                CompileTimeConstant<?> value = resolveAnnotationArgument(annotationFqName, argument, taskList);
123                values.add(value == null ? NullValue.NULL : value);
124            }
125    
126            return new ArrayValue(values, valueParameter.getType());
127        }
128    
129        @Nullable
130        private CompileTimeConstant<?> resolveFromReference(@Nullable JavaElement element, @NotNull PostponedTasks taskList) {
131            if (!(element instanceof JavaField)) return null;
132    
133            JavaField field = (JavaField) element;
134            if (!field.isEnumEntry()) return null;
135    
136            FqName fqName = field.getContainingClass().getFqName();
137            if (fqName == null) return null;
138    
139            ClassDescriptor enumClass = classResolver.resolveClass(fqName, INCLUDE_KOTLIN_SOURCES, taskList);
140            if (enumClass == null) return null;
141    
142            ClassifierDescriptor classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(field.getName());
143            if (!(classifier instanceof ClassDescriptor)) return null;
144    
145            return new EnumValue((ClassDescriptor) classifier);
146        }
147    
148        @Nullable
149        private CompileTimeConstant<?> resolveFromJavaClassObjectType(@NotNull JavaType javaType) {
150            JetType type = typeTransformer.transformToType(javaType, TypeVariableResolver.EMPTY);
151    
152            ClassDescriptor jlClass = classResolver.resolveClass(JL_CLASS_FQ_NAME, IGNORE_KOTLIN_SOURCES);
153            if (jlClass == null) return null;
154    
155            List<TypeProjectionImpl> arguments = Collections.singletonList(new TypeProjectionImpl(type));
156            JetTypeImpl javaClassType = new JetTypeImpl(
157                    jlClass.getAnnotations(),
158                    jlClass.getTypeConstructor(),
159                    false,
160                    arguments,
161                    jlClass.getMemberScope(arguments)
162            );
163    
164            return new JavaClassValue(javaClassType);
165        }
166    
167        @Nullable
168        public static CompileTimeConstant<?> resolveCompileTimeConstantValue(@Nullable Object value, @Nullable JetType expectedType) {
169            if (value instanceof String) {
170                return new StringValue((String) value);
171            }
172            else if (value instanceof Byte) {
173                return new ByteValue((Byte) value);
174            }
175            else if (value instanceof Short) {
176                return new ShortValue((Short) value);
177            }
178            else if (value instanceof Character) {
179                return new CharValue((Character) value);
180            }
181            else if (value instanceof Integer) {
182                KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
183                Integer integer = (Integer) value;
184                if (builtIns.getShortType().equals(expectedType)) {
185                    return new ShortValue(integer.shortValue());
186                }
187                else if (builtIns.getByteType().equals(expectedType)) {
188                    return new ByteValue(integer.byteValue());
189                }
190                else if (builtIns.getCharType().equals(expectedType)) {
191                    return new CharValue((char) integer.intValue());
192                }
193                return new IntValue(integer);
194            }
195            else if (value instanceof Long) {
196                return new LongValue((Long) value);
197            }
198            else if (value instanceof Float) {
199                return new FloatValue((Float) value);
200            }
201            else if (value instanceof Double) {
202                return new DoubleValue((Double) value);
203            }
204            else if (value instanceof Boolean) {
205                return BooleanValue.valueOf((Boolean) value);
206            }
207            else if (value == null) {
208                return NullValue.NULL;
209            }
210            return null;
211        }
212    }