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.Nullable;
022    import org.jetbrains.jet.lang.descriptors.*;
023    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
024    import org.jetbrains.jet.lang.resolve.constants.*;
025    import org.jetbrains.jet.lang.resolve.constants.StringValue;
026    import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils;
027    import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule;
028    import org.jetbrains.jet.lang.resolve.name.FqName;
029    import org.jetbrains.jet.lang.resolve.name.Name;
030    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
031    import org.jetbrains.jet.lang.types.JetType;
032    import org.jetbrains.jet.lang.types.TypeProjection;
033    
034    import javax.inject.Inject;
035    import java.util.ArrayList;
036    import java.util.Collection;
037    import java.util.List;
038    
039    public 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    }