blob: 5637c925a4d4f2e0e7d467fd30b4b7ea6aa6accf [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.*;
import com.intellij.util.IncorrectOperationException;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* @author ven
*/
public class BringVariableIntoScopeFix implements IntentionAction {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.BringVariableIntoScopeFix");
private final PsiReferenceExpression myUnresolvedReference;
private PsiLocalVariable myOutOfScopeVariable;
public BringVariableIntoScopeFix(PsiReferenceExpression unresolvedReference) {
myUnresolvedReference = unresolvedReference;
}
@Override
@NotNull
public String getText() {
PsiLocalVariable variable = myOutOfScopeVariable;
String varText = variable == null ? "" : PsiFormatUtil.formatVariable(variable, PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_TYPE, PsiSubstitutor.EMPTY);
return QuickFixBundle.message("bring.variable.to.scope.text", varText);
}
@Override
@NotNull
public String getFamilyName() {
return QuickFixBundle.message("bring.variable.to.scope.family");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
if (!(file instanceof PsiJavaFile)) return false;
if (myUnresolvedReference.isQualified()) return false;
final String referenceName = myUnresolvedReference.getReferenceName();
if (referenceName == null) return false;
PsiManager manager = file.getManager();
if (!myUnresolvedReference.isValid() || !manager.isInProject(myUnresolvedReference)) return false;
PsiElement container = PsiTreeUtil.getParentOfType(myUnresolvedReference, PsiCodeBlock.class, PsiClass.class);
if (!(container instanceof PsiCodeBlock)) return false;
myOutOfScopeVariable = null;
while(container.getParent() instanceof PsiStatement || container.getParent() instanceof PsiCatchSection) container = container.getParent();
container.accept(new JavaRecursiveElementWalkingVisitor() {
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {}
@Override public void visitExpression(PsiExpression expression) {
//Don't look inside expressions
}
@Override public void visitLocalVariable(PsiLocalVariable variable) {
if (referenceName.equals(variable.getName())) {
if (myOutOfScopeVariable == null) {
myOutOfScopeVariable = variable;
}
else {
myOutOfScopeVariable = null; //2 conflict variables
}
}
}
});
return myOutOfScopeVariable != null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
LOG.assertTrue(myOutOfScopeVariable != null);
PsiManager manager = file.getManager();
myOutOfScopeVariable.normalizeDeclaration();
PsiUtil.setModifierProperty(myOutOfScopeVariable, PsiModifier.FINAL, false);
PsiElement commonParent = PsiTreeUtil.findCommonParent(myOutOfScopeVariable, myUnresolvedReference);
LOG.assertTrue(commonParent != null);
PsiElement child = myOutOfScopeVariable.getTextRange().getStartOffset() < myUnresolvedReference.getTextRange().getStartOffset() ? myOutOfScopeVariable
: myUnresolvedReference;
while(child.getParent() != commonParent) child = child.getParent();
PsiDeclarationStatement newDeclaration = (PsiDeclarationStatement)JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createStatementFromText("int i = 0", null);
PsiVariable variable = (PsiVariable)newDeclaration.getDeclaredElements()[0].replace(myOutOfScopeVariable);
if (variable.getInitializer() != null) {
variable.getInitializer().delete();
}
while(!(child instanceof PsiStatement) || !(child.getParent() instanceof PsiCodeBlock)) {
child = child.getParent();
commonParent = commonParent.getParent();
}
LOG.assertTrue(commonParent != null);
PsiDeclarationStatement added = (PsiDeclarationStatement)commonParent.addBefore(newDeclaration, child);
final PsiElement[] declaredElements = added.getDeclaredElements();
LOG.assertTrue(declaredElements.length > 0, added.getText());
PsiLocalVariable addedVar = (PsiLocalVariable)declaredElements[0];
CodeStyleManager.getInstance(manager.getProject()).reformat(commonParent);
//Leave initializer assignment
PsiExpression initializer = myOutOfScopeVariable.getInitializer();
if (initializer != null) {
PsiExpressionStatement assignment = (PsiExpressionStatement)JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createStatementFromText(myOutOfScopeVariable
.getName() + "= e;", null);
((PsiAssignmentExpression)assignment.getExpression()).getRExpression().replace(initializer);
assignment = (PsiExpressionStatement)CodeStyleManager.getInstance(manager.getProject()).reformat(assignment);
PsiDeclarationStatement declStatement = PsiTreeUtil.getParentOfType(myOutOfScopeVariable, PsiDeclarationStatement.class);
LOG.assertTrue(declStatement != null);
PsiElement parent = declStatement.getParent();
if (parent instanceof PsiForStatement) {
declStatement.replace(assignment);
}
else {
parent.addAfter(assignment, declStatement);
}
}
if (myOutOfScopeVariable.isValid()) {
myOutOfScopeVariable.delete();
}
if (HighlightControlFlowUtil.checkVariableInitializedBeforeUsage(myUnresolvedReference, addedVar, new THashMap<PsiElement, Collection<PsiReferenceExpression>>(),file) != null) {
initialize(addedVar);
}
DaemonCodeAnalyzer.getInstance(project).updateVisibleHighlighters(editor);
}
private static void initialize(final PsiLocalVariable variable) throws IncorrectOperationException {
PsiType type = variable.getType();
String init = PsiTypesUtil.getDefaultValueOfType(type);
PsiElementFactory factory = JavaPsiFacade.getInstance(variable.getProject()).getElementFactory();
PsiExpression initializer = factory.createExpressionFromText(init, variable);
variable.setInitializer(initializer);
}
@Override
public boolean startInWriteAction() {
return true;
}
}