/*********************************************************************************************************************** * * Copyright (C) 2010-2014 by the Stratosphere project (http://stratosphere.eu) * * 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 eu.stratosphere.api.java.typeutils; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import eu.stratosphere.types.TypeInformation; import org.apache.commons.lang3.Validate; import org.apache.hadoop.io.Writable; import eu.stratosphere.api.common.io.InputFormat; import eu.stratosphere.api.java.functions.CoGroupFunction; import eu.stratosphere.api.java.functions.CrossFunction; import eu.stratosphere.api.java.functions.FlatMapFunction; import eu.stratosphere.api.java.functions.GroupReduceFunction; import eu.stratosphere.api.java.functions.InvalidTypesException; import eu.stratosphere.api.java.functions.JoinFunction; import eu.stratosphere.api.java.functions.KeySelector; import eu.stratosphere.api.java.functions.MapFunction; import eu.stratosphere.api.java.tuple.Tuple; import eu.stratosphere.types.Value; public class TypeExtractor { @SuppressWarnings("unchecked") public static <IN, OUT> TypeInformation<OUT> getMapReturnTypes(MapFunction<IN, OUT> mapFunction, TypeInformation<IN> inType) { validateInputType(MapFunction.class, mapFunction.getClass(), 0, inType); if(mapFunction instanceof ResultTypeQueryable) { return ((ResultTypeQueryable<OUT>) mapFunction).getProducedType(); } return createTypeInfo(MapFunction.class, mapFunction.getClass(), 1, inType, null); } @SuppressWarnings("unchecked") public static <IN, OUT> TypeInformation<OUT> getFlatMapReturnTypes(FlatMapFunction<IN, OUT> flatMapFunction, TypeInformation<IN> inType) { validateInputType(FlatMapFunction.class, flatMapFunction.getClass(), 0, inType); if(flatMapFunction instanceof ResultTypeQueryable) { return ((ResultTypeQueryable<OUT>) flatMapFunction).getProducedType(); } return createTypeInfo(FlatMapFunction.class, flatMapFunction.getClass(), 1, inType, null); } @SuppressWarnings("unchecked") public static <IN, OUT> TypeInformation<OUT> getGroupReduceReturnTypes(GroupReduceFunction<IN, OUT> groupReduceFunction, TypeInformation<IN> inType) { validateInputType(GroupReduceFunction.class, groupReduceFunction.getClass(), 0, inType); if(groupReduceFunction instanceof ResultTypeQueryable) { return ((ResultTypeQueryable<OUT>) groupReduceFunction).getProducedType(); } return createTypeInfo(GroupReduceFunction.class, groupReduceFunction.getClass(), 1, inType, null); } @SuppressWarnings("unchecked") public static <IN1, IN2, OUT> TypeInformation<OUT> getJoinReturnTypes(JoinFunction<IN1, IN2, OUT> joinFunction, TypeInformation<IN1> in1Type, TypeInformation<IN2> in2Type) { validateInputType(JoinFunction.class, joinFunction.getClass(), 0, in1Type); validateInputType(JoinFunction.class, joinFunction.getClass(), 1, in2Type); if(joinFunction instanceof ResultTypeQueryable) { return ((ResultTypeQueryable<OUT>) joinFunction).getProducedType(); } return createTypeInfo(JoinFunction.class, joinFunction.getClass(), 2, in1Type, in2Type); } @SuppressWarnings("unchecked") public static <IN1, IN2, OUT> TypeInformation<OUT> getCoGroupReturnTypes(CoGroupFunction<IN1, IN2, OUT> coGroupFunction, TypeInformation<IN1> in1Type, TypeInformation<IN2> in2Type) { validateInputType(CoGroupFunction.class, coGroupFunction.getClass(), 0, in1Type); validateInputType(CoGroupFunction.class, coGroupFunction.getClass(), 1, in2Type); if(coGroupFunction instanceof ResultTypeQueryable) { return ((ResultTypeQueryable<OUT>) coGroupFunction).getProducedType(); } return createTypeInfo(CoGroupFunction.class, coGroupFunction.getClass(), 2, in1Type, in2Type); } @SuppressWarnings("unchecked") public static <IN1, IN2, OUT> TypeInformation<OUT> getCrossReturnTypes(CrossFunction<IN1, IN2, OUT> crossFunction, TypeInformation<IN1> in1Type, TypeInformation<IN2> in2Type) { validateInputType(CrossFunction.class, crossFunction.getClass(), 0, in1Type); validateInputType(CrossFunction.class, crossFunction.getClass(), 1, in2Type); if(crossFunction instanceof ResultTypeQueryable) { return ((ResultTypeQueryable<OUT>) crossFunction).getProducedType(); } return createTypeInfo(CrossFunction.class, crossFunction.getClass(), 2, in1Type, in2Type); } @SuppressWarnings("unchecked") public static <IN, OUT> TypeInformation<OUT> getKeySelectorTypes(KeySelector<IN, OUT> selector, TypeInformation<IN> inType) { validateInputType(KeySelector.class, selector.getClass(), 0, inType); if(selector instanceof ResultTypeQueryable) { return ((ResultTypeQueryable<OUT>) selector).getProducedType(); } return createTypeInfo(KeySelector.class, selector.getClass(), 1, inType, null); } @SuppressWarnings("unchecked") public static <IN> TypeInformation<IN> getInputFormatTypes(InputFormat<IN, ?> inputFormat) { if(inputFormat instanceof ResultTypeQueryable) { return ((ResultTypeQueryable<IN>) inputFormat).getProducedType(); } return createTypeInfo(InputFormat.class, inputFormat.getClass(), 0, null, null); } // -------------------------------------------------------------------------------------------- // Generic utility methods // -------------------------------------------------------------------------------------------- public static TypeInformation<?> createTypeInfo(Type t) { ArrayList<Type> typeHierarchy = new ArrayList<Type>(); typeHierarchy.add(t); return createTypeInfoWithTypeHierarchy(typeHierarchy, t, null, null); } @SuppressWarnings("unchecked") public static <IN1, IN2, OUT> TypeInformation<OUT> createTypeInfo(Class<?> baseClass, Class<?> clazz, int returnParamPos, TypeInformation<IN1> in1Type, TypeInformation<IN2> in2Type) { ArrayList<Type> typeHierarchy = new ArrayList<Type>(); Type returnType = getParameterType(baseClass, typeHierarchy, clazz, returnParamPos); TypeInformation<OUT> typeInfo = null; // return type is a variable -> try to get the type info from the input of the base class directly if (returnType instanceof TypeVariable<?>) { ParameterizedType immediateBaseChild = (ParameterizedType) typeHierarchy.get(typeHierarchy.size() - 1); typeInfo = (TypeInformation<OUT>) createTypeInfoWithImmediateBaseChildInput(immediateBaseChild, (TypeVariable<?>) returnType, in1Type, in2Type); if (typeInfo != null) { return typeInfo; } } // get info from hierarchy return (TypeInformation<OUT>) createTypeInfoWithTypeHierarchy(typeHierarchy, returnType, in1Type, in2Type); } public static Type getParameterType(Class<?> baseClass, Class<?> clazz, int pos) { return getParameterType(baseClass, null, clazz, pos); } private static void validateInputType(Class<?> baseClass, Class<?> clazz, int inputParamPos, TypeInformation<?> inType) { ArrayList<Type> typeHierarchy = new ArrayList<Type>(); try { validateInfo(typeHierarchy, getParameterType(baseClass, typeHierarchy, clazz, inputParamPos), inType); } catch(InvalidTypesException e) { throw new InvalidTypesException("Input mismatch: " + e.getMessage()); } } @SuppressWarnings("unchecked") private static void validateInfo(ArrayList<Type> typeHierarchy, Type type, TypeInformation<?> typeInfo) { if (type == null) { throw new InvalidTypesException("Unknown Error. Type is null."); } if (typeInfo == null) { throw new InvalidTypesException("Unknown Error. TypeInformation is null."); } if (!(type instanceof TypeVariable<?>)) { // check for basic type if (typeInfo.isBasicType()) { TypeInformation<?> actual = null; // check if basic type at all if (!(type instanceof Class<?>) || (actual = BasicTypeInfo.getInfoFor((Class<?>) type)) == null) { throw new InvalidTypesException("Basic type expected."); } // check if correct basic type if (!typeInfo.equals(actual)) { throw new InvalidTypesException("Basic type '" + typeInfo + "' expected but was '" + actual + "'."); } } // check for tuple else if (typeInfo.isTupleType()) { // check if tuple at all if (!(type instanceof Class<?> && Tuple.class.isAssignableFrom((Class<?>) type)) && !(type instanceof ParameterizedType && Tuple.class.isAssignableFrom((Class<?>) ((ParameterizedType) type) .getRawType()))) { throw new InvalidTypesException("Tuple type expected."); } // do not allow usage of Tuple as type if (type instanceof Class<?> && ((Class<?>) type).equals(Tuple.class)) { throw new InvalidTypesException("Concrete subclass of Tuple expected."); } // go up the hierarchy until we reach immediate child of Tuple (with or without generics) while (!(type instanceof ParameterizedType && ((Class<?>) ((ParameterizedType) type).getRawType()).getSuperclass().equals( Tuple.class)) && !(type instanceof Class<?> && ((Class<?>) type).getSuperclass().equals(Tuple.class))) { typeHierarchy.add(type); // parameterized type if (type instanceof ParameterizedType) { type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass(); } // class else { type = ((Class<?>) type).getGenericSuperclass(); } } // check if immediate child of Tuple has generics if (type instanceof Class<?>) { throw new InvalidTypesException("Parameterized Tuple type expected."); } TupleTypeInfo<?> tti = (TupleTypeInfo<?>) typeInfo; Type[] subTypes = ((ParameterizedType) type).getActualTypeArguments(); if (subTypes.length != tti.getArity()) { throw new InvalidTypesException("Tuple arity '" + tti.getArity() + "' expected but was '" + subTypes.length + "'."); } for (int i = 0; i < subTypes.length; i++) { validateInfo(new ArrayList<Type>(typeHierarchy), subTypes[i], ((TupleTypeInfo<?>) typeInfo).getTypeAt(i)); } } // check for Writable else if (typeInfo instanceof WritableTypeInfo<?>) { // check if writable at all if (!(type instanceof Class<?> && Writable.class.isAssignableFrom((Class<?>) type))) { throw new InvalidTypesException("Writable type expected."); } // check writable type contents Class<?> clazz = null; if (((WritableTypeInfo<?>) typeInfo).getTypeClass() != (clazz = (Class<?>) type)) { throw new InvalidTypesException("Writable type '" + ((WritableTypeInfo<?>) typeInfo).getTypeClass().getCanonicalName() + "' expected but was '" + clazz.getCanonicalName() + "'."); } } // check for basic array else if (typeInfo instanceof BasicArrayTypeInfo<?, ?>) { Type component = null; // check if array at all if (!(type instanceof Class<?> && ((Class<?>) type).isArray() && (component = ((Class<?>) type).getComponentType()) != null) && !(type instanceof GenericArrayType && (component = ((GenericArrayType) type).getGenericComponentType()) != null)) { throw new InvalidTypesException("Array type expected."); } if (component instanceof TypeVariable<?>) { component = materializeTypeVariable(typeHierarchy, (TypeVariable<?>) component); if (component == null) { return; } } validateInfo(typeHierarchy, component, ((BasicArrayTypeInfo<?, ?>) typeInfo).getComponentInfo()); } // check for object array else if (typeInfo instanceof ObjectArrayTypeInfo<?, ?>) { // check if array at all if (!(type instanceof Class<?> && ((Class<?>) type).isArray()) && !(type instanceof GenericArrayType)) { throw new InvalidTypesException("Object array type expected."); } // check component Type component = null; if (type instanceof Class<?>) { component = ((Class<?>) type).getComponentType(); } else { component = ((GenericArrayType) type).getGenericComponentType(); } if (component instanceof TypeVariable<?>) { component = materializeTypeVariable(typeHierarchy, (TypeVariable<?>) component); if (component == null) { return; } } validateInfo(typeHierarchy, component, ((ObjectArrayTypeInfo<?, ?>) typeInfo).getComponentInfo()); } // check for value else if (typeInfo instanceof ValueTypeInfo<?>) { // check if value at all if (!(type instanceof Class<?> && Value.class.isAssignableFrom((Class<?>) type))) { throw new InvalidTypesException("Value type expected."); } TypeInformation<?> actual = null; // check value type contents if (!((ValueTypeInfo<?>) typeInfo).equals(actual = ValueTypeInfo.getValueTypeInfo((Class<? extends Value>) type))) { throw new InvalidTypesException("Value type '" + typeInfo + "' expected but was '" + actual + "'."); } } // check for custom object else if (typeInfo instanceof GenericTypeInfo<?>) { Class<?> clazz = null; if (!(type instanceof Class<?> && ((GenericTypeInfo<?>) typeInfo).getTypeClass() == (clazz = (Class<?>) type)) && !(type instanceof ParameterizedType && (clazz = (Class<?>) ((ParameterizedType) type).getRawType()) == ((GenericTypeInfo<?>) typeInfo) .getTypeClass())) { throw new InvalidTypesException("Generic object type '" + ((GenericTypeInfo<?>) typeInfo).getTypeClass().getCanonicalName() + "' expected but was '" + clazz.getCanonicalName() + "'."); } } } else { type = materializeTypeVariable(typeHierarchy, (TypeVariable<?>) type); if (type != null) { validateInfo(typeHierarchy, type, typeInfo); } } } private static Type getParameterType(Class<?> baseClass, ArrayList<Type> typeHierarchy, Class<?> clazz, int pos) { Type t = clazz.getGenericSuperclass(); // check if type is child of the base class if (!(t instanceof Class<?> && baseClass.isAssignableFrom((Class<?>) t)) && !(t instanceof ParameterizedType && baseClass.isAssignableFrom((Class<?>) ((ParameterizedType) t).getRawType()))) { throw new IllegalArgumentException("A generic function base class must be a super class."); } if (typeHierarchy != null) { typeHierarchy.add(t); } Type curT = t; // go up the hierarchy until we reach the base class (with or without generics) // collect the types while moving up for a later top-down while (!(curT instanceof ParameterizedType && ((Class<?>) ((ParameterizedType) curT).getRawType()).equals(baseClass)) && !(curT instanceof Class<?> && ((Class<?>) curT).equals(baseClass))) { if (typeHierarchy != null) { typeHierarchy.add(curT); } // parameterized type if (curT instanceof ParameterizedType) { curT = ((Class<?>) ((ParameterizedType) curT).getRawType()).getGenericSuperclass(); } // class else { curT = ((Class<?>) curT).getGenericSuperclass(); } } // check if immediate child of base class has generics if (curT instanceof Class<?>) { throw new InvalidTypesException("Function needs to be parameterized by using generics."); } if (typeHierarchy != null) { typeHierarchy.add(curT); } ParameterizedType baseClassChild = (ParameterizedType) curT; return baseClassChild.getActualTypeArguments()[pos]; } @SuppressWarnings({ "unchecked", "rawtypes" }) private static <IN1, IN2, OUT> TypeInformation<OUT> createTypeInfoWithTypeHierarchy(ArrayList<Type> typeHierarchy, Type t, TypeInformation<IN1> in1Type, TypeInformation<IN2> in2Type) { // check if type is a subclass of tuple if ((t instanceof Class<?> && Tuple.class.isAssignableFrom((Class<?>) t)) || (t instanceof ParameterizedType && Tuple.class.isAssignableFrom((Class<?>) ((ParameterizedType) t).getRawType()))) { Type curT = t; // do not allow usage of Tuple as type if (curT instanceof Class<?> && ((Class<?>) curT).equals(Tuple.class)) { throw new InvalidTypesException( "Usage of class Tuple as a type is not allowed. Use a concrete subclass (e.g. Tuple1, Tuple2, etc.) instead."); } // go up the hierarchy until we reach immediate child of Tuple (with or without generics) // collect the types while moving up for a later top-down while (!(curT instanceof ParameterizedType && ((Class<?>) ((ParameterizedType) curT).getRawType()).getSuperclass().equals( Tuple.class)) && !(curT instanceof Class<?> && ((Class<?>) curT).getSuperclass().equals(Tuple.class))) { typeHierarchy.add(curT); // parameterized type if (curT instanceof ParameterizedType) { curT = ((Class<?>) ((ParameterizedType) curT).getRawType()).getGenericSuperclass(); } // class else { curT = ((Class<?>) curT).getGenericSuperclass(); } } // check if immediate child of Tuple has generics if (curT instanceof Class<?>) { throw new InvalidTypesException("Tuple needs to be parameterized by using generics."); } ParameterizedType tupleChild = (ParameterizedType) curT; Type[] subtypes = new Type[tupleChild.getActualTypeArguments().length]; // materialize possible type variables for (int i = 0; i < subtypes.length; i++) { // materialize immediate TypeVariables if (tupleChild.getActualTypeArguments()[i] instanceof TypeVariable<?>) { Type varContent = materializeTypeVariable(typeHierarchy, (TypeVariable<?>) tupleChild.getActualTypeArguments()[i]); // variable could not be materialized if (varContent == null) { // add the TypeVariable as subtype for step in next section subtypes[i] = tupleChild.getActualTypeArguments()[i]; } else { // add class or parameterized type subtypes[i] = varContent; } } // class or parameterized type else { subtypes[i] = tupleChild.getActualTypeArguments()[i]; } } TypeInformation<?>[] tupleSubTypes = new TypeInformation<?>[subtypes.length]; for (int i = 0; i < subtypes.length; i++) { // sub type could not be determined with materializing // try to derive the type info of the TypeVariable from the immediate base child input as a last attempt if (subtypes[i] instanceof TypeVariable<?>) { ParameterizedType immediateBaseChild = (ParameterizedType) typeHierarchy.get(typeHierarchy.size() - 1); tupleSubTypes[i] = createTypeInfoWithImmediateBaseChildInput(immediateBaseChild, (TypeVariable<?>) subtypes[i], in1Type, in2Type); // variable could not be determined if (tupleSubTypes[i] == null) { throw new InvalidTypesException("Type of TypeVariable '" + ((TypeVariable<?>) subtypes[i]).getName() + "' in '" + ((TypeVariable<?>) subtypes[i]).getGenericDeclaration() + "' could not be determined. This is most likely a type erasure problem. " + "The type extraction currently supports types with generic variables only in cases where " + "all variables in the return type can be deduced from the input type(s)."); } } else { tupleSubTypes[i] = createTypeInfoWithTypeHierarchy(new ArrayList<Type>(typeHierarchy), subtypes[i], in1Type, in2Type); } } // TODO: Check that type that extends Tuple does not have additional fields. // Right now, these fields are not be serialized by the TupleSerializer. // We might want to add an ExtendedTupleSerializer for that. if (t instanceof Class<?>) { return new TupleTypeInfo(((Class<? extends Tuple>) t), tupleSubTypes); } else if (t instanceof ParameterizedType) { return new TupleTypeInfo(((Class<? extends Tuple>) ((ParameterizedType) t).getRawType()), tupleSubTypes); } } // type depends on another type // e.g. class MyMapper<E> extends MapFunction<String, E> else if (t instanceof TypeVariable) { Type typeVar = materializeTypeVariable(typeHierarchy, (TypeVariable<?>) t); if (typeVar != null) { return createTypeInfoWithTypeHierarchy(typeHierarchy, typeVar, in1Type, in2Type); } // try to derive the type info of the TypeVariable from the immediate base child input as a last attempt else { ParameterizedType immediateBaseChild = (ParameterizedType) typeHierarchy.get(typeHierarchy.size() - 1); TypeInformation<OUT> typeInfo = (TypeInformation<OUT>) createTypeInfoWithImmediateBaseChildInput(immediateBaseChild, (TypeVariable<?>) t, in1Type, in2Type); if (typeInfo != null) { return typeInfo; } else { throw new InvalidTypesException("Type of TypeVariable '" + ((TypeVariable<?>) t).getName() + "' in '" + ((TypeVariable<?>) t).getGenericDeclaration() + "' could not be determined. This is most likely a type erasure problem. " + "The type extraction currently supports types with generic variables only in cases where " + "all variables in the return type can be deduced from the input type(s)."); } } } // arrays with generics // (due to a Java 6 bug, it is possible that BasicArrayTypes also get classified as ObjectArrayTypes // since the JVM classifies e.g. String[] as GenericArrayType instead of Class) else if (t instanceof GenericArrayType) { GenericArrayType genericArray = (GenericArrayType) t; TypeInformation<?> componentInfo = createTypeInfoWithTypeHierarchy(typeHierarchy, genericArray.getGenericComponentType(), in1Type, in2Type); return ObjectArrayTypeInfo.getInfoFor(t, componentInfo); } // objects with generics are treated as raw type else if (t instanceof ParameterizedType) { return getForClass((Class<OUT>) ((ParameterizedType) t).getRawType()); } // no tuple, no TypeVariable, no generic type else if (t instanceof Class) { return getForClass((Class<OUT>) t); } throw new InvalidTypesException("Type Information could not be created."); } private static <IN1, IN2> TypeInformation<?> createTypeInfoWithImmediateBaseChildInput(ParameterizedType baseChild, TypeVariable<?> typeVar, TypeInformation<IN1> in1Type, TypeInformation<IN2> in2Type) { Type[] baseChildArgs = baseChild.getActualTypeArguments(); TypeInformation<?> info = null; if (in1Type != null) { info = findCorrespondingInfo(typeVar, baseChildArgs[0], in1Type); } if (info == null && in2Type != null) { info = findCorrespondingInfo(typeVar, baseChildArgs[1], in2Type); } if (info != null) { return info; } return null; } private static TypeInformation<?> findCorrespondingInfo(TypeVariable<?> typeVar, Type type, TypeInformation<?> corrInfo) { if (type instanceof TypeVariable) { TypeVariable<?> variable = (TypeVariable<?>) type; if (variable.getName().equals(typeVar.getName()) && variable.getGenericDeclaration().equals(typeVar.getGenericDeclaration())) { return corrInfo; } } else if (type instanceof ParameterizedType && Tuple.class.isAssignableFrom((Class<?>) ((ParameterizedType) type).getRawType())) { ParameterizedType tuple = (ParameterizedType) type; Type[] args = tuple.getActualTypeArguments(); for (int i = 0; i < args.length; i++) { TypeInformation<?> info = findCorrespondingInfo(typeVar, args[i], ((TupleTypeInfo<?>) corrInfo).getTypeAt(i)); if (info != null) { return info; } } } return null; } private static Type materializeTypeVariable(ArrayList<Type> typeHierarchy, TypeVariable<?> typeVar) { TypeVariable<?> inTypeTypeVar = typeVar; // iterate thru hierarchy from top to bottom until type variable gets a class assigned for (int i = typeHierarchy.size() - 1; i >= 0; i--) { Type curT = typeHierarchy.get(i); // parameterized type if (curT instanceof ParameterizedType) { Class<?> rawType = ((Class<?>) ((ParameterizedType) curT).getRawType()); for (int paramIndex = 0; paramIndex < rawType.getTypeParameters().length; paramIndex++) { TypeVariable<?> curVarOfCurT = rawType.getTypeParameters()[paramIndex]; // check if variable names match if (curVarOfCurT.getName().equals(inTypeTypeVar.getName()) && curVarOfCurT.getGenericDeclaration().equals(inTypeTypeVar.getGenericDeclaration())) { Type curVarType = ((ParameterizedType) curT).getActualTypeArguments()[paramIndex]; // another type variable level if (curVarType instanceof TypeVariable<?>) { inTypeTypeVar = (TypeVariable<?>) curVarType; } // class else { return curVarType; } } } } } // can not be materialized, most likely due to type erasure return null; } @SuppressWarnings("unchecked") public static <X> TypeInformation<X> getForClass(Class<X> clazz) { Validate.notNull(clazz); // check for abstract classes or interfaces if (Modifier.isInterface(clazz.getModifiers()) || (Modifier.isAbstract(clazz.getModifiers()) && !clazz.isArray())) { throw new InvalidTypesException("Interfaces and abstract classes are not valid types."); } // check for arrays if (clazz.isArray()) { // primitive arrays: int[], byte[], ... PrimitiveArrayTypeInfo<X> primitiveArrayInfo = PrimitiveArrayTypeInfo.getInfoFor(clazz); if (primitiveArrayInfo != null) { return primitiveArrayInfo; } // basic type arrays: String[], Integer[], Double[] BasicArrayTypeInfo<X, ?> basicArrayInfo = BasicArrayTypeInfo.getInfoFor(clazz); if (basicArrayInfo != null) { return basicArrayInfo; } // object arrays else { return ObjectArrayTypeInfo.getInfoFor(clazz); } } // check for writable types if(Writable.class.isAssignableFrom(clazz)) { return (TypeInformation<X>) WritableTypeInfo.getWritableTypeInfo((Class<? extends Writable>) clazz); } // check for basic types TypeInformation<X> basicTypeInfo = BasicTypeInfo.getInfoFor(clazz); if (basicTypeInfo != null) { return basicTypeInfo; } // check for subclasses of Value if (Value.class.isAssignableFrom(clazz)) { Class<? extends Value> valueClass = clazz.asSubclass(Value.class); return (TypeInformation<X>) ValueTypeInfo.getValueTypeInfo(valueClass); } // check for subclasses of Tuple if (Tuple.class.isAssignableFrom(clazz)) { throw new InvalidTypesException("Type information extraction for tuples cannot be done based on the class."); } // return a generic type return new GenericTypeInfo<X>(clazz); } @SuppressWarnings({ "unchecked", "rawtypes" }) public static <X> TypeInformation<X> getForObject(X value) { Validate.notNull(value); // check if we can extract the types from tuples, otherwise work with the class if (value instanceof Tuple) { Tuple t = (Tuple) value; int numFields = t.getArity(); TypeInformation<?>[] infos = new TypeInformation[numFields]; for (int i = 0; i < numFields; i++) { Object field = t.getField(i); if (field == null) { throw new InvalidTypesException("Automatic type extraction is not possible on candidates with null values. " + "Please specify the types directly."); } infos[i] = getForObject(field); } return (TypeInformation<X>) new TupleTypeInfo(value.getClass(), infos); } else { return getForClass((Class<X>) value.getClass()); } } private TypeExtractor() { } }