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.expression;
018    
019    import com.google.dart.compiler.backend.js.ast.JsExpression;
020    import com.google.dart.compiler.backend.js.ast.JsInvocation;
021    import com.google.dart.compiler.backend.js.ast.JsName;
022    import com.google.dart.compiler.backend.js.ast.JsNameRef;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
026    import org.jetbrains.jet.lang.psi.*;
027    import org.jetbrains.k2js.translate.context.TranslationContext;
028    import org.jetbrains.k2js.translate.general.AbstractTranslator;
029    import org.jetbrains.k2js.translate.general.Translation;
030    import org.jetbrains.k2js.translate.utils.BindingUtils;
031    import org.jetbrains.k2js.translate.utils.TranslationUtils;
032    
033    import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
034    
035    public 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    }