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.foreach;
018    
019    import com.google.dart.compiler.backend.js.ast.JsBlock;
020    import com.google.dart.compiler.backend.js.ast.JsExpression;
021    import com.google.dart.compiler.backend.js.ast.JsName;
022    import com.google.dart.compiler.backend.js.ast.JsStatement;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.jet.lang.psi.JetForExpression;
026    import org.jetbrains.jet.lang.psi.JetMultiDeclaration;
027    import org.jetbrains.jet.lang.psi.JetParameter;
028    import org.jetbrains.k2js.translate.context.TranslationContext;
029    import org.jetbrains.k2js.translate.expression.MultiDeclarationTranslator;
030    import org.jetbrains.k2js.translate.general.AbstractTranslator;
031    import org.jetbrains.k2js.translate.general.Translation;
032    
033    import static org.jetbrains.k2js.translate.utils.JsAstUtils.newVar;
034    import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody;
035    import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopParameter;
036    
037    public abstract class ForTranslator extends AbstractTranslator {
038    
039        @NotNull
040        public static JsStatement translate(@NotNull JetForExpression expression,
041                                            @NotNull TranslationContext context) {
042            if (RangeLiteralForTranslator.isApplicable(expression, context)) {
043                return RangeLiteralForTranslator.doTranslate(expression, context);
044            }
045            if (RangeForTranslator.isApplicable(expression, context)) {
046                return RangeForTranslator.doTranslate(expression, context);
047            }
048            if (ArrayForTranslator.isApplicable(expression, context)) {
049                return ArrayForTranslator.doTranslate(expression, context);
050            }
051            return IteratorForTranslator.doTranslate(expression, context);
052        }
053    
054        @NotNull
055        protected final JetForExpression expression;
056        @NotNull
057        protected final JsName parameterName;
058        @Nullable
059        protected final JetMultiDeclaration multiParameter;
060    
061        protected ForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) {
062            super(context);
063            this.expression = forExpression;
064            this.multiParameter = forExpression.getMultiParameter();
065            this.parameterName = declareParameter();
066        }
067    
068        @NotNull
069        private JsName declareParameter() {
070            JetParameter loopParameter = getLoopParameter(expression);
071            if (loopParameter != null) {
072                return context().getNameForElement(loopParameter);
073            }
074            assert parameterIsMultiDeclaration() : "If loopParameter is null, multi parameter must be not null";
075            return context().scope().declareTemporary();
076        }
077    
078        private boolean parameterIsMultiDeclaration() {
079            return multiParameter != null;
080        }
081    
082        @NotNull
083        private JsStatement makeCurrentVarInit(@Nullable JsExpression itemValue) {
084            if (multiParameter == null) {
085                return newVar(parameterName, itemValue);
086            } else {
087                return MultiDeclarationTranslator.translate(multiParameter, parameterName, itemValue, context());
088            }
089        }
090    
091        @NotNull
092        protected JsStatement translateBody(@Nullable JsExpression itemValue) {
093            JsStatement realBody = Translation.translateAsStatement(getLoopBody(expression), context());
094            if (itemValue == null && !parameterIsMultiDeclaration()) {
095                return realBody;
096            } else {
097                JsStatement currentVarInit = makeCurrentVarInit(itemValue);
098                if (realBody instanceof JsBlock) {
099                    JsBlock block = (JsBlock) realBody;
100                    block.getStatements().add(0, currentVarInit);
101                    return block;
102                }
103                else {
104                    return new JsBlock(currentVarInit, realBody);
105                }
106            }
107        }
108    }