/*
* 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 com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorUtils;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorUtils;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* ObjectInspectorFactory is the primary way to create new ObjectInspector
* instances.
* <p/>
* SerDe classes should call the static functions in this library to create an
* ObjectInspector to return to the caller of SerDe2.getObjectInspector().
* <p/>
* The reason of having caches here is that ObjectInspectors
* do not have an internal state - so ObjectInspectors with the
* same construction parameters should result in exactly the same
* ObjectInspector.
*/
public final class ObjectInspectorFactory {
private static ConcurrentHashMap<Type, ObjectInspector> objectInspectorCache =
new ConcurrentHashMap<>();
public static ObjectInspector getReflectionObjectInspector(Type t) {
ObjectInspector oi = objectInspectorCache.get(t);
if (oi == null) {
oi = getReflectionObjectInspectorNoCache(t);
objectInspectorCache.put(t, oi);
}
return oi;
}
private static ObjectInspector getReflectionObjectInspectorNoCache(Type t) {
if (t instanceof GenericArrayType) {
GenericArrayType at = (GenericArrayType) t;
return getStandardListObjectInspector(getReflectionObjectInspector(at.getGenericComponentType()));
}
Map<TypeVariable, Type> genericTypes = null;
if (t instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) t;
Type rawType = pt.getRawType();
// Collection?
if (Collection.class.isAssignableFrom((Class<?>) rawType)) {
return getStandardListObjectInspector(getReflectionObjectInspector(pt.getActualTypeArguments()[0]));
}
// Map?
if (Map.class.isAssignableFrom((Class<?>) rawType)) {
return getStandardMapObjectInspector(getReflectionObjectInspector(pt.getActualTypeArguments()[0]),
getReflectionObjectInspector(pt.getActualTypeArguments()[1]));
}
// Otherwise convert t to RawType so we will fall into the following if block.
t = rawType;
ImmutableMap.Builder<TypeVariable, Type> builder = ImmutableMap.builder();
for (int i = 0; i < pt.getActualTypeArguments().length; i++) {
builder.put(((Class<?>) t).getTypeParameters()[i], pt.getActualTypeArguments()[i]);
}
genericTypes = builder.build();
}
// Must be a class.
if (!(t instanceof Class)) {
throw new RuntimeException(ObjectInspectorFactory.class.getName() + " internal error:" + t);
}
Class<?> c = (Class<?>) t;
// Java Primitive Type?
if (PrimitiveObjectInspectorUtils.isPrimitiveJavaType(c)) {
return PrimitiveObjectInspectorFactory.getPrimitiveJavaObjectInspector(
PrimitiveObjectInspectorUtils.getTypeEntryFromPrimitiveJavaType(c).primitiveCategory);
}
// Java Primitive Class?
if (PrimitiveObjectInspectorUtils.isPrimitiveJavaClass(c)) {
return PrimitiveObjectInspectorFactory.getPrimitiveJavaObjectInspector(
PrimitiveObjectInspectorUtils.getTypeEntryFromPrimitiveJavaClass(c).primitiveCategory);
}
// Primitive Writable class?
if (PrimitiveObjectInspectorUtils.isPrimitiveWritableClass(c)) {
return PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(
PrimitiveObjectInspectorUtils.getTypeEntryFromPrimitiveWritableClass(c).primitiveCategory);
}
// Enum class?
if (Enum.class.isAssignableFrom(c)) {
return PrimitiveObjectInspectorFactory.getPrimitiveJavaObjectInspector(
PrimitiveObjectInspector.PrimitiveCategory.STRING);
}
// Array
if (c.isArray()) {
return getStandardListObjectInspector(getReflectionObjectInspector(c.getComponentType()));
}
// Must be struct because List and Map need to be ParameterizedType
Preconditions.checkState(!List.class.isAssignableFrom(c));
Preconditions.checkState(!Map.class.isAssignableFrom(c));
Preconditions.checkState(!c.isInterface(), "Cannot inspect an interface.");
ReflectionStructObjectInspector oi = new ReflectionStructObjectInspector();
// put it into the cache BEFORE it is initialized to make sure we can catch
// recursive types.
objectInspectorCache.put(t, oi);
Field[] fields = ObjectInspectorUtils.getDeclaredNonStaticFields(c);
List<ObjectInspector> structFieldObjectInspectors = new ArrayList<>(fields.length);
for (int i = 0; i < fields.length; i++) {
// Exclude transient fields and synthetic fields. The latter has the effect of excluding the implicit
// "this" pointer present in nested classes and that references the parent.
if (Modifier.isTransient(fields[i].getModifiers()) || fields[i].isSynthetic()) {
continue;
}
if (!oi.shouldIgnoreField(fields[i].getName())) {
Type newType = fields[i].getGenericType();
if (newType instanceof TypeVariable) {
Preconditions.checkNotNull(genericTypes, "Type was not recognized as a parameterized type.");
Preconditions.checkNotNull(genericTypes.get(newType),
"Generic type " + newType + " not a parameter of class " + c);
newType = genericTypes.get(newType);
}
structFieldObjectInspectors.add(getReflectionObjectInspector(newType));
}
}
oi.init(c, structFieldObjectInspectors);
return oi;
}
static ConcurrentHashMap<ObjectInspector, StandardListObjectInspector> cachedStandardListObjectInspector =
new ConcurrentHashMap<>();
public static StandardListObjectInspector getStandardListObjectInspector(ObjectInspector listElementObjectInspector) {
StandardListObjectInspector result = cachedStandardListObjectInspector.get(listElementObjectInspector);
if (result == null) {
result = new StandardListObjectInspector(listElementObjectInspector);
cachedStandardListObjectInspector.put(listElementObjectInspector, result);
}
return result;
}
static ConcurrentHashMap<List<ObjectInspector>, StandardMapObjectInspector> cachedStandardMapObjectInspector =
new ConcurrentHashMap<>();
public static StandardMapObjectInspector getStandardMapObjectInspector(ObjectInspector mapKeyObjectInspector,
ObjectInspector mapValueObjectInspector) {
List<ObjectInspector> signature = ImmutableList.of(mapKeyObjectInspector, mapValueObjectInspector);
StandardMapObjectInspector result = cachedStandardMapObjectInspector.get(signature);
if (result == null) {
result = new StandardMapObjectInspector(mapKeyObjectInspector, mapValueObjectInspector);
cachedStandardMapObjectInspector.put(signature, result);
}
return result;
}
static ConcurrentHashMap<List<ObjectInspector>, StandardUnionObjectInspector>
cachedStandardUnionObjectInspector =
new ConcurrentHashMap<>();
public static StandardUnionObjectInspector getStandardUnionObjectInspector(
List<ObjectInspector> unionObjectInspectors) {
StandardUnionObjectInspector result = cachedStandardUnionObjectInspector
.get(unionObjectInspectors);
if (result == null) {
result = new StandardUnionObjectInspector(unionObjectInspectors);
cachedStandardUnionObjectInspector.put(unionObjectInspectors, result);
}
return result;
}
static ConcurrentHashMap<ArrayList<List<?>>, StandardStructObjectInspector> cachedStandardStructObjectInspector =
new ConcurrentHashMap<>();
public static StandardStructObjectInspector getStandardStructObjectInspector(
List<String> structFieldNames,
List<ObjectInspector> structFieldObjectInspectors) {
return getStandardStructObjectInspector(structFieldNames, structFieldObjectInspectors, null);
}
public static StandardStructObjectInspector getStandardStructObjectInspector(
List<String> structFieldNames,
List<ObjectInspector> structFieldObjectInspectors,
List<String> structComments) {
ArrayList<List<?>> signature = new ArrayList<>(3);
signature.add(structFieldNames);
signature.add(structFieldObjectInspectors);
if (structComments != null) {
signature.add(structComments);
}
StandardStructObjectInspector result = cachedStandardStructObjectInspector.get(signature);
if (result == null) {
result = new StandardStructObjectInspector(structFieldNames, structFieldObjectInspectors, structComments);
cachedStandardStructObjectInspector.put(signature, result);
}
return result;
}
static ConcurrentHashMap<List<StructObjectInspector>, UnionStructObjectInspector> cachedUnionStructObjectInspector =
new ConcurrentHashMap<>();
public static UnionStructObjectInspector getUnionStructObjectInspector(
List<StructObjectInspector> structObjectInspectors) {
UnionStructObjectInspector result = cachedUnionStructObjectInspector
.get(structObjectInspectors);
if (result == null) {
result = new UnionStructObjectInspector(structObjectInspectors);
cachedUnionStructObjectInspector.put(structObjectInspectors, result);
}
return result;
}
private ObjectInspectorFactory() {
// prevent instantiation
}
}