/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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 com.asakusafw.dmdl.directio.hive.common;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
import com.asakusafw.directio.hive.serde.StringValueSerdeFactory;
import com.asakusafw.directio.hive.serde.TimestampValueSerdeFactory;
import com.asakusafw.directio.hive.serde.ValueSerdeFactory;
import com.asakusafw.dmdl.java.emitter.EmitContext;
import com.asakusafw.dmdl.model.BasicTypeKind;
import com.asakusafw.dmdl.semantics.PropertyDeclaration;
import com.asakusafw.dmdl.semantics.type.BasicType;
/**
* Attributes for Hive column field.
* @since 0.7.0
*/
public class HiveFieldTrait extends BaseTrait<HiveFieldTrait> {
private boolean columnPresent = true;
private String columnName;
private TypeKind typeKind = TypeKind.NATURAL;
private int decimalPrecision;
private int decimalScale;
private int stringLength;
/**
* Returns whether this field is a table column or not.
* @return {@code true} if this field is a column
*/
public boolean isColumnPresent() {
return columnPresent;
}
/**
* Sets whether this field is a table column or not.
* @param present {@code true} to make this field be column, {@code false} otherwise
*/
public void setColumnPresent(boolean present) {
this.columnPresent = present;
}
/**
* Returns the explicit column name.
* @return the explicit column name
*/
public String getColumnName() {
return columnName;
}
/**
* Sets the explicit column name.
* @param name the explicit column name
*/
public void setColumnName(String name) {
this.columnName = name;
}
/**
* Returns the explicit/inferred column name.
* @param property the target property
* @return the explicit/inferred column name
*/
public static String getColumnName(PropertyDeclaration property) {
HiveFieldTrait trait = property.getTrait(HiveFieldTrait.class);
if (trait == null || trait.getColumnName() == null) {
return property.getName().identifier;
}
return trait.getColumnName();
}
/**
* Returns the type kind.
* @return the type kind
*/
public TypeKind getTypeKind() {
return typeKind;
}
/**
* Returns the explicit decimal precision.
* @return the explicit decimal precision
*/
public int getDecimalPrecision() {
return decimalPrecision;
}
/**
* Returns the explicit decimal scale.
* @return the explicit decimal scale
*/
public int getDecimalScale() {
return decimalScale;
}
/**
* Returns the explicit char/varchar length.
* @return the explicit char/varchar length
*/
public int getStringLength() {
return stringLength;
}
/**
* Sets the timestamp type info.
*/
public void setTimestampTypeInfo() {
this.typeKind = TypeKind.TIMESTAMP;
}
/**
* Sets the string type info.
*/
public void setStringTypeInfo() {
this.typeKind = TypeKind.STRING;
}
/**
* Sets the decimal type info.
* @param precision the precision
* @param scale the scale
*/
public void setDecimalTypeInfo(int precision, int scale) {
this.typeKind = TypeKind.DECIMAL;
this.decimalPrecision = precision;
this.decimalScale = scale;
}
/**
* Sets the char type info.
* @param length the character string length
*/
public void setCharTypeInfo(int length) {
this.typeKind = TypeKind.CHAR;
this.stringLength = length;
}
/**
* Sets the varchar type info.
* @param length the character string length
*/
public void setVarcharTypeInfo(int length) {
this.typeKind = TypeKind.VARCHAR;
this.stringLength = length;
}
/**
* Returns the type information for the property.
* @param property the target property
* @return the type information
*/
public static TypeInfo getTypeInfo(PropertyDeclaration property) {
HiveFieldTrait field = HiveFieldTrait.get(property);
switch (field.getTypeKind()) {
case NATURAL:
return getNaturalTypeInfo(property);
case TIMESTAMP:
return TimestampValueSerdeFactory.getCommonTypeInfo();
case STRING:
return StringValueSerdeFactory.getCommonTypeInfo();
case CHAR:
return ValueSerdeFactory.getChar(field.getStringLength()).getTypeInfo();
case VARCHAR:
return ValueSerdeFactory.getVarchar(field.getStringLength()).getTypeInfo();
case DECIMAL:
return ValueSerdeFactory.getDecimal(field.getDecimalPrecision(), field.getDecimalScale()).getTypeInfo();
default:
throw new AssertionError(field.getTypeKind());
}
}
/**
* Returns the natural type information for the property.
* @param property the target property
* @return the natural type information
*/
public static TypeInfo getNaturalTypeInfo(PropertyDeclaration property) {
if ((property.getType() instanceof BasicType) == false) {
return TypeInfoFactory.unknownTypeInfo;
}
Class<?> valueClass = EmitContext.getFieldTypeAsClass(property);
ValueSerdeFactory serde = ValueSerdeFactory.fromClass(valueClass);
return serde.getTypeInfo();
}
/**
* Returns the {@link HiveFieldTrait} for the target property declaration.
* @param declaration the target declaration
* @return the related trait
*/
public static HiveFieldTrait get(PropertyDeclaration declaration) {
HiveFieldTrait trait = declaration.getTrait(HiveFieldTrait.class);
if (trait == null) {
trait = new HiveFieldTrait();
declaration.putTrait(HiveFieldTrait.class, trait);
}
return trait;
}
/**
* The special type kind.
*/
public enum TypeKind {
/**
* Use property natural type.
*/
NATURAL(c -> ValueSerdeFactory.fromClass(c) != null),
/**
* timestamp type.
*/
TIMESTAMP(c -> TimestampValueSerdeFactory.fromClass(c) != null),
/**
* string type.
*/
STRING(c -> StringValueSerdeFactory.fromClass(c) != null),
/**
* decimal type.
*/
DECIMAL(BasicTypeKind.DECIMAL),
/**
* char type.
*/
CHAR(BasicTypeKind.TEXT),
/**
* varchar type.
*/
VARCHAR(BasicTypeKind.TEXT),
;
private final Set<BasicTypeKind> supportedKinds;
TypeKind(Callable<Set<BasicTypeKind>> lazy) {
try {
this.supportedKinds = Collections.unmodifiableSet(lazy.call());
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
TypeKind(Predicate<? super Class<?>> filter) {
this.supportedKinds = Stream.of(BasicTypeKind.values())
.filter(k -> filter.test(EmitContext.getFieldTypeAsClass(k)))
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> EnumSet.noneOf(BasicTypeKind.class)),
Collections::unmodifiableSet));
}
TypeKind(BasicTypeKind... kinds) {
this.supportedKinds = Stream.of(kinds)
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> EnumSet.noneOf(BasicTypeKind.class)),
Collections::unmodifiableSet));
}
/**
* Returns the supported kinds.
* @return the supported kinds
*/
public Set<BasicTypeKind> getSupportedKinds() {
return supportedKinds;
}
}
}