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 }