blob: e60743c0ae711641e398a650d663596b18cc8f23 [file] [log] [blame]
/*
* Copyright 2000-2013 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.intention.impl;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class SurroundAutoCloseableAction extends PsiElementBaseIntentionAction {
@Override
public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) {
if (!element.getLanguage().isKindOf(JavaLanguage.INSTANCE)) return false;
if (!PsiUtil.getLanguageLevel(element).isAtLeast(LanguageLevel.JDK_1_7)) return false;
final PsiLocalVariable variable = PsiTreeUtil.getParentOfType(element, PsiLocalVariable.class);
if (variable == null) return false;
final PsiExpression initializer = variable.getInitializer();
if (initializer == null) return false;
final PsiElement declaration = variable.getParent();
if (!(declaration instanceof PsiDeclarationStatement)) return false;
final PsiElement codeBlock = declaration.getParent();
if (!(codeBlock instanceof PsiCodeBlock)) return false;
final PsiType type = variable.getType();
if (!(type instanceof PsiClassType)) return false;
final PsiClass aClass = ((PsiClassType)type).resolve();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
final PsiClass autoCloseable = facade.findClass(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, ProjectScope.getLibrariesScope(project));
if (!InheritanceUtil.isInheritorOrSelf(aClass, autoCloseable, true)) return false;
return true;
}
@Override
public void invoke(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) throws IncorrectOperationException {
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) {
return;
}
final PsiLocalVariable variable = PsiTreeUtil.getParentOfType(element, PsiLocalVariable.class);
if (variable == null) return;
final PsiExpression initializer = variable.getInitializer();
if (initializer == null) return;
final PsiElement declaration = variable.getParent();
if (!(declaration instanceof PsiDeclarationStatement)) return;
final PsiElement codeBlock = declaration.getParent();
if (!(codeBlock instanceof PsiCodeBlock)) return;
final LocalSearchScope scope = new LocalSearchScope(codeBlock);
PsiElement last = null;
for (PsiReference reference : ReferencesSearch.search(variable, scope).findAll()) {
final PsiElement usage = PsiTreeUtil.findPrevParent(codeBlock, reference.getElement());
if ((last == null || usage.getTextOffset() > last.getTextOffset())) {
last = usage;
}
}
final String text = "try (" + variable.getTypeElement().getText() + " " + variable.getName() + " = " + initializer.getText() + ") {}";
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
final PsiTryStatement armStatement = (PsiTryStatement)declaration.replace(factory.createStatementFromText(text, codeBlock));
List<PsiElement> toFormat = null;
if (last != null) {
final PsiElement first = armStatement.getNextSibling();
if (first != null) {
toFormat = moveStatements(first, last, armStatement);
}
}
final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
final PsiElement formattedElement = codeStyleManager.reformat(armStatement);
if (toFormat != null) {
for (PsiElement psiElement : toFormat) {
codeStyleManager.reformat(psiElement);
}
}
if (last == null) {
final PsiCodeBlock tryBlock = ((PsiTryStatement)formattedElement).getTryBlock();
if (tryBlock != null) {
final PsiJavaToken brace = tryBlock.getLBrace();
if (brace != null) {
editor.getCaretModel().moveToOffset(brace.getTextOffset() + 1);
}
}
}
}
private static List<PsiElement> moveStatements(@NotNull PsiElement first, PsiElement last, PsiTryStatement statement) {
PsiCodeBlock tryBlock = statement.getTryBlock();
assert tryBlock != null : statement.getText();
PsiElement parent = statement.getParent();
List<PsiElement> toFormat = new SmartList<PsiElement>();
PsiElement stopAt = last.getNextSibling();
for (PsiElement child = first; child != null && child != stopAt; child = child.getNextSibling()) {
if (!(child instanceof PsiDeclarationStatement)) continue;
PsiElement anchor = child;
for (PsiElement declared : ((PsiDeclarationStatement)child).getDeclaredElements()) {
if (!(declared instanceof PsiLocalVariable)) continue;
final int endOffset = last.getTextRange().getEndOffset();
boolean contained = ReferencesSearch.search(declared, new LocalSearchScope(parent)).forEach(new Processor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
return reference.getElement().getTextOffset() <= endOffset;
}
});
if (!contained) {
PsiLocalVariable var = (PsiLocalVariable)declared;
PsiElementFactory factory = JavaPsiFacade.getElementFactory(statement.getProject());
String name = var.getName();
assert name != null : child.getText();
toFormat.add(parent.addBefore(factory.createVariableDeclarationStatement(name, var.getType(), null), statement));
PsiExpression varInit = var.getInitializer();
assert varInit != null : child.getText();
String varAssignText = name + " = " + varInit.getText() + ";";
anchor = parent.addAfter(factory.createStatementFromText(varAssignText, parent), anchor);
var.delete();
}
}
if (child == last && !child.isValid()) {
last = anchor;
}
}
tryBlock.addRangeBefore(first, last, tryBlock.getRBrace());
parent.deleteChildRange(first, last);
return toFormat;
}
@NotNull
@Override
public String getFamilyName() {
return CodeInsightBundle.message("intention.surround.resource.with.ARM.block");
}
@NotNull
@Override
public String getText() {
return getFamilyName();
}
}