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.jet.lang.descriptors.*;
021    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
022    import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorImpl;
023    import org.jetbrains.jet.lang.descriptors.impl.PropertyDescriptorImpl;
024    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
025    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPropertyDescriptor;
026    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPropertyDescriptorForObject;
027    import org.jetbrains.jet.lang.resolve.java.scope.NamedMembers;
028    import org.jetbrains.jet.lang.resolve.java.structure.JavaField;
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.TypeUtils;
033    
034    import javax.inject.Inject;
035    import java.util.*;
036    
037    import static org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils.resolveOverrides;
038    
039    public final class JavaPropertyResolver {
040        private JavaTypeTransformer typeTransformer;
041        private JavaResolverCache cache;
042        private JavaAnnotationResolver annotationResolver;
043        private ExternalSignatureResolver externalSignatureResolver;
044        private ErrorReporter errorReporter;
045    
046        @Inject
047        public void setTypeTransformer(@NotNull JavaTypeTransformer javaTypeTransformer) {
048            this.typeTransformer = javaTypeTransformer;
049        }
050    
051        @Inject
052        public void setCache(JavaResolverCache cache) {
053            this.cache = cache;
054        }
055    
056        @Inject
057        public void setAnnotationResolver(JavaAnnotationResolver annotationResolver) {
058            this.annotationResolver = annotationResolver;
059        }
060    
061        @Inject
062        public void setExternalSignatureResolver(ExternalSignatureResolver externalSignatureResolver) {
063            this.externalSignatureResolver = externalSignatureResolver;
064        }
065    
066        @Inject
067        public void setErrorReporter(ErrorReporter errorReporter) {
068            this.errorReporter = errorReporter;
069        }
070    
071        @NotNull
072        public Set<VariableDescriptor> resolveFieldGroup(@NotNull NamedMembers members, @NotNull ClassOrNamespaceDescriptor owner) {
073            Name propertyName = members.getName();
074    
075            List<JavaField> fields = members.getFields();
076    
077            Set<PropertyDescriptor> propertiesFromCurrent = new HashSet<PropertyDescriptor>(1);
078            assert fields.size() <= 1;
079            if (fields.size() == 1) {
080                JavaField field = fields.iterator().next();
081                if (DescriptorResolverUtils.isCorrectOwnerForEnumMember(owner, field)) {
082                    propertiesFromCurrent.add(resolveProperty(owner, propertyName, field));
083                }
084            }
085    
086            Set<PropertyDescriptor> properties = new HashSet<PropertyDescriptor>();
087            if (owner instanceof ClassDescriptor) {
088                ClassDescriptor classDescriptor = (ClassDescriptor) owner;
089    
090                Collection<PropertyDescriptor> propertiesFromSupertypes = getPropertiesFromSupertypes(propertyName, classDescriptor);
091    
092                properties.addAll(resolveOverrides(propertyName, propertiesFromSupertypes, propertiesFromCurrent, classDescriptor,
093                                                   errorReporter));
094            }
095    
096            properties.addAll(propertiesFromCurrent);
097    
098            return new HashSet<VariableDescriptor>(properties);
099        }
100    
101        @NotNull
102        private PropertyDescriptor resolveProperty(@NotNull ClassOrNamespaceDescriptor owner, @NotNull Name name, @NotNull JavaField field) {
103            boolean isVar = !field.isFinal();
104    
105            PropertyDescriptorImpl propertyDescriptor = createPropertyDescriptor(owner, name, field, isVar);
106            propertyDescriptor.initialize(null, null);
107    
108            TypeVariableResolver typeVariableResolver =
109                    new TypeVariableResolverImpl(Collections.<TypeParameterDescriptor>emptyList(), propertyDescriptor);
110    
111            JetType propertyType = getPropertyType(field, typeVariableResolver);
112    
113            ExternalSignatureResolver.AlternativeFieldSignature effectiveSignature =
114                    externalSignatureResolver.resolveAlternativeFieldSignature(field, propertyType, isVar);
115            List<String> signatureErrors = effectiveSignature.getErrors();
116            if (!signatureErrors.isEmpty()) {
117                externalSignatureResolver.reportSignatureErrors(propertyDescriptor, signatureErrors);
118            }
119    
120            propertyDescriptor.setType(
121                    effectiveSignature.getReturnType(),
122                    Collections.<TypeParameterDescriptor>emptyList(),
123                    DescriptorUtils.getExpectedThisObjectIfNeeded(owner),
124                    (JetType) null
125            );
126    
127            cache.recordField(field, propertyDescriptor);
128    
129            return propertyDescriptor;
130        }
131    
132        @NotNull
133        private PropertyDescriptorImpl createPropertyDescriptor(
134                @NotNull ClassOrNamespaceDescriptor owner,
135                @NotNull Name propertyName,
136                @NotNull JavaField field,
137                boolean isVar
138        ) {
139            List<AnnotationDescriptor> annotations = annotationResolver.resolveAnnotations(field);
140            Visibility visibility = field.getVisibility();
141    
142            if (field.isEnumEntry()) {
143                assert !isVar : "Enum entries should be immutable.";
144                assert DescriptorUtils.isEnumClassObject(owner) : "Enum entries should be put into class object of enum only: " + owner;
145                //TODO: this is a hack to indicate that this enum entry is an object
146                // class descriptor for enum entries is not used by backends so for now this should be safe to use
147                ClassDescriptorImpl dummyClassDescriptorForEnumEntryObject =
148                        new ClassDescriptorImpl(owner, Collections.<AnnotationDescriptor>emptyList(), Modality.FINAL, propertyName);
149                dummyClassDescriptorForEnumEntryObject.initialize(
150                        true,
151                        Collections.<TypeParameterDescriptor>emptyList(),
152                        Collections.<JetType>emptyList(), JetScope.EMPTY,
153                        Collections.<ConstructorDescriptor>emptySet(), null,
154                        false);
155                return new JavaPropertyDescriptorForObject(owner, annotations, visibility, propertyName, dummyClassDescriptorForEnumEntryObject);
156            }
157    
158            return new JavaPropertyDescriptor(owner, annotations, visibility, isVar, propertyName);
159        }
160    
161        @NotNull
162        private JetType getPropertyType(@NotNull JavaField field, @NotNull TypeVariableResolver typeVariableResolver) {
163            JetType propertyType = typeTransformer.transformToType(field.getType(), typeVariableResolver);
164    
165            if (annotationResolver.hasNotNullAnnotation(field) || isStaticFinalField(field) /* TODO: WTF? */) {
166                return TypeUtils.makeNotNullable(propertyType);
167            }
168    
169            return propertyType;
170        }
171    
172        @NotNull
173        private static Set<PropertyDescriptor> getPropertiesFromSupertypes(@NotNull Name name, @NotNull ClassDescriptor descriptor) {
174            Set<PropertyDescriptor> result = new HashSet<PropertyDescriptor>();
175            for (JetType supertype : descriptor.getTypeConstructor().getSupertypes()) {
176                for (VariableDescriptor property : supertype.getMemberScope().getProperties(name)) {
177                    result.add((PropertyDescriptor) property);
178                }
179            }
180    
181            return result;
182        }
183    
184        private static boolean isStaticFinalField(@NotNull JavaField field) {
185            return field.isFinal() && field.isStatic();
186        }
187    }