blob: a2a3bec7d952928efb43d2d14d81d7603a2beb60 [file] [log] [blame]
/*
* 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 org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.typeinfo.BasicArrayTypeInfo;
import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
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.java.tuple.Tuple1;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.core.memory.DataInputView;
import org.apache.flink.core.memory.DataOutputView;
import org.apache.flink.types.Value;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
/**
* Pojo Type tests.
*
* <p>Pojo is a bean-style class with getters, setters and empty ctor
* OR a class with all fields public (or for every private field, there has to be a public getter/setter)
* everything else is a generic type (that can't be used for field selection)
*/
public class PojoTypeExtractionTest {
/**
* Simple test type that implements the {@link Value} interface.
*/
public static class MyValue implements Value {
private static final long serialVersionUID = 8607223484689147046L;
@Override
public void write(DataOutputView out) throws IOException {}
@Override
public void read(DataInputView in) throws IOException {}
}
public static class HasDuplicateField extends WC {
@SuppressWarnings("unused")
private int count; // duplicate
}
@Test
public void testDuplicateFieldException() {
TypeInformation<?> ti = TypeExtractor.createTypeInfo(HasDuplicateField.class);
Assert.assertTrue(ti instanceof GenericTypeInfo<?>);
}
// test with correct pojo types
public static class WC { // is a pojo
public ComplexNestedClass complex; // is a pojo
private int count; // is a BasicType
public WC() {
}
public int getCount() {
return count;
}
public void setCount(int c) {
this.count = c;
}
}
public static class ComplexNestedClass { // pojo
public static int ignoreStaticField;
public transient int ignoreTransientField;
public Date date; // generic type
public Integer someNumberWithÜnicödeNäme; // BasicType
public float someFloat; // BasicType
public Tuple3<Long, Long, String> word; //Tuple Type with three basic types
public Object nothing; // generic type
public MyValue valueType; // writableType
public List<String> collection;
}
// all public test
public static class AllPublic extends ComplexNestedClass {
public ArrayList<String> somethingFancy; // generic type
public FancyCollectionSubtype<Integer> fancyIds; // generic type
public String[] fancyArray; // generic type
}
public static class FancyCollectionSubtype<T> extends HashSet<T> {
private static final long serialVersionUID = -3494469602638179921L;
}
public static class ParentSettingGenerics extends PojoWithGenerics<Integer, Long> {
public String field3;
}
public static class PojoWithGenerics<T1, T2> {
public int key;
public T1 field1;
public T2 field2;
}
public static class ComplexHierarchyTop extends ComplexHierarchy<Tuple1<String>> {}
public static class ComplexHierarchy<T> extends PojoWithGenerics<FromTuple,T> {}
// extends from Tuple and adds a field
public static class FromTuple extends Tuple3<String, String, Long> {
private static final long serialVersionUID = 1L;
public int special;
}
public static class IncorrectPojo {
private int isPrivate;
public int getIsPrivate() {
return isPrivate;
}
// setter is missing (intentional)
}
// correct pojo
public static class BeanStylePojo {
public String abc;
private int field;
public int getField() {
return this.field;
}
public void setField(int f) {
this.field = f;
}
}
public static class WrongCtorPojo {
public int a;
public WrongCtorPojo(int a) {
this.a = a;
}
}
public static class PojoWithGenericFields {
private Collection<String> users;
private boolean favorited;
public boolean isFavorited() {
return favorited;
}
public void setFavorited(boolean favorited) {
this.favorited = favorited;
}
public Collection<String> getUsers() {
return users;
}
public void setUsers(Collection<String> users) {
this.users = users;
}
}
@Test
public void testPojoWithGenericFields() {
TypeInformation<?> typeForClass = TypeExtractor.createTypeInfo(PojoWithGenericFields.class);
Assert.assertTrue(typeForClass instanceof PojoTypeInfo<?>);
}
// in this test, the location of the getters and setters is mixed across the type hierarchy.
public static class TypedPojoGetterSetterCheck extends GenericPojoGetterSetterCheck<String> {
public void setPackageProtected(String in) {
this.packageProtected = in;
}
}
public static class GenericPojoGetterSetterCheck<T> {
T packageProtected;
public T getPackageProtected() {
return packageProtected;
}
}
@Test
public void testIncorrectPojos() {
TypeInformation<?> typeForClass = TypeExtractor.createTypeInfo(IncorrectPojo.class);
Assert.assertTrue(typeForClass instanceof GenericTypeInfo<?>);
typeForClass = TypeExtractor.createTypeInfo(WrongCtorPojo.class);
Assert.assertTrue(typeForClass instanceof GenericTypeInfo<?>);
}
@Test
public void testCorrectPojos() {
TypeInformation<?> typeForClass = TypeExtractor.createTypeInfo(BeanStylePojo.class);
Assert.assertTrue(typeForClass instanceof PojoTypeInfo<?>);
typeForClass = TypeExtractor.createTypeInfo(TypedPojoGetterSetterCheck.class);
Assert.assertTrue(typeForClass instanceof PojoTypeInfo<?>);
}
@Test
public void testPojoWC() {
TypeInformation<?> typeForClass = TypeExtractor.createTypeInfo(WC.class);
checkWCPojoAsserts(typeForClass);
WC t = new WC();
t.complex = new ComplexNestedClass();
TypeInformation<?> typeForObject = TypeExtractor.getForObject(t);
checkWCPojoAsserts(typeForObject);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void checkWCPojoAsserts(TypeInformation<?> typeInfo) {
Assert.assertFalse(typeInfo.isBasicType());
Assert.assertFalse(typeInfo.isTupleType());
Assert.assertEquals(10, typeInfo.getTotalFields());
Assert.assertTrue(typeInfo instanceof PojoTypeInfo);
PojoTypeInfo<?> pojoType = (PojoTypeInfo<?>) typeInfo;
List<FlatFieldDescriptor> ffd = new ArrayList<FlatFieldDescriptor>();
String[] fields = {"count",
"complex.date",
"complex.collection",
"complex.nothing",
"complex.someFloat",
"complex.someNumberWithÜnicödeNäme",
"complex.valueType",
"complex.word.f0",
"complex.word.f1",
"complex.word.f2"};
int[] positions = {9,
1,
0,
2,
3,
4,
5,
6,
7,
8};
Assert.assertEquals(fields.length, positions.length);
for(int i = 0; i < fields.length; i++) {
pojoType.getFlatFields(fields[i], 0, ffd);
Assert.assertEquals("Too many keys returned", 1, ffd.size());
Assert.assertEquals("position of field "+fields[i]+" wrong", positions[i], ffd.get(0).getPosition());
ffd.clear();
}
pojoType.getFlatFields("complex.word.*", 0, ffd);
Assert.assertEquals(3, ffd.size());
// check if it returns 5,6,7
for(FlatFieldDescriptor ffdE : ffd) {
final int pos = ffdE.getPosition();
Assert.assertTrue(pos <= 8 );
Assert.assertTrue(6 <= pos );
if(pos == 6) {
Assert.assertEquals(Long.class, ffdE.getType().getTypeClass());
}
if(pos == 7) {
Assert.assertEquals(Long.class, ffdE.getType().getTypeClass());
}
if(pos == 8) {
Assert.assertEquals(String.class, ffdE.getType().getTypeClass());
}
}
ffd.clear();
// scala style full tuple selection for pojos
pojoType.getFlatFields("complex.word._", 0, ffd);
Assert.assertEquals(3, ffd.size());
ffd.clear();
pojoType.getFlatFields("complex.*", 0, ffd);
Assert.assertEquals(9, ffd.size());
// check if it returns 0-7
for(FlatFieldDescriptor ffdE : ffd) {
final int pos = ffdE.getPosition();
Assert.assertTrue(ffdE.getPosition() <= 8 );
Assert.assertTrue(0 <= ffdE.getPosition() );
if(pos == 0) {
Assert.assertEquals(List.class, ffdE.getType().getTypeClass());
}
if(pos == 1) {
Assert.assertEquals(Date.class, ffdE.getType().getTypeClass());
}
if(pos == 2) {
Assert.assertEquals(Object.class, ffdE.getType().getTypeClass());
}
if(pos == 3) {
Assert.assertEquals(Float.class, ffdE.getType().getTypeClass());
}
if(pos == 4) {
Assert.assertEquals(Integer.class, ffdE.getType().getTypeClass());
}
if(pos == 5) {
Assert.assertEquals(MyValue.class, ffdE.getType().getTypeClass());
}
if(pos == 6) {
Assert.assertEquals(Long.class, ffdE.getType().getTypeClass());
}
if(pos == 7) {
Assert.assertEquals(Long.class, ffdE.getType().getTypeClass());
}
if(pos == 8) {
Assert.assertEquals(String.class, ffdE.getType().getTypeClass());
}
if(pos == 9) {
Assert.assertEquals(Integer.class, ffdE.getType().getTypeClass());
}
}
ffd.clear();
pojoType.getFlatFields("*", 0, ffd);
Assert.assertEquals(10, ffd.size());
// check if it returns 0-8
for(FlatFieldDescriptor ffdE : ffd) {
Assert.assertTrue(ffdE.getPosition() <= 9 );
Assert.assertTrue(0 <= ffdE.getPosition() );
if(ffdE.getPosition() == 9) {
Assert.assertEquals(Integer.class, ffdE.getType().getTypeClass());
}
}
ffd.clear();
TypeInformation<?> typeComplexNested = pojoType.getTypeAt(0); // ComplexNestedClass complex
Assert.assertTrue(typeComplexNested instanceof PojoTypeInfo);
Assert.assertEquals(7, typeComplexNested.getArity());
Assert.assertEquals(9, typeComplexNested.getTotalFields());
PojoTypeInfo<?> pojoTypeComplexNested = (PojoTypeInfo<?>) typeComplexNested;
boolean dateSeen = false, intSeen = false, floatSeen = false,
tupleSeen = false, objectSeen = false, writableSeen = false, collectionSeen = false;
for(int i = 0; i < pojoTypeComplexNested.getArity(); i++) {
PojoField field = pojoTypeComplexNested.getPojoFieldAt(i);
String name = field.getField().getName();
if(name.equals("date")) {
if(dateSeen) {
Assert.fail("already seen");
}
dateSeen = true;
Assert.assertEquals(BasicTypeInfo.DATE_TYPE_INFO, field.getTypeInformation());
Assert.assertEquals(Date.class, field.getTypeInformation().getTypeClass());
} else if(name.equals("someNumberWithÜnicödeNäme")) {
if(intSeen) {
Assert.fail("already seen");
}
intSeen = true;
Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, field.getTypeInformation());
Assert.assertEquals(Integer.class, field.getTypeInformation().getTypeClass());
} else if(name.equals("someFloat")) {
if(floatSeen) {
Assert.fail("already seen");
}
floatSeen = true;
Assert.assertEquals(BasicTypeInfo.FLOAT_TYPE_INFO, field.getTypeInformation());
Assert.assertEquals(Float.class, field.getTypeInformation().getTypeClass());
} else if(name.equals("word")) {
if(tupleSeen) {
Assert.fail("already seen");
}
tupleSeen = true;
Assert.assertTrue(field.getTypeInformation() instanceof TupleTypeInfo<?>);
Assert.assertEquals(Tuple3.class, field.getTypeInformation().getTypeClass());
// do some more advanced checks on the tuple
TupleTypeInfo<?> tupleTypeFromComplexNested = (TupleTypeInfo<?>) field.getTypeInformation();
Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tupleTypeFromComplexNested.getTypeAt(0));
Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, tupleTypeFromComplexNested.getTypeAt(1));
Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, tupleTypeFromComplexNested.getTypeAt(2));
} else if(name.equals("nothing")) {
if(objectSeen) {
Assert.fail("already seen");
}
objectSeen = true;
Assert.assertEquals(new GenericTypeInfo<Object>(Object.class), field.getTypeInformation());
Assert.assertEquals(Object.class, field.getTypeInformation().getTypeClass());
} else if(name.equals("valueType")) {
if(writableSeen) {
Assert.fail("already seen");
}
writableSeen = true;
Assert.assertEquals(new ValueTypeInfo<>(MyValue.class), field.getTypeInformation());
Assert.assertEquals(MyValue.class, field.getTypeInformation().getTypeClass());
} else if(name.equals("collection")) {
if(collectionSeen) {
Assert.fail("already seen");
}
collectionSeen = true;
Assert.assertEquals(new GenericTypeInfo(List.class), field.getTypeInformation());
} else {
Assert.fail("field "+field+" is not expected");
}
}
Assert.assertTrue("Field was not present", dateSeen);
Assert.assertTrue("Field was not present", intSeen);
Assert.assertTrue("Field was not present", floatSeen);
Assert.assertTrue("Field was not present", tupleSeen);
Assert.assertTrue("Field was not present", objectSeen);
Assert.assertTrue("Field was not present", writableSeen);
Assert.assertTrue("Field was not present", collectionSeen);
TypeInformation<?> typeAtOne = pojoType.getTypeAt(1); // int count
Assert.assertTrue(typeAtOne instanceof BasicTypeInfo);
Assert.assertEquals(typeInfo.getTypeClass(), WC.class);
Assert.assertEquals(typeInfo.getArity(), 2);
}
@Test
public void testPojoAllPublic() {
TypeInformation<?> typeForClass = TypeExtractor.createTypeInfo(AllPublic.class);
checkAllPublicAsserts(typeForClass);
TypeInformation<?> typeForObject = TypeExtractor.getForObject(new AllPublic() );
checkAllPublicAsserts(typeForObject);
}
private void checkAllPublicAsserts(TypeInformation<?> typeInformation) {
Assert.assertTrue(typeInformation instanceof PojoTypeInfo);
Assert.assertEquals(10, typeInformation.getArity());
Assert.assertEquals(12, typeInformation.getTotalFields());
// check if the three additional fields are identified correctly
boolean arrayListSeen = false, multisetSeen = false, strArraySeen = false;
PojoTypeInfo<?> pojoTypeForClass = (PojoTypeInfo<?>) typeInformation;
for(int i = 0; i < pojoTypeForClass.getArity(); i++) {
PojoField field = pojoTypeForClass.getPojoFieldAt(i);
String name = field.getField().getName();
if(name.equals("somethingFancy")) {
if(arrayListSeen) {
Assert.fail("already seen");
}
arrayListSeen = true;
Assert.assertTrue(field.getTypeInformation() instanceof GenericTypeInfo);
Assert.assertEquals(ArrayList.class, field.getTypeInformation().getTypeClass());
} else if(name.equals("fancyIds")) {
if(multisetSeen) {
Assert.fail("already seen");
}
multisetSeen = true;
Assert.assertTrue(field.getTypeInformation() instanceof GenericTypeInfo);
Assert.assertEquals(FancyCollectionSubtype.class, field.getTypeInformation().getTypeClass());
} else if(name.equals("fancyArray")) {
if(strArraySeen) {
Assert.fail("already seen");
}
strArraySeen = true;
Assert.assertEquals(BasicArrayTypeInfo.STRING_ARRAY_TYPE_INFO, field.getTypeInformation());
Assert.assertEquals(String[].class, field.getTypeInformation().getTypeClass());
} else if(Arrays.asList("date", "someNumberWithÜnicödeNäme", "someFloat", "word", "nothing", "valueType", "collection").contains(name)) {
// ignore these, they are inherited from the ComplexNestedClass
}
else {
Assert.fail("field "+field+" is not expected");
}
}
Assert.assertTrue("Field was not present", arrayListSeen);
Assert.assertTrue("Field was not present", multisetSeen);
Assert.assertTrue("Field was not present", strArraySeen);
}
@Test
public void testPojoExtendingTuple() {
TypeInformation<?> typeForClass = TypeExtractor.createTypeInfo(FromTuple.class);
checkFromTuplePojo(typeForClass);
FromTuple ft = new FromTuple();
ft.f0 = ""; ft.f1 = ""; ft.f2 = 0L;
TypeInformation<?> typeForObject = TypeExtractor.getForObject(ft);
checkFromTuplePojo(typeForObject);
}
private void checkFromTuplePojo(TypeInformation<?> typeInformation) {
Assert.assertTrue(typeInformation instanceof PojoTypeInfo<?>);
Assert.assertEquals(4, typeInformation.getTotalFields());
PojoTypeInfo<?> pojoTypeForClass = (PojoTypeInfo<?>) typeInformation;
for(int i = 0; i < pojoTypeForClass.getArity(); i++) {
PojoField field = pojoTypeForClass.getPojoFieldAt(i);
String name = field.getField().getName();
if(name.equals("special")) {
Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, field.getTypeInformation());
} else if(name.equals("f0") || name.equals("f1")) {
Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, field.getTypeInformation());
} else if(name.equals("f2")) {
Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, field.getTypeInformation());
} else {
Assert.fail("unexpected field");
}
}
}
@Test
public void testPojoWithGenerics() {
TypeInformation<?> typeForClass = TypeExtractor.createTypeInfo(ParentSettingGenerics.class);
Assert.assertTrue(typeForClass instanceof PojoTypeInfo<?>);
PojoTypeInfo<?> pojoTypeForClass = (PojoTypeInfo<?>) typeForClass;
for(int i = 0; i < pojoTypeForClass.getArity(); i++) {
PojoField field = pojoTypeForClass.getPojoFieldAt(i);
String name = field.getField().getName();
if(name.equals("field1")) {
Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, field.getTypeInformation());
} else if (name.equals("field2")) {
Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, field.getTypeInformation());
} else if (name.equals("field3")) {
Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, field.getTypeInformation());
} else if (name.equals("key")) {
Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, field.getTypeInformation());
} else {
Assert.fail("Unexpected field "+field);
}
}
}
/**
* Test if the TypeExtractor is accepting untyped generics,
* making them GenericTypes
*/
@Test
public void testPojoWithGenericsSomeFieldsGeneric() {
TypeInformation<?> typeForClass = TypeExtractor.createTypeInfo(PojoWithGenerics.class);
Assert.assertTrue(typeForClass instanceof PojoTypeInfo<?>);
PojoTypeInfo<?> pojoTypeForClass = (PojoTypeInfo<?>) typeForClass;
for(int i = 0; i < pojoTypeForClass.getArity(); i++) {
PojoField field = pojoTypeForClass.getPojoFieldAt(i);
String name = field.getField().getName();
if(name.equals("field1")) {
Assert.assertEquals(new GenericTypeInfo<Object>(Object.class), field.getTypeInformation());
} else if (name.equals("field2")) {
Assert.assertEquals(new GenericTypeInfo<Object>(Object.class), field.getTypeInformation());
} else if (name.equals("key")) {
Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, field.getTypeInformation());
} else {
Assert.fail("Unexpected field "+field);
}
}
}
@Test
public void testPojoWithComplexHierarchy() {
TypeInformation<?> typeForClass = TypeExtractor.createTypeInfo(ComplexHierarchyTop.class);
Assert.assertTrue(typeForClass instanceof PojoTypeInfo<?>);
PojoTypeInfo<?> pojoTypeForClass = (PojoTypeInfo<?>) typeForClass;
for(int i = 0; i < pojoTypeForClass.getArity(); i++) {
PojoField field = pojoTypeForClass.getPojoFieldAt(i);
String name = field.getField().getName();
if(name.equals("field1")) {
Assert.assertTrue(field.getTypeInformation() instanceof PojoTypeInfo<?>); // From tuple is pojo (not tuple type!)
} else if (name.equals("field2")) {
Assert.assertTrue(field.getTypeInformation() instanceof TupleTypeInfo<?>);
Assert.assertTrue( ((TupleTypeInfo<?>)field.getTypeInformation()).getTypeAt(0).equals(BasicTypeInfo.STRING_TYPE_INFO) );
} else if (name.equals("key")) {
Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, field.getTypeInformation());
} else {
Assert.fail("Unexpected field "+field);
}
}
}
public static class MyMapper<T> implements MapFunction<PojoWithGenerics<Long, T>, PojoWithGenerics<T,T>> {
private static final long serialVersionUID = 1L;
@Override
public PojoWithGenerics<T, T> map(PojoWithGenerics<Long, T> value)
throws Exception {
return null;
}
}
@Test
public void testGenericPojoTypeInference1() {
MyMapper<String> function = new MyMapper<>();
TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(
function,
TypeInformation.of(new TypeHint<PojoWithGenerics<Long, String>>(){}));
Assert.assertTrue(ti instanceof PojoTypeInfo<?>);
PojoTypeInfo<?> pti = (PojoTypeInfo<?>) ti;
for(int i = 0; i < pti.getArity(); i++) {
PojoField field = pti.getPojoFieldAt(i);
String name = field.getField().getName();
if(name.equals("field1")) {
Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, field.getTypeInformation());
} else if (name.equals("field2")) {
Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, field.getTypeInformation());
} else if (name.equals("key")) {
Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, field.getTypeInformation());
} else {
Assert.fail("Unexpected field "+field);
}
}
}
public static class PojoTuple<A, B, C> extends Tuple3<B, C, Long> {
private static final long serialVersionUID = 1L;
public A extraField;
}
public static class MyMapper2<D, E> implements MapFunction<Tuple2<E, D>, PojoTuple<E, D, D>> {
private static final long serialVersionUID = 1L;
@Override
public PojoTuple<E, D, D> map(Tuple2<E, D> value) throws Exception {
return null;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testGenericPojoTypeInference2() {
MyMapper2<Boolean, Character> function = new MyMapper2<>();
TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(
function,
TypeInformation.of(new TypeHint<Tuple2<Character,Boolean>>(){}));
Assert.assertTrue(ti instanceof PojoTypeInfo<?>);
PojoTypeInfo<?> pti = (PojoTypeInfo<?>) ti;
for(int i = 0; i < pti.getArity(); i++) {
PojoField field = pti.getPojoFieldAt(i);
String name = field.getField().getName();
if(name.equals("extraField")) {
Assert.assertEquals(BasicTypeInfo.CHAR_TYPE_INFO, field.getTypeInformation());
} else if (name.equals("f0")) {
Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, field.getTypeInformation());
} else if (name.equals("f1")) {
Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, field.getTypeInformation());
} else if (name.equals("f2")) {
Assert.assertEquals(BasicTypeInfo.LONG_TYPE_INFO, field.getTypeInformation());
} else {
Assert.fail("Unexpected field "+field);
}
}
}
public static class MyMapper3<D, E> implements MapFunction<PojoTuple<E, D, D>, Tuple2<E, D>> {
private static final long serialVersionUID = 1L;
@Override
public Tuple2<E, D> map(PojoTuple<E, D, D> value) throws Exception {
return null;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testGenericPojoTypeInference3() {
MyMapper3<Boolean, Character> function = new MyMapper3<>();
TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(
function,
TypeInformation.of(new TypeHint<PojoTuple<Character, Boolean, Boolean>>(){}));
Assert.assertTrue(ti instanceof TupleTypeInfo<?>);
TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti;
Assert.assertEquals(BasicTypeInfo.CHAR_TYPE_INFO, tti.getTypeAt(0));
Assert.assertEquals(BasicTypeInfo.BOOLEAN_TYPE_INFO, tti.getTypeAt(1));
}
public static class PojoWithParameterizedFields1<Z> {
public Tuple2<Z, Z> field;
}
public static class MyMapper4<A> implements MapFunction<PojoWithParameterizedFields1<A>, A> {
private static final long serialVersionUID = 1L;
@Override
public A map(PojoWithParameterizedFields1<A> value) throws Exception {
return null;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testGenericPojoTypeInference4() {
MyMapper4<Byte> function = new MyMapper4<>();
TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(
function,
TypeInformation.of(new TypeHint<PojoWithParameterizedFields1<Byte>>(){}));
Assert.assertEquals(BasicTypeInfo.BYTE_TYPE_INFO, ti);
}
public static class PojoWithParameterizedFields2<Z> {
public PojoWithGenerics<Z, Z> field;
}
public static class MyMapper5<A> implements MapFunction<PojoWithParameterizedFields2<A>, A> {
private static final long serialVersionUID = 1L;
@Override
public A map(PojoWithParameterizedFields2<A> value) throws Exception {
return null;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testGenericPojoTypeInference5() {
MyMapper5<Byte> function = new MyMapper5<>();
TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(
function,
TypeInformation.of(new TypeHint<PojoWithParameterizedFields2<Byte>>(){}));
Assert.assertEquals(BasicTypeInfo.BYTE_TYPE_INFO, ti);
}
public static class PojoWithParameterizedFields3<Z> {
public Z[] field;
}
public static class MyMapper6<A> implements MapFunction<PojoWithParameterizedFields3<A>, A> {
private static final long serialVersionUID = 1L;
@Override
public A map(PojoWithParameterizedFields3<A> value) throws Exception {
return null;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testGenericPojoTypeInference6() {
MyMapper6<Integer> function = new MyMapper6<>();
TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(
function,
TypeInformation.of(new TypeHint<PojoWithParameterizedFields3<Integer>>(){}));
Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, ti);
}
public static class MyMapper7<A> implements MapFunction<PojoWithParameterizedFields4<A>, A> {
private static final long serialVersionUID = 1L;
@Override
public A map(PojoWithParameterizedFields4<A> value) throws Exception {
return null;
}
}
public static class PojoWithParameterizedFields4<Z> {
public Tuple1<Z>[] field;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testGenericPojoTypeInference7() {
MyMapper7<Integer> function = new MyMapper7<>();
TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(
function,
TypeInformation.of(new TypeHint<PojoWithParameterizedFields4<Integer>>(){}));
Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, ti);
}
public static class RecursivePojo1 {
public RecursivePojo1 field;
}
public static class RecursivePojo2 {
public Tuple1<RecursivePojo2> field;
}
public static class RecursivePojo3 {
public NestedPojo field;
}
public static class NestedPojo {
public RecursivePojo3 field;
}
@Test
public void testRecursivePojo1() {
TypeInformation<?> ti = TypeExtractor.createTypeInfo(RecursivePojo1.class);
Assert.assertTrue(ti instanceof PojoTypeInfo);
Assert.assertEquals(GenericTypeInfo.class, ((PojoTypeInfo) ti).getPojoFieldAt(0).getTypeInformation().getClass());
}
@Test
public void testRecursivePojo2() {
TypeInformation<?> ti = TypeExtractor.createTypeInfo(RecursivePojo2.class);
Assert.assertTrue(ti instanceof PojoTypeInfo);
PojoField pf = ((PojoTypeInfo) ti).getPojoFieldAt(0);
Assert.assertTrue(pf.getTypeInformation() instanceof TupleTypeInfo);
Assert.assertEquals(GenericTypeInfo.class, ((TupleTypeInfo) pf.getTypeInformation()).getTypeAt(0).getClass());
}
@Test
public void testRecursivePojo3() {
TypeInformation<?> ti = TypeExtractor.createTypeInfo(RecursivePojo3.class);
Assert.assertTrue(ti instanceof PojoTypeInfo);
PojoField pf = ((PojoTypeInfo) ti).getPojoFieldAt(0);
Assert.assertTrue(pf.getTypeInformation() instanceof PojoTypeInfo);
Assert.assertEquals(GenericTypeInfo.class, ((PojoTypeInfo) pf.getTypeInformation()).getPojoFieldAt(0).getTypeInformation().getClass());
}
public static class FooBarPojo {
public int foo, bar;
public FooBarPojo() {}
}
public static class DuplicateMapper implements MapFunction<FooBarPojo, Tuple2<FooBarPojo, FooBarPojo>> {
@Override
public Tuple2<FooBarPojo, FooBarPojo> map(FooBarPojo value) throws Exception {
return null;
}
}
@Test
public void testDualUseOfPojo() {
MapFunction<?, ?> function = new DuplicateMapper();
TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(function, (TypeInformation) TypeExtractor.createTypeInfo(FooBarPojo.class));
Assert.assertTrue(ti instanceof TupleTypeInfo);
TupleTypeInfo<?> tti = ((TupleTypeInfo) ti);
Assert.assertTrue(tti.getTypeAt(0) instanceof PojoTypeInfo);
Assert.assertTrue(tti.getTypeAt(1) instanceof PojoTypeInfo);
}
public static class PojoWithRecursiveGenericField<K,V> {
public PojoWithRecursiveGenericField<K,V> parent;
public PojoWithRecursiveGenericField(){}
}
@Test
public void testPojoWithRecursiveGenericField() {
TypeInformation<?> ti = TypeExtractor.createTypeInfo(PojoWithRecursiveGenericField.class);
Assert.assertTrue(ti instanceof PojoTypeInfo);
Assert.assertEquals(GenericTypeInfo.class, ((PojoTypeInfo) ti).getPojoFieldAt(0).getTypeInformation().getClass());
}
public static class MutualPojoA {
public MutualPojoB field;
}
public static class MutualPojoB {
public MutualPojoA field;
}
@Test
public void testPojosWithMutualRecursion() {
TypeInformation<?> ti = TypeExtractor.createTypeInfo(MutualPojoB.class);
Assert.assertTrue(ti instanceof PojoTypeInfo);
TypeInformation<?> pti = ((PojoTypeInfo) ti).getPojoFieldAt(0).getTypeInformation();
Assert.assertTrue(pti instanceof PojoTypeInfo);
Assert.assertEquals(GenericTypeInfo.class, ((PojoTypeInfo) pti).getPojoFieldAt(0).getTypeInformation().getClass());
}
public static class Container<T> {
public T field;
}
public static class MyType extends Container<Container<Object>> {}
@Test
public void testRecursivePojoWithTypeVariable() {
TypeInformation<?> ti = TypeExtractor.createTypeInfo(MyType.class);
Assert.assertTrue(ti instanceof PojoTypeInfo);
TypeInformation<?> pti = ((PojoTypeInfo) ti).getPojoFieldAt(0).getTypeInformation();
Assert.assertTrue(pti instanceof PojoTypeInfo);
Assert.assertEquals(GenericTypeInfo.class, ((PojoTypeInfo) pti).getPojoFieldAt(0).getTypeInformation().getClass());
}
/**
* POJO generated using Lombok.
*/
@Getter
@Setter
@NoArgsConstructor
public static class TestLombok{
private int age = 10;
private String name;
}
@Test
public void testLombokPojo() {
TypeInformation<TestLombok> ti = TypeExtractor.getForClass(TestLombok.class);
Assert.assertTrue(ti instanceof PojoTypeInfo);
PojoTypeInfo<TestLombok> pti = (PojoTypeInfo<TestLombok>) ti;
Assert.assertEquals(BasicTypeInfo.INT_TYPE_INFO, pti.getTypeAt(0));
Assert.assertEquals(BasicTypeInfo.STRING_TYPE_INFO, pti.getTypeAt(1));
}
}