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 com.google.common.collect.Sets;
020    import com.intellij.psi.*;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.*;
024    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
025    import org.jetbrains.jet.lang.descriptors.impl.*;
026    import org.jetbrains.jet.lang.resolve.*;
027    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
028    import org.jetbrains.jet.lang.resolve.java.*;
029    import org.jetbrains.jet.lang.resolve.java.kotlinSignature.AlternativeFieldSignatureData;
030    import org.jetbrains.jet.lang.resolve.java.kt.DescriptorKindUtils;
031    import org.jetbrains.jet.lang.resolve.java.kt.JetMethodAnnotation;
032    import org.jetbrains.jet.lang.resolve.java.provider.PsiDeclarationProvider;
033    import org.jetbrains.jet.lang.resolve.java.provider.NamedMembers;
034    import org.jetbrains.jet.lang.resolve.java.wrapper.*;
035    import org.jetbrains.jet.lang.resolve.name.Name;
036    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
037    import org.jetbrains.jet.lang.types.JetType;
038    import org.jetbrains.jet.lang.types.TypeUtils;
039    
040    import javax.inject.Inject;
041    import java.util.*;
042    
043    import static org.jetbrains.jet.lang.resolve.java.provider.DeclarationOrigin.JAVA;
044    
045    public final class JavaPropertyResolver {
046    
047        private JavaSemanticServices semanticServices;
048        private JavaSignatureResolver javaSignatureResolver;
049        private BindingTrace trace;
050        private JavaAnnotationResolver annotationResolver;
051    
052        public JavaPropertyResolver() {
053        }
054    
055        @Inject
056        public void setSemanticServices(JavaSemanticServices semanticServices) {
057            this.semanticServices = semanticServices;
058        }
059    
060        @Inject
061        public void setTrace(BindingTrace trace) {
062            this.trace = trace;
063        }
064    
065        @Inject
066        public void setJavaSignatureResolver(JavaSignatureResolver javaSignatureResolver) {
067            this.javaSignatureResolver = javaSignatureResolver;
068        }
069    
070        @Inject
071        public void setAnnotationResolver(JavaAnnotationResolver annotationResolver) {
072            this.annotationResolver = annotationResolver;
073        }
074    
075        @NotNull
076        public Set<VariableDescriptor> resolveFieldGroupByName(
077                @NotNull Name fieldName,
078                @NotNull PsiDeclarationProvider scopeData,
079                @NotNull ClassOrNamespaceDescriptor ownerDescriptor
080        ) {
081            NamedMembers namedMembers = scopeData.getMembersCache().get(fieldName);
082            if (namedMembers == null) {
083                return Collections.emptySet();
084            }
085    
086            return resolveNamedGroupProperties(ownerDescriptor, scopeData, namedMembers, fieldName,
087                                               "class or namespace " + DescriptorUtils.getFQName(ownerDescriptor));
088        }
089    
090        @NotNull
091        private Set<VariableDescriptor> resolveNamedGroupProperties(
092                @NotNull ClassOrNamespaceDescriptor ownerDescriptor,
093                @NotNull PsiDeclarationProvider scopeData,
094                @NotNull NamedMembers namedMembers,
095                @NotNull Name propertyName,
096                @NotNull String context
097        ) {
098            Collection<PropertyPsiData> psiDataCollection = PropertyPsiData.assemblePropertyPsiDataFromElements(
099                    namedMembers.getPropertyPsiDataElements());
100    
101            Set<PropertyDescriptor> propertiesFromCurrent = new HashSet<PropertyDescriptor>(1);
102    
103            int regularPropertiesCount = getNumberOfNonExtensionProperties(psiDataCollection);
104    
105            for (PropertyPsiData propertyPsiData : psiDataCollection) {
106    
107                // we cannot have more then one property with given name even if java code
108                // has several fields, getters and setter of different types
109                if (!propertyPsiData.isExtension() && regularPropertiesCount > 1) {
110                    continue;
111                }
112    
113                if (!DescriptorResolverUtils.isCorrectOwnerForEnumMember(ownerDescriptor, propertyPsiData.getCharacteristicPsi())) {
114                    continue;
115                }
116    
117                propertiesFromCurrent.add(resolveProperty(ownerDescriptor, scopeData, propertyName, context, propertyPsiData));
118            }
119    
120            Set<PropertyDescriptor> propertiesFromSupertypes = getPropertiesFromSupertypes(propertyName, ownerDescriptor);
121            Set<PropertyDescriptor> properties = Sets.newHashSet();
122    
123            generateOverrides(ownerDescriptor, propertyName, propertiesFromCurrent, propertiesFromSupertypes, properties);
124            OverrideResolver.resolveUnknownVisibilities(properties, trace);
125    
126            properties.addAll(propertiesFromCurrent);
127    
128            return Sets.<VariableDescriptor>newHashSet(properties);
129        }
130    
131        private static void generateOverrides(
132                @NotNull ClassOrNamespaceDescriptor owner,
133                @NotNull Name propertyName,
134                @NotNull Set<PropertyDescriptor> propertiesFromCurrent,
135                @NotNull Set<PropertyDescriptor> propertiesFromSupertypes,
136                @NotNull final Set<PropertyDescriptor> properties
137        ) {
138            if (!(owner instanceof ClassDescriptor)) {
139                return;
140            }
141            ClassDescriptor classDescriptor = (ClassDescriptor) owner;
142    
143            OverrideResolver.generateOverridesInFunctionGroup(
144                    propertyName, propertiesFromSupertypes, propertiesFromCurrent, classDescriptor,
145                    new OverrideResolver.DescriptorSink() {
146                        @Override
147                        public void addToScope(@NotNull CallableMemberDescriptor fakeOverride) {
148                            properties.add((PropertyDescriptor) fakeOverride);
149                        }
150    
151                        @Override
152                        public void conflict(
153                                @NotNull CallableMemberDescriptor fromSuper,
154                                @NotNull CallableMemberDescriptor fromCurrent
155                        ) {
156                            // nop
157                        }
158                    });
159        }
160    
161        @NotNull
162        private PropertyDescriptor resolveProperty(
163                @NotNull ClassOrNamespaceDescriptor owner,
164                @NotNull PsiDeclarationProvider scopeData,
165                @NotNull Name propertyName,
166                @NotNull String context,
167                @NotNull PropertyPsiData psiData
168        ) {
169            boolean isFinal = isPropertyFinal(scopeData, psiData);
170            boolean isVar = psiData.isVar();
171    
172            PropertyPsiDataElement characteristicMember = psiData.getCharacteristicMember();
173    
174            Visibility visibility = DescriptorResolverUtils.resolveVisibility(psiData.getCharacteristicPsi(), null);
175            CallableMemberDescriptor.Kind kind = CallableMemberDescriptor.Kind.DECLARATION;
176    
177            PropertyPsiDataElement getter = psiData.getGetter();
178            if (getter != null) {
179                JetMethodAnnotation methodAnnotation = ((PsiMethodWrapper) getter.getMember()).getJetMethodAnnotation();
180                visibility = DescriptorResolverUtils.resolveVisibility(psiData.getCharacteristicPsi(), methodAnnotation);
181                kind = DescriptorKindUtils.flagsToKind(methodAnnotation.kind());
182            }
183    
184            boolean isEnumEntry = psiData.getCharacteristicPsi() instanceof PsiEnumConstant;
185            PropertyDescriptorImpl propertyDescriptor = new PropertyDescriptorImpl(
186                    owner,
187                    annotationResolver.resolveAnnotations(psiData.getCharacteristicPsi()),
188                    DescriptorResolverUtils.resolveModality(characteristicMember.getMember(),
189                                                            isFinal || isEnumEntry || psiData.isPropertyForNamedObject()),
190                    visibility,
191                    isVar,
192                    propertyName,
193                    kind);
194    
195            //TODO: this is a hack to indicate that this enum entry is an object
196            // class descriptor for enum entries is not used by backends so for now this should be safe to use
197            // remove this when JavaDescriptorResolver gets rewritten
198            if (isEnumEntry) {
199                assert DescriptorUtils.isEnumClassObject(owner) : "Enum entries should be put into class object of enum only: " + owner;
200                ClassDescriptorImpl dummyClassDescriptorForEnumEntryObject =
201                        new ClassDescriptorImpl(owner, Collections.<AnnotationDescriptor>emptyList(), Modality.FINAL, propertyName);
202                dummyClassDescriptorForEnumEntryObject.initialize(
203                        true,
204                        Collections.<TypeParameterDescriptor>emptyList(),
205                        Collections.<JetType>emptyList(), JetScope.EMPTY,
206                        Collections.<ConstructorDescriptor>emptySet(), null,
207                        false);
208                trace.record(BindingContext.OBJECT_DECLARATION_CLASS, propertyDescriptor, dummyClassDescriptorForEnumEntryObject);
209            }
210    
211            PropertyGetterDescriptorImpl getterDescriptor = resolveGetter(visibility, kind, getter, propertyDescriptor);
212            PropertySetterDescriptorImpl setterDescriptor = resolveSetter(psiData, kind, propertyDescriptor);
213    
214            propertyDescriptor.initialize(getterDescriptor, setterDescriptor);
215    
216            List<TypeParameterDescriptor> typeParameters = resolvePropertyTypeParameters(psiData, characteristicMember, propertyDescriptor);
217    
218            TypeVariableResolver typeVariableResolverForPropertyInternals = TypeVariableResolvers.typeVariableResolverFromTypeParameters(
219                    typeParameters, propertyDescriptor, "property " + propertyName + " in " + context);
220    
221            JetType propertyType = getPropertyType(psiData, characteristicMember, typeVariableResolverForPropertyInternals);
222            JetType receiverType = getReceiverType(characteristicMember, typeVariableResolverForPropertyInternals);
223    
224    
225            propertyType = getAlternativeSignatureData(isVar, characteristicMember, propertyDescriptor, propertyType);
226    
227            propertyDescriptor.setType(
228                    propertyType,
229                    typeParameters,
230                    DescriptorUtils.getExpectedThisObjectIfNeeded(owner),
231                    receiverType
232            );
233            initializeSetterAndGetter(propertyDescriptor, getterDescriptor, setterDescriptor, propertyType, psiData);
234    
235            if (kind == CallableMemberDescriptor.Kind.DECLARATION) {
236                trace.record(BindingContext.VARIABLE, psiData.getCharacteristicPsi(), propertyDescriptor);
237            }
238    
239            recordObjectDeclarationClassIfNeeded(psiData, owner, propertyDescriptor, propertyType);
240    
241            if (scopeData.getDeclarationOrigin() == JAVA) {
242                trace.record(JavaBindingContext.IS_DECLARED_IN_JAVA, propertyDescriptor);
243            }
244    
245            if (AnnotationUtils.isPropertyAcceptableAsAnnotationParameter(propertyDescriptor) && psiData.getCharacteristicPsi() instanceof PsiField) {
246                PsiExpression initializer = ((PsiField) psiData.getCharacteristicPsi()).getInitializer();
247                if (initializer instanceof PsiLiteralExpression) {
248                    CompileTimeConstant<?> constant = JavaCompileTimeConstResolver.getCompileTimeConstFromLiteralExpressionWithExpectedType(
249                            (PsiLiteralExpression) initializer, propertyType);
250                    if (constant != null) {
251                        trace.record(BindingContext.COMPILE_TIME_INITIALIZER, propertyDescriptor, constant);
252                    }
253                }
254            }
255            return propertyDescriptor;
256        }
257    
258        @NotNull
259        private JetType getAlternativeSignatureData(
260                boolean isVar,
261                PropertyPsiDataElement characteristicMember,
262                PropertyDescriptor propertyDescriptor,
263                JetType propertyType
264        ) {
265            if (!characteristicMember.isField()) {
266                return propertyType;
267            }
268            AlternativeFieldSignatureData signatureData =
269                    new AlternativeFieldSignatureData((PsiFieldWrapper) characteristicMember.getMember(), propertyType, isVar);
270            if (!signatureData.hasErrors()) {
271                if (signatureData.isAnnotated()) {
272                    return signatureData.getReturnType();
273                }
274            }
275            else {
276                trace.record(JavaBindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, propertyDescriptor,
277                             Collections.singletonList(signatureData.getError()));
278            }
279            return propertyType;
280        }
281    
282        private static void initializeSetterAndGetter(
283                @NotNull PropertyDescriptor propertyDescriptor,
284                @Nullable PropertyGetterDescriptorImpl getterDescriptor,
285                @Nullable PropertySetterDescriptorImpl setterDescriptor,
286                @NotNull JetType propertyType,
287                @NotNull PropertyPsiData data
288        ) {
289            if (getterDescriptor != null) {
290                getterDescriptor.initialize(propertyType);
291            }
292            if (setterDescriptor != null) {
293                PropertyPsiDataElement setter = data.getSetter();
294                assert setter != null;
295                List<PsiParameterWrapper> parameters = ((PsiMethodWrapper) setter.getMember()).getParameters();
296                assert parameters.size() != 0;
297                int valueIndex = parameters.size() - 1;
298                PsiParameterWrapper valueParameter = parameters.get(valueIndex);
299                setterDescriptor.initialize(new ValueParameterDescriptorImpl(
300                        setterDescriptor,
301                        0,
302                        Collections.<AnnotationDescriptor>emptyList(),
303                        Name.identifierNoValidate(valueParameter.getJetValueParameter().name()),
304                        propertyDescriptor.getType(),
305                        false,
306                        null));
307            }
308        }
309    
310        private void recordObjectDeclarationClassIfNeeded(
311                PropertyPsiData psiData,
312                DeclarationDescriptor realOwner,
313                PropertyDescriptor propertyDescriptor,
314                JetType propertyType
315        ) {
316            if (!psiData.isPropertyForNamedObject()) {
317                return;
318            }
319            ClassDescriptor objectDescriptor = (ClassDescriptor) propertyType.getConstructor().getDeclarationDescriptor();
320    
321            assert objectDescriptor != null;
322            assert objectDescriptor.getKind() == ClassKind.OBJECT;
323            assert objectDescriptor.getContainingDeclaration() == realOwner;
324    
325            trace.record(BindingContext.OBJECT_DECLARATION_CLASS, propertyDescriptor, objectDescriptor);
326        }
327    
328        @Nullable
329        private PropertyGetterDescriptorImpl resolveGetter(
330                Visibility visibility,
331                CallableMemberDescriptor.Kind kind,
332                PropertyPsiDataElement getter,
333                PropertyDescriptor propertyDescriptor
334        ) {
335            if (getter == null) {
336                return null;
337            }
338            return new PropertyGetterDescriptorImpl(
339                    propertyDescriptor,
340                    annotationResolver.resolveAnnotations(getter.getMember().getPsiMember()),
341                    propertyDescriptor.getModality(),
342                    visibility,
343                    true,
344                    false,
345                    kind);
346        }
347    
348        @Nullable
349        private PropertySetterDescriptorImpl resolveSetter(
350                PropertyPsiData psiData,
351                CallableMemberDescriptor.Kind kind,
352                PropertyDescriptor propertyDescriptor
353        ) {
354            PropertyPsiDataElement setter = psiData.getSetter();
355            if (setter == null) {
356                return null;
357            }
358            Visibility setterVisibility = DescriptorResolverUtils.resolveVisibility(setter.getMember().getPsiMember(), null);
359            if (setter.getMember() instanceof PsiMethodWrapper) {
360                setterVisibility = DescriptorResolverUtils.resolveVisibility(
361                        setter.getMember().getPsiMember(),
362                        ((PsiMethodWrapper) setter.getMember())
363                                .getJetMethodAnnotation());
364            }
365            return new PropertySetterDescriptorImpl(
366                    propertyDescriptor,
367                    annotationResolver.resolveAnnotations(setter.getMember().getPsiMember()),
368                    propertyDescriptor.getModality(),
369                    setterVisibility,
370                    true,
371                    false,
372                    kind);
373        }
374    
375        private List<TypeParameterDescriptor> resolvePropertyTypeParameters(
376                @NotNull PropertyPsiData members,
377                @NotNull PropertyPsiDataElement characteristicMember,
378                @NotNull PropertyDescriptor propertyDescriptor
379        ) {
380            // TODO: Can't get type parameters from field - only from accessors
381            if (characteristicMember == members.getSetter() || characteristicMember == members.getGetter()) {
382                PsiMethodWrapper method = (PsiMethodWrapper) characteristicMember.getMember();
383                return javaSignatureResolver.resolveMethodTypeParameters(method, propertyDescriptor);
384            }
385    
386            return Collections.emptyList();
387        }
388    
389        @NotNull
390        private JetType getPropertyType(
391                PropertyPsiData members,
392                PropertyPsiDataElement characteristicMember,
393                TypeVariableResolver typeVariableResolverForPropertyInternals
394        ) {
395            if (!characteristicMember.getType().getTypeString().isEmpty()) {
396                return semanticServices.getTypeTransformer().transformToType(
397                        characteristicMember.getType().getTypeString(), typeVariableResolverForPropertyInternals);
398            }
399            JetType propertyType = semanticServices.getTypeTransformer().transformToType(
400                    characteristicMember.getType().getPsiType(), typeVariableResolverForPropertyInternals);
401    
402            boolean hasNotNullAnnotation = JavaAnnotationResolver.findAnnotationWithExternal(
403                    characteristicMember.getType().getPsiNotNullOwner(),
404                    JvmAbi.JETBRAINS_NOT_NULL_ANNOTATION.getFqName().asString()) != null;
405    
406            if (hasNotNullAnnotation || members.isStaticFinalField()) {
407                propertyType = TypeUtils.makeNotNullable(propertyType);
408            }
409            return propertyType;
410        }
411    
412        @Nullable
413        private JetType getReceiverType(
414                PropertyPsiDataElement characteristicMember,
415                TypeVariableResolver typeVariableResolverForPropertyInternals
416        ) {
417            if (characteristicMember.getReceiverType() == null) {
418                return null;
419            }
420            if (!characteristicMember.getReceiverType().getTypeString().isEmpty()) {
421                return semanticServices.getTypeTransformer().transformToType(characteristicMember.getReceiverType().getTypeString(), typeVariableResolverForPropertyInternals);
422            }
423            return semanticServices.getTypeTransformer().transformToType(characteristicMember.getReceiverType().getPsiType(), typeVariableResolverForPropertyInternals);
424        }
425    
426        private static int getNumberOfNonExtensionProperties(@NotNull Collection<PropertyPsiData> propertyPsiDataCollection) {
427            int regularPropertiesCount = 0;
428            for (PropertyPsiData members : propertyPsiDataCollection) {
429                if (!members.isExtension()) {
430                    ++regularPropertiesCount;
431                }
432            }
433            return regularPropertiesCount;
434        }
435    
436        private static boolean isPropertyFinal(PsiDeclarationProvider scopeData, PropertyPsiData psiData) {
437            if (scopeData.getDeclarationOrigin() == JAVA) {
438                return true;
439            }
440            return psiData.isFinal();
441        }
442    
443        @NotNull
444        private static Set<PropertyDescriptor> getPropertiesFromSupertypes(
445                @NotNull Name propertyName, @NotNull ClassOrNamespaceDescriptor ownerDescriptor
446        ) {
447            Set<PropertyDescriptor> r = new HashSet<PropertyDescriptor>();
448            for (JetType supertype : DescriptorResolverUtils.getSupertypes(ownerDescriptor)) {
449                for (VariableDescriptor property : supertype.getMemberScope().getProperties(propertyName)) {
450                    r.add((PropertyDescriptor) property);
451                }
452            }
453            return r;
454        }
455    }