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