/* * Copyright © 2014 Cask Data, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package co.cask.cdap.hive.objectinspector; import co.cask.cdap.common.utils.ImmutablePair; import com.google.common.collect.ImmutableList; import com.google.common.reflect.TypeToken; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorUtils; import org.apache.hadoop.hive.serde2.objectinspector.StructField; import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector; import org.junit.Assert; import org.junit.Test; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * */ public class ObjectInspectorFactoryTest { private String getObjectName(Type t) { ObjectInspector objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(t); return objectInspector.getTypeName(); } private void assertObjectInspection(Type t, Object data) throws Exception { Field[] tmpFields; // Build the expected fields, based on the type t. Exclude the transient fields. if (t instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) t; // TODO either test getDeclaredNonStaticFields or use another method tmpFields = ObjectInspectorUtils.getDeclaredNonStaticFields((Class<?>) pt.getRawType()); } else { tmpFields = ObjectInspectorUtils.getDeclaredNonStaticFields((Class<?>) t); } ImmutableList.Builder builder = ImmutableList.builder(); for (Field f : tmpFields) { if (!Modifier.isTransient(f.getModifiers()) && !f.isSynthetic()) { builder.add(f); } } List<Field> expectedFields = builder.build(); ObjectInspector oi1 = ObjectInspectorFactory.getReflectionObjectInspector(t); ObjectInspector oi2 = ObjectInspectorFactory.getReflectionObjectInspector(t); Assert.assertEquals(oi1, oi2); // metadata Assert.assertEquals(ObjectInspector.Category.STRUCT, oi1.getCategory()); StructObjectInspector soi = (StructObjectInspector) oi1; List<? extends StructField> inspectorFields = soi.getAllStructFieldRefs(); Assert.assertEquals(expectedFields.size(), inspectorFields.size()); // null for (int i = 0; i < inspectorFields.size(); i++) { Assert.assertNull(soi.getStructFieldData(null, inspectorFields.get(i))); } Assert.assertNull(soi.getStructFieldsDataAsList(null)); // non nulls ArrayList<Object> afields = new ArrayList<>(); for (int i = 0; i < expectedFields.size(); i++) { Assert.assertEquals(expectedFields.get(i).get(data), soi.getStructFieldData(data, inspectorFields.get(i))); afields.add(soi.getStructFieldData(data, inspectorFields.get(i))); } Assert.assertEquals(afields, soi.getStructFieldsDataAsList(data)); } @Test public void reflectionObjectInspectorTest() throws Exception { Assert.assertEquals("array<string>", getObjectName(new TypeToken<List<String>>() { }.getType())); Assert.assertEquals("array<struct<address:struct<street:string>>>", getObjectName(new TypeToken<List<DummyEmployee<DummyAddress<String>>>>() { }.getType())); Assert.assertEquals("array<string>", getObjectName(new TypeToken<ArrayList<String>>() { }.getType())); Assert.assertEquals("struct<first:array<string>,second:int>", getObjectName(new TypeToken<ImmutablePair<ImmutableList<String>, Integer>>() { }.getType())); Assert.assertEquals("struct<address:struct<street:string>>", getObjectName(new TypeToken<DummyEmployee<DummyAddress<String>>>() { }.getType())); Assert.assertEquals("struct<myint:int,myinteger:int,mystring:string,dummystruct:this," + "myliststring:array<string>,mymapstringstring:map<string,string>," + "employee:struct<address:struct<street:string>>,ints:array<int>>", getObjectName(DummyStruct.class)); // Make sure we don't have infinite loop with nested classes Assert.assertEquals("struct<i:int>", getObjectName(DummyParentStruct.DummyInnerClass.class)); Assert.assertEquals("struct<innerclass:struct<i:int>>", getObjectName(DummyParentStruct.class)); DummyStruct a = new DummyStruct(); a.myInt = 1; a.myInteger = 2; a.myString = "test"; a.dummyStruct = a; a.myListString = Arrays.asList(new String[]{"a", "b", "c"}); a.myMapStringString = new HashMap<>(); a.myMapStringString.put("key", "value"); a.employee = new DummyEmployee<>(new DummyAddress<>("foo")); a.ints = new int[] { 1, 2 }; assertObjectInspection(DummyStruct.class, a); // NOTE: type has to come from TokenType, otherwise, if doing new DummyEmployee<...>().getClass(), // type will not be recognized as ParameterizedType assertObjectInspection(new TypeToken<DummyEmployee<DummyAddress<String>>>() { }.getType(), new DummyEmployee<>(new DummyAddress<>("foo"))); } ////////////// Dummy classes used for this class test ///////////// private class DummyEmployee<A> { public A address; DummyEmployee(A a) { address = a; } } private class DummyAddress<B> { public B street; DummyAddress(B s) { street = s; } } public class DummyStruct { public int myInt; public Integer myInteger; public String myString; // Note: this is a recursive struct public DummyStruct dummyStruct; public List<String> myListString; public Map<String, String> myMapStringString; public DummyEmployee<DummyAddress<String>> employee; // Test arrays public int[] ints; // Test transient field public transient int t; } public class DummyParentStruct { public DummyInnerClass innerClass; public class DummyInnerClass { public int i; } } }