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.k2js.translate.reference;
018
019import com.google.dart.compiler.backend.js.ast.JsExpression;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
023import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
024import org.jetbrains.jet.lang.psi.JetExpression;
025import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
026import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
027import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
028import org.jetbrains.k2js.translate.context.TranslationContext;
029import org.jetbrains.k2js.translate.general.AbstractTranslator;
030
031import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.isNativeObject;
032import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
033import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCallForProperty;
034import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelectorAsSimpleName;
035import static org.jetbrains.jet.lang.psi.JetPsiUtil.isBackingFieldReference;
036
037public abstract class PropertyAccessTranslator extends AbstractTranslator implements AccessTranslator {
038
039    @NotNull
040    public static PropertyAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression,
041            @Nullable JsExpression qualifier,
042            @NotNull CallType callType,
043            @NotNull TranslationContext context) {
044        PropertyAccessTranslator result;
045        PropertyDescriptor propertyDescriptor = getPropertyDescriptor(expression, context);
046        if (isNativeObject(propertyDescriptor) || isBackingFieldReference(expression)) {
047            result = new NativePropertyAccessTranslator(propertyDescriptor, qualifier, context);
048        }
049        else {
050            ResolvedCall<?> resolvedCall = getResolvedCallForProperty(context.bindingContext(), expression);
051            result = new KotlinPropertyAccessTranslator(propertyDescriptor, qualifier, resolvedCall, context);
052        }
053        result.setCallType(callType);
054        return result;
055    }
056
057    @NotNull
058    private static PropertyDescriptor getPropertyDescriptor(@NotNull JetSimpleNameExpression expression,
059            @NotNull TranslationContext context) {
060        DeclarationDescriptor descriptor =
061                getDescriptorForReferenceExpression(context.bindingContext(), expression);
062        assert descriptor instanceof PropertyDescriptor : "Must be a property descriptor.";
063        return (PropertyDescriptor) descriptor;
064    }
065
066
067    @NotNull
068    public static JsExpression translateAsPropertyGetterCall(@NotNull JetSimpleNameExpression expression,
069            @Nullable JsExpression qualifier,
070            @NotNull CallType callType,
071            @NotNull TranslationContext context) {
072        return (newInstance(expression, qualifier, callType, context))
073                .translateAsGet();
074    }
075
076
077    private static boolean canBePropertyGetterCall(@NotNull JetQualifiedExpression expression,
078            @NotNull TranslationContext context) {
079        JetSimpleNameExpression selector = getSelectorAsSimpleName(expression);
080        assert selector != null : "Only names are allowed after the dot";
081        return canBePropertyGetterCall(selector, context);
082    }
083
084    private static boolean canBePropertyGetterCall(@NotNull JetSimpleNameExpression expression,
085            @NotNull TranslationContext context) {
086        return (getDescriptorForReferenceExpression
087                        (context.bindingContext(), expression) instanceof PropertyDescriptor);
088    }
089
090    public static boolean canBePropertyGetterCall(@NotNull JetExpression expression,
091            @NotNull TranslationContext context) {
092        if (expression instanceof JetQualifiedExpression) {
093            return canBePropertyGetterCall((JetQualifiedExpression) expression, context);
094        }
095        if (expression instanceof JetSimpleNameExpression) {
096            return canBePropertyGetterCall((JetSimpleNameExpression) expression, context);
097        }
098        return false;
099    }
100
101    public static boolean canBePropertyAccess(@NotNull JetExpression expression,
102            @NotNull TranslationContext context) {
103        return canBePropertyGetterCall(expression, context);
104    }
105
106    //TODO: we use normal by default but may cause bugs
107    //TODO: inspect
108    private /*var*/ CallType callType = CallType.NORMAL;
109
110    protected PropertyAccessTranslator(@NotNull TranslationContext context) {
111        super(context);
112    }
113
114    public void setCallType(@NotNull CallType callType) {
115        this.callType = callType;
116    }
117
118    @NotNull
119    protected CallType getCallType() {
120        assert callType != null : "CallType not set";
121        return callType;
122    }
123
124    @NotNull
125    protected abstract JsExpression translateAsGet(@Nullable JsExpression receiver);
126
127    @NotNull
128    protected abstract JsExpression translateAsSet(@Nullable JsExpression receiver, @NotNull JsExpression setTo);
129}