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 */
017
018package org.apache.commons.lang3.function;
019
020import java.lang.invoke.MethodHandle;
021import java.lang.invoke.MethodHandleProxies;
022import java.lang.invoke.MethodHandles;
023import java.lang.reflect.Method;
024import java.util.Objects;
025import java.util.function.BiConsumer;
026import java.util.function.BiFunction;
027import java.util.function.Function;
028import java.util.function.Supplier;
029
030import org.apache.commons.lang3.exception.UncheckedIllegalAccessException;
031
032/**
033 * Converts {@link Method} objects to lambdas.
034 * <p>
035 * More specifically, produces instances of single-method interfaces which redirect calls to methods; see
036 * {@link #asInterfaceInstance(Class, Method)}.
037 * </p>
038 * <h2>Calling supplier methods with no arguments</h2>
039 * <p>
040 * If the interface's single-method defines no arguments, use {@link #asFunction(Method)} and then apply the function
041 * passing in the object to receive the method call.
042 * </p>
043 * <p>
044 * For example to invoke {@link String#length()}:
045 * </p>
046 *
047 * <pre>
048 * final Method method = String.class.getMethod("length");
049 * final Function&lt;String, Integer&gt; function = MethodInvokers.asFunction(method);
050 * assertEquals(3, function.apply("ABC"));
051 * </pre>
052 *
053 * <h2>Calling function methods with one argument</h2>
054 * <p>
055 * If the interface's single-method defines one argument, use {@link #asBiFunction(Method)} and then apply the function
056 * passing in the object to receive the method call. The second argument to the function is the only argument to the
057 * method.
058 * </p>
059 * <p>
060 * For example to invoke {@link String#charAt(int)}:
061 * </p>
062 *
063 * <pre>
064 * final Method method = String.class.getMethod("charAt", int.class);
065 * final BiFunction&lt;String, Integer, Character&gt; function = MethodInvokers.asBiFunction(method);
066 * assertEquals('C', function.apply("ABC", 2));
067 * </pre>
068 *
069 * @since 3.13.0
070 */
071public final class MethodInvokers {
072
073    /**
074     * Produces a {@link BiConsumer} for a given <em>consumer</em> Method. For example, a classic setter method (as opposed
075     * to a fluent setter). You call the BiConsumer with two arguments: (1) the object receiving the method call, and (2)
076     * the method argument.
077     *
078     * @param <T> the type of the first argument to the operation: The type containing the Method.
079     * @param <U> the type of the second argument to the operation: The type of the method argument.
080     * @param method the method to invoke.
081     * @return a correctly-typed wrapper for the given target.
082     */
083    @SuppressWarnings("unchecked")
084    public static <T, U> BiConsumer<T, U> asBiConsumer(final Method method) {
085        return asInterfaceInstance(BiConsumer.class, method);
086    }
087
088    /**
089     * Produces a {@link BiFunction} for a given a <em>function</em> Method. You call the BiFunction with two arguments: (1)
090     * the object receiving the method call, and (2) the method argument. The BiFunction return type must match the method's
091     * return type.
092     * <p>
093     * For example to invoke {@link String#charAt(int)}:
094     * </p>
095     *
096     * <pre>
097     * final Method method = String.class.getMethod("charAt", int.class);
098     * final BiFunction&lt;String, Integer, Character&gt; function = MethodInvokers.asBiFunction(method);
099     * assertEquals('C', function.apply("ABC", 2));
100     * </pre>
101     *
102     * @param <T> the type of the first argument to the function: The type containing the method.
103     * @param <U> the type of the second argument to the function: the method argument type.
104     * @param <R> the type of the result of the function: The method return type.
105     * @param method the method to invoke.
106     * @return a correctly-typed wrapper for the given target.
107     */
108    @SuppressWarnings("unchecked")
109    public static <T, U, R> BiFunction<T, U, R> asBiFunction(final Method method) {
110        return asInterfaceInstance(BiFunction.class, method);
111    }
112
113    /**
114     * Produces a {@link FailableBiConsumer} for a given <em>consumer</em> Method. For example, a classic setter method (as
115     * opposed to a fluent setter). You call the FailableBiConsumer with two arguments: (1) the object receiving the method
116     * call, and (2) the method argument.
117     *
118     * @param <T> the type of the first argument to the operation: The type containing the Method.
119     * @param <U> the type of the second argument to the operation: The type of the method argument.
120     * @param method the method to invoke.
121     * @return a correctly-typed wrapper for the given target.
122     */
123    @SuppressWarnings("unchecked")
124    public static <T, U> FailableBiConsumer<T, U, Throwable> asFailableBiConsumer(final Method method) {
125        return asInterfaceInstance(FailableBiConsumer.class, method);
126    }
127
128    /**
129     * Produces a {@link FailableBiFunction} for a given a <em>function</em> Method. You call the FailableBiFunction with
130     * two arguments: (1) the object receiving the method call, and (2) the method argument. The BiFunction return type must
131     * match the method's return type.
132     *
133     * @param <T> the type of the first argument to the function: The type containing the method.
134     * @param <U> the type of the second argument to the function: the method argument type.
135     * @param <R> the type of the result of the function: The method return type.
136     * @param method the method to invoke.
137     * @return a correctly-typed wrapper for the given target.
138     */
139    @SuppressWarnings("unchecked")
140    public static <T, U, R> FailableBiFunction<T, U, R, Throwable> asFailableBiFunction(final Method method) {
141        return asInterfaceInstance(FailableBiFunction.class, method);
142    }
143
144    /**
145     * Produces a {@link FailableFunction} for a given a <em>supplier</em> Method. You call the Function with one argument:
146     * the object receiving the method call. The FailableFunction return type must match the method's return type.
147     *
148     * @param <T> the type of the first argument to the function: The type containing the method.
149     * @param <R> the type of the result of the function: The method return type.
150     * @param method the method to invoke.
151     * @return a correctly-typed wrapper for the given target.
152     */
153    @SuppressWarnings("unchecked")
154    public static <T, R> FailableFunction<T, R, Throwable> asFailableFunction(final Method method) {
155        return asInterfaceInstance(FailableFunction.class, method);
156    }
157
158    /**
159     * Produces a {@link FailableSupplier} for a given a <em>supplier</em> Method. The FailableSupplier return type must
160     * match the method's return type.
161     * <p>
162     * Only works with static methods.
163     * </p>
164     *
165     * @param <R> The Method return type.
166     * @param method the method to invoke.
167     * @return a correctly-typed wrapper for the given target.
168     */
169    @SuppressWarnings("unchecked")
170    public static <R> FailableSupplier<R, Throwable> asFailableSupplier(final Method method) {
171        return asInterfaceInstance(FailableSupplier.class, method);
172    }
173
174    /**
175     * Produces a {@link Function} for a given a <em>supplier</em> Method. You call the Function with one argument: the
176     * object receiving the method call. The Function return type must match the method's return type.
177     * <p>
178     * For example to invoke {@link String#length()}:
179     * </p>
180     *
181     * <pre>
182     * final Method method = String.class.getMethod("length");
183     * final Function&lt;String, Integer&gt; function = MethodInvokers.asFunction(method);
184     * assertEquals(3, function.apply("ABC"));
185     * </pre>
186     *
187     * @param <T> the type of the first argument to the function: The type containing the method.
188     * @param <R> the type of the result of the function: The method return type.
189     * @param method the method to invoke.
190     * @return a correctly-typed wrapper for the given target.
191     */
192    @SuppressWarnings("unchecked")
193    public static <T, R> Function<T, R> asFunction(final Method method) {
194        return asInterfaceInstance(Function.class, method);
195    }
196
197    /**
198     * Produces an instance of the given single-method interface which redirects its calls to the given method.
199     * <p>
200     * For the definition of "single-method", see {@linkplain MethodHandleProxies#asInterfaceInstance(Class, MethodHandle)}.
201     * </p>
202     *
203     * @param <T> The interface type.
204     * @param interfaceClass a class object representing {@code T}.
205     * @param method the method to invoke.
206     * @return a correctly-typed wrapper for the given target.
207     * @see MethodHandleProxies#asInterfaceInstance(Class, MethodHandle)
208     */
209    public static <T> T asInterfaceInstance(final Class<T> interfaceClass, final Method method) {
210        return MethodHandleProxies.asInterfaceInstance(Objects.requireNonNull(interfaceClass, "interfaceClass"), unreflectUnchecked(method));
211    }
212
213    /**
214     * Produces a {@link Supplier} for a given a <em>supplier</em> Method. The Supplier return type must match the method's
215     * return type.
216     * <p>
217     * Only works with static methods.
218     * </p>
219     *
220     * @param <R> The Method return type.
221     * @param method the method to invoke.
222     * @return a correctly-typed wrapper for the given target.
223     */
224    @SuppressWarnings("unchecked")
225    public static <R> Supplier<R> asSupplier(final Method method) {
226        return asInterfaceInstance(Supplier.class, method);
227    }
228
229    /**
230     * Throws NullPointerException if {@code method} is {@code null}.
231     *
232     * @param method The method to test.
233     * @return The given method.
234     * @throws NullPointerException if {@code method} is {@code null}.
235     */
236    private static Method requireMethod(final Method method) {
237        return Objects.requireNonNull(method, "method");
238    }
239
240    private static MethodHandle unreflect(final Method method) throws IllegalAccessException {
241        return MethodHandles.lookup().unreflect(requireMethod(method));
242    }
243
244    private static MethodHandle unreflectUnchecked(final Method method) {
245        try {
246            return unreflect(method);
247        } catch (final IllegalAccessException e) {
248            throw new UncheckedIllegalAccessException(e);
249        }
250    }
251
252    /**
253     * No need to create instances.
254     */
255    private MethodInvokers() {
256        // noop
257    }
258
259}