/****************************************************************************** * Copyright (C) 2016 Yevgeny Krasik * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * ******************************************************************************/ package com.github.ykrasik.jaci.reflection; import com.github.ykrasik.jaci.util.function.Spplr; import java.util.Arrays; import java.util.List; import java.util.Objects; /** * Suppliers that supply through reflection, usually by invoking a method. * * @author Yevgeny Krasik */ public final class ReflectionSuppliers { private ReflectionSuppliers() { } /** * Create a supplier that will invoke the method specified by the give method name * on the given object instance through reflection. * The method must be no-args and must return a value of the specified type. * The method may be private, in which case it will be made accessible outside it's class. * * @param instance Instance to invoke the method one. * @param methodName Method name to invoke. Method must be no-args and return a value of the specified type. * @param suppliedClass Expected return type of the method. * @param <T> Supplier return type. * @return A {@link Spplr} that will invoke the no-args method specified by the given name on the given instance. */ public static <T> Spplr<T> reflectionSupplier(Object instance, String methodName, Class<T> suppliedClass) { final ReflectionMethod method = ReflectionUtils.getNoArgsMethod(instance.getClass(), methodName); ReflectionUtils.assertReturnValue(method, suppliedClass); return new ReflectionSupplier<>(instance, method); } /** * Similar to {@link #reflectionSupplier(Object, String, Class)}, except allows for an alternative return type. * Will first try to create a reflection supplier using the primary class, but if that fails (the method doesn't * return the primary class), will re-try with the secondary class. * Useful for cases where the return type could be either a primitive or the primitive's boxed version * (i.e. Integer.type or Integer.class). * * @param instance Instance to invoke the method one. * @param methodName Method name to invoke. Method must be no-args and return a value of * either the primary class or the secondary type. * @param primaryClass Primary return type of the method. Will be tried first. * @param secondaryClass Secondary return type of the method. * Will be tried if the method doesn't return the primary type. * @param <T> Supplier return type. * @return A {@link Spplr} that will invoke the no-args method specified by the given name on the given instance. */ public static <T> Spplr<T> reflectionSupplier(Object instance, String methodName, Class<T> primaryClass, Class<T> secondaryClass) { try { return reflectionSupplier(instance, methodName, primaryClass); } catch (IllegalArgumentException e) { // Try the alternative return value. return reflectionSupplier(instance, methodName, secondaryClass); } } /** * A {@link Spplr} that invokes a (possibly private) no-args method through reflection. * * @author Yevgeny Krasik */ private static class ReflectionSupplier<T> implements Spplr<T> { private final Object instance; private final ReflectionMethod method; private ReflectionSupplier(Object instance, ReflectionMethod method) { this.instance = Objects.requireNonNull(instance, "instance"); this.method = Objects.requireNonNull(method, "method"); } @Override public T get() { return ReflectionUtils.invokeNoArgs(instance, method); } @Override public String toString() { final StringBuilder sb = new StringBuilder("ReflectionSupplier{"); sb.append("instance=").append(instance); sb.append(", method=").append(method); sb.append('}'); return sb.toString(); } } /** * Create a supplier that will invoke the method specified by the give method name * on the given object instance through reflection. * The created supplier will supply a {@link List} of values. * The method must be no-args and must return an array of the specified type. * The method may be private, in which case it will be made accessible outside it's class. * * @param instance Instance to invoke the method one. * @param methodName Method name to invoke. Method must be no-args and return an array of the specified type. * @param suppliedClass Expected array return type of the method. * @param <T> Supplier return type. * @return A {@link Spplr} that will invoke the no-args method specified by the given name on the given instance. */ public static <T> Spplr<List<T>> reflectionListSupplier(Object instance, String methodName, Class<T[]> suppliedClass) { final Spplr<T[]> supplier = reflectionSupplier(instance, methodName, suppliedClass); return new ArrayListSupplier<>(supplier); } /** * A {@link Spplr} that wraps another {@link Spplr} that returns an array of {@code T}, delegates to that supplier * and instead wraps the returned array in a {@link List} of {@code T}. * * @author Yevgeny Krasik */ private static class ArrayListSupplier<T> implements Spplr<List<T>> { private final Spplr<T[]> supplier; private ArrayListSupplier(Spplr<T[]> supplier) { this.supplier = Objects.requireNonNull(supplier); } @Override public List<T> get() { return Arrays.asList(supplier.get()); } @Override public String toString() { final StringBuilder sb = new StringBuilder("ReflectionListSupplier{"); sb.append("supplier=").append(supplier); sb.append('}'); return sb.toString(); } } }