Try-with-resource support: reuse variable quick fix

This commit is contained in:
Roman Shevchenko
2011-02-22 20:33:23 +01:00
parent d9bce4699a
commit 2bc8375a90
7 changed files with 125 additions and 45 deletions
@@ -27,21 +27,19 @@ import com.intellij.psi.scope.processor.VariablesNotProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
* User: cdr
* @author cdr
* Date: Nov 20, 2002
* Time: 3:01:25 PM
* To change this template use Options | File Templates.
*/
public class ReuseVariableDeclarationFix implements IntentionAction {
private final PsiVariable variable;
private final PsiIdentifier identifier;
private final PsiVariable myVariable;
private final PsiIdentifier myIdentifier;
public ReuseVariableDeclarationFix(PsiVariable variable, PsiIdentifier identifier) {
this.variable = variable;
this.identifier = identifier;
public ReuseVariableDeclarationFix(final PsiVariable variable, final PsiIdentifier identifier) {
this.myVariable = variable;
this.myIdentifier = identifier;
}
@NotNull
@@ -51,55 +49,63 @@ public class ReuseVariableDeclarationFix implements IntentionAction {
@NotNull
public String getText() {
return QuickFixBundle.message("reuse.variable.declaration.text", variable.getName());
return QuickFixBundle.message("reuse.variable.declaration.text", myVariable.getName());
}
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
PsiVariable previousVariable = findPreviousVariable();
return
variable != null
&& variable.isValid()
&& variable instanceof PsiLocalVariable
&& previousVariable != null
&& Comparing.equal(previousVariable.getType(), variable.getType())
&& identifier != null
&& identifier.isValid()
&& variable.getManager().isInProject(variable);
public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) {
final PsiVariable previousVariable = findPreviousVariable();
return myVariable != null &&
myVariable.isValid() &&
myVariable instanceof PsiLocalVariable &&
!(myVariable.getParent() instanceof PsiResource && myVariable.getInitializer() == null) &&
previousVariable != null &&
Comparing.equal(previousVariable.getType(), myVariable.getType()) &&
myIdentifier != null &&
myIdentifier.isValid() &&
myVariable.getManager().isInProject(myVariable);
}
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
PsiVariable refVariable = findPreviousVariable();
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
final PsiVariable refVariable = findPreviousVariable();
if (refVariable == null) return;
if (!CodeInsightUtil.preparePsiElementsForWrite(variable, refVariable)) return;
PsiUtil.setModifierProperty(refVariable, PsiModifier.FINAL, false);
if (variable.getInitializer() == null) {
variable.delete();
if (!CodeInsightUtil.preparePsiElementsForWrite(myVariable, refVariable)) return;
final PsiExpression initializer = myVariable.getInitializer();
if (initializer == null) {
myVariable.delete();
return;
}
PsiDeclarationStatement declaration = (PsiDeclarationStatement) variable.getParent();
PsiElementFactory factory = JavaPsiFacade.getInstance(variable.getProject()).getElementFactory();
PsiStatement statement = factory.createStatementFromText(variable.getName() + " = " + variable.getInitializer().getText()+";", variable);
declaration.replace(statement);
PsiUtil.setModifierProperty(refVariable, PsiModifier.FINAL, false);
final PsiElementFactory factory = JavaPsiFacade.getInstance(myVariable.getProject()).getElementFactory();
final PsiElement replacement;
final PsiElement parent = myVariable.getParent();
if (parent instanceof PsiResource) {
replacement = factory.createResourceFromText(myVariable.getName() + " = " + initializer.getText(), null);
}
else {
replacement = factory.createStatementFromText(myVariable.getName() + " = " + initializer.getText() + ";", null);
}
parent.replace(replacement);
}
@Nullable
private PsiVariable findPreviousVariable() {
PsiElement scope = variable.getParent();
PsiElement scope = myVariable.getParent();
while (scope != null) {
if (scope instanceof PsiFile || scope instanceof PsiMethod || scope instanceof PsiClassInitializer) break;
scope = scope.getParent();
}
if (scope == null) return null;
VariablesNotProcessor proc = new VariablesNotProcessor(variable, false);
PsiScopesUtil.treeWalkUp(proc, identifier, scope);
if(proc.size() > 0)
return proc.getResult(0);
return null;
final VariablesNotProcessor proc = new VariablesNotProcessor(myVariable, false);
PsiScopesUtil.treeWalkUp(proc, myIdentifier, scope);
return proc.size() > 0 ? proc.getResult(0) : null;
}
public boolean startInWriteAction() {
return true;
}
}
@@ -64,6 +64,13 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
};
private static final JavaParserUtil.ParserWrapper RESOURCE = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
DeclarationParser.parseResource(builder);
}
};
private static final JavaParserUtil.ParserWrapper TYPE = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
@@ -162,6 +169,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiAnnotation createAnnotationFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, ANNOTATION, false), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
@@ -172,16 +180,19 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiDocTag createDocTagFromText(@NotNull final String text) throws IncorrectOperationException {
return createDocCommentFromText(join("/**\n", text, "\n */")).getTags()[0];
}
@NotNull
@Override
public PsiDocTag createDocTagFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
return createDocTagFromText(text);
}
@NotNull
@Override
public PsiDocComment createDocCommentFromText(@NotNull final String text) throws IncorrectOperationException {
final PsiMethod method = createMethodFromText(join(text, "void m();"), null);
final PsiDocComment comment = method.getDocComment();
@@ -190,11 +201,13 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiDocComment createDocCommentFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
return createDocCommentFromText(text);
}
@NotNull
@Override
public PsiClass createClassFromText(@NotNull final String body, final PsiElement context) throws IncorrectOperationException {
final PsiJavaFile aFile = createDummyJavaFile(join("class _Dummy_ { ", body, " }"));
final PsiClass[] classes = aFile.getClasses();
@@ -205,6 +218,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiField createFieldFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, DECLARATION, false), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
@@ -215,6 +229,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiMethod createMethodFromText(@NotNull final String text, final PsiElement context, final LanguageLevel level) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, DECLARATION, false, level), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
@@ -225,12 +240,14 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public final PsiMethod createMethodFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final LanguageLevel level = LanguageLevelProjectExtension.getInstance(myManager.getProject()).getLanguageLevel();
return createMethodFromText(text, context, level);
}
@NotNull
@Override
public PsiParameter createParameterFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, PARAMETER, false), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
@@ -241,11 +258,24 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiResource createResourceFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, RESOURCE, false), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
if (!(element instanceof PsiResource)) {
throw new IncorrectOperationException("Incorrect resource \"" + text + "\".");
}
return (PsiResource)element;
}
@NotNull
@Override
public PsiType createTypeFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
return createTypeInner(text, context, false);
}
@NotNull
@Override
public PsiTypeElement createTypeElementFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final boolean multiCatch = context instanceof PsiParameter &&
context.getParent() instanceof PsiCatchSection &&
@@ -271,6 +301,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiJavaCodeReferenceElement createReferenceFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final boolean isStaticImport = context instanceof PsiImportStaticStatement &&
!((PsiImportStaticStatement)context).isOnDemand();
@@ -284,6 +315,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiCodeBlock createCodeBlockFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, CODE_BLOCK, true), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
@@ -294,6 +326,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiStatement createStatementFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, STATEMENT, false), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
@@ -304,6 +337,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiExpression createExpressionFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, EXPRESSION, false), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
@@ -320,6 +354,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiTypeParameter createTypeParameterFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, TYPE_PARAMETER, false), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
@@ -330,6 +365,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiComment createCommentFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final PsiJavaFile aFile = createDummyJavaFile(text);
for (PsiElement aChildren : aFile.getChildren()) {
@@ -347,6 +383,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiEnumConstant createEnumConstantFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, ENUM_CONSTANT, false), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
@@ -357,6 +394,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
}
@NotNull
@Override
public PsiCatchSection createCatchSection(@NotNull final PsiClassType exceptionType, @NotNull final String exceptionName,
final PsiElement context) throws IncorrectOperationException {
final String text = join("catch (", exceptionType.getCanonicalText(), " ", exceptionName, ") {}");
@@ -396,6 +434,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ
psiCatchSection.getCatchBlock().replace(codeBlockFromText);
}
@Override
public PsiType createPrimitiveType(@NotNull final String text, @NotNull final PsiAnnotation[] annotations) throws IncorrectOperationException {
final PsiPrimitiveType primitiveType = getPrimitiveType(text);
if (primitiveType == null) {
@@ -61,7 +61,7 @@ public class PsiResourceImpl extends CompositePsiElement implements PsiResource
}
public String getName() {
final PsiElement element = getResourceElement();
final PsiElement element = getFirstChild();
if (element instanceof PsiLocalVariable) return ((PsiLocalVariable)element).getName();
if (element instanceof PsiAssignmentExpression) return ((PsiAssignmentExpression)element).getLExpression().toString();
return "";
@@ -0,0 +1,14 @@
// "Reuse previous variable 'r' declaration" "true"
import java.io.*;
class a {
static class MyResource implements AutoCloseable {
public void close() { }
}
void m() {
MyResource r;
try (r = new MyResource()) {
}
}
}
@@ -0,0 +1,14 @@
// "Reuse previous variable 'r' declaration" "true"
import java.io.*;
class a {
static class MyResource implements AutoCloseable {
public void close() { }
}
void m() {
MyResource r;
try (MyResource <caret>r = new MyResource()) {
}
}
}
@@ -1,9 +1,7 @@
package com.intellij.codeInsight.daemon.quickFix;
public class ReuseVariableDeclarationtTest extends LightQuickFixTestCase {
public class ReuseVariableDeclarationTest extends LightQuickFixTestCase {
public void test() throws Exception { doAllTests(); }
@@ -11,6 +9,4 @@ public class ReuseVariableDeclarationtTest extends LightQuickFixTestCase {
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/reuseVariableDeclaration";
}
}
@@ -116,6 +116,17 @@ public interface PsiJavaParserFacade extends PsiParserFacade {
@NotNull
PsiParameter createParameterFromText(@NotNull @NonNls String text, PsiElement context) throws IncorrectOperationException;
/**
* Creates a Java try-resource from the specified text.
*
* @param text the text of the resource to create.
* @param context the PSI element used as context for resolving references from the resource.
* @return the created resource instance.
* @throws com.intellij.util.IncorrectOperationException if the text is not a valid resource definition.
*/
@NotNull
PsiResource createResourceFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException;
/**
* Creates a Java type from the specified text.
*