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