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.k2js.translate.reference;
018    
019    import com.google.dart.compiler.backend.js.ast.JsExpression;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
023    import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
024    import org.jetbrains.jet.lang.psi.JetExpression;
025    import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
026    import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
027    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
028    import org.jetbrains.k2js.translate.context.TranslationContext;
029    import org.jetbrains.k2js.translate.general.AbstractTranslator;
030    
031    import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.isNativeObject;
032    import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
033    import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCallForProperty;
034    import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelectorAsSimpleName;
035    import static org.jetbrains.jet.lang.psi.JetPsiUtil.isBackingFieldReference;
036    
037    public 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    }