001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.function; 018 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021 022/** 023 * A functional interface representing an operation that accepts three arguments, returns no result, and may throw a checked exception. 024 * 025 * <p> 026 * This interface extends {@link Consumer3} to allow the functional method 027 * to throw checked exceptions. The default {@link #apply(Object, Object, Object)} method wraps any checked exceptions in a 028 * {@link RuntimeException}, making it compatible with standard {@link Consumer3} usage while still allowing 029 * the implementation to throw checked exceptions via {@link #acceptThrows(Object, Object, Object)}. 030 * 031 * <h5 class='section'>Features:</h5> 032 * <ul class='spaced-list'> 033 * <li>Functional interface - can be used with lambda expressions and method references 034 * <li>Exception support - allows checked exceptions to be thrown via {@link #acceptThrows(Object, Object, Object)} 035 * <li>Compatible with Consumer3 - implements {@link Consumer3#apply(Object, Object, Object)} by wrapping exceptions 036 * <li>Dual interface - can be used as both Consumer3 and ThrowingConsumer3 037 * </ul> 038 * 039 * <h5 class='section'>Use Cases:</h5> 040 * <ul class='spaced-list'> 041 * <li>Three-argument operations that may throw I/O exceptions 042 * <li>Validation operations that may throw validation exceptions 043 * <li>Side-effect operations that may fail with checked exceptions 044 * <li>Operations that need to propagate checked exceptions 045 * </ul> 046 * 047 * <h5 class='section'>Usage:</h5> 048 * <p class='bjava'> 049 * <jc>// Lambda with exception</jc> 050 * ThrowingConsumer3<String,Integer,Boolean> <jv>fileWriter</jv> = (<jv>path</jv>, <jv>content</jv>, <jv>append</jv>) -> { 051 * <jk>if</jk> (<jv>append</jv>) { 052 * Files.write(Paths.get(<jv>path</jv>), <jv>content</jv>.getBytes(), StandardOpenOption.APPEND); 053 * } <jk>else</jk> { 054 * Files.write(Paths.get(<jv>path</jv>), <jv>content</jv>.getBytes()); 055 * } 056 * }; 057 * 058 * <jc>// Using acceptThrows to get checked exception</jc> 059 * <jk>try</jk> { 060 * <jv>fileWriter</jv>.acceptThrows(<js>"/tmp/output.txt"</js>, <js>"Hello World"</js>, <jk>true</jk>); 061 * } <jk>catch</jk> (IOException <jv>e</jv>) { 062 * <jc>// Handle checked exception</jc> 063 * } 064 * 065 * <jc>// Using apply wraps exception in RuntimeException</jc> 066 * <jv>fileWriter</jv>.apply(<js>"/tmp/output.txt"</js>, <js>"Hello World"</js>, <jk>true</jk>); <jc>// May throw RuntimeException</jc> 067 * </p> 068 * 069 * <h5 class='section'>Exception Handling:</h5> 070 * <ul class='spaced-list'> 071 * <li>{@link #acceptThrows(Object, Object, Object)} - Throws checked exceptions directly 072 * <li>{@link #apply(Object, Object, Object)} - Wraps checked exceptions in {@link RuntimeException} 073 * <li>Use {@code acceptThrows} when you need to handle specific checked exceptions 074 * <li>Use {@code apply} when you want standard Consumer3 behavior 075 * </ul> 076 * 077 * <h5 class='section'>See Also:</h5><ul> 078 * <li class='jc'>{@link ThrowingConsumer} - Single-argument consumer that throws exceptions 079 * <li class='jc'>{@link ThrowingConsumer2} - Two-argument consumer that throws exceptions 080 * <li class='jc'>{@link ThrowingConsumer4} - Four-argument consumer that throws exceptions 081 * <li class='jc'>{@link ThrowingConsumer5} - Five-argument consumer that throws exceptions 082 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsBasics">juneau-commons Basics</a> 083 * </ul> 084 * 085 * @param <A> The type of the first argument to the operation. 086 * @param <B> The type of the second argument to the operation. 087 * @param <C> The type of the third argument to the operation. 088 */ 089@FunctionalInterface 090public interface ThrowingConsumer3<A,B,C> extends Consumer3<A,B,C> { 091 092 /** 093 * Performs this operation on the given arguments, wrapping any checked exceptions in a {@link RuntimeException}. 094 * 095 * <p> 096 * This is the default implementation that makes this interface compatible with {@link Consumer3}. 097 * It calls {@link #acceptThrows(Object, Object, Object)} and wraps any checked exceptions. 098 * 099 * @param a The first operation argument. 100 * @param b The second operation argument. 101 * @param c The third operation argument. 102 * @throws RuntimeException if {@link #acceptThrows(Object, Object, Object)} throws a checked exception. 103 */ 104 @Override 105 default void apply(A a, B b, C c) { 106 try { 107 acceptThrows(a, b, c); 108 } catch (Exception e) { 109 throw toRex(e); 110 } 111 } 112 113 /** 114 * Returns a composed {@link ThrowingConsumer3} that performs, in sequence, this operation followed by the {@code after} operation. 115 * 116 * <p> 117 * This method enables operation composition, allowing you to chain multiple operations together. 118 * If performing either operation throws an exception, it is relayed to the caller of the composed operation. 119 * If performing this operation throws an exception, the {@code after} operation will not be performed. 120 * 121 * @param after The operation to perform after this operation. Must not be <jk>null</jk>. 122 * @return A composed {@link ThrowingConsumer3} that performs in sequence this operation followed by the {@code after} operation. 123 * @throws NullPointerException if {@code after} is <jk>null</jk>. 124 */ 125 default ThrowingConsumer3<A,B,C> andThen(ThrowingConsumer3<? super A,? super B,? super C> after) { // NOSONAR - false positive on generics 126 assertArgNotNull("after", after); 127 return (A a, B b, C c) -> { 128 acceptThrows(a, b, c); 129 after.acceptThrows(a, b, c); 130 }; 131 } 132 133 /** 134 * Performs this operation on the given arguments, potentially throwing a checked exception. 135 * 136 * <p> 137 * This is the functional method that implementations must provide. It allows checked exceptions 138 * to be thrown directly, unlike the standard {@link Consumer3#apply(Object, Object, Object)} method. 139 * 140 * <h5 class='section'>Example:</h5> 141 * <p class='bjava'> 142 * ThrowingConsumer3<String,Integer,Boolean> <jv>validator</jv> = (<jv>name</jv>, <jv>age</jv>, <jv>active</jv>) -> { 143 * <jk>if</jk> (<jv>name</jv> == <jk>null</jk> || <jv>name</jv>.isEmpty()) { 144 * <jk>throw new</jk> ValidationException(<js>"Name cannot be empty"</js>); 145 * } 146 * <jk>if</jk> (<jv>age</jv> < 0) { 147 * <jk>throw new</jk> ValidationException(<js>"Age cannot be negative"</js>); 148 * } 149 * }; 150 * 151 * <jk>try</jk> { 152 * <jv>validator</jv>.acceptThrows(<js>""</js>, -1, <jk>true</jk>); 153 * } <jk>catch</jk> (ValidationException <jv>e</jv>) { 154 * <jc>// Handle checked exception</jc> 155 * } 156 * </p> 157 * 158 * @param a The first operation argument. 159 * @param b The second operation argument. 160 * @param c The third operation argument. 161 * @throws Exception If an error occurs during operation execution. 162 */ 163 void acceptThrows(A a, B b, C c) throws Exception; 164} 165