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