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 022import java.util.function.*; 023 024/** 025 * A functional interface representing a function that accepts five arguments, produces a result, and may throw a checked exception. 026 * 027 * <p> 028 * This interface extends {@link Function5} to allow the functional method 029 * to throw checked exceptions. The default {@link #apply(Object, Object, Object, Object, Object)} method wraps any checked exceptions in a 030 * {@link RuntimeException}, making it compatible with standard {@link Function5} usage while still allowing 031 * the implementation to throw checked exceptions via {@link #applyThrows(Object, Object, Object, Object, Object)}. 032 * 033 * <h5 class='section'>Features:</h5> 034 * <ul class='spaced-list'> 035 * <li>Functional interface - can be used with lambda expressions and method references 036 * <li>Exception support - allows checked exceptions to be thrown via {@link #applyThrows(Object, Object, Object, Object, Object)} 037 * <li>Compatible with Function5 - implements {@link Function5#apply(Object, Object, Object, Object, Object)} by wrapping exceptions 038 * <li>Dual interface - can be used as both Function5 and ThrowingFunction5 039 * </ul> 040 * 041 * <h5 class='section'>Use Cases:</h5> 042 * <ul class='spaced-list'> 043 * <li>Five-argument transformations that may throw I/O exceptions 044 * <li>Parsing operations that may throw validation exceptions 045 * <li>Data conversion that may fail with checked exceptions 046 * <li>Operations that need to propagate checked exceptions 047 * </ul> 048 * 049 * <h5 class='section'>Usage:</h5> 050 * <p class='bjava'> 051 * <jc>// Lambda with exception</jc> 052 * ThrowingFunction5<String,String,String,String,String,File> <jv>fileParser</jv> = (<jv>dir</jv>, <jv>subdir1</jv>, <jv>subdir2</jv>, <jv>subdir3</jv>, <jv>name</jv>) -> { 053 * Path <jv>path</jv> = Paths.get(<jv>dir</jv>, <jv>subdir1</jv>, <jv>subdir2</jv>, <jv>subdir3</jv>, <jv>name</jv>); 054 * <jk>if</jk> (! Files.exists(<jv>path</jv>)) { 055 * <jk>throw new</jk> FileNotFoundException(<js>"File not found: "</js> + <jv>path</jv>); 056 * } 057 * <jk>return</jk> <jv>path</jv>.toFile(); 058 * }; 059 * 060 * <jc>// Using applyThrows to get checked exception</jc> 061 * <jk>try</jk> { 062 * File <jv>file</jv> = <jv>fileParser</jv>.applyThrows(<js>"/tmp"</js>, <js>"data"</js>, <js>"2024"</js>, <js>"01"</js>, <js>"file.txt"</js>); 063 * } <jk>catch</jk> (FileNotFoundException <jv>e</jv>) { 064 * <jc>// Handle checked exception</jc> 065 * } 066 * 067 * <jc>// Using apply wraps exception in RuntimeException</jc> 068 * File <jv>file</jv> = <jv>fileParser</jv>.apply(<js>"/tmp"</js>, <js>"data"</js>, <js>"2024"</js>, <js>"01"</js>, <js>"file.txt"</js>); <jc>// May throw RuntimeException</jc> 069 * </p> 070 * 071 * <h5 class='section'>Exception Handling:</h5> 072 * <ul class='spaced-list'> 073 * <li>{@link #applyThrows(Object, Object, Object, Object, Object)} - Throws checked exceptions directly 074 * <li>{@link #apply(Object, Object, Object, Object, Object)} - Wraps checked exceptions in {@link RuntimeException} 075 * <li>Use {@code applyThrows} when you need to handle specific checked exceptions 076 * <li>Use {@code apply} when you want standard Function5 behavior 077 * </ul> 078 * 079 * <h5 class='section'>See Also:</h5><ul> 080 * <li class='jc'>{@link ThrowingFunction} - Single-argument function that throws exceptions 081 * <li class='jc'>{@link ThrowingFunction2} - Two-argument function that throws exceptions 082 * <li class='jc'>{@link ThrowingFunction3} - Three-argument function that throws exceptions 083 * <li class='jc'>{@link ThrowingFunction4} - Four-argument function that throws exceptions 084 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsBasics">juneau-commons Basics</a> 085 * </ul> 086 * 087 * @param <A> The type of the first argument to the function. 088 * @param <B> The type of the second argument to the function. 089 * @param <C> The type of the third argument to the function. 090 * @param <D> The type of the fourth argument to the function. 091 * @param <E> The type of the fifth argument to the function. 092 * @param <R> The type of the result of the function. 093 */ 094@FunctionalInterface 095public interface ThrowingFunction5<A,B,C,D,E,R> extends Function5<A,B,C,D,E,R> { 096 097 /** 098 * Applies this function to the given arguments, wrapping any checked exceptions in a {@link RuntimeException}. 099 * 100 * <p> 101 * This is the default implementation that makes this interface compatible with {@link Function5}. 102 * It calls {@link #applyThrows(Object, Object, Object, Object, Object)} and wraps any checked exceptions. 103 * 104 * @param a The first function argument. 105 * @param b The second function argument. 106 * @param c The third function argument. 107 * @param d The fourth function argument. 108 * @param e The fifth function argument. 109 * @return The function result. 110 * @throws RuntimeException if {@link #applyThrows(Object, Object, Object, Object, Object)} throws a checked exception. 111 */ 112 @Override 113 default R apply(A a, B b, C c, D d, E e) { 114 try { 115 return applyThrows(a, b, c, d, e); 116 } catch (Exception ex) { 117 throw toRex(ex); 118 } 119 } 120 121 /** 122 * Returns a composed function that first applies this function to its input, and then applies 123 * the {@code after} function to the result. 124 * 125 * <p> 126 * This method enables function composition, allowing you to chain multiple transformations together. 127 * The composed function will throw exceptions from this function, but the {@code after} function 128 * is a standard {@link Function} that does not throw checked exceptions. 129 * 130 * @param <V> The type of output of the {@code after} function, and of the composed function. 131 * @param after The function to apply after this function is applied. Must not be <jk>null</jk>. 132 * @return A composed {@link ThrowingFunction5} that first applies this function and then applies the {@code after} function. 133 * @throws NullPointerException if {@code after} is <jk>null</jk>. 134 */ 135 default <V> ThrowingFunction5<A,B,C,D,E,V> andThen(Function<? super R,? extends V> after) { 136 assertArgNotNull("after", after); 137 return (A a, B b, C c, D d, E e) -> after.apply(applyThrows(a, b, c, d, e)); 138 } 139 140 /** 141 * Applies this function to the given arguments, potentially throwing a checked exception. 142 * 143 * <p> 144 * This is the functional method that implementations must provide. It allows checked exceptions 145 * to be thrown directly, unlike the standard {@link Function5#apply(Object, Object, Object, Object, Object)} method. 146 * 147 * <h5 class='section'>Example:</h5> 148 * <p class='bjava'> 149 * ThrowingFunction5<String,Integer,Boolean,Double,Long,Integer> <jv>parser</jv> = (<jv>s</jv>, <jv>base</jv>, <jv>signed</jv>, <jv>multiplier</jv>, <jv>offset</jv>) -> { 150 * <jk>try</jk> { 151 * <jk>int</jk> <jv>value</jv> = Integer.parseInt(<jv>s</jv>, <jv>base</jv>); 152 * <jv>value</jv> = <jv>signed</jv> ? <jv>value</jv> : Math.abs(<jv>value</jv>); 153 * <jk>return</jk> (<jk>int</jk>)(<jv>value</jv> * <jv>multiplier</jv> + <jv>offset</jv>); 154 * } <jk>catch</jk> (NumberFormatException <jv>e</jv>) { 155 * <jk>throw new</jk> ParseException(<js>"Invalid number: "</js> + <jv>s</jv> + <js>" in base "</js> + <jv>base</jv>, 0); 156 * } 157 * }; 158 * 159 * <jk>try</jk> { 160 * <jk>int</jk> <jv>value</jv> = <jv>parser</jv>.applyThrows(<js>"123"</js>, 10, <jk>true</jk>, 2.0, 100L); 161 * } <jk>catch</jk> (ParseException <jv>e</jv>) { 162 * <jc>// Handle checked exception</jc> 163 * } 164 * </p> 165 * 166 * @param a The first function argument. 167 * @param b The second function argument. 168 * @param c The third function argument. 169 * @param d The fourth function argument. 170 * @param e The fifth function argument. 171 * @return The function result. 172 * @throws Exception If an error occurs during function execution. 173 */ 174 R applyThrows(A a, B b, C c, D d, E e) throws Exception; 175} 176