Java tutorial
/* * Copyright 2017-2018 Crown Copyright * * 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 uk.gov.gchq.koryphe.signature; import org.apache.commons.lang3.reflect.TypeUtils; import uk.gov.gchq.koryphe.ValidationResult; import uk.gov.gchq.koryphe.tuple.Tuple; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Collection; import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; /** * A <code>Signature</code> is the type metadata for the input or output of a {@link java.util.function.Function}. */ public abstract class Signature { /** * Tests whether this <code>Signature</code> is compatible with the types supplied. * * @param arguments Class or Tuple of classes to test. * @return ValidationResult containing the isValid flag and errors messages. */ public abstract ValidationResult assignable(final Class<?>... arguments); public abstract Class[] getClasses(); public abstract Integer getNumClasses(); /** * Get the input signature of a predicate. * * @param predicate the predicate. * @return Input signature. */ public static Signature getInputSignature(final Predicate predicate) { return createSignatureFromTypeVariable(predicate, Predicate.class, 0, true); } /** * Get the input signature of a function. * * @param function the function. * @return Input signature. */ public static Signature getInputSignature(final Function function) { return createSignatureFromTypeVariable(function, Function.class, 0, true); } /** * Get the input signature of a BiFunction. * * @param function the BiFunction (second argument must be the same type as the output). * @param <F> the type of the BiFunction * @param <I> the first input type of the BiFunction * @param <O> the second input type and output type of the BiFunction * @return Input signature */ public static <F extends BiFunction<I, O, O>, I, O> Signature getInputSignature(final F function) { return createSignatureFromTypeVariable(function, BiFunction.class, 0, true); } /** * Get the output signature of a function. * * @param function Function. * @return Output signature. */ public static Signature getOutputSignature(final Function function) { return createSignatureFromTypeVariable(function, Function.class, 1, false); } /** * Get the output signature of a function. * * @param function BiFunction. * @param <F> the type of the BiFunction * @param <I> the first input type of the BiFunction * @param <O> the second input type and output type of the BiFunction * @return Output signature */ public static <F extends BiFunction<I, O, O>, I, O> Signature getOutputSignature(final F function) { return createSignatureFromTypeVariable(function, BiFunction.class, 2, false); } /** * Create a <code>Signature</code> for the type variable at the given index. * * @param input Function to create signature for. * @param functionClass The input class * @param typeVariableIndex 0 for I or 1 for O. * @param isInput if true then it is an input signature otherwise it is an output signature * @return Signature of the type variable. */ private static Signature createSignatureFromTypeVariable(final Object input, final Class functionClass, final int typeVariableIndex, final boolean isInput) { TypeVariable<?> tv; if (input.getClass().getTypeParameters().length > typeVariableIndex) { tv = input.getClass().getTypeParameters()[typeVariableIndex]; } else { tv = functionClass.getTypeParameters()[typeVariableIndex]; } final Map<TypeVariable<?>, Type> typeArgs = TypeUtils.getTypeArguments(input.getClass(), functionClass); Type type = typeArgs.containsKey(tv) ? typeArgs.get(tv) : Object.class; return createSignature(input, type, typeArgs, isInput); } private static Signature createSignature(final Object input, final Type type, final Map<TypeVariable<?>, Type> typeArgs, final boolean isInput) { Class clazz = null; if (input.getClass().getTypeParameters().length > 0) { TypeVariable<?>[] inputClassTypeParameters = input.getClass().getTypeParameters(); Type[] inputClassTypeParameterBounds = inputClassTypeParameters[0].getBounds(); if (inputClassTypeParameterBounds.length > 0) { clazz = getTypeClass(inputClassTypeParameterBounds[0], typeArgs); } } else { clazz = getTypeClass(type, typeArgs); } if (Tuple.class.isAssignableFrom(clazz)) { final TypeVariable[] tupleTypes = getTypeClass(type, typeArgs).getTypeParameters(); final Map<TypeVariable<?>, Type> classTypeArgs = TypeUtils.getTypeArguments(type, clazz); Collection<? extends Type> types = TypeUtils.getTypeArguments(type, clazz).values(); Class[] classes = new Class[types.size()]; int i = 0; for (final TypeVariable tupleType : tupleTypes) { classes[i++] = getTypeClass(classTypeArgs.get(tupleType), typeArgs); } return new TupleSignature(input, clazz, classes, isInput); } return new SingletonSignature(input, clazz, isInput); } protected static Class getTypeClass(final Type type, final Map<TypeVariable<?>, Type> typeArgs) { Type rawType = type; if (rawType instanceof ParameterizedType) { rawType = ((ParameterizedType) type).getRawType(); } if (rawType instanceof Class) { return (Class) rawType; } if (rawType instanceof TypeVariable) { final Type t = typeArgs.get(rawType); if (null != t) { return getTypeClass(t, typeArgs); } } try { return Class.forName(rawType.getTypeName()); } catch (final ClassNotFoundException e) { // cannot resolve - default to UnknownGenericType; return UnknownGenericType.class; } } public static class UnknownGenericType { } }