diff --git a/plugins/IntentionPowerPak/src/META-INF/plugin.xml b/plugins/IntentionPowerPak/src/META-INF/plugin.xml
index 878e3435132a..fe23d3c568a2 100644
--- a/plugins/IntentionPowerPak/src/META-INF/plugin.xml
+++ b/plugins/IntentionPowerPak/src/META-INF/plugin.xml
@@ -390,6 +390,10 @@
com.siyeh.ipp.exceptions.ReplaceArmWithTryFinallyIntention
intention.category.other
+
+ com.siyeh.ipp.exceptions.MergeNestedTryStatementsIntention
+ intention.category.other
+
com.siyeh.ipp.exceptions.ObscureThrownExceptionsIntention
intention.category.other
diff --git a/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties b/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties
index 636391392211..82381bfe9db5 100644
--- a/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties
+++ b/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties
@@ -151,6 +151,8 @@ split.multicatch.intention.name=Split multi-catch into separate 'catch' blocks
split.multicatch.intention.family.name=Split Multi-Catch into Separate Catch Blocks
replace.arm.with.try.finally.intention.name=Replace Automatic Resource Management with 'try finally'
replace.arm.with.try.finally.intention.family.name=Replace Automatic Resource Management with Try-Finally
+merge.nested.try.statements.intention.name=Merge nested try statements
+merge.nested.try.statements.intention.family.name=Merge Nest Try Statements
obscure.thrown.exceptions.intention.family.name=Replace Exceptions in Throws Clause with Single More General Exception
add.array.creation.expression.intention.family.name=Add Array Creation Expression
replace.diamond.with.explicit.type.arguments.intention.name=Replace '<>' with explicit type arguments
diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/MergeNestedTryStatementsIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/MergeNestedTryStatementsIntention.java
new file mode 100644
index 000000000000..18f85334023d
--- /dev/null
+++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/MergeNestedTryStatementsIntention.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2000-2012 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.siyeh.ipp.exceptions;
+
+import com.intellij.psi.*;
+import com.intellij.util.IncorrectOperationException;
+import com.siyeh.ipp.base.Intention;
+import com.siyeh.ipp.base.PsiElementPredicate;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author Bas Leijdekkers
+ */
+public class MergeNestedTryStatementsIntention extends Intention {
+
+ @NotNull
+ @Override
+ protected PsiElementPredicate getElementPredicate() {
+ return new NestedTryStatementsPredicate();
+ }
+
+ @Override
+ protected void processIntention(@NotNull PsiElement element) throws IncorrectOperationException {
+ final PsiTryStatement tryStatement1 = (PsiTryStatement)element.getParent();
+ final StringBuilder newTryStatement = new StringBuilder("try (");
+ final PsiResourceList list1 = tryStatement1.getResourceList();
+ if (list1 == null) {
+ return;
+ }
+ final List variables1 = list1.getResourceVariables();
+ boolean semicolon = false;
+ for (PsiResourceVariable variable : variables1) {
+ if (semicolon) {
+ newTryStatement.append(';');
+ } else {
+ semicolon = true;
+ }
+ newTryStatement.append(variable.getText());
+ }
+ final PsiCodeBlock tryBlock1 = tryStatement1.getTryBlock();
+ if (tryBlock1 == null) {
+ return;
+ }
+ final PsiStatement[] statements = tryBlock1.getStatements();
+ final PsiTryStatement tryStatement2 = (PsiTryStatement)statements[0];
+ final PsiResourceList list2 = tryStatement2.getResourceList();
+ if (list2 == null) {
+ return;
+ }
+ final List variables2 = list2.getResourceVariables();
+ for (PsiResourceVariable variable : variables2) {
+ newTryStatement.append(';');
+ newTryStatement.append(variable.getText());
+ }
+ newTryStatement.append(")");
+ final PsiCodeBlock tryBlock2 = tryStatement2.getTryBlock();
+ if (tryBlock2 == null) {
+ return;
+ }
+ newTryStatement.append(tryBlock2.getText());
+ final PsiElementFactory factory = JavaPsiFacade.getElementFactory(element.getProject());
+ final PsiStatement newStatement = factory.createStatementFromText(newTryStatement.toString(), element);
+ tryStatement1.replace(newStatement);
+ }
+}
diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/NestedTryStatementsPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/NestedTryStatementsPredicate.java
new file mode 100644
index 000000000000..17f0e9b153b6
--- /dev/null
+++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/NestedTryStatementsPredicate.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2012 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.siyeh.ipp.exceptions;
+
+import com.intellij.psi.*;
+import com.intellij.psi.tree.IElementType;
+import com.siyeh.ipp.base.PsiElementPredicate;
+
+/**
+ * @author Bas Leijdekkers
+ */
+public class NestedTryStatementsPredicate implements PsiElementPredicate {
+
+ @Override
+ public boolean satisfiedBy(PsiElement element) {
+ if (!(element instanceof PsiJavaToken)) {
+ return false;
+ }
+ final PsiJavaToken javaToken = (PsiJavaToken)element;
+ final IElementType tokenType = javaToken.getTokenType();
+ if (!JavaTokenType.TRY_KEYWORD.equals(tokenType)) {
+ return false;
+ }
+ final PsiElement parent = element.getParent();
+ if (!isSimpleTryWithResources(parent)) {
+ return false;
+ }
+ final PsiTryStatement tryStatement = (PsiTryStatement)parent;
+ final PsiCodeBlock block = tryStatement.getTryBlock();
+ if (block == null) {
+ return false;
+ }
+ final PsiStatement[] statements = block.getStatements();
+ if (statements.length != 1) {
+ return false;
+ }
+ final PsiStatement statement = statements[0];
+ return isSimpleTryWithResources(statement);
+ }
+
+ private static boolean isSimpleTryWithResources(PsiElement element) {
+ if (!(element instanceof PsiTryStatement)) {
+ return false;
+ }
+ final PsiTryStatement tryStatement = (PsiTryStatement)element;
+ final PsiResourceList resourceList = tryStatement.getResourceList();
+ if (resourceList == null) {
+ return false;
+ }
+ final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
+ if (finallyBlock != null) {
+ return false;
+ }
+ final PsiCodeBlock block = tryStatement.getTryBlock();
+ return block != null;
+ }
+}
diff --git a/plugins/IntentionPowerPak/src/intentionDescriptions/MergeNestedTryStatementsIntention/after.java.template b/plugins/IntentionPowerPak/src/intentionDescriptions/MergeNestedTryStatementsIntention/after.java.template
new file mode 100644
index 000000000000..a8613e304c27
--- /dev/null
+++ b/plugins/IntentionPowerPak/src/intentionDescriptions/MergeNestedTryStatementsIntention/after.java.template
@@ -0,0 +1,10 @@
+import java.io.*;
+
+public class X {
+ void f(File file1, File file2) throws FileNotFoundException {
+ try (FileInputStream in = new FileInputStream(file1); FileOutputStream out = new FileOutputStream(file2)) {
+ // do something
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/IntentionPowerPak/src/intentionDescriptions/MergeNestedTryStatementsIntention/before.java.template b/plugins/IntentionPowerPak/src/intentionDescriptions/MergeNestedTryStatementsIntention/before.java.template
new file mode 100644
index 000000000000..bc283f663329
--- /dev/null
+++ b/plugins/IntentionPowerPak/src/intentionDescriptions/MergeNestedTryStatementsIntention/before.java.template
@@ -0,0 +1,11 @@
+import java.io.*;
+
+public class X {
+ void f(File file1, File file2) throws FileNotFoundException {
+ try (FileInputStream in = new FileInputStream(file1)) {
+ try (FileOutputStream out = new FileOutputStream(file2)) {
+ // do something
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/IntentionPowerPak/src/intentionDescriptions/MergeNestedTryStatementsIntention/description.html b/plugins/IntentionPowerPak/src/intentionDescriptions/MergeNestedTryStatementsIntention/description.html
new file mode 100644
index 000000000000..d4f830222325
--- /dev/null
+++ b/plugins/IntentionPowerPak/src/intentionDescriptions/MergeNestedTryStatementsIntention/description.html
@@ -0,0 +1,6 @@
+
+
+This intention merges two try-with-resources statements into one, if the first is located inside
+the second.
+
+
diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/mergeTry/Simple.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/mergeTry/Simple.java
new file mode 100644
index 000000000000..a51451b43edd
--- /dev/null
+++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/mergeTry/Simple.java
@@ -0,0 +1,16 @@
+package com.siyeh.ipp.exceptions.mergeTry;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class Simple {
+ void foo(File file1, File file2) throws IOException {
+ try (FileInputStream in = new FileInputStream(file1)) {
+ try (FileOutputStream out = new FileOutputStream(file2)) {
+ // do something
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/mergeTry/Simple_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/mergeTry/Simple_after.java
new file mode 100644
index 000000000000..6555dd3d546e
--- /dev/null
+++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/mergeTry/Simple_after.java
@@ -0,0 +1,12 @@
+package com.siyeh.ipp.exceptions.mergeTry;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class Simple {
+ void foo(File file1, File file2) throws IOException {
+ try (FileInputStream in = new FileInputStream(file1); FileOutputStream out = new FileOutputStream(file2)) {
+ // do something
+ }
\ No newline at end of file
diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/exceptions/MergeNestedTryStatementsIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/exceptions/MergeNestedTryStatementsIntentionTest.java
new file mode 100644
index 000000000000..599d1d74b783
--- /dev/null
+++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/exceptions/MergeNestedTryStatementsIntentionTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+
+/*
+ * (c) 2012 Desert Island BV
+ * created: 16 06 2012
+ */
+package com.siyeh.ipp.exceptions;
+
+import com.siyeh.IntentionPowerPackBundle;
+import com.siyeh.ipp.IPPTestCase;
+
+/**
+ * @author Bas Leijdekkers
+ */
+public class MergeNestedTryStatementsIntentionTest extends IPPTestCase {
+
+ public void testSimple() { doTest(); }
+
+ @Override
+ protected String getIntentionName() {
+ return IntentionPowerPackBundle.message("merge.nested.try.statements.intention.name");
+ }
+
+ @Override
+ protected String getRelativePath() {
+ return "exceptions/mergeTry";
+ }
+}