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 com.google.common.collect.Lists;
020    import com.intellij.psi.*;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.*;
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.DescriptorResolverUtils;
028    import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule;
029    import org.jetbrains.jet.lang.resolve.name.FqName;
030    import org.jetbrains.jet.lang.resolve.name.Name;
031    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
032    import org.jetbrains.jet.lang.types.JetType;
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.List;
040    
041    public final class JavaCompileTimeConstResolver {
042        private JavaAnnotationResolver annotationResolver;
043        private JavaClassResolver classResolver;
044    
045        public JavaCompileTimeConstResolver() {
046        }
047    
048        @Inject
049        public void setAnnotationResolver(JavaAnnotationResolver annotationResolver) {
050            this.annotationResolver = annotationResolver;
051        }
052    
053        @Inject
054        public void setClassResolver(JavaClassResolver classResolver) {
055            this.classResolver = classResolver;
056        }
057    
058        @Nullable
059        public CompileTimeConstant<?> getCompileTimeConstFromExpression(
060                FqName annotationFqName, Name parameterName,
061                PsiAnnotationMemberValue value, PostponedTasks postponedTasks
062        ) {
063            if (value instanceof PsiLiteralExpression) {
064                return getCompileTimeConstFromLiteralExpression((PsiLiteralExpression) value);
065            }
066            // Enum
067            else if (value instanceof PsiReferenceExpression) {
068                return getCompileTimeConstFromReferenceExpression((PsiReferenceExpression) value, postponedTasks);
069            }
070            // Array
071            else if (value instanceof PsiArrayInitializerMemberValue) {
072                return getCompileTimeConstFromArrayExpression(annotationFqName, parameterName, (PsiArrayInitializerMemberValue) value,
073                                                              postponedTasks);
074            }
075            // Annotation
076            else if (value instanceof PsiAnnotation) {
077                return getCompileTimeConstFromAnnotation((PsiAnnotation) value, postponedTasks);
078            }
079            return null;
080        }
081    
082        @Nullable
083        private CompileTimeConstant<?> getCompileTimeConstFromAnnotation(PsiAnnotation value, PostponedTasks taskList) {
084            AnnotationDescriptor annotationDescriptor = annotationResolver.resolveAnnotation(value, taskList);
085            if (annotationDescriptor != null) {
086                return new AnnotationValue(annotationDescriptor);
087            }
088            return null;
089        }
090    
091        @Nullable
092        private CompileTimeConstant<?> getCompileTimeConstFromArrayExpression(
093                FqName annotationFqName,
094                Name valueName, PsiArrayInitializerMemberValue value,
095                PostponedTasks taskList
096        ) {
097            PsiAnnotationMemberValue[] initializers = value.getInitializers();
098            List<CompileTimeConstant<?>> values = getCompileTimeConstantForArrayValues(annotationFqName, valueName, taskList, initializers);
099    
100            ClassDescriptor classDescriptor =
101                    classResolver.resolveClass(annotationFqName, DescriptorSearchRule.INCLUDE_KOTLIN, taskList);
102    
103            //TODO: nullability issues
104            ValueParameterDescriptor valueParameterDescriptor =
105                    DescriptorResolverUtils.getValueParameterDescriptorForAnnotationParameter(valueName, classDescriptor);
106            if (valueParameterDescriptor == null) {
107                return null;
108            }
109            JetType expectedArrayType = valueParameterDescriptor.getType();
110            return new ArrayValue(values, expectedArrayType);
111        }
112    
113        private List<CompileTimeConstant<?>> getCompileTimeConstantForArrayValues(
114                FqName annotationQualifiedName,
115                Name valueName,
116                PostponedTasks taskList,
117                PsiAnnotationMemberValue[] initializers
118        ) {
119            List<CompileTimeConstant<?>> values = new ArrayList<CompileTimeConstant<?>>();
120            for (PsiAnnotationMemberValue initializer : initializers) {
121                CompileTimeConstant<?> compileTimeConstant =
122                        getCompileTimeConstFromExpression(annotationQualifiedName, valueName, initializer, taskList);
123                if (compileTimeConstant == null) {
124                    compileTimeConstant = NullValue.NULL;
125                }
126                values.add(compileTimeConstant);
127            }
128            return values;
129        }
130    
131        @Nullable
132        private CompileTimeConstant<?> getCompileTimeConstFromReferenceExpression(PsiReferenceExpression value, PostponedTasks taskList) {
133            PsiElement resolveElement = value.resolve();
134            if (resolveElement instanceof PsiEnumConstant) {
135                PsiElement psiElement = resolveElement.getParent();
136                if (psiElement instanceof PsiClass) {
137                    PsiClass psiClass = (PsiClass) psiElement;
138                    String fqName = psiClass.getQualifiedName();
139                    if (fqName == null) {
140                        return null;
141                    }
142    
143                    JetScope scope;
144                    ClassDescriptor classDescriptor = classResolver.resolveClass(new FqName(fqName), DescriptorSearchRule.INCLUDE_KOTLIN, taskList);
145                    if (classDescriptor == null) {
146                        return null;
147                    }
148                    ClassDescriptor classObjectDescriptor = classDescriptor.getClassObjectDescriptor();
149                    if (classObjectDescriptor == null) {
150                        return null;
151                    }
152                    scope = classObjectDescriptor.getMemberScope(Lists.<TypeProjection>newArrayList());
153    
154                    Name identifier = Name.identifier(((PsiEnumConstant) resolveElement).getName());
155                    Collection<VariableDescriptor> properties = scope.getProperties(identifier);
156                    for (VariableDescriptor variableDescriptor : properties) {
157                        if (variableDescriptor.getReceiverParameter() == null) {
158                            return new EnumValue((PropertyDescriptor) variableDescriptor);
159                        }
160                    }
161                    return null;
162                }
163            }
164            return null;
165        }
166    
167        @Nullable
168        private static CompileTimeConstant<?> getCompileTimeConstFromLiteralExpression(PsiLiteralExpression value) {
169            return getCompileTimeConstFromLiteralExpressionWithExpectedType(value, null);
170        }
171    
172        @Nullable
173        public static CompileTimeConstant<?> getCompileTimeConstFromLiteralExpressionWithExpectedType(
174                @NotNull PsiLiteralExpression value,
175                @Nullable JetType expectedType
176        ) {
177            Object literalValue = value.getValue();
178            if (literalValue instanceof String) {
179                return new StringValue((String) literalValue);
180            }
181            else if (literalValue instanceof Byte) {
182                return new ByteValue((Byte) literalValue);
183            }
184            else if (literalValue instanceof Short) {
185                return new ShortValue((Short) literalValue);
186            }
187            else if (literalValue instanceof Character) {
188                return new CharValue((Character) literalValue);
189            }
190            else if (literalValue instanceof Integer) {
191                KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
192                if (builtIns.getShortType().equals(expectedType)) {
193                    return new ShortValue(((Integer) literalValue).shortValue());
194                }
195                else if (builtIns.getByteType().equals(expectedType)) {
196                    return new ByteValue(((Integer) literalValue).byteValue());
197                }
198                else if (builtIns.getCharType().equals(expectedType)) {
199                    return new CharValue((char) ((Integer)literalValue).intValue());
200                }
201                return new IntValue((Integer) literalValue);
202            }
203            else if (literalValue instanceof Long) {
204                return new LongValue((Long) literalValue);
205            }
206            else if (literalValue instanceof Float) {
207                return new FloatValue((Float) literalValue);
208            }
209            else if (literalValue instanceof Double) {
210                return new DoubleValue((Double) literalValue);
211            }
212            else if (literalValue instanceof Boolean) {
213                return ((Boolean) literalValue) ? BooleanValue.TRUE : BooleanValue.FALSE;
214            }
215            else if (literalValue == null) {
216                return NullValue.NULL;
217            }
218            return null;
219        }
220    }