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.expression;
018
019import com.google.dart.compiler.backend.js.ast.JsExpression;
020import com.google.dart.compiler.backend.js.ast.JsInvocation;
021import com.google.dart.compiler.backend.js.ast.JsName;
022import com.google.dart.compiler.backend.js.ast.JsNameRef;
023import org.jetbrains.annotations.NotNull;
024import org.jetbrains.annotations.Nullable;
025import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
026import org.jetbrains.jet.lang.psi.*;
027import org.jetbrains.k2js.translate.context.TranslationContext;
028import org.jetbrains.k2js.translate.general.AbstractTranslator;
029import org.jetbrains.k2js.translate.general.Translation;
030import org.jetbrains.k2js.translate.utils.BindingUtils;
031import org.jetbrains.k2js.translate.utils.TranslationUtils;
032
033import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
034
035public final class PatternTranslator extends AbstractTranslator {
036
037    @NotNull
038    public static PatternTranslator newInstance(@NotNull TranslationContext context) {
039        return new PatternTranslator(context);
040    }
041
042    private PatternTranslator(@NotNull TranslationContext context) {
043        super(context);
044    }
045
046    @NotNull
047    public JsExpression translateIsExpression(@NotNull JetIsExpression expression) {
048        JsExpression left = Translation.translateAsExpression(expression.getLeftHandSide(), context());
049        JetTypeReference typeReference = expression.getTypeRef();
050        assert typeReference != null;
051        JsExpression result = translateIsCheck(left, typeReference);
052        if (expression.isNegated()) {
053            return negated(result);
054        }
055        return result;
056    }
057
058    @NotNull
059    public JsExpression translateIsCheck(@NotNull JsExpression subject, @NotNull JetTypeReference typeReference) {
060        JsExpression result = translateAsIntrinsicTypeCheck(subject, typeReference);
061        if (result != null) {
062            return result;
063        }
064        return translateAsIsCheck(subject, typeReference);
065    }
066
067    @NotNull
068    private JsExpression translateAsIsCheck(@NotNull JsExpression expressionToMatch,
069                                            @NotNull JetTypeReference typeReference) {
070        JsInvocation isCheck = new JsInvocation(context().namer().isOperationReference(),
071                                                     expressionToMatch, getClassReference(typeReference));
072        if (isNullable(typeReference)) {
073            return addNullCheck(expressionToMatch, isCheck);
074        }
075        return isCheck;
076    }
077
078    @Nullable
079    private JsExpression translateAsIntrinsicTypeCheck(@NotNull JsExpression expressionToMatch,
080                                                       @NotNull JetTypeReference typeReference) {
081        JsExpression result = null;
082        JsName className = getClassReference(typeReference).getName();
083        if (className.getIdent().equals("String")) {
084            result = typeof(expressionToMatch, program().getStringLiteral("string"));
085        }
086        if (className.getIdent().equals("Int")) {
087            result = typeof(expressionToMatch, program().getStringLiteral("number"));
088        }
089        return result;
090    }
091
092    @NotNull
093    private static JsExpression addNullCheck(@NotNull JsExpression expressionToMatch, @NotNull JsInvocation isCheck) {
094        return or(TranslationUtils.isNullCheck(expressionToMatch), isCheck);
095    }
096
097    private boolean isNullable(JetTypeReference typeReference) {
098        return BindingUtils.getTypeByReference(bindingContext(), typeReference).isNullable();
099    }
100
101    @NotNull
102    private JsNameRef getClassReference(@NotNull JetTypeReference typeReference) {
103        return getClassNameReferenceForTypeReference(typeReference);
104    }
105
106    @NotNull
107    private JsNameRef getClassNameReferenceForTypeReference(@NotNull JetTypeReference typeReference) {
108        ClassDescriptor referencedClass = BindingUtils.getClassDescriptorForTypeReference
109            (bindingContext(), typeReference);
110        return TranslationUtils.getQualifiedReference(context(), referencedClass);
111    }
112
113    @NotNull
114    public JsExpression translateExpressionPattern(@NotNull JsExpression expressionToMatch, @NotNull JetExpression patternExpression) {
115        JsExpression expressionToMatchAgainst = translateExpressionForExpressionPattern(patternExpression);
116        return equality(expressionToMatch, expressionToMatchAgainst);
117    }
118
119    @NotNull
120    public JsExpression translateExpressionForExpressionPattern(@NotNull JetExpression patternExpression) {
121        return Translation.translateAsExpression(patternExpression, context());
122    }
123}