diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java new file mode 100644 index 000000000000..6ff4d75d7d10 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2014 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.template.postfix.templates; + +import com.intellij.codeInsight.ExceptionUtil; +import com.intellij.codeInsight.intention.impl.TypeExpression; +import com.intellij.codeInsight.template.Template; +import com.intellij.codeInsight.template.TemplateManager; +import com.intellij.codeInsight.template.impl.MacroCallNode; +import com.intellij.codeInsight.template.impl.TextExpression; +import com.intellij.codeInsight.template.macro.SuggestVariableNameMacro; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.search.ProjectScope; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; + + +public class TryWithResourcesPostfixTemplate extends PostfixTemplate { + protected TryWithResourcesPostfixTemplate() { + super("twr", "Description", "Example"); + } + + @Override + public boolean isApplicable(@NotNull PsiElement element, @NotNull Document copyDocument, int newOffset) { + if (!PsiUtil.isLanguageLevel7OrHigher(element)) return false; + + PsiExpression initializer = getTopmostExpression(element); + + if (initializer == null) return false; + + final PsiType type = initializer.getType(); + if (!(type instanceof PsiClassType)) return false; + final PsiClass aClass = ((PsiClassType)type).resolve(); + Project project = element.getProject(); + 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 expand(@NotNull PsiElement context, @NotNull Editor editor) { + PsiExpression expression = getTopmostExpression(context); + assert expression != null; + + Project project = context.getProject(); + + editor.getDocument().deleteString(expression.getTextRange().getStartOffset(), expression.getTextRange().getEndOffset()); + + TemplateManager manager = TemplateManager.getInstance(project); + Template template = manager.createTemplate("", ""); + template.setToReformat(true); + template.addTextSegment("try ("); + MacroCallNode name = new MacroCallNode(new SuggestVariableNameMacro()); + + template.addVariable("type", new TypeExpression(project, new PsiType[]{expression.getType()}), false); + template.addTextSegment(" "); + template.addVariable("name", name, name, true); + template.addTextSegment(" = "); + template.addVariable("variable", new TextExpression(expression.getText()), false); + template.addTextSegment(") {\n"); + template.addEndVariable(); + template.addTextSegment("\n}"); + + Collection unhandled = getUnhandled(expression); + for (PsiClassType exception : unhandled) { + MacroCallNode variable = new MacroCallNode(new SuggestVariableNameMacro()); + template.addTextSegment("catch("); + template.addVariable("type " + exception.getClassName(), new TypeExpression(project, new PsiType[]{exception}), false); + template.addTextSegment(" "); + template.addVariable("name " + exception.getClassName(), variable, variable, false); + template.addTextSegment(") {}"); + } + + manager.startTemplate(editor, template); + } + + @NotNull + private static Collection getUnhandled(@NotNull PsiExpression expression) { + assert expression.getType() != null; + PsiMethod methodCloser = PsiUtil.getResourceCloserMethodForType((PsiClassType)expression.getType(), expression.getProject()); + PsiSubstitutor substitutor = PsiUtil.resolveGenericsClassInType(expression.getType()).getSubstitutor(); + + return methodCloser != null + ? ExceptionUtil.getUnhandledExceptions(methodCloser, expression, null, substitutor) + : Collections.emptyList(); + } +} diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 2fd2f9b6e8bc..f6b2bbe46940 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -998,10 +998,14 @@ public final class PsiUtil extends PsiUtilCore { public static PsiMethod getResourceCloserMethod(@NotNull final PsiResourceVariable resource) { final PsiType resourceType = resource.getType(); if (!(resourceType instanceof PsiClassType)) return null; - final PsiClass resourceClass = ((PsiClassType)resourceType).resolve(); + return getResourceCloserMethodForType((PsiClassType)resourceType, resource.getProject()); + } + + @Nullable + public static PsiMethod getResourceCloserMethodForType(@NotNull final PsiClassType resourceType, Project project) { + final PsiClass resourceClass = resourceType.resolve(); if (resourceClass == null) return null; - final Project project = resource.getProject(); final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); final PsiClass autoCloseable = facade.findClass(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, ProjectScope.getLibrariesScope(project)); if (autoCloseable == null) return null; diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java index ada64c0d7452..b989c3054b06 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java @@ -537,7 +537,7 @@ public class ExceptionUtil { } @NotNull - private static List getUnhandledExceptions(@Nullable PsiMethod method, + public static List getUnhandledExceptions(@Nullable PsiMethod method, PsiElement element, PsiElement topElement, @NotNull PsiSubstitutor substitutor) { diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simple.java b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simple.java new file mode 100644 index 000000000000..152b358b127c --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simple.java @@ -0,0 +1,12 @@ +import java.lang.AutoCloseable + +public class Foo { + void m() { + getStream().twr + } + + AutoCloseable getStream() + { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleNotAutoCloseable.java b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleNotAutoCloseable.java new file mode 100644 index 000000000000..3b4e51617655 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleNotAutoCloseable.java @@ -0,0 +1,12 @@ +import java.lang.AutoCloseable + +public class Foo { + void m() { + getStream().twr + } + + Object getStream() + { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleNotAutoCloseable_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleNotAutoCloseable_after.java new file mode 100644 index 000000000000..868f54ab96e5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleNotAutoCloseable_after.java @@ -0,0 +1,12 @@ +import java.lang.AutoCloseable + +public class Foo { + void m() { + getStream().twr + } + + Object getStream() + { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithConflict.java b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithConflict.java new file mode 100644 index 000000000000..c23feda2ca8e --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithConflict.java @@ -0,0 +1,17 @@ +import java.lang.AutoCloseable + +public class Foo { + void m() { + getStream().twr + } + + AutoCloseable getStream() + { + return null; + } + + private class Exception extends java.lang.Exception + { + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithConflict_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithConflict_after.java new file mode 100644 index 000000000000..fff4f9dfa135 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithConflict_after.java @@ -0,0 +1,20 @@ +import java.lang.AutoCloseable + +public class Foo { + void m() { + try (AutoCloseable stream = getStream()) { + + } catch (java.lang.Exception e) { + } + } + + AutoCloseable getStream() + { + return null; + } + + private class Exception extends java.lang.Exception + { + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithMyException.java b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithMyException.java new file mode 100644 index 000000000000..dca1e61f9bb8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithMyException.java @@ -0,0 +1,23 @@ +import java.lang.AutoCloseable +import java.lang.Exception; + +public class Foo { + void m() { + getStream().twr + } + + MyStream getStream() + { + return null; + } + + private class MyStream implements AutoCloseable + { + public void close() throws MyException {} + } + + private class MyException extends Exception + { + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithMyException_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithMyException_after.java new file mode 100644 index 000000000000..d820e55355ed --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simpleWithMyException_after.java @@ -0,0 +1,26 @@ +import java.lang.AutoCloseable +import java.lang.Exception; + +public class Foo { + void m() { + try (MyStream stream = getStream()) { + + } catch (MyException e) { + } + } + + MyStream getStream() + { + return null; + } + + private class MyStream implements AutoCloseable + { + public void close() throws MyException {} + } + + private class MyException extends Exception + { + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simple_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simple_after.java new file mode 100644 index 000000000000..f8d33d92c815 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/twr/simple_after.java @@ -0,0 +1,15 @@ +import java.lang.AutoCloseable + +public class Foo { + void m() { + try (AutoCloseable stream = getStream()) { + + } catch (Exception e) { + } + } + + AutoCloseable getStream() + { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateTestCase.java b/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateTestCase.java index 8960088fce80..33e8447a1b9f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateTestCase.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateTestCase.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2014 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.template.postfix.templates; import com.intellij.JavaTestUtil; @@ -37,5 +52,7 @@ abstract public class PostfixTemplateTestCase extends LightCodeInsightFixtureTes myFixture.addClass("package java.util;\n" + "public class ArrayList extends AbstractList\n" + " implements List, Iterable, RandomAccess, Cloneable, java.io.Serializable {}"); + myFixture.addClass("package java.lang;\n" + + "public interface AutoCloseable {}"); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplateTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplateTest.java new file mode 100644 index 000000000000..bdaed7fbc116 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplateTest.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2014 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.template.postfix.templates; + +import com.intellij.openapi.roots.LanguageLevelProjectExtension; +import com.intellij.pom.java.LanguageLevel; +import org.jetbrains.annotations.NotNull; + + +public class TryWithResourcesPostfixTemplateTest extends PostfixTemplateTestCase { + + @NotNull + @Override + protected String getSuffix() { + return "twr"; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_7); + } + + public void testSimple() { + doTest(); + } + + public void testSimpleWithMyException() { + doTest(); + } + + public void testSimpleWithConflict() { + doTest(); + } + + public void testSimpleNotAutoCloseable() { + doTest(); + } +} diff --git a/resources/src/META-INF/PostfixTemplates.xml b/resources/src/META-INF/PostfixTemplates.xml index af5d57ecef32..90294e0d619c 100644 --- a/resources/src/META-INF/PostfixTemplates.xml +++ b/resources/src/META-INF/PostfixTemplates.xml @@ -26,6 +26,7 @@ +