/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.flink.api.java.typeutils; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.flink.api.common.ExecutionConfig; import org.apache.flink.api.common.functions.InvalidTypesException; import org.apache.flink.api.common.functions.JoinFunction; import org.apache.flink.api.common.functions.MapFunction; import org.apache.flink.api.common.functions.RichCoGroupFunction; import org.apache.flink.api.common.functions.RichCrossFunction; import org.apache.flink.api.common.functions.RichFlatJoinFunction; import org.apache.flink.api.common.functions.RichFlatMapFunction; import org.apache.flink.api.common.functions.RichGroupReduceFunction; import org.apache.flink.api.common.functions.RichJoinFunction; import org.apache.flink.api.common.functions.RichMapFunction; import org.apache.flink.api.common.functions.RuntimeContext; import org.apache.flink.api.common.typeinfo.BasicArrayTypeInfo; import org.apache.flink.api.common.typeinfo.BasicTypeInfo; import org.apache.flink.api.common.typeinfo.PrimitiveArrayTypeInfo; import org.apache.flink.api.common.typeinfo.SqlTimeTypeInfo; import org.apache.flink.api.common.typeinfo.TypeHint; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.common.typeutils.CompositeType.FlatFieldDescriptor; import org.apache.flink.api.common.typeutils.TypeSerializer; import org.apache.flink.api.java.functions.KeySelector; import org.apache.flink.api.java.tuple.Tuple; import org.apache.flink.api.java.tuple.Tuple0; import org.apache.flink.api.java.tuple.Tuple1; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.api.java.tuple.Tuple3; import org.apache.flink.api.java.tuple.Tuple9; import org.apache.flink.types.Either; import org.apache.flink.configuration.Configuration; import org.apache.flink.types.DoubleValue; import org.apache.flink.types.IntValue; import org.apache.flink.types.StringValue; import org.apache.flink.types.Value; import org.apache.flink.types.Row; import org.apache.flink.util.Collector; import org.junit.Assert; import org.junit.Test; @SuppressWarnings("serial") public class TypeExtractorTest { @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testBasicType() { // use getGroupReduceReturnTypes() RichGroupReduceFunction<?, ?> function = new RichGroupReduceFunction<Boolean, Boolean>() { private static final long serialVersionUID = 1L; @Override public void reduce(Iterable<Boolean> values, Collector<Boolean> out) throws Exception { // nothing to do } }; TypeInformation<?> ti = TypeExtractor.getGroupReduceReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Boolean")); Assert.assertTrue(ti.isBasicType()); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, ti); Assert.assertEquals(Boolean.class, ti.getTypeClass()); // use getForClass() Assert.assertTrue(TypeExtractor.getForClass(Boolean.class).isBasicType()); Assert.assertEquals(ti, TypeExtractor.getForClass(Boolean.class)); // use getForObject() Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, TypeExtractor.getForObject(true)); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testTupleWithBasicTypes() throws Exception { // use getMapReturnTypes() RichMapFunction<?, ?> function = new RichMapFunction<Tuple9<Integer, Long, Double, Float, Boolean, String, Character, Short, Byte>, Tuple9<Integer, Long, Double, Float, Boolean, String, Character, Short, Byte>>() { private static final long serialVersionUID = 1L; @Override public Tuple9<Integer, Long, Double, Float, Boolean, String, Character, Short, Byte> map( Tuple9<Integer, Long, Double, Float, Boolean, String, Character, Short, Byte> value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple9<Integer, Long, Double, Float, Boolean, String, Character, Short, Byte>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(9, ti.getArity()); Assert.assertTrue(ti instanceof TupleTypeInfo); List<FlatFieldDescriptor> ffd = new ArrayList<FlatFieldDescriptor>(); ((TupleTypeInfo) ti).getFlatFields("f3", 0, ffd); Assert.assertTrue(ffd.size() == 1); Assert.assertEquals(3, ffd.get(0).getPosition() ); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(Tuple9.class, tti.getTypeClass()); for (int i = 0; i < 9; i++) { Assert.assertTrue(tti.getTypeAt(i) instanceof BasicTypeInfo); } Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tti.getTypeAt(1)); Assert.assertEquals(BasicTypeInfo.DOUBLE_TYPE_INFO, tti.getTypeAt(2)); Assert.assertEquals(BasicTypeInfo.FLOAT_TYPE_INFO, tti.getTypeAt(3)); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, tti.getTypeAt(4)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(5)); Assert.assertEquals(BasicTypeInfo.CHAR_TYPE_INFO, tti.getTypeAt(6)); Assert.assertEquals(BasicTypeInfo.SHORT_TYPE_INFO, tti.getTypeAt(7)); Assert.assertEquals(BasicTypeInfo.BYTE_TYPE_INFO, tti.getTypeAt(8)); // use getForObject() Tuple9<Integer, Long, Double, Float, Boolean, String, Character, Short, Byte> t = new Tuple9<Integer, Long, Double, Float, Boolean, String, Character, Short, Byte>( 1, 1L, 1.0, 1.0F, false, "Hello World", 'w', (short) 1, (byte) 1); Assert.assertTrue(TypeExtractor.getForObject(t) instanceof TupleTypeInfo); TupleTypeInfo<?> tti2 = (TupleTypeInfo<?>) TypeExtractor.getForObject(t); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, tti2.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tti2.getTypeAt(1)); Assert.assertEquals(BasicTypeInfo.DOUBLE_TYPE_INFO, tti2.getTypeAt(2)); Assert.assertEquals(BasicTypeInfo.FLOAT_TYPE_INFO, tti2.getTypeAt(3)); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, tti2.getTypeAt(4)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti2.getTypeAt(5)); Assert.assertEquals(BasicTypeInfo.CHAR_TYPE_INFO, tti2.getTypeAt(6)); Assert.assertEquals(BasicTypeInfo.SHORT_TYPE_INFO, tti2.getTypeAt(7)); Assert.assertEquals(BasicTypeInfo.BYTE_TYPE_INFO, tti2.getTypeAt(8)); // test that getForClass does not work try { TypeExtractor.getForClass(Tuple9.class); Assert.fail("Exception expected here"); } catch (InvalidTypesException e) { // that is correct } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testTupleWithTuples() { // use getFlatMapReturnTypes() RichFlatMapFunction<?, ?> function = new RichFlatMapFunction<Tuple3<Tuple1<String>, Tuple1<Integer>, Tuple2<Long, Long>>, Tuple3<Tuple1<String>, Tuple1<Integer>, Tuple2<Long, Long>>>() { private static final long serialVersionUID = 1L; @Override public void flatMap(Tuple3<Tuple1<String>, Tuple1<Integer>, Tuple2<Long, Long>> value, Collector<Tuple3<Tuple1<String>, Tuple1<Integer>, Tuple2<Long, Long>>> out) throws Exception { // nothing to do } }; TypeInformation<?> ti = TypeExtractor.getFlatMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple3<Tuple1<String>, Tuple1<Integer>, Tuple2<Long, Long>>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(3, ti.getArity()); Assert.assertTrue(ti instanceof TupleTypeInfo); List<FlatFieldDescriptor> ffd = new ArrayList<FlatFieldDescriptor>(); ((TupleTypeInfo) ti).getFlatFields("f0.f0", 0, ffd); Assert.assertEquals(0, ffd.get(0).getPosition() ); ffd.clear(); ((TupleTypeInfo) ti).getFlatFields("f0.f0", 0, ffd); Assert.assertTrue( ffd.get(0).getType() instanceof BasicTypeInfo ); Assert.assertTrue( ffd.get(0).getType().getTypeClass().equals(String.class) ); ffd.clear(); ((TupleTypeInfo) ti).getFlatFields("f1.f0", 0, ffd); Assert.assertEquals(1, ffd.get(0).getPosition() ); ffd.clear(); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(Tuple3.class, tti.getTypeClass()); Assert.assertTrue(tti.getTypeAt(0).isTupleType()); Assert.assertTrue(tti.getTypeAt(1).isTupleType()); Assert.assertTrue(tti.getTypeAt(2).isTupleType()); Assert.assertEquals(Tuple1.class, tti.getTypeAt(0).getTypeClass()); Assert.assertEquals(Tuple1.class, tti.getTypeAt(1).getTypeClass()); Assert.assertEquals(Tuple2.class, tti.getTypeAt(2).getTypeClass()); Assert.assertEquals(1, tti.getTypeAt(0).getArity()); Assert.assertEquals(1, tti.getTypeAt(1).getArity()); Assert.assertEquals(2, tti.getTypeAt(2).getArity()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, ((TupleTypeInfo<?>) tti.getTypeAt(0)).getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, ((TupleTypeInfo<?>) tti.getTypeAt(1)).getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, ((TupleTypeInfo<?>) tti.getTypeAt(2)).getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, ((TupleTypeInfo<?>) tti.getTypeAt(2)).getTypeAt(1)); // use getForObject() Tuple3<Tuple1<String>, Tuple1<Integer>, Tuple2<Long, Long>> t = new Tuple3<Tuple1<String>, Tuple1<Integer>, Tuple2<Long, Long>>( new Tuple1<String>("hello"), new Tuple1<Integer>(1), new Tuple2<Long, Long>(2L, 3L)); Assert.assertTrue(TypeExtractor.getForObject(t) instanceof TupleTypeInfo); TupleTypeInfo<?> tti2 = (TupleTypeInfo<?>) TypeExtractor.getForObject(t); Assert.assertEquals(1, tti2.getTypeAt(0).getArity()); Assert.assertEquals(1, tti2.getTypeAt(1).getArity()); Assert.assertEquals(2, tti2.getTypeAt(2).getArity()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, ((TupleTypeInfo<?>) tti2.getTypeAt(0)).getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, ((TupleTypeInfo<?>) tti2.getTypeAt(1)).getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, ((TupleTypeInfo<?>) tti2.getTypeAt(2)).getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, ((TupleTypeInfo<?>) tti2.getTypeAt(2)).getTypeAt(1)); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testTuple0() { // use getFlatMapReturnTypes() RichFlatMapFunction<?, ?> function = new RichFlatMapFunction<Tuple0, Tuple0>() { private static final long serialVersionUID = 1L; @Override public void flatMap(Tuple0 value, Collector<Tuple0> out) throws Exception { // nothing to do } }; TypeInformation<?> ti = TypeExtractor.getFlatMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple0")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(0, ti.getArity()); Assert.assertTrue(ti instanceof TupleTypeInfo); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testSubclassOfTuple() { // use getJoinReturnTypes() RichFlatJoinFunction<?, ?, ?> function = new RichFlatJoinFunction<CustomTuple, String, CustomTuple>() { private static final long serialVersionUID = 1L; @Override public void join(CustomTuple first, String second, Collector<CustomTuple> out) throws Exception { out.collect(null); } }; TypeInformation<?> ti = TypeExtractor.getFlatJoinReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple2<String, Integer>"), (TypeInformation) TypeInfoParser.parse("String")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, ((TupleTypeInfo<?>) ti).getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, ((TupleTypeInfo<?>) ti).getTypeAt(1)); Assert.assertEquals(CustomTuple.class, ((TupleTypeInfo<?>) ti).getTypeClass()); // use getForObject() CustomTuple t = new CustomTuple("hello", 1); TypeInformation<?> ti2 = TypeExtractor.getForObject(t); Assert.assertTrue(ti2.isTupleType()); Assert.assertEquals(2, ti2.getArity()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, ((TupleTypeInfo<?>) ti2).getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, ((TupleTypeInfo<?>) ti2).getTypeAt(1)); Assert.assertEquals(CustomTuple.class, ((TupleTypeInfo<?>) ti2).getTypeClass()); } public static class CustomTuple extends Tuple2<String, Integer> { private static final long serialVersionUID = 1L; public CustomTuple(String myField1, Integer myField2) { this.setFields(myField1, myField2); } public String getMyField1() { return this.f0; } public int getMyField2() { return this.f1; } } public static class PojoWithNonPublicDefaultCtor { public int foo, bar; PojoWithNonPublicDefaultCtor() {} } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testPojo() { // use getCrossReturnTypes() RichCrossFunction<?, ?, ?> function = new RichCrossFunction<CustomType, Integer, CustomType>() { private static final long serialVersionUID = 1L; @Override public CustomType cross(CustomType first, Integer second) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getCrossReturnTypes(function, (TypeInformation) TypeInfoParser.parse("org.apache.flink.api.java.typeutils.TypeExtractorTest$CustomType"), (TypeInformation) TypeInfoParser.parse("Integer")); Assert.assertFalse(ti.isBasicType()); Assert.assertFalse(ti.isTupleType()); Assert.assertTrue(ti instanceof PojoTypeInfo); Assert.assertEquals(ti.getTypeClass(), CustomType.class); // use getForClass() Assert.assertTrue(TypeExtractor.getForClass(CustomType.class) instanceof PojoTypeInfo); Assert.assertEquals(TypeExtractor.getForClass(CustomType.class).getTypeClass(), ti.getTypeClass()); // use getForObject() CustomType t = new CustomType("World", 1); TypeInformation<?> ti2 = TypeExtractor.getForObject(t); Assert.assertFalse(ti2.isBasicType()); Assert.assertFalse(ti2.isTupleType()); Assert.assertTrue(ti2 instanceof PojoTypeInfo); Assert.assertEquals(ti2.getTypeClass(), CustomType.class); Assert.assertFalse(TypeExtractor.getForClass(PojoWithNonPublicDefaultCtor.class) instanceof PojoTypeInfo); } @Test public void testRow() { Row row = new Row(2); row.setField(0, "string"); row.setField(1, 15); TypeInformation<Row> rowInfo = TypeExtractor.getForObject(row); Assert.assertEquals(rowInfo.getClass(), RowTypeInfo.class); Assert.assertEquals(2, rowInfo.getArity()); Assert.assertEquals( new RowTypeInfo( BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.INT_TYPE_INFO), rowInfo); Row nullRow = new Row(2); TypeInformation<Row> genericRowInfo = TypeExtractor.getForObject(nullRow); Assert.assertEquals(genericRowInfo, new GenericTypeInfo<>(Row.class)); } public static class CustomType { public String myField1; public int myField2; public CustomType() { } public CustomType(String myField1, int myField2) { this.myField1 = myField1; this.myField2 = myField2; } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testTupleWithPojo() { // use getMapReturnTypes() RichMapFunction<?, ?> function = new RichMapFunction<Tuple2<Long, CustomType>, Tuple2<Long, CustomType>>() { private static final long serialVersionUID = 1L; @Override public Tuple2<Long, CustomType> map(Tuple2<Long, CustomType> value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple2<Long,org.apache.flink.api.java.typeutils.TypeExtractorTest$CustomType>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(Tuple2.class, tti.getTypeClass()); List<FlatFieldDescriptor> ffd = new ArrayList<FlatFieldDescriptor>(); tti.getFlatFields("f0", 0, ffd); Assert.assertEquals(1, ffd.size()); Assert.assertEquals(0, ffd.get(0).getPosition() ); // Long Assert.assertTrue( ffd.get(0).getType().getTypeClass().equals(Long.class) ); ffd.clear(); tti.getFlatFields("f1.myField1", 0, ffd); Assert.assertEquals(1, ffd.get(0).getPosition() ); Assert.assertTrue( ffd.get(0).getType().getTypeClass().equals(String.class) ); ffd.clear(); tti.getFlatFields("f1.myField2", 0, ffd); Assert.assertEquals(2, ffd.get(0).getPosition() ); Assert.assertTrue( ffd.get(0).getType().getTypeClass().equals(Integer.class) ); Assert.assertEquals(Long.class, tti.getTypeAt(0).getTypeClass()); Assert.assertTrue(tti.getTypeAt(1) instanceof PojoTypeInfo); Assert.assertEquals(CustomType.class, tti.getTypeAt(1).getTypeClass()); // use getForObject() Tuple2<?, ?> t = new Tuple2<Long, CustomType>(1L, new CustomType("Hello", 1)); TypeInformation<?> ti2 = TypeExtractor.getForObject(t); Assert.assertTrue(ti2.isTupleType()); Assert.assertEquals(2, ti2.getArity()); TupleTypeInfo<?> tti2 = (TupleTypeInfo<?>) ti2; Assert.assertEquals(Tuple2.class, tti2.getTypeClass()); Assert.assertEquals(Long.class, tti2.getTypeAt(0).getTypeClass()); Assert.assertTrue(tti2.getTypeAt(1) instanceof PojoTypeInfo); Assert.assertEquals(CustomType.class, tti2.getTypeAt(1).getTypeClass()); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testValue() { // use getKeyExtractorType() KeySelector<?, ?> function = new KeySelector<StringValue, StringValue>() { private static final long serialVersionUID = 1L; @Override public StringValue getKey(StringValue value) { return null; } }; TypeInformation<?> ti = TypeExtractor.getKeySelectorTypes(function, (TypeInformation) TypeInfoParser.parse("StringValue")); Assert.assertFalse(ti.isBasicType()); Assert.assertFalse(ti.isTupleType()); Assert.assertTrue(ti instanceof ValueTypeInfo); Assert.assertEquals(ti.getTypeClass(), StringValue.class); // use getForClass() Assert.assertTrue(TypeExtractor.getForClass(StringValue.class) instanceof ValueTypeInfo); Assert.assertEquals(TypeExtractor.getForClass(StringValue.class).getTypeClass(), ti.getTypeClass()); // use getForObject() StringValue v = new StringValue("Hello"); Assert.assertTrue(TypeExtractor.getForObject(v) instanceof ValueTypeInfo); Assert.assertEquals(TypeExtractor.getForObject(v).getTypeClass(), ti.getTypeClass()); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testTupleOfValues() { // use getMapReturnTypes() RichMapFunction<?, ?> function = new RichMapFunction<Tuple2<StringValue, IntValue>, Tuple2<StringValue, IntValue>>() { private static final long serialVersionUID = 1L; @Override public Tuple2<StringValue, IntValue> map(Tuple2<StringValue, IntValue> value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple2<StringValue, IntValue>")); Assert.assertFalse(ti.isBasicType()); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(StringValue.class, ((TupleTypeInfo<?>) ti).getTypeAt(0).getTypeClass()); Assert.assertEquals(IntValue.class, ((TupleTypeInfo<?>) ti).getTypeAt(1).getTypeClass()); // use getForObject() Tuple2<StringValue, IntValue> t = new Tuple2<StringValue, IntValue>(new StringValue("x"), new IntValue(1)); TypeInformation<?> ti2 = TypeExtractor.getForObject(t); Assert.assertFalse(ti2.isBasicType()); Assert.assertTrue(ti2.isTupleType()); Assert.assertEquals(((TupleTypeInfo<?>) ti2).getTypeAt(0).getTypeClass(), StringValue.class); Assert.assertEquals(((TupleTypeInfo<?>) ti2).getTypeAt(1).getTypeClass(), IntValue.class); } public static class LongKeyValue<V> extends Tuple2<Long, V> { private static final long serialVersionUID = 1L; public LongKeyValue(Long field1, V field2) { this.f0 = field1; this.f1 = field2; } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testGenericsNotInSuperclass() { // use getMapReturnTypes() RichMapFunction<?, ?> function = new RichMapFunction<LongKeyValue<String>, LongKeyValue<String>>() { private static final long serialVersionUID = 1L; @Override public LongKeyValue<String> map(LongKeyValue<String> value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple2<Long, String>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(LongKeyValue.class, tti.getTypeClass()); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(1)); } public static class ChainedOne<X, Y> extends Tuple3<X, Long, Y> { private static final long serialVersionUID = 1L; public ChainedOne(X field0, Long field1, Y field2) { this.f0 = field0; this.f1 = field1; this.f2 = field2; } } public static class ChainedTwo<V> extends ChainedOne<String, V> { private static final long serialVersionUID = 1L; public ChainedTwo(String field0, Long field1, V field2) { super(field0, field1, field2); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testChainedGenericsNotInSuperclass() { // use TypeExtractor RichMapFunction<?, ?> function = new RichMapFunction<ChainedTwo<Integer>, ChainedTwo<Integer>>() { private static final long serialVersionUID = 1L; @Override public ChainedTwo<Integer> map(ChainedTwo<Integer> value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple3<String, Long, Integer>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(3, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(ChainedTwo.class, tti.getTypeClass()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tti.getTypeAt(1)); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, tti.getTypeAt(2)); } public static class ChainedThree extends ChainedTwo<String> { private static final long serialVersionUID = 1L; public ChainedThree(String field0, Long field1, String field2) { super(field0, field1, field2); } } public static class ChainedFour extends ChainedThree { private static final long serialVersionUID = 1L; public ChainedFour(String field0, Long field1, String field2) { super(field0, field1, field2); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testGenericsInDirectSuperclass() { // use TypeExtractor RichMapFunction<?, ?> function = new RichMapFunction<ChainedThree, ChainedThree>() { private static final long serialVersionUID = 1L; @Override public ChainedThree map(ChainedThree value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple3<String, Long, String>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(3, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(ChainedThree.class, tti.getTypeClass()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tti.getTypeAt(1)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(2)); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testGenericsNotInSuperclassWithNonGenericClassAtEnd() { // use TypeExtractor RichMapFunction<?, ?> function = new RichMapFunction<ChainedFour, ChainedFour>() { private static final long serialVersionUID = 1L; @Override public ChainedFour map(ChainedFour value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple3<String, Long, String>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(3, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(ChainedFour.class, tti.getTypeClass()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tti.getTypeAt(1)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(2)); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testMissingTupleGenerics() { RichMapFunction<?, ?> function = new RichMapFunction<String, Tuple2>() { private static final long serialVersionUID = 1L; @Override public Tuple2 map(String value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("String"), "name", true); Assert.assertTrue(ti instanceof MissingTypeInfo); try { TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("String")); Assert.fail("Expected an exception"); } catch (InvalidTypesException e) { // expected } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testTupleSupertype() { RichMapFunction<?, ?> function = new RichMapFunction<String, Tuple>() { private static final long serialVersionUID = 1L; @Override public Tuple map(String value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("String"), "name", true); Assert.assertTrue(ti instanceof MissingTypeInfo); try { TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("String")); Assert.fail("Expected an exception"); } catch (InvalidTypesException e) { // expected } } public static class SameTypeVariable<X> extends Tuple2<X, X> { private static final long serialVersionUID = 1L; public SameTypeVariable(X field0, X field1) { super(field0, field1); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testSameGenericVariable() { RichMapFunction<?, ?> function = new RichMapFunction<SameTypeVariable<String>, SameTypeVariable<String>>() { private static final long serialVersionUID = 1L; @Override public SameTypeVariable<String> map(SameTypeVariable<String> value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple2<String, String>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(SameTypeVariable.class, tti.getTypeClass()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(1)); } public static class Nested<V, T> extends Tuple2<V, Tuple2<T, T>> { private static final long serialVersionUID = 1L; public Nested(V field0, Tuple2<T, T> field1) { super(field0, field1); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testNestedTupleGenerics() { RichMapFunction<?, ?> function = new RichMapFunction<Nested<String, Integer>, Nested<String, Integer>>() { private static final long serialVersionUID = 1L; @Override public Nested<String, Integer> map(Nested<String, Integer> value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple2<String, Tuple2<Integer, Integer>>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(Nested.class, tti.getTypeClass()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertTrue(tti.getTypeAt(1).isTupleType()); Assert.assertEquals(2, tti.getTypeAt(1).getArity()); // Nested TupleTypeInfo<?> tti2 = (TupleTypeInfo<?>) tti.getTypeAt(1); Assert.assertEquals(Tuple2.class, tti2.getTypeClass()); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, tti2.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, tti2.getTypeAt(1)); } public static class Nested2<T> extends Nested<T, Nested<Integer, T>> { private static final long serialVersionUID = 1L; public Nested2(T field0, Tuple2<Nested<Integer, T>, Nested<Integer, T>> field1) { super(field0, field1); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testNestedTupleGenerics2() { RichMapFunction<?, ?> function = new RichMapFunction<Nested2<Boolean>, Nested2<Boolean>>() { private static final long serialVersionUID = 1L; @Override public Nested2<Boolean> map(Nested2<Boolean> value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple2<Boolean, Tuple2<Tuple2<Integer, Tuple2<Boolean, Boolean>>, Tuple2<Integer, Tuple2<Boolean, Boolean>>>>")); // Should be // Tuple2<Boolean, Tuple2<Tuple2<Integer, Tuple2<Boolean, Boolean>>, Tuple2<Integer, Tuple2<Boolean, Boolean>>>> // 1st nested level Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, tti.getTypeAt(0)); Assert.assertTrue(tti.getTypeAt(1).isTupleType()); // 2nd nested level TupleTypeInfo<?> tti2 = (TupleTypeInfo<?>) tti.getTypeAt(1); Assert.assertTrue(tti2.getTypeAt(0).isTupleType()); Assert.assertTrue(tti2.getTypeAt(1).isTupleType()); // 3rd nested level TupleTypeInfo<?> tti3 = (TupleTypeInfo<?>) tti2.getTypeAt(0); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, tti3.getTypeAt(0)); Assert.assertTrue(tti3.getTypeAt(1).isTupleType()); // 4th nested level TupleTypeInfo<?> tti4 = (TupleTypeInfo<?>) tti3.getTypeAt(1); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, tti4.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, tti4.getTypeAt(1)); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testFunctionWithMissingGenerics() { RichMapFunction function = new RichMapFunction() { private static final long serialVersionUID = 1L; @Override public String map(Object value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, TypeInfoParser.parse("String"), "name", true); Assert.assertTrue(ti instanceof MissingTypeInfo); try { TypeExtractor.getMapReturnTypes(function, TypeInfoParser.parse("String")); Assert.fail("Expected an exception"); } catch (InvalidTypesException e) { // expected } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testFunctionDependingOnInputAsSuperclass() { IdentityMapper<Boolean> function = new IdentityMapper<Boolean>() { private static final long serialVersionUID = 1L; }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Boolean")); Assert.assertTrue(ti.isBasicType()); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, ti); } public class IdentityMapper<T> extends RichMapFunction<T, T> { private static final long serialVersionUID = 1L; @Override public T map(T value) throws Exception { return null; } } @Test public void testFunctionDependingOnInputFromInput() { IdentityMapper<Boolean> function = new IdentityMapper<Boolean>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, BasicTypeInfo.BOOLEAN_TYPE_INFO); Assert.assertTrue(ti.isBasicType()); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, ti); } @Test public void testFunctionDependingOnInputWithMissingInput() { IdentityMapper<Boolean> function = new IdentityMapper<Boolean>(); try { TypeExtractor.getMapReturnTypes(function, null); Assert.fail("exception expected"); } catch (InvalidTypesException e) { // right } } public class IdentityMapper2<T> extends RichMapFunction<Tuple2<T, String>, T> { private static final long serialVersionUID = 1L; @Override public T map(Tuple2<T, String> value) throws Exception { return null; } } @Test public void testFunctionDependingOnInputWithTupleInput() { IdentityMapper2<Boolean> function = new IdentityMapper2<Boolean>(); TypeInformation<Tuple2<Boolean, String>> inputType = new TupleTypeInfo<Tuple2<Boolean, String>>(BasicTypeInfo.BOOLEAN_TYPE_INFO, BasicTypeInfo.STRING_TYPE_INFO); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, inputType); Assert.assertTrue(ti.isBasicType()); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, ti); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testFunctionDependingOnInputWithCustomTupleInput() { IdentityMapper<SameTypeVariable<String>> function = new IdentityMapper<SameTypeVariable<String>>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple2<String, String>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(1)); } public class IdentityMapper3<T, V> extends RichMapFunction<T, V> { private static final long serialVersionUID = 1L; @Override public V map(T value) throws Exception { return null; } } @Test public void testFunctionDependingOnUnknownInput() { IdentityMapper3<Boolean, String> function = new IdentityMapper3<Boolean, String>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, BasicTypeInfo.BOOLEAN_TYPE_INFO, "name", true); Assert.assertTrue(ti instanceof MissingTypeInfo); try { TypeExtractor.getMapReturnTypes(function, BasicTypeInfo.BOOLEAN_TYPE_INFO); Assert.fail("Expected an exception"); } catch (InvalidTypesException e) { // expected } } public class IdentityMapper4<D> extends IdentityMapper<D> { private static final long serialVersionUID = 1L; } @Test public void testFunctionDependingOnInputWithFunctionHierarchy() { IdentityMapper4<String> function = new IdentityMapper4<String>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, BasicTypeInfo.STRING_TYPE_INFO); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, ti); } public class IdentityMapper5<D> extends IdentityMapper<Tuple2<D, D>> { private static final long serialVersionUID = 1L; } @Test public void testFunctionDependingOnInputWithFunctionHierarchy2() { IdentityMapper5<String> function = new IdentityMapper5<String>(); @SuppressWarnings({ "rawtypes", "unchecked" }) TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, new TupleTypeInfo(BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.STRING_TYPE_INFO)); Assert.assertTrue(ti.isTupleType()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(1)); } public class Mapper extends IdentityMapper<String> { private static final long serialVersionUID = 1L; @Override public String map(String value) throws Exception { return null; } } public class Mapper2 extends Mapper { private static final long serialVersionUID = 1L; @Override public String map(String value) throws Exception { return null; } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testFunctionWithNoGenericSuperclass() { RichMapFunction<?, ?> function = new Mapper2(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("String")); Assert.assertTrue(ti.isBasicType()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, ti); } public class OneAppender<T> extends RichMapFunction<T, Tuple2<T, Integer>> { private static final long serialVersionUID = 1L; public Tuple2<T, Integer> map(T value) { return new Tuple2<T, Integer>(value, 1); } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testFunctionDependingPartialOnInput() { RichMapFunction<?, ?> function = new OneAppender<DoubleValue>() { private static final long serialVersionUID = 1L; }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("DoubleValue")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertTrue(tti.getTypeAt(0) instanceof ValueTypeInfo<?>); ValueTypeInfo<?> vti = (ValueTypeInfo<?>) tti.getTypeAt(0); Assert.assertEquals(DoubleValue.class, vti.getTypeClass()); Assert.assertTrue(tti.getTypeAt(1).isBasicType()); Assert.assertEquals(Integer.class , tti.getTypeAt(1).getTypeClass()); } @Test public void testFunctionDependingPartialOnInput2() { RichMapFunction<DoubleValue, ?> function = new OneAppender<DoubleValue>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, new ValueTypeInfo<DoubleValue>(DoubleValue.class)); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertTrue(tti.getTypeAt(0) instanceof ValueTypeInfo<?>); ValueTypeInfo<?> vti = (ValueTypeInfo<?>) tti.getTypeAt(0); Assert.assertEquals(DoubleValue.class, vti.getTypeClass()); Assert.assertTrue(tti.getTypeAt(1).isBasicType()); Assert.assertEquals(Integer.class , tti.getTypeAt(1).getTypeClass()); } public class FieldDuplicator<T> extends RichMapFunction<T, Tuple2<T, T>> { private static final long serialVersionUID = 1L; public Tuple2<T, T> map(T value) { return new Tuple2<T, T>(value, value); } } @Test public void testFunctionInputInOutputMultipleTimes() { RichMapFunction<Float, ?> function = new FieldDuplicator<Float>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, BasicTypeInfo.FLOAT_TYPE_INFO); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(BasicTypeInfo.FLOAT_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.FLOAT_TYPE_INFO, tti.getTypeAt(1)); } @Test public void testFunctionInputInOutputMultipleTimes2() { RichMapFunction<Tuple2<Float, Float>, ?> function = new FieldDuplicator<Tuple2<Float, Float>>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, new TupleTypeInfo<Tuple2<Float, Float>>( BasicTypeInfo.FLOAT_TYPE_INFO, BasicTypeInfo.FLOAT_TYPE_INFO)); // should be // Tuple2<Tuple2<Float, Float>, Tuple2<Float, Float>> Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; // 2nd nested level Assert.assertTrue(tti.getTypeAt(0).isTupleType()); TupleTypeInfo<?> tti2 = (TupleTypeInfo<?>) tti.getTypeAt(0); Assert.assertEquals(BasicTypeInfo.FLOAT_TYPE_INFO, tti2.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.FLOAT_TYPE_INFO, tti2.getTypeAt(1)); Assert.assertTrue(tti.getTypeAt(0).isTupleType()); TupleTypeInfo<?> tti3 = (TupleTypeInfo<?>) tti.getTypeAt(1); Assert.assertEquals(BasicTypeInfo.FLOAT_TYPE_INFO, tti3.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.FLOAT_TYPE_INFO, tti3.getTypeAt(1)); } public interface Testable {} public static abstract class AbstractClassWithoutMember {} public static abstract class AbstractClassWithMember { public int x; } @Test public void testAbstractAndInterfaceTypes() { // interface RichMapFunction<String, ?> function = new RichMapFunction<String, Testable>() { private static final long serialVersionUID = 1L; @Override public Testable map(String value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, BasicTypeInfo.STRING_TYPE_INFO); Assert.assertTrue(ti instanceof GenericTypeInfo); // abstract class with out class member RichMapFunction<String, ?> function2 = new RichMapFunction<String, AbstractClassWithoutMember>() { private static final long serialVersionUID = 1L; @Override public AbstractClassWithoutMember map(String value) throws Exception { return null; } }; ti = TypeExtractor.getMapReturnTypes(function2, BasicTypeInfo.STRING_TYPE_INFO); Assert.assertTrue(ti instanceof GenericTypeInfo); // abstract class with class member RichMapFunction<String, ?> function3 = new RichMapFunction<String, AbstractClassWithMember>() { private static final long serialVersionUID = 1L; @Override public AbstractClassWithMember map(String value) throws Exception { return null; } }; ti = TypeExtractor.getMapReturnTypes(function3, BasicTypeInfo.STRING_TYPE_INFO); Assert.assertTrue(ti instanceof PojoTypeInfo); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testValueSupertypeException() { RichMapFunction<?, ?> function = new RichMapFunction<StringValue, Value>() { private static final long serialVersionUID = 1L; @Override public Value map(StringValue value) throws Exception { return null; } }; TypeInformation<?> ti =TypeExtractor.getMapReturnTypes(function, (TypeInformation)TypeInfoParser.parse("StringValue"), "name", true); Assert.assertTrue(ti instanceof MissingTypeInfo); try { TypeExtractor.getMapReturnTypes(function, (TypeInformation)TypeInfoParser.parse("StringValue")); Assert.fail("Expected an exception"); } catch (InvalidTypesException e) { // expected } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testBasicArray() { // use getCoGroupReturnTypes() RichCoGroupFunction<?, ?, ?> function = new RichCoGroupFunction<String[], String[], String[]>() { private static final long serialVersionUID = 1L; @Override public void coGroup(Iterable<String[]> first, Iterable<String[]> second, Collector<String[]> out) throws Exception { // nothing to do } }; TypeInformation<?> ti = TypeExtractor.getCoGroupReturnTypes(function, (TypeInformation) TypeInfoParser.parse("String[]"), (TypeInformation) TypeInfoParser.parse("String[]")); Assert.assertFalse(ti.isBasicType()); Assert.assertFalse(ti.isTupleType()); // Due to a Java 6 bug the classification can be slightly wrong Assert.assertTrue(ti instanceof BasicArrayTypeInfo<?,?> || ti instanceof ObjectArrayTypeInfo<?,?>); if(ti instanceof BasicArrayTypeInfo<?,?>) { Assert.assertEquals(BasicArrayTypeInfo.STRING_ARRAY_TYPE_INFO, ti); } else { Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, ((ObjectArrayTypeInfo<?,?>) ti).getComponentInfo()); } } @Test public void testBasicArray2() { RichMapFunction<Boolean[], ?> function = new IdentityMapper<Boolean[]>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, BasicArrayTypeInfo.BOOLEAN_ARRAY_TYPE_INFO); Assert.assertTrue(ti instanceof BasicArrayTypeInfo<?, ?>); BasicArrayTypeInfo<?, ?> bati = (BasicArrayTypeInfo<?, ?>) ti; Assert.assertTrue(bati.getComponentInfo().isBasicType()); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, bati.getComponentInfo()); } public static class CustomArrayObject {} @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testCustomArray() { RichMapFunction<?, ?> function = new RichMapFunction<CustomArrayObject[], CustomArrayObject[]>() { private static final long serialVersionUID = 1L; @Override public CustomArrayObject[] map(CustomArrayObject[] value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("org.apache.flink.api.java.typeutils.TypeExtractorTest$CustomArrayObject[]")); Assert.assertTrue(ti instanceof ObjectArrayTypeInfo<?, ?>); Assert.assertEquals(CustomArrayObject.class, ((ObjectArrayTypeInfo<?, ?>) ti).getComponentInfo().getTypeClass()); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testTupleArray() { RichMapFunction<?, ?> function = new RichMapFunction<Tuple2<String, String>[], Tuple2<String, String>[]>() { private static final long serialVersionUID = 1L; @Override public Tuple2<String, String>[] map(Tuple2<String, String>[] value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple2<String, String>[]")); Assert.assertTrue(ti instanceof ObjectArrayTypeInfo<?, ?>); ObjectArrayTypeInfo<?, ?> oati = (ObjectArrayTypeInfo<?, ?>) ti; Assert.assertTrue(oati.getComponentInfo().isTupleType()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) oati.getComponentInfo(); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(1)); } public class CustomArrayObject2<F> extends Tuple1<F> { private static final long serialVersionUID = 1L; } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testCustomArrayWithTypeVariable() { RichMapFunction<CustomArrayObject2<Boolean>[], ?> function = new IdentityMapper<CustomArrayObject2<Boolean>[]>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple1<Boolean>[]")); Assert.assertTrue(ti instanceof ObjectArrayTypeInfo<?, ?>); ObjectArrayTypeInfo<?, ?> oati = (ObjectArrayTypeInfo<?, ?>) ti; Assert.assertTrue(oati.getComponentInfo().isTupleType()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) oati.getComponentInfo(); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, tti.getTypeAt(0)); } public class GenericArrayClass<T> extends RichMapFunction<T[], T[]> { private static final long serialVersionUID = 1L; @Override public T[] map(T[] value) throws Exception { return null; } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testParameterizedArrays() { GenericArrayClass<Boolean> function = new GenericArrayClass<Boolean>(){ private static final long serialVersionUID = 1L; }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Boolean[]")); Assert.assertTrue(ti instanceof ObjectArrayTypeInfo<?,?>); ObjectArrayTypeInfo<?, ?> oati = (ObjectArrayTypeInfo<?, ?>) ti; Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, oati.getComponentInfo()); } public static class MyObject<T> { public T myField; } public static class InType extends MyObject<String> {} @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testParameterizedPojo() { RichMapFunction<?, ?> function = new RichMapFunction<InType, MyObject<String>>() { private static final long serialVersionUID = 1L; @Override public MyObject<String> map(InType value) throws Exception { return null; } }; TypeInformation<?> inType = TypeExtractor.createTypeInfo(InType.class); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) inType); Assert.assertTrue(ti instanceof PojoTypeInfo); } @Test public void testFunctionDependingOnInputWithTupleInputWithTypeMismatch() { IdentityMapper2<Boolean> function = new IdentityMapper2<Boolean>(); TypeInformation<Tuple2<Boolean, String>> inputType = new TupleTypeInfo<Tuple2<Boolean, String>>(BasicTypeInfo.BOOLEAN_TYPE_INFO, BasicTypeInfo.INT_TYPE_INFO); // input is: Tuple2<Boolean, Integer> // allowed: Tuple2<?, String> try { TypeExtractor.getMapReturnTypes(function, inputType); Assert.fail("exception expected"); } catch (InvalidTypesException e) { // right } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testInputMismatchExceptions() { RichMapFunction<?, ?> function = new RichMapFunction<Tuple2<String, String>, String>() { private static final long serialVersionUID = 1L; @Override public String map(Tuple2<String, String> value) throws Exception { return null; } }; try { TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple2<Integer, String>")); Assert.fail("exception expected"); } catch (InvalidTypesException e) { // right } try { TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeInfoParser.parse("Tuple3<String, String, String>")); Assert.fail("exception expected"); } catch (InvalidTypesException e) { // right } RichMapFunction<?, ?> function2 = new RichMapFunction<StringValue, String>() { private static final long serialVersionUID = 1L; @Override public String map(StringValue value) throws Exception { return null; } }; try { TypeExtractor.getMapReturnTypes(function2, (TypeInformation) TypeInfoParser.parse("IntValue")); Assert.fail("exception expected"); } catch (InvalidTypesException e) { // right } RichMapFunction<?, ?> function3 = new RichMapFunction<Tuple1<Integer>[], String>() { private static final long serialVersionUID = 1L; @Override public String map(Tuple1<Integer>[] value) throws Exception { return null; } }; try { TypeExtractor.getMapReturnTypes(function3, (TypeInformation) TypeInfoParser.parse("Integer[]")); Assert.fail("exception expected"); } catch (InvalidTypesException e) { // right } } public static class DummyFlatMapFunction<A,B,C,D> extends RichFlatMapFunction<Tuple2<A,B>, Tuple2<C,D>> { private static final long serialVersionUID = 1L; @Override public void flatMap(Tuple2<A, B> value, Collector<Tuple2<C, D>> out) throws Exception { } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testTypeErasure() { TypeInformation<?> ti = TypeExtractor.getFlatMapReturnTypes(new DummyFlatMapFunction<String, Integer, String, Boolean>(), (TypeInformation) TypeInfoParser.parse("Tuple2<String, Integer>"), "name", true); Assert.assertTrue(ti instanceof MissingTypeInfo); try { TypeExtractor.getFlatMapReturnTypes(new DummyFlatMapFunction<String, Integer, String, Boolean>(), (TypeInformation) TypeInfoParser.parse("Tuple2<String, Integer>")); Assert.fail("Expected an exception"); } catch (InvalidTypesException e) { // expected } } public static class MyQueryableMapper<A> extends RichMapFunction<String, A> implements ResultTypeQueryable<A> { private static final long serialVersionUID = 1L; @SuppressWarnings("unchecked") @Override public TypeInformation<A> getProducedType() { return (TypeInformation<A>) BasicTypeInfo.INT_TYPE_INFO; } @Override public A map(String value) throws Exception { return null; } } @Test public void testResultTypeQueryable() { TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(new MyQueryableMapper<Integer>(), BasicTypeInfo.STRING_TYPE_INFO); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, ti); } @Test public void testTupleWithPrimitiveArray() { RichMapFunction<Integer, Tuple9<int[],double[],long[],byte[],char[],float[],short[], boolean[], String[]>> function = new RichMapFunction<Integer, Tuple9<int[],double[],long[],byte[],char[],float[],short[], boolean[], String[]>>() { private static final long serialVersionUID = 1L; @Override public Tuple9<int[], double[], long[], byte[], char[], float[], short[], boolean[], String[]> map(Integer value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, BasicTypeInfo.INT_TYPE_INFO); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(PrimitiveArrayTypeInfo.INT_PRIMITIVE_ARRAY_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(PrimitiveArrayTypeInfo.DOUBLE_PRIMITIVE_ARRAY_TYPE_INFO, tti.getTypeAt(1)); Assert.assertEquals(PrimitiveArrayTypeInfo.LONG_PRIMITIVE_ARRAY_TYPE_INFO, tti.getTypeAt(2)); Assert.assertEquals(PrimitiveArrayTypeInfo.BYTE_PRIMITIVE_ARRAY_TYPE_INFO, tti.getTypeAt(3)); Assert.assertEquals(PrimitiveArrayTypeInfo.CHAR_PRIMITIVE_ARRAY_TYPE_INFO, tti.getTypeAt(4)); Assert.assertEquals(PrimitiveArrayTypeInfo.FLOAT_PRIMITIVE_ARRAY_TYPE_INFO, tti.getTypeAt(5)); Assert.assertEquals(PrimitiveArrayTypeInfo.SHORT_PRIMITIVE_ARRAY_TYPE_INFO, tti.getTypeAt(6)); Assert.assertEquals(PrimitiveArrayTypeInfo.BOOLEAN_PRIMITIVE_ARRAY_TYPE_INFO, tti.getTypeAt(7)); Assert.assertEquals(BasicArrayTypeInfo.STRING_ARRAY_TYPE_INFO, tti.getTypeAt(8)); } @Test public void testFunction() { RichMapFunction<String, Boolean> mapInterface = new RichMapFunction<String, Boolean>() { private static final long serialVersionUID = 1L; @Override public void setRuntimeContext(RuntimeContext t) { } @Override public void open(Configuration parameters) throws Exception { } @Override public RuntimeContext getRuntimeContext() { return null; } @Override public void close() throws Exception { } @Override public Boolean map(String record) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(mapInterface, BasicTypeInfo.STRING_TYPE_INFO); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, ti); } @Test public void testInterface() { MapFunction<String, Boolean> mapInterface = new MapFunction<String, Boolean>() { private static final long serialVersionUID = 1L; @Override public Boolean map(String record) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(mapInterface, BasicTypeInfo.STRING_TYPE_INFO); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, ti); } @Test public void testCreateTypeInfoFromInstance() { ResultTypeQueryable instance = new ResultTypeQueryable<Long>() { @Override public TypeInformation<Long> getProducedType() { return BasicTypeInfo.LONG_TYPE_INFO; } }; TypeInformation<?> ti = TypeExtractor.createTypeInfo(instance, null, null, 0); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, ti); // method also needs to work for instances that do not implement ResultTypeQueryable MapFunction<Integer, Long> func = new MapFunction<Integer, Long>() { @Override public Long map(Integer value) throws Exception { return value.longValue(); } }; ti = TypeExtractor.createTypeInfo(func, MapFunction.class, func.getClass(), 0); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, ti); } @SuppressWarnings({ "serial", "unchecked", "rawtypes" }) @Test public void testExtractKeySelector() { KeySelector<String, Integer> selector = new KeySelector<String, Integer>() { @Override public Integer getKey(String value) { return null; } }; TypeInformation<?> ti = TypeExtractor.getKeySelectorTypes(selector, BasicTypeInfo.STRING_TYPE_INFO); Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, ti); try { TypeExtractor.getKeySelectorTypes((KeySelector) selector, BasicTypeInfo.BOOLEAN_TYPE_INFO); Assert.fail(); } catch (InvalidTypesException e) { // good } catch (Exception e) { Assert.fail("wrong exception type"); } } public static class DuplicateValue<T> implements MapFunction<Tuple1<T>, Tuple2<T, T>> { private static final long serialVersionUID = 1L; @Override public Tuple2<T, T> map(Tuple1<T> vertex) { return new Tuple2<T, T>(vertex.f0, vertex.f0); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testDuplicateValue() { TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction) new DuplicateValue<String>(), TypeInfoParser.parse("Tuple1<String>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(1)); } public static class DuplicateValueNested<T> implements MapFunction<Tuple1<Tuple1<T>>, Tuple2<T, T>> { private static final long serialVersionUID = 1L; @Override public Tuple2<T, T> map(Tuple1<Tuple1<T>> vertex) { return new Tuple2<T, T>(null, null); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testDuplicateValueNested() { TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction) new DuplicateValueNested<String>(), TypeInfoParser.parse("Tuple1<Tuple1<String>>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(2, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(1)); } public static class Edge<K, V> extends Tuple3<K, K, V> { private static final long serialVersionUID = 1L; } public static class EdgeMapper<K, V> implements MapFunction<Edge<K, V>, Edge<K, V>> { private static final long serialVersionUID = 1L; @Override public Edge<K, V> map(Edge<K, V> value) throws Exception { return null; } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testInputInference1() { EdgeMapper<String, Double> em = new EdgeMapper<String, Double>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction) em, TypeInfoParser.parse("Tuple3<String, String, Double>")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(3, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tti.getTypeAt(1)); Assert.assertEquals(BasicTypeInfo.DOUBLE_TYPE_INFO, tti.getTypeAt(2)); } public static class EdgeMapper2<V> implements MapFunction<V, Edge<Long, V>> { private static final long serialVersionUID = 1L; @Override public Edge<Long, V> map(V value) throws Exception { return null; } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testInputInference2() { EdgeMapper2<Boolean> em = new EdgeMapper2<Boolean>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction) em, TypeInfoParser.parse("Boolean")); Assert.assertTrue(ti.isTupleType()); Assert.assertEquals(3, ti.getArity()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tti.getTypeAt(1)); Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, tti.getTypeAt(2)); } public static class EdgeMapper3<K, V> implements MapFunction<Edge<K, V>, V> { private static final long serialVersionUID = 1L; @Override public V map(Edge<K, V> value) throws Exception { return null; } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testInputInference3() { EdgeMapper3<Boolean, String> em = new EdgeMapper3<Boolean, String>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction) em, TypeInfoParser.parse("Tuple3<Boolean,Boolean,String>")); Assert.assertTrue(ti.isBasicType()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, ti); } public static class EdgeMapper4<K, V> implements MapFunction<Edge<K, V>[], V> { private static final long serialVersionUID = 1L; @Override public V map(Edge<K, V>[] value) throws Exception { return null; } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testInputInference4() { EdgeMapper4<Boolean, String> em = new EdgeMapper4<Boolean, String>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction) em, TypeInfoParser.parse("Tuple3<Boolean,Boolean,String>[]")); Assert.assertTrue(ti.isBasicType()); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, ti); } public static class CustomTuple2WithArray<K> extends Tuple2<K[], K> { public CustomTuple2WithArray() { // default constructor } } public class JoinWithCustomTuple2WithArray<T> extends RichJoinFunction<CustomTuple2WithArray<T>, CustomTuple2WithArray<T>, CustomTuple2WithArray<T>> { @Override public CustomTuple2WithArray<T> join(CustomTuple2WithArray<T> first, CustomTuple2WithArray<T> second) throws Exception { return null; } } @Test public void testInputInferenceWithCustomTupleAndRichFunction() { JoinFunction<CustomTuple2WithArray<Long>, CustomTuple2WithArray<Long>, CustomTuple2WithArray<Long>> function = new JoinWithCustomTuple2WithArray<>(); TypeInformation<?> ti = TypeExtractor.getJoinReturnTypes( function, new TypeHint<CustomTuple2WithArray<Long>>(){}.getTypeInfo(), new TypeHint<CustomTuple2WithArray<Long>>(){}.getTypeInfo()); Assert.assertTrue(ti.isTupleType()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tti.getTypeAt(1)); Assert.assertTrue(tti.getTypeAt(0) instanceof ObjectArrayTypeInfo<?, ?>); ObjectArrayTypeInfo<?, ?> oati = (ObjectArrayTypeInfo<?, ?>) tti.getTypeAt(0); Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, oati.getComponentInfo()); } public static enum MyEnum { ONE, TWO, THREE } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testEnumType() { MapFunction<?, ?> mf = new MapFunction<MyEnum, MyEnum>() { private static final long serialVersionUID = 1L; @Override public MyEnum map(MyEnum value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction) mf, new EnumTypeInfo(MyEnum.class)); Assert.assertTrue(ti instanceof EnumTypeInfo); Assert.assertEquals(ti.getTypeClass(), MyEnum.class); } public static class MapperWithMultiDimGenericArray<T> implements MapFunction<T[][][], Tuple1<T>[][][]> { private static final long serialVersionUID = 1L; @Override public Tuple1<T>[][][] map(T[][][] value) throws Exception { return null; } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testMultiDimensionalArray() { // tuple array MapFunction<?,?> function = new MapFunction<Tuple2<Integer, Double>[][], Tuple2<Integer, Double>[][]>() { private static final long serialVersionUID = 1L; @Override public Tuple2<Integer, Double>[][] map( Tuple2<Integer, Double>[][] value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction)function, TypeInfoParser.parse("Tuple2<Integer, Double>[][]")); Assert.assertEquals("ObjectArrayTypeInfo<ObjectArrayTypeInfo<Java Tuple2<Integer, Double>>>", ti.toString()); // primitive array function = new MapFunction<int[][][], int[][][]>() { private static final long serialVersionUID = 1L; @Override public int[][][] map( int[][][] value) throws Exception { return null; } }; ti = TypeExtractor.getMapReturnTypes((MapFunction)function, TypeInfoParser.parse("int[][][]")); Assert.assertEquals("ObjectArrayTypeInfo<ObjectArrayTypeInfo<int[]>>", ti.toString()); // basic array function = new MapFunction<Integer[][][], Integer[][][]>() { private static final long serialVersionUID = 1L; @Override public Integer[][][] map( Integer[][][] value) throws Exception { return null; } }; ti = TypeExtractor.getMapReturnTypes((MapFunction)function, TypeInfoParser.parse("Integer[][][]")); Assert.assertEquals("ObjectArrayTypeInfo<ObjectArrayTypeInfo<BasicArrayTypeInfo<Integer>>>", ti.toString()); // pojo array function = new MapFunction<CustomType[][][], CustomType[][][]>() { private static final long serialVersionUID = 1L; @Override public CustomType[][][] map( CustomType[][][] value) throws Exception { return null; } }; ti = TypeExtractor.getMapReturnTypes((MapFunction)function, TypeInfoParser.parse("org.apache.flink.api.java.typeutils.TypeExtractorTest$CustomType<" + "myField1=String,myField2=int" + ">[][][]")); Assert.assertEquals("ObjectArrayTypeInfo<ObjectArrayTypeInfo<ObjectArrayTypeInfo<" + "PojoType<org.apache.flink.api.java.typeutils.TypeExtractorTest$CustomType, fields = [myField1: String, myField2: Integer]>" + ">>>", ti.toString()); // generic array ti = TypeExtractor.getMapReturnTypes((MapFunction) new MapperWithMultiDimGenericArray<String>(), TypeInfoParser.parse("String[][][]")); Assert.assertEquals("ObjectArrayTypeInfo<ObjectArrayTypeInfo<ObjectArrayTypeInfo<Java Tuple1<String>>>>", ti.toString()); } @SuppressWarnings("rawtypes") public static class MapWithResultTypeQueryable implements MapFunction, ResultTypeQueryable { private static final long serialVersionUID = 1L; @Override public TypeInformation getProducedType() { return BasicTypeInfo.STRING_TYPE_INFO; } @Override public Object map(Object value) throws Exception { return null; } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testInputMismatchWithRawFuntion() { MapFunction<?, ?> function = new MapWithResultTypeQueryable(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction)function, BasicTypeInfo.INT_TYPE_INFO); Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, ti); } public static class Either1<T> extends Either<String, T> { @Override public String left() throws IllegalStateException { return null; } @Override public T right() throws IllegalStateException { return null; } } public static class Either2 extends Either1<Tuple1<Integer>> { // nothing to do here } public static class EitherMapper<T> implements MapFunction<T, Either1<T>> { @Override public Either1<T> map(T value) throws Exception { return null; } } public static class EitherMapper2 implements MapFunction<String, Either2> { @Override public Either2 map(String value) throws Exception { return null; } } public static class EitherMapper3 implements MapFunction<Either2, Either2> { @Override public Either2 map(Either2 value) throws Exception { return null; } } @Test public void testEither() { MapFunction<?, ?> function = new MapFunction<Either<String, Boolean>, Either<String, Boolean>>() { @Override public Either<String, Boolean> map(Either<String, Boolean> value) throws Exception { return null; } }; TypeInformation<?> expected = new EitherTypeInfo(BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.BOOLEAN_TYPE_INFO); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction) function, expected); Assert.assertEquals(expected, ti); } @Test public void testEitherHierarchy() { MapFunction<?, ?> function = new EitherMapper<Boolean>(); TypeInformation<?> ti = TypeExtractor.getMapReturnTypes((MapFunction) function, BasicTypeInfo.BOOLEAN_TYPE_INFO); TypeInformation<?> expected = new EitherTypeInfo(BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.BOOLEAN_TYPE_INFO); Assert.assertEquals(expected, ti); function = new EitherMapper2(); ti = TypeExtractor.getMapReturnTypes((MapFunction) function, BasicTypeInfo.STRING_TYPE_INFO); expected = new EitherTypeInfo(BasicTypeInfo.STRING_TYPE_INFO, new TupleTypeInfo(BasicTypeInfo.INT_TYPE_INFO)); Assert.assertEquals(expected, ti); function = new EitherMapper3(); ti = TypeExtractor.getMapReturnTypes((MapFunction) function, expected); Assert.assertEquals(expected, ti); Either<String, Tuple1<Integer>> either = new Either2(); ti = TypeExtractor.getForObject(either); Assert.assertEquals(expected, ti); } @Test(expected=InvalidTypesException.class) public void testEitherFromObjectException() { Either<String, Tuple1<Integer>> either = Either.Left("test"); TypeExtractor.getForObject(either); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testGenericTypeWithSubclassInput() { Map<String, Object> inputMap = new HashMap<>(); inputMap.put("a", "b"); TypeInformation<?> inputType = TypeExtractor.createTypeInfo(inputMap.getClass()); MapFunction<?, ?> function = new MapFunction<Map<String, Object>,Map<String, Object>>(){ @Override public Map<String, Object> map(Map<String, Object> stringObjectMap) throws Exception { return stringObjectMap; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) inputType); TypeInformation<?> expected = TypeExtractor.createTypeInfo(Map.class); Assert.assertEquals(expected, ti); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test(expected=InvalidTypesException.class) public void testGenericTypeWithSuperclassInput() { TypeInformation<?> inputType = TypeExtractor.createTypeInfo(Map.class); MapFunction<?, ?> function = new MapFunction<HashMap<String, Object>,Map<String, Object>>(){ @Override public Map<String, Object> map(HashMap<String, Object> stringObjectMap) throws Exception { return stringObjectMap; } }; TypeExtractor.getMapReturnTypes(function, (TypeInformation) inputType); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void testInputWithCustomTypeInfo() { TypeInformation<?> customTypeInfo = new TypeInformation<Object>() { @Override public boolean isBasicType() { return true; } @Override public boolean isTupleType() { return true; } @Override public int getArity() { return 0; } @Override public int getTotalFields() { return 0; } @Override public Class getTypeClass() { return null; } @Override public boolean isKeyType() { return false; } @Override public TypeSerializer<Object> createSerializer(ExecutionConfig config) { return null; } @Override public String toString() { return null; } @Override public boolean equals(Object obj) { return false; } @Override public int hashCode() { return 0; } @Override public boolean canEqual(Object obj) { return false; } }; MapFunction<?, ?> function = new MapFunction<Tuple1<String>, Tuple1<Object>>() { @Override public Tuple1<Object> map(Tuple1<String> value) throws Exception { return null; } }; TypeExtractor.getMapReturnTypes(function, (TypeInformation) new TupleTypeInfo<Tuple1<Object>>(customTypeInfo)); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testBigBasicTypes() { MapFunction<?, ?> function = new MapFunction<Tuple2<BigInteger, BigDecimal>, Tuple2<BigInteger, BigDecimal>>() { @Override public Tuple2<BigInteger, BigDecimal> map(Tuple2<BigInteger, BigDecimal> value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes( function, (TypeInformation) TypeInformation.of(new TypeHint<Tuple2<BigInteger, BigDecimal>>() { })); Assert.assertTrue(ti.isTupleType()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(BasicTypeInfo.BIG_INT_TYPE_INFO, tti.getTypeAt(0)); Assert.assertEquals(BasicTypeInfo.BIG_DEC_TYPE_INFO, tti.getTypeAt(1)); // use getForClass() Assert.assertTrue(TypeExtractor.getForClass(BigInteger.class).isBasicType()); Assert.assertTrue(TypeExtractor.getForClass(BigDecimal.class).isBasicType()); Assert.assertEquals(tti.getTypeAt(0), TypeExtractor.getForClass(BigInteger.class)); Assert.assertEquals(tti.getTypeAt(1), TypeExtractor.getForClass(BigDecimal.class)); // use getForObject() Assert.assertEquals(BasicTypeInfo.BIG_INT_TYPE_INFO, TypeExtractor.getForObject(new BigInteger("42"))); Assert.assertEquals(BasicTypeInfo.BIG_DEC_TYPE_INFO, TypeExtractor.getForObject(new BigDecimal("42.42"))); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testSqlTimeTypes() { MapFunction<?, ?> function = new MapFunction<Tuple3<Date, Time, Timestamp>, Tuple3<Date, Time, Timestamp>>() { @Override public Tuple3<Date, Time, Timestamp> map(Tuple3<Date, Time, Timestamp> value) throws Exception { return null; } }; TypeInformation<?> ti = TypeExtractor.getMapReturnTypes( function, (TypeInformation) TypeInformation.of(new TypeHint<Tuple3<Date, Time, Timestamp>>() { })); Assert.assertTrue(ti.isTupleType()); TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti; Assert.assertEquals(SqlTimeTypeInfo.DATE, tti.getTypeAt(0)); Assert.assertEquals(SqlTimeTypeInfo.TIME, tti.getTypeAt(1)); Assert.assertEquals(SqlTimeTypeInfo.TIMESTAMP, tti.getTypeAt(2)); // use getForClass() Assert.assertEquals(tti.getTypeAt(0), TypeExtractor.getForClass(Date.class)); Assert.assertEquals(tti.getTypeAt(1), TypeExtractor.getForClass(Time.class)); Assert.assertEquals(tti.getTypeAt(2), TypeExtractor.getForClass(Timestamp.class)); // use getForObject() Assert.assertEquals(SqlTimeTypeInfo.DATE, TypeExtractor.getForObject(Date.valueOf("1998-12-12"))); Assert.assertEquals(SqlTimeTypeInfo.TIME, TypeExtractor.getForObject(Time.valueOf("12:37:45"))); Assert.assertEquals(SqlTimeTypeInfo.TIMESTAMP, TypeExtractor.getForObject(Timestamp.valueOf("1998-12-12 12:37:45"))); } }