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.utils.dangerous;
018
019import com.google.common.collect.Lists;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.lang.psi.JetBlockExpression;
023import org.jetbrains.jet.lang.psi.JetElement;
024import org.jetbrains.jet.lang.psi.JetExpression;
025import org.jetbrains.k2js.translate.context.TranslationContext;
026
027import java.util.List;
028
029/**
030 * This module uses a methaphor for naming.
031 * <p/>
032 * Dangerous are the nodes that can be expressions in Kotlin but can't be expressions in JavaScript.
033 * These are: when, if, inlined functions.
034 * The issue with them is that we have to translate them to a list of statements. And also all the expressions which must be computed before
035 * the dangerous expressions.
036 * RootNode is a node which contains such an expression. For example, it may be a statement expression belongs to.
037 */
038public class DangerousData {
039    @NotNull
040    private final List<JetExpression> nodesToBeGeneratedBefore = Lists.newArrayList();
041
042
043    @NotNull
044    public static DangerousData collect(@NotNull JetExpression expression, @NotNull TranslationContext context) {
045        if (cantContainDangerousElements(expression)) {
046            return emptyData();
047        }
048        return doCollectData(expression, context);
049    }
050
051    private static boolean cantContainDangerousElements(@NotNull JetElement element) {
052        return element instanceof JetBlockExpression;
053    }
054
055    @NotNull
056    private static DangerousData doCollectData(@NotNull JetExpression expression,
057                                               @NotNull TranslationContext context) {
058        DangerousData data = new DangerousData();
059        FindDangerousVisitor visitor = new FindDangerousVisitor(context);
060        expression.accept(visitor, data);
061        if (!data.exists()) {
062            return emptyData();
063        }
064        data.setRootNode(expression);
065        FindPreviousVisitor findPreviousVisitor = new FindPreviousVisitor(data);
066        expression.accept(findPreviousVisitor, data);
067        return data;
068    }
069
070    private static final DangerousData EMPTY = new DangerousData() {
071        @Override
072        public boolean exists() {
073            return false;
074        }
075
076        @Override
077        public boolean shouldBeTranslated() {
078            return false;
079        }
080    };
081
082    @NotNull
083    public static DangerousData emptyData() {
084        return EMPTY;
085    }
086
087    @Nullable
088    private JetExpression dangerousNode = null;
089
090    @Nullable
091    private JetExpression rootNode = null;
092
093    public void setDangerousNode(@NotNull JetExpression dangerousNode) {
094        assert this.dangerousNode == null : "Should be assigned only once";
095        this.dangerousNode = dangerousNode;
096    }
097
098    @NotNull
099    public List<JetExpression> getNodesToBeGeneratedBefore() {
100        return nodesToBeGeneratedBefore;
101    }
102
103    public boolean exists() {
104        return dangerousNode != null;
105    }
106
107    public boolean shouldBeTranslated() {
108        return exists() && !nodesToBeGeneratedBefore.isEmpty();
109    }
110
111    @NotNull
112    public JetExpression getDangerousNode() {
113        assert dangerousNode != null;
114        return dangerousNode;
115    }
116
117    @NotNull
118    public JetExpression getRootNode() {
119        assert rootNode != null;
120        return rootNode;
121    }
122
123    @SuppressWarnings("NullableProblems")
124    public void setRootNode(@NotNull JetExpression rootNode) {
125        this.rootNode = rootNode;
126    }
127}