diff --git a/src/main/java/spoon/reflect/code/CtTry.java b/src/main/java/spoon/reflect/code/CtTry.java index 589eb96182c..ac108559d16 100644 --- a/src/main/java/spoon/reflect/code/CtTry.java +++ b/src/main/java/spoon/reflect/code/CtTry.java @@ -47,6 +47,18 @@ public interface CtTry extends CtStatement, TemplateParameter, CtBodyHolde @PropertySetter(role = CATCH) T addCatcher(CtCatch catcher); + /** + * Adds a catch block at the specified position in the try statement. + * Behaves similarly to {@link java.util.List#add(int, Object)}. + * + * @param position the position at which the catcher is to be inserted + * @param catcher the catch statement to be inserted + * @return this try statement + * @throws IndexOutOfBoundsException if the position is out of range (position < 0 || position > number of catchers) + */ + @PropertySetter(role = CATCH) + CtTry addCatcherAt(int position, CtCatch catcher); + /** * Removes a catch block. */ diff --git a/src/main/java/spoon/support/reflect/code/CtTryImpl.java b/src/main/java/spoon/support/reflect/code/CtTryImpl.java index 5a8c6d4a642..f50ea9e2ff3 100644 --- a/src/main/java/spoon/support/reflect/code/CtTryImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtTryImpl.java @@ -64,16 +64,22 @@ public T setCatchers(List catchers) { @Override public T addCatcher(CtCatch catcher) { + addCatcherAt(catchers.size(), catcher); + return (T) this; + } + + @Override + public CtTry addCatcherAt(int position, CtCatch catcher) { if (catcher == null) { - return (T) this; + return this; } if (catchers == CtElementImpl.emptyList()) { catchers = new ArrayList<>(CATCH_CASES_CONTAINER_DEFAULT_CAPACITY); } catcher.setParent(this); getFactory().getEnvironment().getModelChangeListener().onListAdd(this, CATCH, this.catchers, catcher); - catchers.add(catcher); - return (T) this; + catchers.add(position, catcher); + return this; } @Override diff --git a/src/test/java/spoon/test/trycatch/TryCatchTest.java b/src/test/java/spoon/test/trycatch/TryCatchTest.java index 8f1ed9b6368..e1aa99ccde7 100644 --- a/src/test/java/spoon/test/trycatch/TryCatchTest.java +++ b/src/test/java/spoon/test/trycatch/TryCatchTest.java @@ -26,6 +26,7 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import spoon.Launcher; @@ -36,7 +37,6 @@ import spoon.reflect.code.CtResource; import spoon.reflect.code.CtTry; import spoon.reflect.code.CtTryWithResource; -import spoon.reflect.code.CtVariableRead; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtVariable; @@ -56,11 +56,13 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static spoon.testing.utils.ModelUtils.build; @@ -442,4 +444,52 @@ public void testTryWithVariableAsResource() { tryStmt.removeResource(ctResource); } + + @Nested + class AddCatcherAt { + @Test + void addsCatcherAtTheSpecifiedPosition() { + // contract: the catcher should be added at the specified position + // arrange + Factory factory = createFactory(); + + CtTry tryStatement = factory.createTry(); + + CtCatch first = createCatch(factory, IOException.class); + CtCatch second = createCatch(factory, ExceptionInInitializerError.class); + CtCatch third = createCatch(factory, NoSuchMethodException.class); + + // act + tryStatement + .addCatcherAt(0, third) + .addCatcherAt(0, first) + .addCatcherAt(1, second); + + // assert + assertThat(tryStatement.getCatchers(), contains(first, second, third)); + } + + @Test + void throwsOutOfBoundsException_whenPositionIsOutOfBounds() { + // contract: `addCatcherAt` should throw an out-of-bounds exception when the specified position is out of + // bounds of the catcher collection + + // arrange + Factory factory = createFactory(); + + CtTry tryStatement = factory.createTry(); + CtCatch catcherAtWrongPosition = createCatch(factory, Exception.class); + + // act & assert + assertThrows(IndexOutOfBoundsException.class, + () -> tryStatement.addCatcherAt(1, catcherAtWrongPosition)); + } + + private CtCatch createCatch(Factory factory, Class typeToCatch) { + CtTypeReference typeReference = factory.Type().createReference(typeToCatch); + return factory.createCatch().setParameter( + factory.createCatchVariable().setType(typeReference) + ); + } + } }