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 }