From 9b83f9699c6f693d3bd660a62db2588d3f435fd6 Mon Sep 17 00:00:00 2001 From: Marc Baloup Date: Tue, 9 May 2023 11:57:05 +0200 Subject: [PATCH] Reflect wrapper initialization does not crash anymore on the first exception. It accumulates all the exceptions and shows everything at the end. --- .../paper/commands/PaperBrigadierCommand.java | 4 +- .../paper/reflect/PandalibPaperReflect.java | 165 +++++++++--------- .../lib/util/LevenshteinDistance.java | 8 +- .../lib/util/ThrowableAccumulator.java | 104 +++++++++++ 4 files changed, 197 insertions(+), 84 deletions(-) create mode 100644 pandalib-util/src/main/java/fr/pandacube/lib/util/ThrowableAccumulator.java diff --git a/pandalib-paper/src/main/java/fr/pandacube/lib/paper/commands/PaperBrigadierCommand.java b/pandalib-paper/src/main/java/fr/pandacube/lib/paper/commands/PaperBrigadierCommand.java index d27664a..504d0f9 100644 --- a/pandalib-paper/src/main/java/fr/pandacube/lib/paper/commands/PaperBrigadierCommand.java +++ b/pandalib-paper/src/main/java/fr/pandacube/lib/paper/commands/PaperBrigadierCommand.java @@ -58,6 +58,8 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; +import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; + /** * Abstract class to hold a command to be integrated into a Paper server vanilla command dispatcher. */ @@ -67,7 +69,7 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand nmsDispatcher; static { - PandalibPaperReflect.init(); + wrapEx(PandalibPaperReflect::init); vanillaCommandDispatcher = ReflectWrapper.wrapTyped(Bukkit.getServer(), CraftServer.class) .getServer() .vanillaCommandDispatcher(); diff --git a/pandalib-paper/src/main/java/fr/pandacube/lib/paper/reflect/PandalibPaperReflect.java b/pandalib-paper/src/main/java/fr/pandacube/lib/paper/reflect/PandalibPaperReflect.java index 001d5a7..7cd09db 100644 --- a/pandalib-paper/src/main/java/fr/pandacube/lib/paper/reflect/PandalibPaperReflect.java +++ b/pandalib-paper/src/main/java/fr/pandacube/lib/paper/reflect/PandalibPaperReflect.java @@ -74,6 +74,7 @@ import fr.pandacube.lib.paper.reflect.wrapper.paper.PaperAdventure; import fr.pandacube.lib.paper.reflect.wrapper.paper.QueuedChangesMapLong2Object; import fr.pandacube.lib.paper.reflect.wrapper.paper.configuration.FallbackValue_Int; import fr.pandacube.lib.paper.reflect.wrapper.paper.configuration.WorldConfiguration; +import fr.pandacube.lib.util.ThrowableAccumulator; import static fr.pandacube.lib.reflect.wrapper.WrapperRegistry.initWrapper; @@ -86,8 +87,9 @@ public class PandalibPaperReflect { /** * Initializes the reflect tools in {@code pandalib-paper-reflect} module. + * @throws Exception if a problem occurs when initializing wrapper classes. */ - public static void init() { + public static void init() throws Exception { NMSReflect.init(); synchronized (PandalibPaperReflect.class) { if (isInit) @@ -97,105 +99,112 @@ public class PandalibPaperReflect { initWrapperClasses(); } - private static void initWrapperClasses() { + private static void initWrapperClasses() throws Exception { + + ThrowableAccumulator thAcc = new ThrowableAccumulator<>(Throwable.class); + // brigadier - initWrapper(CommandNode.class, CommandNode.REFLECT.get()); + thAcc.catchThrowable(() -> initWrapper(CommandNode.class, CommandNode.REFLECT.get())); // craftbukkit - initWrapper(CraftItemStack.class, CraftItemStack.REFLECT.get()); - initWrapper(CraftMapView.class, CraftMapView.REFLECT.get()); - initWrapper(CraftNamespacedKey.class, CraftNamespacedKey.REFLECT.get()); - initWrapper(CraftPlayer.class, CraftPlayer.REFLECT.get()); - initWrapper(CraftServer.class, CraftServer.REFLECT.get()); - initWrapper(CraftVector.class, CraftVector.REFLECT.get()); - initWrapper(CraftWorld.class, CraftWorld.REFLECT.get()); - initWrapper(RenderData.class, RenderData.REFLECT.get()); - initWrapper(VanillaCommandWrapper.class, VanillaCommandWrapper.REFLECT.get()); + thAcc.catchThrowable(() -> initWrapper(CraftItemStack.class, CraftItemStack.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(CraftMapView.class, CraftMapView.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(CraftNamespacedKey.class, CraftNamespacedKey.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(CraftPlayer.class, CraftPlayer.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(CraftServer.class, CraftServer.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(CraftVector.class, CraftVector.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(CraftWorld.class, CraftWorld.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(RenderData.class, RenderData.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(VanillaCommandWrapper.class, VanillaCommandWrapper.REFLECT.get())); // dataconverter - initWrapper(MCDataConverter.class, MCDataConverter.REFLECT.get()); - initWrapper(MCDataType.class, MCDataType.REFLECT.get()); - initWrapper(MCTypeRegistry.class, MCTypeRegistry.REFLECT.get()); + thAcc.catchThrowable(() -> initWrapper(MCDataConverter.class, MCDataConverter.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(MCDataType.class, MCDataType.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(MCTypeRegistry.class, MCTypeRegistry.REFLECT.get())); // minecraft.commands - initWrapper(BlockPosArgument.class, BlockPosArgument.MAPPING.runtimeClass()); - initWrapper(Commands.class, Commands.MAPPING.runtimeClass()); - initWrapper(CommandSourceStack.class, CommandSourceStack.MAPPING.runtimeClass()); - initWrapper(ComponentArgument.class, ComponentArgument.MAPPING.runtimeClass()); - initWrapper(Coordinates.class, Coordinates.MAPPING.runtimeClass()); - initWrapper(EntityArgument.class, EntityArgument.MAPPING.runtimeClass()); - initWrapper(EntitySelector.class, EntitySelector.MAPPING.runtimeClass()); - initWrapper(GameProfileArgument.class, GameProfileArgument.MAPPING.runtimeClass()); - initWrapper(ResourceLocationArgument.class, ResourceLocationArgument.MAPPING.runtimeClass()); - initWrapper(Vec3Argument.class, Vec3Argument.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(BlockPosArgument.class, BlockPosArgument.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(Commands.class, Commands.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(CommandSourceStack.class, CommandSourceStack.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ComponentArgument.class, ComponentArgument.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(Coordinates.class, Coordinates.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(EntityArgument.class, EntityArgument.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(EntitySelector.class, EntitySelector.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(GameProfileArgument.class, GameProfileArgument.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ResourceLocationArgument.class, ResourceLocationArgument.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(Vec3Argument.class, Vec3Argument.MAPPING.runtimeClass())); // minecraft.core - initWrapper(BlockPos.class, BlockPos.MAPPING.runtimeClass()); - initWrapper(Vec3i.class, Vec3i.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(BlockPos.class, BlockPos.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(Vec3i.class, Vec3i.MAPPING.runtimeClass())); // minecraft.nbt - initWrapper(CollectionTag.class, CollectionTag.MAPPING.runtimeClass()); - initWrapper(CompoundTag.class, CompoundTag.MAPPING.runtimeClass()); - initWrapper(ListTag.class, ListTag.MAPPING.runtimeClass()); - initWrapper(NbtIo.class, NbtIo.MAPPING.runtimeClass()); - initWrapper(StringTag.class, StringTag.MAPPING.runtimeClass()); - initWrapper(Tag.class, Tag.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(CollectionTag.class, CollectionTag.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(CompoundTag.class, CompoundTag.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ListTag.class, ListTag.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(NbtIo.class, NbtIo.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(StringTag.class, StringTag.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(Tag.class, Tag.MAPPING.runtimeClass())); // minecraft.network.chat - initWrapper(Component.class, Component.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(Component.class, Component.MAPPING.runtimeClass())); // minecraft.network.protocol - initWrapper(ClientboundCustomPayloadPacket.class, ClientboundCustomPayloadPacket.MAPPING.runtimeClass()); - initWrapper(ClientboundGameEventPacket.class, ClientboundGameEventPacket.MAPPING.runtimeClass()); - initWrapper(ClientboundGameEventPacket.Type.class, ClientboundGameEventPacket.Type.MAPPING.runtimeClass()); - initWrapper(Packet.class, Packet.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(ClientboundCustomPayloadPacket.class, ClientboundCustomPayloadPacket.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ClientboundGameEventPacket.class, ClientboundGameEventPacket.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ClientboundGameEventPacket.Type.class, ClientboundGameEventPacket.Type.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(Packet.class, Packet.MAPPING.runtimeClass())); // minecraft.network - initWrapper(FriendlyByteBuf.class, FriendlyByteBuf.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(FriendlyByteBuf.class, FriendlyByteBuf.MAPPING.runtimeClass())); // minecraft.resources - initWrapper(ResourceLocation.class, ResourceLocation.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(ResourceLocation.class, ResourceLocation.MAPPING.runtimeClass())); // minecraft.server - initWrapper(ChunkMap.class, ChunkMap.MAPPING.runtimeClass()); - initWrapper(DedicatedPlayerList.class, DedicatedPlayerList.MAPPING.runtimeClass()); - initWrapper(DedicatedServer.class, DedicatedServer.MAPPING.runtimeClass()); - initWrapper(DedicatedServerProperties.class, DedicatedServerProperties.MAPPING.runtimeClass()); - initWrapper(MinecraftServer.class, MinecraftServer.MAPPING.runtimeClass()); - initWrapper(PlayerList.class, PlayerList.MAPPING.runtimeClass()); - initWrapper(ServerChunkCache.class, ServerChunkCache.MAPPING.runtimeClass()); - initWrapper(ServerGamePacketListenerImpl.class, ServerGamePacketListenerImpl.MAPPING.runtimeClass()); - initWrapper(ServerLevel.class, ServerLevel.MAPPING.runtimeClass()); - initWrapper(ServerPlayer.class, ServerPlayer.MAPPING.runtimeClass()); - initWrapper(Settings.class, Settings.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(ChunkMap.class, ChunkMap.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(DedicatedPlayerList.class, DedicatedPlayerList.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(DedicatedServer.class, DedicatedServer.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(DedicatedServerProperties.class, DedicatedServerProperties.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(MinecraftServer.class, MinecraftServer.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(PlayerList.class, PlayerList.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ServerChunkCache.class, ServerChunkCache.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ServerGamePacketListenerImpl.class, ServerGamePacketListenerImpl.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ServerLevel.class, ServerLevel.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ServerPlayer.class, ServerPlayer.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(Settings.class, Settings.MAPPING.runtimeClass())); // minecraft.util - initWrapper(ProgressListener.class, ProgressListener.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(ProgressListener.class, ProgressListener.MAPPING.runtimeClass())); // minecraft.world.block - initWrapper(BambooStalkBlock.class, BambooStalkBlock.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(BambooStalkBlock.class, BambooStalkBlock.MAPPING.runtimeClass())); // minecraft.world - initWrapper(AABB.class, AABB.MAPPING.runtimeClass()); - initWrapper(ChunkPos.class, ChunkPos.MAPPING.runtimeClass()); - initWrapper(ChunkStorage.class, ChunkStorage.MAPPING.runtimeClass()); - initWrapper(DamageSource.class, DamageSource.MAPPING.runtimeClass()); - initWrapper(DamageSources.class, DamageSources.MAPPING.runtimeClass()); - initWrapper(Entity.class, Entity.MAPPING.runtimeClass()); - initWrapper(ItemStack.class, ItemStack.MAPPING.runtimeClass()); - initWrapper(Level.class, Level.MAPPING.runtimeClass()); - initWrapper(MapItemSavedData.class, MapItemSavedData.MAPPING.runtimeClass()); - initWrapper(PlayerDataStorage.class, PlayerDataStorage.MAPPING.runtimeClass()); - initWrapper(SavedData.class, SavedData.MAPPING.runtimeClass()); - initWrapper(Vec3.class, Vec3.MAPPING.runtimeClass()); - initWrapper(VoxelShape.class, VoxelShape.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(AABB.class, AABB.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ChunkPos.class, ChunkPos.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ChunkStorage.class, ChunkStorage.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(DamageSource.class, DamageSource.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(DamageSources.class, DamageSources.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(Entity.class, Entity.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(ItemStack.class, ItemStack.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(Level.class, Level.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(MapItemSavedData.class, MapItemSavedData.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(PlayerDataStorage.class, PlayerDataStorage.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(SavedData.class, SavedData.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(Vec3.class, Vec3.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(VoxelShape.class, VoxelShape.MAPPING.runtimeClass())); // minecraft - initWrapper(DetectedVersion.class, DetectedVersion.MAPPING.runtimeClass()); - initWrapper(GameVersion.class, GameVersion.REFLECT.get()); - initWrapper(SharedConstants.class, SharedConstants.MAPPING.runtimeClass()); - initWrapper(WorldVersion.class, WorldVersion.MAPPING.runtimeClass()); + thAcc.catchThrowable(() -> initWrapper(DetectedVersion.class, DetectedVersion.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(GameVersion.class, GameVersion.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(SharedConstants.class, SharedConstants.MAPPING.runtimeClass())); + thAcc.catchThrowable(() -> initWrapper(WorldVersion.class, WorldVersion.MAPPING.runtimeClass())); // netty - initWrapper(ByteBuf.class, ByteBuf.REFLECT.get()); - initWrapper(Unpooled.class, Unpooled.REFLECT.get()); + thAcc.catchThrowable(() -> initWrapper(ByteBuf.class, ByteBuf.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(Unpooled.class, Unpooled.REFLECT.get())); // paper.configuration - initWrapper(FallbackValue_Int.class, FallbackValue_Int.REFLECT.get()); - initWrapper(WorldConfiguration.class, WorldConfiguration.REFLECT.get()); - initWrapper(WorldConfiguration.Chunks.class, WorldConfiguration.Chunks.REFLECT.get()); + thAcc.catchThrowable(() -> initWrapper(FallbackValue_Int.class, FallbackValue_Int.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(WorldConfiguration.class, WorldConfiguration.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(WorldConfiguration.Chunks.class, WorldConfiguration.Chunks.REFLECT.get())); // paper - initWrapper(AABBVoxelShape.class, AABBVoxelShape.REFLECT.get()); - initWrapper(PaperAdventure.class, PaperAdventure.REFLECT.get()); - initWrapper(QueuedChangesMapLong2Object.class, QueuedChangesMapLong2Object.REFLECT.get()); + thAcc.catchThrowable(() -> initWrapper(AABBVoxelShape.class, AABBVoxelShape.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(PaperAdventure.class, PaperAdventure.REFLECT.get())); + thAcc.catchThrowable(() -> initWrapper(QueuedChangesMapLong2Object.class, QueuedChangesMapLong2Object.REFLECT.get())); + + + thAcc.throwCatched(); + } } diff --git a/pandalib-util/src/main/java/fr/pandacube/lib/util/LevenshteinDistance.java b/pandalib-util/src/main/java/fr/pandacube/lib/util/LevenshteinDistance.java index 09ec1e1..746a54c 100644 --- a/pandalib-util/src/main/java/fr/pandacube/lib/util/LevenshteinDistance.java +++ b/pandalib-util/src/main/java/fr/pandacube/lib/util/LevenshteinDistance.java @@ -7,9 +7,9 @@ import java.util.function.ToIntBiFunction; /** * Implementation of the Levenshtein distance algorithm * that operate on characters. Its purpose is to compute a "distance" between two strings of characters, that represents - * how many edition operation it is needed to perform on the first string ({@code initialString}) to optain the second + * how many edition operations must be performed on the first string ({@code initialString}) to optain the second * one ({@code finalString}). - * + *

* All the parameters of the algorithm are configurable: *

    *
  • The score of adding a character
  • @@ -21,15 +21,13 @@ import java.util.function.ToIntBiFunction; * ToIntBiFunction)} (for a full control of the parameters) or {@link #LevenshteinDistance(String, String)} (to keep the * default parameters value); then to call the method {@link #getCurrentDistance()} to compute the Levenshtein distance * between the two strings. - * + *

    * A more advanced usage offer the possibility to progressively compute a distance from a predefined * {@code initialString} to a {@code finalString} that is feeded progressively using {@link #add(char)} or * {@link #add(String)}. This is useful if the {@code finalString} is an input that is currently being typed by the * user, so the application can progressively update a list of suggested words based on the distance. * For this usage, you can use those constructors to avoid initializing the {@code finalString}: * {@link #LevenshteinDistance(String, int, int, ToIntBiFunction)} or {@link #LevenshteinDistance(String)}. - * - * */ public class LevenshteinDistance { diff --git a/pandalib-util/src/main/java/fr/pandacube/lib/util/ThrowableAccumulator.java b/pandalib-util/src/main/java/fr/pandacube/lib/util/ThrowableAccumulator.java new file mode 100644 index 0000000..9e44d73 --- /dev/null +++ b/pandalib-util/src/main/java/fr/pandacube/lib/util/ThrowableAccumulator.java @@ -0,0 +1,104 @@ +package fr.pandacube.lib.util; + +import fr.pandacube.lib.util.ThrowableUtil.RunnableException; +import fr.pandacube.lib.util.ThrowableUtil.SupplierException; + +import java.util.function.Supplier; + +/** + * A class that delay and accumulate thown exceptions, that can be thrown later using {@link #throwCatched()}. + * @param the type of {@link Throwable} to accumulate. + */ +public class ThrowableAccumulator { + T base = null; + final Class throwableType; + + /** + * Creates a new {@link ThrowableAccumulator} with the specified throwable type. + * @param throwableType The type of the {@link Throwable} to accumulate. + */ + public ThrowableAccumulator(Class throwableType) { + this.throwableType = throwableType; + } + + /** + * Run the provided {@link RunnableException}, catching an eventual exception to accumulate for later use. + * @param run the {@link RunnableException} to run. + * @throws Exception if an exception not handled by this accumulator is thrown. + */ + public void catchThrowable(RunnableException run) throws Exception { + try { + run.run(); + } catch (Throwable t) { + accumulateThrowable(t); + } + } + + /** + * Run the provided {@link SupplierException}, catching an eventual exception to accumulate for later use. + * @param supp the {@link SupplierException} to run. + * @param returnValueIfException The value to return if this accumulator catch an exception. + * @return The return value of the supplier, or {@code returnValueIfException} if this accumulator catch an exception. + * @throws Exception if an exception not handled by this accumulator is thrown. + * @param the type of the return value of the supplier. + */ + public R catchThrowable(SupplierException supp, R returnValueIfException) throws Exception { + return catchThrowable(supp, (Supplier) () -> returnValueIfException); + } + + /** + * Run the provided {@link SupplierException}, catching an eventual exception to accumulate for later use. + * @param supp the {@link SupplierException} to run. + * @param returnValueIfException The value to return if this accumulator catch an exception. + * @return The return value of the supplier, or the return value of {@code returnValueIfException} if this + * accumulator catch an exception. + * @throws Exception if an exception not handled by this accumulator is thrown. + * @param the type of the return value of both suppliers. + */ + public R catchThrowable(SupplierException supp, Supplier returnValueIfException) throws Exception { + try { + return supp.get(); + } catch (Throwable t) { + accumulateThrowable(t); + } + return returnValueIfException.get(); + } + + + private void accumulateThrowable(Throwable t) throws Exception { + if (throwableType.isInstance(t)) { + synchronized (this) { + if (base == null) + base = throwableType.cast(t); + else { + base.addSuppressed(t); + } + } + } + else { + throwEx(t); + } + } + + /** + * Throws an exception if there is at least one catched by this accumulator. + * If multiple exception where catched, all the exception after the first one are added to the first one as + * suppressed exceptions. + * If no exception were catched, this method does nothing. + * @throws Exception the first accumulated throwable, the other ones being suppressed. + */ + public void throwCatched() throws Exception { + synchronized (this) { + if (base != null) + throwEx(base); + } + } + + private void throwEx(Throwable t) throws Exception { + if (t instanceof Error e) + throw e; + else if (t instanceof Exception e) + throw e; + } + +}