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.Maps;
020import com.intellij.psi.PsiElement;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.jet.lang.psi.*;
023
024import java.util.Map;
025
026import static org.jetbrains.k2js.translate.utils.PsiUtils.getBaseExpression;
027
028//TODO: refactor
029public final class FindPreviousVisitor extends JetTreeVisitor<DangerousData> {
030
031    @NotNull
032    private final Map<JetElement, Void> hasDangerous = Maps.newHashMap();
033
034    public FindPreviousVisitor(@NotNull DangerousData data) {
035        JetElement node = data.getDangerousNode();
036        PsiElement last = data.getRootNode().getParent();
037        while (node != last) {
038            hasDangerous.put(node, null);
039            PsiElement parent = node.getParent();
040            assert parent instanceof JetElement;
041            node = (JetElement)parent;
042        }
043    }
044
045    @Override
046    public Void visitJetElement(JetElement element, DangerousData data) {
047        if (data.getDangerousNode() == element) {
048            return null;
049        }
050        if (!hasDangerous(element)) {
051            addElement(element, data);
052        }
053        else {
054            acceptChildrenThatAreBeforeTheDangerousNode(element, data);
055        }
056        return null;
057    }
058
059    //TODO: return value not used, wtf?
060    private static boolean addElement(@NotNull JetElement element, @NotNull DangerousData data) {
061        if (element instanceof JetExpression) {
062            data.getNodesToBeGeneratedBefore().add((JetExpression)element);
063            return true;
064        }
065        return false;
066    }
067
068    private void acceptChildrenThatAreBeforeTheDangerousNode(@NotNull JetElement element, @NotNull DangerousData data) {
069        PsiElement current = element.getFirstChild();
070        while (current != null) {
071            if (current instanceof JetElement) {
072                ((JetElement)current).accept(this, data);
073                if (hasDangerous(element)) {
074                    break;
075                }
076            }
077            current = current.getNextSibling();
078        }
079    }
080
081    @Override
082    public Void visitPrefixExpression(@NotNull JetPrefixExpression expression, @NotNull DangerousData data) {
083        if (data.getDangerousNode() == expression) {
084            return null;
085        }
086        if (!hasDangerous(expression)) {
087            addElement(expression, data);
088            return null;
089        }
090        else {
091            if (hasDangerous(getBaseExpression(expression))) {
092                return null;
093            }
094            else {
095                //TODO:
096                throw new IllegalStateException();
097            }
098        }
099    }
100
101    @Override
102    public Void visitCallExpression(@NotNull JetCallExpression expression, @NotNull DangerousData data) {
103        if (data.getDangerousNode() == expression) {
104            return null;
105        }
106        if (!hasDangerous(expression)) {
107            data.getNodesToBeGeneratedBefore().add(expression);
108        }
109        else {
110            acceptArgumentsThatAreBeforeDangerousNode(expression, data);
111        }
112        return null;
113    }
114
115    private void acceptArgumentsThatAreBeforeDangerousNode(@NotNull JetCallExpression expression, @NotNull DangerousData data) {
116        for (ValueArgument argument : expression.getValueArguments()) {
117            JetExpression argumentExpression = argument.getArgumentExpression();
118            assert argumentExpression != null;
119            argumentExpression.accept(this, data);
120            if (hasDangerous(argumentExpression)) {
121                break;
122            }
123        }
124    }
125
126    private boolean hasDangerous(@NotNull JetElement element) {
127        return hasDangerous.containsKey(element);
128    }
129}