/*
* Copyright © 2015 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.data2.dataset2.lib.partitioned;
import co.cask.cdap.api.common.Bytes;
import co.cask.cdap.api.dataset.lib.Partitioning.FieldType;
/**
* Common utility methods when dealing wit partitioning field types.
*/
public class FieldTypes {
/**
* Parse a string into a value of this field type. For example, {@link FieldType#INT} delegates this
* to {@link Integer#parseInt}.
* @param str the string to parse
*/
public static <T extends Comparable> T parse(String str, FieldType type) {
Comparable value;
switch(type) {
case STRING:
value = str;
break;
case LONG:
value = Long.parseLong(str);
break;
case INT:
value = Integer.parseInt(str);
break;
default:
throw new IllegalArgumentException("Unhandled field type: " + type.name());
}
@SuppressWarnings("unchecked")
T t = (T) value;
return t;
}
/**
* Convert a value of this field type to a byte array.
* @param value the value to convert
* @param <T> the Java type represented by this field type
*/
public static <T extends Comparable> byte[] toBytes(T value, FieldType type) {
switch(type) {
case STRING:
if (value instanceof String) {
return Bytes.toBytes((String) value);
}
break;
case LONG:
if (value instanceof Long) {
// trick to preserve the ordering of the generated byte array
return Bytes.toBytes(((Long) value) ^ Long.MIN_VALUE);
}
break;
case INT:
if (value instanceof Integer) {
// trick to preserve the ordering of the generated byte array
return Bytes.toBytes(((Integer) value) ^ Integer.MIN_VALUE);
}
break;
default:
throw new IllegalArgumentException("Unhandled field type: " + type.name());
}
throw new IllegalArgumentException(String.format(
"Incompatible value %s of type %s for field type %s.", value, value.getClass(), type.name()));
}
/**
* @return the number of bytes that need to be read to deserialize a value of this field type
* at the given position in the byte array. For integer and long values, this is constant,
* but for strings, this is variable depending on the length of the string.
*/
public static int determineLengthInBytes(byte[] bytes, int offset, FieldType type) {
switch(type) {
case STRING:
for (int i = offset; i < bytes.length; i++) {
if (bytes[i] == 0) {
return i - offset;
}
}
return bytes.length - offset;
case LONG:
return Bytes.SIZEOF_LONG;
case INT:
return Bytes.SIZEOF_INT;
default:
throw new IllegalArgumentException("Unhandled field type: " + type.name());
}
}
/**
* Deserialize a value of this field type, starting at given offset in the byte array, consuming
* the given number of bytes.
*/
public static <T extends Comparable> T fromBytes(byte[] bytes, int offset, int length, FieldType type) {
Comparable value;
switch(type) {
case STRING:
value = Bytes.toString(bytes, offset, length);
break;
case LONG:
value = Bytes.toLong(bytes, offset, length) ^ Long.MIN_VALUE;
break;
case INT:
value = Bytes.toInt(bytes, offset, length) ^ Integer.MIN_VALUE;
break;
default:
throw new IllegalArgumentException("Unhandled field type: " + type.name());
}
@SuppressWarnings("unchecked")
T t = (T) value;
return t;
}
/**
* Validate that a given value has the type that is required by this field type.
*/
public static boolean validateType(Comparable value, FieldType type) {
switch(type) {
case STRING:
return value instanceof String;
case LONG:
return value instanceof Long;
case INT:
return value instanceof Integer;
default:
throw new IllegalArgumentException("Unhandled field type: " + type.name());
}
}
/**
* @return the type name used by the Hive DDL to represent this field type
*/
public static String toHiveType(FieldType type) {
switch(type) {
case STRING:
return "STRING";
case LONG:
return "BIGINT";
case INT:
return "INT";
default:
throw new IllegalArgumentException("Unhandled field type: " + type.name());
}
}
}