92a3df479ef4e917fb7c679e6a62a9caf2cf7471
[idea/community.git] / plugins / IntentionPowerPak / src / com / siyeh / ipp / forloop / ReverseForLoopDirectionIntention.java
1 /*
2  * Copyright 2009-2013 Bas Leijdekkers
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.siyeh.ipp.forloop;
17
18 import com.intellij.openapi.project.Project;
19 import com.intellij.psi.*;
20 import com.intellij.psi.tree.IElementType;
21 import com.intellij.util.IncorrectOperationException;
22 import com.siyeh.ig.psiutils.ExpressionUtils;
23 import com.siyeh.ig.psiutils.ParenthesesUtils;
24 import com.siyeh.ipp.base.Intention;
25 import com.siyeh.ipp.base.PsiElementPredicate;
26 import com.siyeh.ipp.psiutils.ComparisonUtils;
27 import com.siyeh.ipp.psiutils.VariableAccessUtils;
28 import org.jetbrains.annotations.NotNull;
29
30 public class ReverseForLoopDirectionIntention extends Intention {
31
32   @NotNull
33   @Override
34   protected PsiElementPredicate getElementPredicate() {
35     return new ReverseForLoopDirectionPredicate();
36   }
37
38   @Override
39   protected void processIntention(@NotNull PsiElement element)
40     throws IncorrectOperationException {
41     final PsiForStatement forStatement =
42       (PsiForStatement)element.getParent();
43     final PsiDeclarationStatement initialization =
44       (PsiDeclarationStatement)forStatement.getInitialization();
45     if (initialization == null) {
46       return;
47     }
48     final PsiBinaryExpression condition =
49       (PsiBinaryExpression)forStatement.getCondition();
50     if (condition == null) {
51       return;
52     }
53     final PsiLocalVariable variable =
54       (PsiLocalVariable)initialization.getDeclaredElements()[0];
55     final PsiExpression initializer = variable.getInitializer();
56     if (initializer == null) {
57       return;
58     }
59     final PsiExpression lhs = condition.getLOperand();
60     final PsiExpression rhs = condition.getROperand();
61     if (rhs == null) {
62       return;
63     }
64     final PsiExpressionStatement update =
65       (PsiExpressionStatement)forStatement.getUpdate();
66     if (update == null) {
67       return;
68     }
69     final PsiExpression updateExpression = update.getExpression();
70     final String variableName = variable.getName();
71     final StringBuilder newUpdateText = new StringBuilder();
72     if (updateExpression instanceof PsiPrefixExpression) {
73       final PsiPrefixExpression prefixExpression =
74         (PsiPrefixExpression)updateExpression;
75       final IElementType tokenType =
76         prefixExpression.getOperationTokenType();
77       if (JavaTokenType.PLUSPLUS == tokenType) {
78         newUpdateText.append("--");
79       }
80       else if (JavaTokenType.MINUSMINUS == tokenType) {
81         newUpdateText.append("++");
82       }
83       else {
84         return;
85       }
86       newUpdateText.append(variableName);
87     }
88     else if (updateExpression instanceof PsiPostfixExpression) {
89       newUpdateText.append(variableName);
90       final PsiPostfixExpression postfixExpression =
91         (PsiPostfixExpression)updateExpression;
92       final IElementType tokenType =
93         postfixExpression.getOperationTokenType();
94       if (JavaTokenType.PLUSPLUS == tokenType) {
95         newUpdateText.append("--");
96       }
97       else if (JavaTokenType.MINUSMINUS == tokenType) {
98         newUpdateText.append("++");
99       }
100       else {
101         return;
102       }
103     }
104     else {
105       return;
106     }
107     final Project project = element.getProject();
108     final PsiElementFactory factory =
109       JavaPsiFacade.getElementFactory(project);
110     final PsiExpression newUpdate = factory.createExpressionFromText(
111       newUpdateText.toString(), element);
112     updateExpression.replace(newUpdate);
113     final IElementType sign = condition.getOperationTokenType();
114     final String negatedSign = ComparisonUtils.getNegatedComparison(sign);
115     final StringBuilder conditionText = new StringBuilder();
116     final StringBuilder newInitializerText = new StringBuilder();
117     if (VariableAccessUtils.evaluatesToVariable(lhs, variable)) {
118       conditionText.append(variableName);
119       conditionText.append(negatedSign);
120       if (sign == JavaTokenType.GE) {
121         conditionText.append(incrementExpression(initializer, true));
122       }
123       else if (sign == JavaTokenType.LE) {
124         conditionText.append(incrementExpression(initializer, false));
125       }
126       else {
127         conditionText.append(initializer.getText());
128       }
129       if (sign == JavaTokenType.LT) {
130         newInitializerText.append(incrementExpression(rhs, false));
131       }
132       else if (sign == JavaTokenType.GT) {
133         newInitializerText.append(incrementExpression(rhs, true));
134       }
135       else {
136         newInitializerText.append(rhs.getText());
137       }
138     }
139     else if (VariableAccessUtils.evaluatesToVariable(rhs, variable)) {
140       if (sign == JavaTokenType.LE) {
141         conditionText.append(incrementExpression(initializer, true));
142       }
143       else if (sign == JavaTokenType.GE) {
144         conditionText.append(incrementExpression(initializer, false));
145       }
146       else {
147         conditionText.append(initializer.getText());
148       }
149       conditionText.append(negatedSign);
150       conditionText.append(variableName);
151       if (sign == JavaTokenType.GT) {
152         newInitializerText.append(incrementExpression(lhs, false));
153       }
154       else if (sign == JavaTokenType.LT) {
155         newInitializerText.append(incrementExpression(lhs, true));
156       }
157       else {
158         newInitializerText.append(lhs.getText());
159       }
160     }
161     else {
162       return;
163     }
164     final PsiExpression newInitializer = factory.createExpressionFromText(
165       newInitializerText.toString(), element);
166     variable.setInitializer(newInitializer);
167     final PsiExpression newCondition = factory.createExpressionFromText(
168       conditionText.toString(), element);
169     condition.replace(newCondition);
170   }
171
172   private static String incrementExpression(PsiExpression expression,
173                                             boolean positive) {
174     if (expression instanceof PsiLiteralExpression) {
175       final PsiLiteralExpression literalExpression =
176         (PsiLiteralExpression)expression;
177       final Number value = (Number)literalExpression.getValue();
178       if (value == null) {
179         return null;
180       }
181       if (positive) {
182         return String.valueOf(value.longValue() + 1L);
183       }
184       else {
185         return String.valueOf(value.longValue() - 1L);
186       }
187     }
188     else {
189       if (expression instanceof PsiBinaryExpression) {
190         // see if we can remove a -1 instead of adding a +1
191         final PsiBinaryExpression binaryExpression =
192           (PsiBinaryExpression)expression;
193         final PsiExpression rhs = binaryExpression.getROperand();
194         if (ExpressionUtils.isOne(rhs)) {
195           final IElementType tokenType =
196             binaryExpression.getOperationTokenType();
197           if (tokenType == JavaTokenType.MINUS && positive) {
198             return binaryExpression.getLOperand().getText();
199           }
200           else if (tokenType == JavaTokenType.PLUS && !positive) {
201             return binaryExpression.getLOperand().getText();
202           }
203         }
204       }
205       final String expressionText;
206       if (ParenthesesUtils.getPrecedence(expression) >
207           ParenthesesUtils.ADDITIVE_PRECEDENCE) {
208         expressionText = '(' + expression.getText() + ')';
209       }
210       else {
211         expressionText = expression.getText();
212       }
213       if (positive) {
214         return expressionText + "+1";
215       }
216       else {
217         return expressionText + "-1";
218       }
219     }
220   }
221 }