/**
* 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.hadoop.hive.serde2.objectinspector;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
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;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorUtils;
/**
* ObjectInspectorFactory is the primary way to create new ObjectInspector
* instances.
*
* SerDe classes should call the static functions in this library to create an
* ObjectInspector to return to the caller of SerDe2.getObjectInspector().
*
* The reason of having caches here is that ObjectInspector is because
* 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 {
/**
* ObjectInspectorOptions describes what ObjectInspector to use. JAVA is to
* use pure JAVA reflection. THRIFT is to use JAVA reflection and filter out
* __isset fields, PROTOCOL_BUFFERS filters out has*.
* New ObjectInspectorOptions can be added here when available.
*
* We choose to use a single HashMap objectInspectorCache to cache all
* situations for efficiency and code simplicity. And we don't expect a case
* that a user need to create 2 or more different types of ObjectInspectors
* for the same Java type.
*/
public enum ObjectInspectorOptions {
JAVA, THRIFT, PROTOCOL_BUFFERS
};
private static HashMap<Type, ObjectInspector> objectInspectorCache = new HashMap<Type, ObjectInspector>();
public static ObjectInspector getReflectionObjectInspector(Type t,
ObjectInspectorOptions options) {
ObjectInspector oi = objectInspectorCache.get(t);
if (oi == null) {
oi = getReflectionObjectInspectorNoCache(t, options);
objectInspectorCache.put(t, oi);
}
verifyObjectInspector(options, oi, ObjectInspectorOptions.JAVA, new Class[]{ThriftStructObjectInspector.class,
ProtocolBuffersStructObjectInspector.class});
verifyObjectInspector(options, oi, ObjectInspectorOptions.THRIFT, new Class[]{ReflectionStructObjectInspector.class,
ProtocolBuffersStructObjectInspector.class});
verifyObjectInspector(options, oi, ObjectInspectorOptions.PROTOCOL_BUFFERS, new Class[]{ThriftStructObjectInspector.class,
ReflectionStructObjectInspector.class});
return oi;
}
/**
* Verify that we don't have an unexpected type of object inspector.
* @param option The option to verify
* @param oi The ObjectInspector to verify
* @param checkOption We're only interested in this option type
* @param classes ObjectInspector should not be of these types
*/
private static void verifyObjectInspector(ObjectInspectorOptions option, ObjectInspector oi,
ObjectInspectorOptions checkOption, Class[] classes) {
if (option.equals(checkOption)) {
for (Class checkClass : classes) {
if (oi.getClass().equals(checkClass)) {
throw new RuntimeException(
"Cannot call getObjectInspectorByReflection with more then one of " +
Arrays.toString(ObjectInspectorOptions.values()) + "!");
}
}
}
}
private static ObjectInspector getReflectionObjectInspectorNoCache(Type t,
ObjectInspectorOptions options) {
if (t instanceof GenericArrayType) {
GenericArrayType at = (GenericArrayType) t;
return getStandardListObjectInspector(getReflectionObjectInspector(at
.getGenericComponentType(), options));
}
if (t instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) t;
// List?
if (List.class.isAssignableFrom((Class<?>) pt.getRawType())) {
return getStandardListObjectInspector(getReflectionObjectInspector(pt
.getActualTypeArguments()[0], options));
}
// Map?
if (Map.class.isAssignableFrom((Class<?>) pt.getRawType())) {
return getStandardMapObjectInspector(getReflectionObjectInspector(pt
.getActualTypeArguments()[0], options),
getReflectionObjectInspector(pt.getActualTypeArguments()[1],
options));
}
// Otherwise convert t to RawType so we will fall into the following if
// block.
t = pt.getRawType();
}
// 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);
}
// Must be struct because List and Map need to be ParameterizedType
assert (!List.class.isAssignableFrom(c));
assert (!Map.class.isAssignableFrom(c));
// Create StructObjectInspector
ReflectionStructObjectInspector oi;
switch (options) {
case JAVA:
oi = new ReflectionStructObjectInspector();
break;
case THRIFT:
oi = new ThriftStructObjectInspector();
break;
case PROTOCOL_BUFFERS:
oi = new ProtocolBuffersStructObjectInspector();
break;
default:
throw new RuntimeException(ObjectInspectorFactory.class.getName()
+ ": internal error.");
}
// 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);
ArrayList<ObjectInspector> structFieldObjectInspectors = new ArrayList<ObjectInspector>(
fields.length);
for (int i = 0; i < fields.length; i++) {
if (!oi.shouldIgnoreField(fields[i].getName())) {
structFieldObjectInspectors.add(getReflectionObjectInspector(fields[i]
.getGenericType(), options));
}
}
oi.init(c, structFieldObjectInspectors);
return oi;
}
static HashMap<ObjectInspector, StandardListObjectInspector> cachedStandardListObjectInspector = new HashMap<ObjectInspector, StandardListObjectInspector>();
public static StandardListObjectInspector getStandardListObjectInspector(
ObjectInspector listElementObjectInspector) {
StandardListObjectInspector result = cachedStandardListObjectInspector
.get(listElementObjectInspector);
if (result == null) {
result = new StandardListObjectInspector(listElementObjectInspector);
cachedStandardListObjectInspector.put(listElementObjectInspector, result);
}
return result;
}
public static StandardConstantListObjectInspector getStandardConstantListObjectInspector(
ObjectInspector listElementObjectInspector, List<?> constantValue) {
return new StandardConstantListObjectInspector(listElementObjectInspector, constantValue);
}
static HashMap<List<ObjectInspector>, StandardMapObjectInspector> cachedStandardMapObjectInspector = new HashMap<List<ObjectInspector>, StandardMapObjectInspector>();
public static StandardMapObjectInspector getStandardMapObjectInspector(
ObjectInspector mapKeyObjectInspector,
ObjectInspector mapValueObjectInspector) {
ArrayList<ObjectInspector> signature = new ArrayList<ObjectInspector>(2);
signature.add(mapKeyObjectInspector);
signature.add(mapValueObjectInspector);
StandardMapObjectInspector result = cachedStandardMapObjectInspector
.get(signature);
if (result == null) {
result = new StandardMapObjectInspector(mapKeyObjectInspector,
mapValueObjectInspector);
cachedStandardMapObjectInspector.put(signature, result);
}
return result;
}
public static StandardConstantMapObjectInspector getStandardConstantMapObjectInspector(
ObjectInspector mapKeyObjectInspector,
ObjectInspector mapValueObjectInspector,
Map<?, ?> constantValue) {
return new StandardConstantMapObjectInspector(mapKeyObjectInspector,
mapValueObjectInspector, constantValue);
}
static HashMap<List<ObjectInspector>, StandardUnionObjectInspector>
cachedStandardUnionObjectInspector =
new HashMap<List<ObjectInspector>, StandardUnionObjectInspector>();
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 HashMap<ArrayList<List<?>>, StandardStructObjectInspector> cachedStandardStructObjectInspector = new HashMap<ArrayList<List<?>>, StandardStructObjectInspector>();
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<List<?>>(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 HashMap<List<StructObjectInspector>, UnionStructObjectInspector> cachedUnionStructObjectInspector = new HashMap<List<StructObjectInspector>, UnionStructObjectInspector>();
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;
}
static HashMap<ArrayList<Object>, ColumnarStructObjectInspector> cachedColumnarStructObjectInspector = new HashMap<ArrayList<Object>, ColumnarStructObjectInspector>();
public static ColumnarStructObjectInspector getColumnarStructObjectInspector(
List<String> structFieldNames,
List<ObjectInspector> structFieldObjectInspectors) {
return getColumnarStructObjectInspector(structFieldNames, structFieldObjectInspectors, null);
}
public static ColumnarStructObjectInspector getColumnarStructObjectInspector(
List<String> structFieldNames,
List<ObjectInspector> structFieldObjectInspectors, List<String> structFieldComments) {
ArrayList<Object> signature = new ArrayList<Object>(3);
signature.add(structFieldNames);
signature.add(structFieldObjectInspectors);
if(structFieldComments != null) {
signature.add(structFieldComments);
}
ColumnarStructObjectInspector result = cachedColumnarStructObjectInspector
.get(signature);
if (result == null) {
result = new ColumnarStructObjectInspector(structFieldNames,
structFieldObjectInspectors, structFieldComments);
cachedColumnarStructObjectInspector.put(signature, result);
}
return result;
}
private ObjectInspectorFactory() {
// prevent instantiation
}
}