d26fc85b0c2b11a32b8e46a0608cd463a1d741c5
[idea/community.git] / plugins / IntentionPowerPak / src / com / siyeh / ipp / base / Intention.java
1 /*
2  * Copyright 2003-2013 Dave Griffith, 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.base;
17
18 import com.intellij.codeInsight.FileModificationService;
19 import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction;
20 import com.intellij.lang.java.JavaLanguage;
21 import com.intellij.openapi.editor.Editor;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.psi.*;
24 import com.intellij.psi.codeStyle.CodeStyleManager;
25 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
26 import com.siyeh.IntentionPowerPackBundle;
27 import com.siyeh.ig.InspectionGadgetsFix;
28 import com.siyeh.ig.psiutils.ParenthesesUtils;
29 import com.siyeh.ipp.psiutils.BoolUtils;
30 import com.siyeh.ipp.psiutils.ComparisonUtils;
31 import org.jetbrains.annotations.NonNls;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34
35 public abstract class Intention extends BaseElementAtCaretIntentionAction {
36
37   private final PsiElementPredicate predicate;
38
39   /**
40    * @noinspection AbstractMethodCallInConstructor, OverridableMethodCallInConstructor
41    */
42   protected Intention() {
43     predicate = getElementPredicate();
44   }
45
46   @Override
47   public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element){
48     if (prepareForWriting() && !FileModificationService.getInstance().preparePsiElementsForWrite(element)) {
49       return;
50     }
51     final PsiElement matchingElement = findMatchingElement(element, editor);
52     if (matchingElement == null) {
53       return;
54     }
55     processIntention(editor, matchingElement);
56   }
57
58   protected boolean prepareForWriting() {
59     return true;
60   }
61
62   protected abstract void processIntention(@NotNull PsiElement element);
63   
64   protected void processIntention(Editor editor, @NotNull PsiElement element) {
65     processIntention(element);
66   }
67
68   @NotNull
69   protected abstract PsiElementPredicate getElementPredicate();
70
71   protected static void replaceExpressionWithNegatedExpression(@NotNull PsiExpression newExpression, @NotNull PsiExpression expression){
72     final Project project = expression.getProject();
73     final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
74     PsiExpression expressionToReplace = expression;
75     final String newExpressionText = newExpression.getText();
76     final String expString;
77     if (BoolUtils.isNegated(expression)) {
78       expressionToReplace = BoolUtils.findNegation(expression);
79       expString = newExpressionText;
80     }
81     else if (ComparisonUtils.isComparison(newExpression)) {
82       final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)newExpression;
83       final String negatedComparison = ComparisonUtils.getNegatedComparison(binaryExpression.getOperationTokenType());
84       final PsiExpression lhs = binaryExpression.getLOperand();
85       final PsiExpression rhs = binaryExpression.getROperand();
86       assert rhs != null;
87       expString = lhs.getText() + negatedComparison + rhs.getText();
88     }
89     else {
90       if (ParenthesesUtils.getPrecedence(newExpression) > ParenthesesUtils.PREFIX_PRECEDENCE) {
91         expString = "!(" + newExpressionText + ')';
92       }
93       else {
94         expString = '!' + newExpressionText;
95       }
96     }
97     final PsiExpression newCall = factory.createExpressionFromText(expString, expression);
98     assert expressionToReplace != null;
99     final PsiElement insertedElement = expressionToReplace.replace(newCall);
100     final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
101     codeStyleManager.reformat(insertedElement);
102   }
103
104   protected static void replaceExpressionWithNegatedExpressionString(@NotNull String newExpression, @NotNull PsiExpression expression) {
105     final Project project = expression.getProject();
106     final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
107     final PsiElementFactory factory = psiFacade.getElementFactory();
108     PsiExpression expressionToReplace = expression;
109     final String expString;
110     if (BoolUtils.isNegated(expression)) {
111       expressionToReplace = BoolUtils.findNegation(expressionToReplace);
112       expString = newExpression;
113     }
114     else {
115       PsiElement parent = expressionToReplace.getParent();
116       while (parent instanceof PsiParenthesizedExpression) {
117         expressionToReplace = (PsiExpression)parent;
118         parent = parent.getParent();
119       }
120       expString = "!(" + newExpression + ')';
121     }
122     final PsiExpression newCall = factory.createExpressionFromText(expString, expression);
123     assert expressionToReplace != null;
124     final PsiElement insertedElement = expressionToReplace.replace(newCall);
125     final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
126     codeStyleManager.reformat(insertedElement);
127   }
128
129   protected static void addStatementBefore(String newStatementText, PsiReturnStatement anchor) {
130     final Project project = anchor.getProject();
131     final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
132     final PsiStatement newStatement = factory.createStatementFromText(newStatementText, anchor);
133     final PsiElement addedStatement = anchor.getParent().addBefore(newStatement, anchor);
134     CodeStyleManager.getInstance(project).reformat(addedStatement);
135   }
136
137   @Nullable
138   PsiElement findMatchingElement(@Nullable PsiElement element, Editor editor) {
139     while (element != null) {
140       if (!JavaLanguage.INSTANCE.equals(element.getLanguage())) {
141         break;
142       }
143       if (predicate instanceof PsiElementEditorPredicate) {
144         if (((PsiElementEditorPredicate)predicate).satisfiedBy(element, editor)) {
145           return element;
146         }
147       }
148       else if (predicate.satisfiedBy(element)) {
149         return element;
150       }
151       element = element.getParent();
152       if (element instanceof PsiFile) {
153         break;
154       }
155     }
156     return null;
157   }
158
159   @Override
160   public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
161     return findMatchingElement(element, editor) != null;
162   }
163
164   @Override
165   public boolean startInWriteAction() {
166     return true;
167   }
168
169   private String getPrefix() {
170     final Class<? extends Intention> aClass = getClass();
171     final String name = aClass.getSimpleName();
172     final StringBuilder buffer = new StringBuilder(name.length() + 10);
173     buffer.append(Character.toLowerCase(name.charAt(0)));
174     for (int i = 1; i < name.length(); i++) {
175       final char c = name.charAt(i);
176       if (Character.isUpperCase(c)) {
177         buffer.append('.');
178         buffer.append(Character.toLowerCase(c));
179       }
180       else {
181         buffer.append(c);
182       }
183     }
184     return buffer.toString();
185   }
186
187   @Override
188   @NotNull
189   public String getText() {
190     //noinspection UnresolvedPropertyKey
191     return IntentionPowerPackBundle.message(getPrefix() + ".name");
192   }
193
194   @NotNull
195   public String getFamilyName() {
196     //noinspection UnresolvedPropertyKey
197     return IntentionPowerPackBundle.defaultableMessage(getPrefix() + ".family.name");
198   }
199 }