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