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 }