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