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.*;
023    import org.jetbrains.jet.lang.psi.JetExpression;
024    import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
025    import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
026    import org.jetbrains.k2js.translate.context.Namer;
027    import org.jetbrains.k2js.translate.context.TranslationContext;
028    import org.jetbrains.k2js.translate.utils.BindingUtils;
029    
030    import static org.jetbrains.jet.lang.psi.JetPsiUtil.isBackingFieldReference;
031    import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
032    import static org.jetbrains.k2js.translate.utils.JsAstUtils.setQualifier;
033    import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelectorAsSimpleName;
034    
035    public final class ReferenceTranslator {
036    
037        private ReferenceTranslator() {
038        }
039    
040        @NotNull
041        public static JsExpression translateSimpleName(@NotNull JetSimpleNameExpression expression,
042                @NotNull TranslationContext context) {
043            return getAccessTranslator(expression, context).translateAsGet();
044        }
045    
046        @NotNull
047        public static JsExpression translateSimpleNameWithQualifier(
048                @NotNull JetSimpleNameExpression expression,
049                @Nullable JsExpression qualifier,
050                @NotNull TranslationContext context
051        ) {
052            JsExpression simpleName = translateSimpleName(expression, context);
053    
054            // Ignore qualifier if expression is EnumEntry and use always use FQ name.
055            DeclarationDescriptor descriptor = BindingUtils.getDescriptorForReferenceExpression(context.bindingContext(), expression);
056            if (descriptor instanceof ClassDescriptor) {
057                ClassDescriptor entryClass = (ClassDescriptor) descriptor;
058                if (entryClass.getKind() == ClassKind.ENUM_ENTRY) {
059                    DeclarationDescriptor enumClass = entryClass.getContainingDeclaration();
060                    qualifier = Namer.getClassObjectAccessor(translateAsFQReference(enumClass, context));
061                }
062            }
063    
064            if (qualifier != null) { // TODO: hack for nested Object
065                setQualifier(simpleName, qualifier);
066            }
067    
068            return simpleName;
069        }
070    
071        @NotNull
072        public static JsExpression translateAsFQReference(@NotNull DeclarationDescriptor referencedDescriptor,
073                @NotNull TranslationContext context) {
074            JsExpression alias = context.getAliasForDescriptor(referencedDescriptor);
075            return alias != null ? alias : context.getQualifiedReference(referencedDescriptor);
076        }
077    
078        @NotNull
079        public static JsExpression translateAsLocalNameReference(@NotNull DeclarationDescriptor descriptor,
080                @NotNull TranslationContext context) {
081            if (descriptor instanceof FunctionDescriptor || descriptor instanceof VariableDescriptor) {
082                JsExpression alias = context.getAliasForDescriptor(descriptor);
083                if (alias != null) {
084                    return alias;
085                }
086            }
087            return context.getNameForDescriptor(descriptor).makeRef();
088        }
089    
090        @NotNull
091        public static AccessTranslator getAccessTranslator(@NotNull JetSimpleNameExpression referenceExpression,
092                @NotNull TranslationContext context) {
093            return getAccessTranslator(referenceExpression, null, context);
094        }
095    
096        @NotNull
097        public static AccessTranslator getAccessTranslator(@NotNull JetSimpleNameExpression referenceExpression,
098                @Nullable JsExpression receiver,
099                @NotNull TranslationContext context) {
100            if (isBackingFieldReference(referenceExpression)) {
101                return BackingFieldAccessTranslator.newInstance(referenceExpression, context);
102            }
103            if (canBePropertyAccess(referenceExpression, context)) {
104                return VariableAccessTranslator.newInstance(context, referenceExpression, receiver);
105            }
106            if (ClassObjectAccessTranslator.isClassObjectReference(referenceExpression, context)) {
107                return ClassObjectAccessTranslator.newInstance(referenceExpression, context);
108            }
109            return ReferenceAccessTranslator.newInstance(referenceExpression, context);
110        }
111    
112        public static boolean canBePropertyAccess(@NotNull JetExpression expression, @NotNull TranslationContext context) {
113            JetSimpleNameExpression simpleNameExpression = null;
114            if (expression instanceof JetQualifiedExpression) {
115                simpleNameExpression = getSelectorAsSimpleName((JetQualifiedExpression) expression);
116            }
117            else if (expression instanceof JetSimpleNameExpression) {
118                simpleNameExpression = (JetSimpleNameExpression) expression;
119            }
120    
121            if (simpleNameExpression == null) return false;
122    
123            DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), simpleNameExpression);
124    
125            // Skip ValueParameterDescriptor because sometime we can miss resolved call for it, e.g. when set something to delegated property.
126            return descriptor instanceof VariableDescriptor && !(descriptor instanceof ValueParameterDescriptor);
127        }
128    
129    }