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