update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / quickfix / DeferFinalAssignmentFix.java
blob74f3a1ffcffa17740872e4caac580deacecdd10a
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 /**
18 * Created by IntelliJ IDEA.
19 * User: cdr
20 * Date: Nov 19, 2002
21 * Time: 12:03:39 PM
22 * To change this template use Options | File Templates.
24 package com.intellij.codeInsight.daemon.impl.quickfix;
26 import com.intellij.codeInsight.CodeInsightUtilBase;
27 import com.intellij.codeInsight.daemon.QuickFixBundle;
28 import com.intellij.codeInsight.intention.IntentionAction;
29 import com.intellij.openapi.diagnostic.Logger;
30 import com.intellij.openapi.editor.Editor;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.psi.*;
33 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
34 import com.intellij.psi.controlFlow.*;
35 import com.intellij.psi.util.PsiTreeUtil;
36 import com.intellij.psi.util.PsiUtil;
37 import com.intellij.util.IncorrectOperationException;
39 import java.util.ArrayList;
40 import java.util.List;
42 import org.jetbrains.annotations.NotNull;
44 public class DeferFinalAssignmentFix implements IntentionAction {
45 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.DeferFinalAssignmentFix");
47 private final PsiVariable variable;
48 private final PsiReferenceExpression expression;
50 public DeferFinalAssignmentFix(PsiVariable variable, PsiReferenceExpression expression) {
51 this.variable = variable;
52 this.expression = expression;
55 @NotNull
56 public String getFamilyName() {
57 return QuickFixBundle.message("defer.final.assignment.with.temp.family");
60 @NotNull
61 public String getText() {
62 return QuickFixBundle.message("defer.final.assignment.with.temp.text", variable.getName());
65 public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
66 if (!CodeInsightUtilBase.prepareFileForWrite(variable.getContainingFile())) return;
68 if (variable instanceof PsiField) {
69 deferField((PsiField)variable);
71 else {
72 deferLocalVariable((PsiLocalVariable)variable);
76 private void deferField(PsiField field) throws IncorrectOperationException {
77 PsiCodeBlock codeBlock = getEnclosingCodeBlock(field, expression);
78 if (codeBlock == null) return;
79 deferVariable(codeBlock, field, null);
82 private PsiCodeBlock getEnclosingCodeBlock(PsiField field, PsiElement element) {
83 PsiClass aClass = field.getContainingClass();
84 if (aClass == null) return null;
85 PsiMethod[] constructors = aClass.getConstructors();
86 for (PsiMethod constructor : constructors) {
87 PsiCodeBlock body = constructor.getBody();
88 if (body == null) continue;
89 if (PsiTreeUtil.isAncestor(body, element, true)) return body;
92 //maybe inside class initalizer ?
93 PsiClassInitializer[] initializers = aClass.getInitializers();
94 for (PsiClassInitializer initializer : initializers) {
95 PsiCodeBlock body = initializer.getBody();
96 if (PsiTreeUtil.isAncestor(body, element, true)) return body;
98 return null;
101 private void deferLocalVariable(PsiLocalVariable variable) throws IncorrectOperationException {
102 PsiElement outerCodeBlock = PsiUtil.getVariableCodeBlock(variable, null);
103 deferVariable(outerCodeBlock, variable, variable.getParent());
106 private void deferVariable(PsiElement outerCodeBlock, PsiVariable variable, PsiElement tempDeclarationAnchor) throws IncorrectOperationException {
107 if (outerCodeBlock == null) return;
108 List<PsiReferenceExpression> outerReferences = new ArrayList<PsiReferenceExpression>();
109 collectReferences(outerCodeBlock, variable, outerReferences);
111 PsiElementFactory factory = JavaPsiFacade.getInstance(variable.getProject()).getElementFactory();
112 Project project = variable.getProject();
113 String tempName = suggestNewName(project, variable);
114 PsiDeclarationStatement tempVariableDeclaration = factory.createVariableDeclarationStatement(tempName, variable.getType(), null);
116 ControlFlow controlFlow;
117 try {
118 controlFlow = ControlFlowFactory.getInstance(project).getControlFlow(outerCodeBlock, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
120 catch (AnalysisCanceledException e) {
121 return;
123 int minOffset = 0;
124 boolean writeReferenceOccurred = false;
125 PsiReferenceExpression writeReference = null;
126 for (int i = outerReferences.size()-1; i>=0; i--) {
127 PsiReferenceExpression reference = outerReferences.get(i);
128 if (!writeReferenceOccurred && !PsiUtil.isAccessedForWriting(reference)) {
129 // trailing read references need not be converted to temp var references
130 outerReferences.remove(i);
131 continue;
133 writeReferenceOccurred = true;
134 writeReference = reference;
135 PsiElement element = PsiUtil.getEnclosingStatement(reference);
136 int endOffset = element == null ? -1 : controlFlow.getEndOffset(element);
137 minOffset = Math.max(minOffset, endOffset);
139 LOG.assertTrue(writeReference != null);
140 PsiStatement finalAssignment = factory.createStatementFromText(writeReference.getText()+" = "+tempName+";", outerCodeBlock);
141 if (!insertToDefinitelyReachedPlace(outerCodeBlock, finalAssignment, controlFlow, minOffset, outerReferences)) return;
143 outerCodeBlock.addAfter(tempVariableDeclaration, tempDeclarationAnchor);
145 replaceReferences(outerReferences, factory.createExpressionFromText(tempName, outerCodeBlock));
149 private boolean insertToDefinitelyReachedPlace(PsiElement codeBlock, PsiStatement finalAssignment, ControlFlow controlFlow, int minOffset, List references) throws IncorrectOperationException {
150 int offset = ControlFlowUtil.getMinDefinitelyReachedOffset(controlFlow, minOffset, references);
151 if (offset == controlFlow.getSize()) {
152 codeBlock.add(finalAssignment);
153 return true;
155 PsiElement element = null; //controlFlow.getEndOffset(codeBlock) == offset ? getEnclosingStatement(controlFlow.getElement(offset)) : null;
156 while (offset < controlFlow.getSize()) {
157 element = controlFlow.getElement(offset);
158 if (element != null) element = PsiUtil.getEnclosingStatement(element);
159 int startOffset = controlFlow.getStartOffset(element);
160 if (startOffset != -1 && startOffset >= minOffset && element instanceof PsiStatement) break;
161 offset++;
163 if (!(offset < controlFlow.getSize())) return false;
164 // inside loop
165 if (ControlFlowUtil.isInstructionReachable(controlFlow, offset, offset)) return false;
166 codeBlock.addBefore(finalAssignment, element);
167 return true;
170 private static void replaceReferences(List references, PsiElement newExpression) throws IncorrectOperationException {
171 for (Object reference1 : references) {
172 PsiElement reference = (PsiElement)reference1;
173 reference.replace(newExpression);
179 private static void collectReferences(PsiElement context, final PsiVariable variable, final List<PsiReferenceExpression> references) {
180 context.accept(new JavaRecursiveElementWalkingVisitor() {
181 @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
182 if (expression.resolve() == variable) references.add(expression);
183 super.visitReferenceExpression(expression);
188 private static String suggestNewName(Project project, PsiVariable variable) {
189 // new name should not conflict with another variable at the variable declaration level and usage level
190 String name = variable.getName();
191 // trim last digit to suggest variable names like i1,i2, i3...
192 if (name.length() > 1 && Character.isDigit(name.charAt(name.length()-1))) {
193 name = name.substring(0,name.length()-1);
195 return JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(name, variable, true);
198 public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
199 return
200 variable != null
201 && variable.isValid()
202 && !(variable instanceof PsiParameter)
203 && !(variable instanceof ImplicitVariable)
204 && expression != null
205 && expression.isValid()
206 && variable.getManager().isInProject(variable)
210 public boolean startInWriteAction() {
211 return true;