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.utils.dangerous;
018    
019    import com.google.common.collect.Lists;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.psi.JetBlockExpression;
023    import org.jetbrains.jet.lang.psi.JetElement;
024    import org.jetbrains.jet.lang.psi.JetExpression;
025    import org.jetbrains.k2js.translate.context.TranslationContext;
026    
027    import 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     */
038    public 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    }