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, propertyName, Modality.FINAL, Collections.<JetType>emptyList());
149                dummyClassDescriptorForEnumEntryObject.initialize(JetScope.EMPTY, Collections.<ConstructorDescriptor>emptySet(), null);
150                return new JavaPropertyDescriptorForObject(owner, annotations, visibility, propertyName,
151                                                           dummyClassDescriptorForEnumEntryObject);
152            }
153    
154            return new JavaPropertyDescriptor(owner, annotations, visibility, isVar, propertyName);
155        }
156    
157        @NotNull
158        private JetType getPropertyType(@NotNull JavaField field, @NotNull TypeVariableResolver typeVariableResolver) {
159            JetType propertyType = typeTransformer.transformToType(field.getType(), typeVariableResolver);
160    
161            if (annotationResolver.hasNotNullAnnotation(field) || isStaticFinalField(field) /* TODO: WTF? */) {
162                return TypeUtils.makeNotNullable(propertyType);
163            }
164    
165            return propertyType;
166        }
167    
168        @NotNull
169        private static Set<PropertyDescriptor> getPropertiesFromSupertypes(@NotNull Name name, @NotNull ClassDescriptor descriptor) {
170            Set<PropertyDescriptor> result = new HashSet<PropertyDescriptor>();
171            for (JetType supertype : descriptor.getTypeConstructor().getSupertypes()) {
172                for (VariableDescriptor property : supertype.getMemberScope().getProperties(name)) {
173                    result.add((PropertyDescriptor) property);
174                }
175            }
176    
177            return result;
178        }
179    
180        private static boolean isStaticFinalField(@NotNull JavaField field) {
181            return field.isFinal() && field.isStatic();
182        }
183    }