/**
* Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
*
* 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.linkedin.pinot.core.data.extractors;
import com.google.common.base.Preconditions;
import com.linkedin.pinot.common.data.FieldSpec;
/**
* The <code>PinotDataType</code> enum represents the data type of a value in a row from recordReader and provides
* utility methods to convert value across types if applicable.
* <p>
* We don't use <code>PinotDataType</code> to maintain type information, but use it to help organize the data and use
* {@link com.linkedin.pinot.common.data.FieldSpec.DataType} to maintain type information separately across
* various readers.
* <p>NOTE:
* <p>- a. we will silently lose information if a conversion causes us to do so (e.g. Integer to Byte).
* <p>- b. We will throw exceptions if a conversion is not possible (e.g. Boolean to Byte).
* <p>- c. we will throw exceptions if the conversion throw exceptions (e.g. String -> Short, where parsing string
* throw exceptions)
*/
public enum PinotDataType {
BOOLEAN {
@Override
public Boolean toBoolean(Object value) {
return (Boolean) value;
}
@Override
public Byte toByte(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: BOOLEAN to: BYTE");
}
@Override
public Character toCharacter(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: BOOLEAN to: CHARACTER");
}
@Override
public Short toShort(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: BOOLEAN to: SHORT");
}
@Override
public Integer toInteger(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: BOOLEAN to: INTEGER");
}
@Override
public Long toLong(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: BOOLEAN to: LONG");
}
@Override
public Float toFloat(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: BOOLEAN to: FLOAT");
}
@Override
public Double toDouble(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: BOOLEAN to: DOUBLE");
}
@Override
public Boolean convert(Object value, PinotDataType sourceType) {
return sourceType.toBoolean(value);
}
},
BYTE {
@Override
public Byte toByte(Object value) {
return (Byte) value;
}
@Override
public Character toCharacter(Object value) {
return (char) ((Byte) value).shortValue();
}
@Override
public Short toShort(Object value) {
return ((Byte) value).shortValue();
}
@Override
public Integer toInteger(Object value) {
return ((Byte) value).intValue();
}
@Override
public Long toLong(Object value) {
return ((Byte) value).longValue();
}
@Override
public Float toFloat(Object value) {
return ((Byte) value).floatValue();
}
@Override
public Double toDouble(Object value) {
return ((Byte) value).doubleValue();
}
@Override
public Byte convert(Object value, PinotDataType sourceType) {
return sourceType.toByte(value);
}
},
CHARACTER {
@Override
public Byte toByte(Object value) {
return (byte) ((Character) value).charValue();
}
@Override
public Character toCharacter(Object value) {
return (Character) value;
}
@Override
public Short toShort(Object value) {
return (short) ((Character) value).charValue();
}
@Override
public Integer toInteger(Object value) {
return (int) ((Character) value);
}
@Override
public Long toLong(Object value) {
return (long) ((Character) value);
}
@Override
public Float toFloat(Object value) {
return (float) ((Character) value);
}
@Override
public Double toDouble(Object value) {
return (double) ((Character) value);
}
@Override
public Character convert(Object value, PinotDataType sourceType) {
return sourceType.toCharacter(value);
}
},
SHORT {
@Override
public Byte toByte(Object value) {
return ((Short) value).byteValue();
}
@Override
public Character toCharacter(Object value) {
return (char) ((Short) value).shortValue();
}
@Override
public Short toShort(Object value) {
return (Short) value;
}
@Override
public Integer toInteger(Object value) {
return ((Short) value).intValue();
}
@Override
public Long toLong(Object value) {
return ((Short) value).longValue();
}
@Override
public Float toFloat(Object value) {
return ((Short) value).floatValue();
}
@Override
public Double toDouble(Object value) {
return ((Short) value).doubleValue();
}
@Override
public Short convert(Object value, PinotDataType sourceType) {
return sourceType.toShort(value);
}
},
INTEGER {
@Override
public Byte toByte(Object value) {
return ((Integer) value).byteValue();
}
@Override
public Character toCharacter(Object value) {
return (char) ((Integer) value).shortValue();
}
@Override
public Short toShort(Object value) {
return ((Integer) value).shortValue();
}
@Override
public Integer toInteger(Object value) {
return (Integer) value;
}
@Override
public Long toLong(Object value) {
return ((Integer) value).longValue();
}
@Override
public Float toFloat(Object value) {
return ((Integer) value).floatValue();
}
@Override
public Double toDouble(Object value) {
return ((Integer) value).doubleValue();
}
@Override
public Integer convert(Object value, PinotDataType sourceType) {
return sourceType.toInteger(value);
}
},
LONG {
@Override
public Byte toByte(Object value) {
return ((Long) value).byteValue();
}
@Override
public Character toCharacter(Object value) {
return (char) ((Long) value).shortValue();
}
@Override
public Short toShort(Object value) {
return ((Long) value).shortValue();
}
@Override
public Integer toInteger(Object value) {
return ((Long) value).intValue();
}
@Override
public Long toLong(Object value) {
return (Long) value;
}
@Override
public Float toFloat(Object value) {
return ((Long) value).floatValue();
}
@Override
public Double toDouble(Object value) {
return ((Long) value).doubleValue();
}
@Override
public Long convert(Object value, PinotDataType sourceType) {
return sourceType.toLong(value);
}
},
FLOAT {
@Override
public Byte toByte(Object value) {
return ((Float) value).byteValue();
}
@Override
public Character toCharacter(Object value) {
return (char) ((Float) value).shortValue();
}
@Override
public Short toShort(Object value) {
return ((Float) value).shortValue();
}
@Override
public Integer toInteger(Object value) {
return ((Float) value).intValue();
}
@Override
public Long toLong(Object value) {
return ((Float) value).longValue();
}
@Override
public Float toFloat(Object value) {
return (Float) value;
}
@Override
public Double toDouble(Object value) {
return ((Float) value).doubleValue();
}
@Override
public Float convert(Object value, PinotDataType sourceType) {
return sourceType.toFloat(value);
}
},
DOUBLE {
@Override
public Byte toByte(Object value) {
return ((Double) value).byteValue();
}
@Override
public Character toCharacter(Object value) {
return (char) ((Double) value).shortValue();
}
@Override
public Short toShort(Object value) {
return ((Double) value).shortValue();
}
@Override
public Integer toInteger(Object value) {
return ((Double) value).intValue();
}
@Override
public Long toLong(Object value) {
return ((Double) value).longValue();
}
@Override
public Float toFloat(Object value) {
return ((Double) value).floatValue();
}
@Override
public Double toDouble(Object value) {
return (Double) value;
}
@Override
public Double convert(Object value, PinotDataType sourceType) {
return sourceType.toDouble(value);
}
},
STRING {
@Override
public Boolean toBoolean(Object value) {
return Boolean.parseBoolean((String) value);
}
@Override
public Byte toByte(Object value) {
return Byte.parseByte((String) value);
}
@Override
public Character toCharacter(Object value) {
return ((String) value).charAt(0);
}
@Override
public Short toShort(Object value) {
return Short.parseShort((String) value);
}
@Override
public Integer toInteger(Object value) {
return Integer.parseInt((String) value);
}
@Override
public Long toLong(Object value) {
return Long.parseLong((String) value);
}
@Override
public Float toFloat(Object value) {
return Float.parseFloat((String) value);
}
@Override
public Double toDouble(Object value) {
return Double.parseDouble((String) value);
}
@Override
public String convert(Object value, PinotDataType sourceType) {
return sourceType.toString(value);
}
},
OBJECT {
@Override
public Byte toByte(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: OBJECT to: BYTE");
}
@Override
public Character toCharacter(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: OBJECT to: CHARACTER");
}
@Override
public Short toShort(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: OBJECT to: SHORT");
}
@Override
public Integer toInteger(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: OBJECT to: INTEGER");
}
@Override
public Long toLong(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: OBJECT to: LONG");
}
@Override
public Float toFloat(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: OBJECT to: FLOAT");
}
@Override
public Double toDouble(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: OBJECT to: DOUBLE");
}
@Override
public Object convert(Object value, PinotDataType sourceType) {
throw new UnsupportedOperationException("Cannot convert value: " + value + "from: " + sourceType + " to: OBJECT");
}
},
BYTE_ARRAY {
@Override
public Byte[] convert(Object value, PinotDataType sourceType) {
return sourceType.toByteArray(value);
}
},
CHARACTER_ARRAY {
@Override
public Character[] convert(Object value, PinotDataType sourceType) {
return sourceType.toCharacterArray(value);
}
},
SHORT_ARRAY {
@Override
public Short[] convert(Object value, PinotDataType sourceType) {
return sourceType.toShortArray(value);
}
},
INTEGER_ARRAY {
@Override
public Integer[] convert(Object value, PinotDataType sourceType) {
return sourceType.toIntegerArray(value);
}
},
LONG_ARRAY {
@Override
public Long[] convert(Object value, PinotDataType sourceType) {
return sourceType.toLongArray(value);
}
},
FLOAT_ARRAY {
@Override
public Float[] convert(Object value, PinotDataType sourceType) {
return sourceType.toFloatArray(value);
}
},
DOUBLE_ARRAY {
@Override
public Double[] convert(Object value, PinotDataType sourceType) {
return sourceType.toDoubleArray(value);
}
},
STRING_ARRAY {
@Override
public Boolean toBoolean(Object value) {
return STRING.toBoolean(((Object[]) value)[0]);
}
@Override
public String[] convert(Object value, PinotDataType sourceType) {
return sourceType.toStringArray(value);
}
},
OBJECT_ARRAY {
@Override
public Object[] convert(Object value, PinotDataType sourceType) {
throw new UnsupportedOperationException(
"Cannot convert value: " + value + "from: " + sourceType + " to: OBJECT_ARRAY");
}
};
public Boolean toBoolean(Object value) {
throw new UnsupportedOperationException("Cannot convert value: " + value + " from: " + this + " to: BOOLEAN");
}
public Byte toByte(Object value) {
return getSingleValueType().toByte(((Object[]) value)[0]);
}
public Character toCharacter(Object value) {
return getSingleValueType().toCharacter(((Object[]) value)[0]);
}
public Short toShort(Object value) {
return getSingleValueType().toShort(((Object[]) value)[0]);
}
public Integer toInteger(Object value) {
return getSingleValueType().toInteger(((Object[]) value)[0]);
}
public Long toLong(Object value) {
return getSingleValueType().toLong(((Object[]) value)[0]);
}
public Float toFloat(Object value) {
return getSingleValueType().toFloat(((Object[]) value)[0]);
}
public Double toDouble(Object value) {
return getSingleValueType().toDouble(((Object[]) value)[0]);
}
public String toString(Object value) {
if (isSingleValue()) {
return value.toString();
} else {
return ((Object[]) value)[0].toString();
}
}
public Byte[] toByteArray(Object value) {
if (isSingleValue()) {
return new Byte[]{toByte(value)};
} else {
Object[] valueArray = (Object[]) value;
int length = valueArray.length;
Byte[] byteArray = new Byte[length];
PinotDataType singleValueType = getSingleValueType();
for (int i = 0; i < length; i++) {
byteArray[i] = singleValueType.toByte(valueArray[i]);
}
return byteArray;
}
}
public Character[] toCharacterArray(Object value) {
if (isSingleValue()) {
return new Character[]{toCharacter(value)};
} else {
Object[] valueArray = (Object[]) value;
int length = valueArray.length;
Character[] characterArray = new Character[length];
PinotDataType singleValueType = getSingleValueType();
for (int i = 0; i < length; i++) {
characterArray[i] = singleValueType.toCharacter(valueArray[i]);
}
return characterArray;
}
}
public Short[] toShortArray(Object value) {
if (isSingleValue()) {
return new Short[]{toShort(value)};
} else {
Object[] valueArray = (Object[]) value;
int length = valueArray.length;
Short[] shortArray = new Short[length];
PinotDataType singleValueType = getSingleValueType();
for (int i = 0; i < length; i++) {
shortArray[i] = singleValueType.toShort(valueArray[i]);
}
return shortArray;
}
}
public Integer[] toIntegerArray(Object value) {
if (isSingleValue()) {
return new Integer[]{toInteger(value)};
} else {
Object[] valueArray = (Object[]) value;
int length = valueArray.length;
Integer[] integerArray = new Integer[length];
PinotDataType singleValueType = getSingleValueType();
for (int i = 0; i < length; i++) {
integerArray[i] = singleValueType.toInteger(valueArray[i]);
}
return integerArray;
}
}
public Long[] toLongArray(Object value) {
if (isSingleValue()) {
return new Long[]{toLong(value)};
} else {
Object[] valueArray = (Object[]) value;
int length = valueArray.length;
Long[] longArray = new Long[length];
PinotDataType singleValueType = getSingleValueType();
for (int i = 0; i < length; i++) {
longArray[i] = singleValueType.toLong(valueArray[i]);
}
return longArray;
}
}
public Float[] toFloatArray(Object value) {
if (isSingleValue()) {
return new Float[]{toFloat(value)};
} else {
Object[] valueArray = (Object[]) value;
int length = valueArray.length;
Float[] floatArray = new Float[length];
PinotDataType singleValueType = getSingleValueType();
for (int i = 0; i < length; i++) {
floatArray[i] = singleValueType.toFloat(valueArray[i]);
}
return floatArray;
}
}
public Double[] toDoubleArray(Object value) {
if (isSingleValue()) {
return new Double[]{toDouble(value)};
} else {
Object[] valueArray = (Object[]) value;
int length = valueArray.length;
Double[] doubleArray = new Double[length];
PinotDataType singleValueType = getSingleValueType();
for (int i = 0; i < length; i++) {
doubleArray[i] = singleValueType.toDouble(valueArray[i]);
}
return doubleArray;
}
}
public String[] toStringArray(Object value) {
if (isSingleValue()) {
return new String[]{toString(value)};
} else {
Object[] valueArray = (Object[]) value;
int length = valueArray.length;
String[] stringArray = new String[length];
PinotDataType singleValueType = getSingleValueType();
for (int i = 0; i < length; i++) {
stringArray[i] = singleValueType.toString(valueArray[i]);
}
return stringArray;
}
}
public abstract Object convert(Object value, PinotDataType sourceType);
public boolean isSingleValue() {
return this.ordinal() < BYTE_ARRAY.ordinal();
}
public PinotDataType getSingleValueType() {
switch (this) {
case BYTE_ARRAY:
return BYTE;
case CHARACTER_ARRAY:
return CHARACTER;
case SHORT_ARRAY:
return SHORT;
case INTEGER_ARRAY:
return INTEGER;
case LONG_ARRAY:
return LONG;
case FLOAT_ARRAY:
return FLOAT;
case DOUBLE_ARRAY:
return DOUBLE;
case STRING_ARRAY:
return STRING;
case OBJECT_ARRAY:
return OBJECT;
default:
throw new UnsupportedOperationException("Cannot get single-value type for: " + this);
}
}
public static PinotDataType getPinotDataType(FieldSpec fieldSpec) {
FieldSpec.DataType dataType = fieldSpec.getDataType();
switch (dataType) {
case BOOLEAN:
Preconditions.checkArgument(fieldSpec.isSingleValueField());
return PinotDataType.BOOLEAN;
case BYTE:
return fieldSpec.isSingleValueField() ? PinotDataType.BYTE : PinotDataType.BYTE_ARRAY;
case CHAR:
return fieldSpec.isSingleValueField() ? PinotDataType.CHARACTER : PinotDataType.CHARACTER_ARRAY;
case SHORT:
return fieldSpec.isSingleValueField() ? PinotDataType.SHORT : PinotDataType.SHORT_ARRAY;
case INT:
return fieldSpec.isSingleValueField() ? PinotDataType.INTEGER : PinotDataType.INTEGER_ARRAY;
case LONG:
return fieldSpec.isSingleValueField() ? PinotDataType.LONG : PinotDataType.LONG_ARRAY;
case FLOAT:
return fieldSpec.isSingleValueField() ? PinotDataType.FLOAT : PinotDataType.FLOAT_ARRAY;
case DOUBLE:
return fieldSpec.isSingleValueField() ? PinotDataType.DOUBLE : PinotDataType.DOUBLE_ARRAY;
case STRING:
return fieldSpec.isSingleValueField() ? PinotDataType.STRING : PinotDataType.STRING_ARRAY;
default:
throw new UnsupportedOperationException(
"Unsupported data type: " + dataType + " in field: " + fieldSpec.getName());
}
}
}