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.PackageViewDescriptor;
024    import org.jetbrains.jet.lang.psi.*;
025    import org.jetbrains.k2js.translate.context.TranslationContext;
026    import org.jetbrains.k2js.translate.utils.ErrorReportingUtils;
027    
028    import static org.jetbrains.k2js.translate.general.Translation.translateAsExpression;
029    import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
030    import static org.jetbrains.k2js.translate.utils.JsAstUtils.setQualifier;
031    import static org.jetbrains.k2js.translate.utils.PsiUtils.getNotNullSimpleNameSelector;
032    import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelector;
033    
034    public final class QualifiedExpressionTranslator {
035    
036        private QualifiedExpressionTranslator() {
037        }
038    
039        @NotNull
040        public static AccessTranslator getAccessTranslator(@NotNull JetQualifiedExpression expression,
041                                                           @NotNull TranslationContext context) {
042            JsExpression receiver = translateReceiver(expression, context);
043            return VariableAccessTranslator.newInstance(context, getNotNullSimpleNameSelector(expression), receiver);
044        }
045    
046        @NotNull
047        public static JsExpression translateQualifiedExpression(@NotNull JetQualifiedExpression expression,
048                                                                @NotNull TranslationContext context) {
049            JsExpression receiver = translateReceiver(expression, context);
050            JetExpression selector = getSelector(expression);
051            return dispatchToCorrectTranslator(receiver, selector, context);
052        }
053    
054        @NotNull
055        private static JsExpression dispatchToCorrectTranslator(
056                @Nullable JsExpression receiver,
057                @NotNull JetExpression selector,
058                @NotNull TranslationContext context
059        ) {
060            if (ReferenceTranslator.canBePropertyAccess(selector, context)) {
061                assert selector instanceof JetSimpleNameExpression : "Selectors for properties must be simple names.";
062                return VariableAccessTranslator.newInstance(context, (JetSimpleNameExpression)selector, receiver).translateAsGet();
063            }
064            if (selector instanceof JetCallExpression) {
065                return invokeCallExpressionTranslator(receiver, selector, context);
066            }
067            //TODO: never get there
068            if (selector instanceof JetSimpleNameExpression) {
069                JsExpression simpleName = ReferenceTranslator.translateSimpleName((JetSimpleNameExpression) selector, context);
070                if (receiver != null) { // TODO: hack for nested Object
071                    setQualifier(simpleName, receiver);
072                }
073                return simpleName;
074            }
075            throw new AssertionError("Unexpected qualified expression: " + selector.getText());
076        }
077    
078        @NotNull
079        private static JsExpression invokeCallExpressionTranslator(
080                @Nullable JsExpression receiver,
081                @NotNull JetExpression selector,
082                @NotNull TranslationContext context
083        ) {
084            try {
085                return CallExpressionTranslator.translate((JetCallExpression) selector, receiver, context);
086            } catch (RuntimeException e) {
087                throw  ErrorReportingUtils.reportErrorWithLocation(selector, e);
088            }
089        }
090    
091        @Nullable
092        private static JsExpression translateReceiver(@NotNull JetQualifiedExpression expression,
093                                                      @NotNull TranslationContext context) {
094            JetExpression receiverExpression = expression.getReceiverExpression();
095            if (isFullQualifierForExpression(receiverExpression, context)) {
096                return null;
097            }
098            return translateAsExpression(receiverExpression, context);
099        }
100    
101        //TODO: prove correctness
102        private static boolean isFullQualifierForExpression(@Nullable JetExpression receiverExpression, @NotNull TranslationContext context) {
103            if (receiverExpression == null) {
104                return false;
105            }
106            if (receiverExpression instanceof JetReferenceExpression) {
107                DeclarationDescriptor descriptorForReferenceExpression =
108                    getDescriptorForReferenceExpression(context.bindingContext(), (JetReferenceExpression)receiverExpression);
109                if (descriptorForReferenceExpression instanceof PackageViewDescriptor) {
110                    return true;
111                }
112            }
113            if (receiverExpression instanceof JetQualifiedExpression) {
114                return isFullQualifierForExpression(((JetQualifiedExpression)receiverExpression).getSelectorExpression(), context);
115            }
116            return false;
117        }
118    }