Cleanup: NotNull/Nullable
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / intention / impl / SplitConditionUtil.java
1 /*
2  * Copyright 2000-2017 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.codeInsight.intention.impl;
17
18 import com.intellij.codeInsight.PsiEquivalenceUtil;
19 import com.intellij.psi.*;
20 import com.intellij.psi.tree.IElementType;
21 import com.intellij.psi.util.PsiTreeUtil;
22 import com.intellij.psi.util.PsiUtil;
23 import com.siyeh.ig.psiutils.CommentTracker;
24 import com.siyeh.ig.psiutils.ControlFlowUtils;
25 import com.siyeh.ipp.psiutils.ErrorUtil;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28
29 import java.util.ArrayList;
30 import java.util.List;
31
32 import static com.intellij.util.ObjectUtils.tryCast;
33
34 public class SplitConditionUtil {
35   public static PsiPolyadicExpression findCondition(PsiElement element) {
36     return findCondition(element, true, true);
37   }
38
39   public static PsiPolyadicExpression findCondition(PsiElement element, boolean acceptAnd, boolean acceptOr) {
40     if (!(element instanceof PsiJavaToken)) {
41       return null;
42     }
43     PsiJavaToken token = (PsiJavaToken)element;
44     if (!(token.getParent() instanceof PsiPolyadicExpression)) return null;
45
46     PsiPolyadicExpression expression = (PsiPolyadicExpression)token.getParent();
47     boolean isAndExpression = acceptAnd && expression.getOperationTokenType() == JavaTokenType.ANDAND;
48     boolean isOrExpression = acceptOr && expression.getOperationTokenType() == JavaTokenType.OROR;
49     if (!isAndExpression && !isOrExpression) return null;
50     if (ErrorUtil.containsError(expression)) {
51       // Incomplete expression like "something &&"
52       return null;
53     }
54
55     while (expression.getParent() instanceof PsiPolyadicExpression) {
56       expression = (PsiPolyadicExpression)expression.getParent();
57       if (isAndExpression && expression.getOperationTokenType() != JavaTokenType.ANDAND) return null;
58       if (isOrExpression && expression.getOperationTokenType() != JavaTokenType.OROR) return null;
59     }
60     return expression;
61   }
62
63   public static PsiExpression getROperands(PsiPolyadicExpression expression, PsiJavaToken separator) {
64     return getROperands(expression, separator, new CommentTracker());
65   }
66
67   public static PsiExpression getROperands(PsiPolyadicExpression expression, PsiJavaToken separator, CommentTracker ct) {
68     PsiElement next = PsiTreeUtil.skipWhitespacesAndCommentsForward(separator);
69     final int offsetInParent;
70     if (next == null) {
71       offsetInParent = separator.getStartOffsetInParent() + separator.getTextLength();
72     } else {
73       ct.markRangeUnchanged(next, expression.getLastChild());
74       offsetInParent = next.getStartOffsetInParent();
75     }
76
77     PsiElementFactory factory = JavaPsiFacade.getElementFactory(expression.getProject());
78     String rOperands = expression.getText().substring(offsetInParent);
79     return factory.createExpressionFromText(rOperands, expression.getParent());
80   }
81
82   public static PsiExpression getLOperands(PsiPolyadicExpression expression, PsiJavaToken separator) {
83     return getLOperands(expression, separator, new CommentTracker());
84   }
85
86   public static PsiExpression getLOperands(PsiPolyadicExpression expression, PsiJavaToken separator, CommentTracker ct) {
87     PsiElement prev = separator;
88     if (prev.getPrevSibling() instanceof PsiWhiteSpace) prev = prev.getPrevSibling();
89     ct.markRangeUnchanged(expression.getFirstChild(), prev.getPrevSibling());
90
91     PsiElementFactory factory = JavaPsiFacade.getElementFactory(expression.getProject());
92     String rOperands = expression.getText().substring(0, prev.getStartOffsetInParent());
93     return factory.createExpressionFromText(rOperands, expression.getParent());
94   }
95
96   @Nullable
97   static PsiIfStatement create(@NotNull PsiElementFactory factory,
98                                @NotNull PsiIfStatement ifStatement,
99                                @NotNull PsiExpression extract,
100                                @NotNull PsiExpression leave,
101                                @NotNull IElementType operation,
102                                CommentTracker tracker) {
103     PsiStatement thenBranch = ifStatement.getThenBranch();
104     if (thenBranch == null) {
105       return null;
106     }
107     PsiStatement elseBranch = ifStatement.getElseBranch();
108
109     if (operation == JavaTokenType.OROR) {
110       return createOrOr(factory, thenBranch, elseBranch, extract, leave, tracker);
111     }
112     if (operation == JavaTokenType.ANDAND) {
113       return createAndAnd(factory, thenBranch, elseBranch, extract, leave, tracker);
114     }
115
116     return null;
117   }
118
119   @NotNull
120   private static PsiIfStatement createAndAnd(@NotNull PsiElementFactory factory,
121                                              @NotNull PsiStatement thenBranch,
122                                              @Nullable PsiStatement elseBranch,
123                                              @NotNull PsiExpression extract,
124                                              @NotNull PsiExpression leave,
125                                              CommentTracker tracker) {
126     List<String> elseChain = new ArrayList<>();
127     boolean chainFinished = false;
128     loop:
129     while (!chainFinished) {
130       PsiIfStatement nextIf = tryCast(ControlFlowUtils.stripBraces(elseBranch), PsiIfStatement.class);
131       if (nextIf == null) break;
132       PsiExpression nextCondition = PsiUtil.skipParenthesizedExprDown(nextIf.getCondition());
133       if (nextCondition == null) break;
134       if (PsiEquivalenceUtil.areElementsEquivalent(extract, nextCondition) && nextIf.getThenBranch() != null) {
135         elseChain.add(tracker.text(nextIf.getThenBranch()));
136         chainFinished = true;
137       }
138       else {
139         if (!(nextCondition instanceof PsiPolyadicExpression)) break;
140         PsiPolyadicExpression nextPolyadic = (PsiPolyadicExpression)nextCondition;
141         if (!nextPolyadic.getOperationTokenType().equals(JavaTokenType.ANDAND)) break;
142         PsiExpression[] nextOperands = nextPolyadic.getOperands();
143         PsiExpression[] operands;
144         if (extract instanceof PsiPolyadicExpression &&
145             ((PsiPolyadicExpression)extract).getOperationTokenType().equals(JavaTokenType.ANDAND)) {
146           operands = ((PsiPolyadicExpression)extract).getOperands();
147         }
148         else {
149           operands = new PsiExpression[]{extract};
150         }
151         if (nextOperands.length <= operands.length) break;
152         for (int i = 0; i < operands.length; i++) {
153           if (!PsiEquivalenceUtil.areElementsEquivalent(nextOperands[i], operands[i])) break loop;
154         }
155         PsiExpression nextExtracted =
156           getROperands(nextPolyadic, nextPolyadic.getTokenBeforeOperand(nextOperands[operands.length]), tracker);
157         elseChain.add(createIfString(nextExtracted, nextIf.getThenBranch(), (PsiStatement)null, tracker));
158       }
159       elseBranch = nextIf.getElseBranch();
160     }
161     if (!chainFinished && elseBranch != null) {
162       elseChain.add(elseBranch.getText());
163     }
164     String thenString;
165     if (elseChain.isEmpty()) {
166       thenString = createIfString(leave, thenBranch, (String)null, tracker);
167     }
168     else {
169       thenString = "{" + createIfString(leave, thenBranch, String.join("\nelse ", elseChain), tracker) + "\n}";
170     }
171     String ifString = createIfString(extract, thenString, elseBranch, tracker);
172     return (PsiIfStatement)factory.createStatementFromText(ifString, thenBranch);
173   }
174
175   @NotNull
176   private static PsiIfStatement createOrOr(@NotNull PsiElementFactory factory,
177                                            @NotNull PsiStatement thenBranch,
178                                            @Nullable PsiStatement elseBranch,
179                                            @NotNull PsiExpression extract,
180                                            @NotNull PsiExpression leave,
181                                            CommentTracker tracker) {
182     return (PsiIfStatement)factory.createStatementFromText(
183       createIfString(extract, thenBranch, createIfString(leave, thenBranch, elseBranch, tracker), tracker), thenBranch);
184   }
185
186   @NotNull
187   private static String createIfString(@NotNull PsiExpression condition,
188                                        @NotNull PsiStatement thenBranch,
189                                        @Nullable PsiStatement elseBranch,
190                                        CommentTracker tracker) {
191     PsiExpression stripped = PsiUtil.skipParenthesizedExprDown(condition);
192     return createIfString(tracker.text(stripped == null ? condition : stripped),
193                           toThenBranchString(tracker.markUnchanged(thenBranch)),
194                           toElseBranchString(elseBranch != null ? tracker.markUnchanged(elseBranch) : null, false));
195   }
196
197   @NotNull
198   private static String createIfString(@NotNull PsiExpression condition,
199                                        @NotNull PsiStatement thenBranch,
200                                        @Nullable String elseBranch,
201                                        CommentTracker tracker) {
202     PsiExpression stripped = PsiUtil.skipParenthesizedExprDown(condition);
203     return createIfString(tracker.text(stripped == null ? condition : stripped),
204                           toThenBranchString(tracker.markUnchanged(thenBranch)), elseBranch);
205   }
206
207   @NotNull
208   private static String createIfString(@NotNull PsiExpression condition,
209                                        @NotNull String thenBranch,
210                                        @Nullable PsiStatement elseBranch,
211                                        CommentTracker tracker) {
212     PsiExpression stripped = PsiUtil.skipParenthesizedExprDown(condition);
213     return createIfString(tracker.text(stripped == null ? condition : stripped),
214                           thenBranch, toElseBranchString(elseBranch != null ? tracker.markUnchanged(elseBranch) : null, true));
215   }
216
217   @NotNull
218   private static String createIfString(@NotNull String condition,
219                                        @NotNull String thenBranch,
220                                        @Nullable String elseBranch) {
221     final String elsePart = elseBranch != null ? "\n else " + elseBranch : "";
222     return "if (" + condition + ")\n" + thenBranch + elsePart;
223   }
224
225   @NotNull
226   private static String toThenBranchString(@NotNull PsiStatement statement) {
227     if (!(statement instanceof PsiBlockStatement)) {
228       return "{ " + statement.getText() + "\n }";
229     }
230
231     return statement.getText();
232   }
233
234   @Nullable
235   private static String toElseBranchString(@Nullable PsiStatement statement, boolean skipElse) {
236     if (statement == null) {
237       return null;
238     }
239
240     if (statement instanceof PsiBlockStatement || skipElse && statement instanceof PsiIfStatement) {
241       return statement.getText();
242     }
243
244     return "{ " + statement.getText() + "\n }";
245   }
246 }