io.deephaven.engine.table.impl.sources.ReinterpretUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of deephaven-engine-table Show documentation
Show all versions of deephaven-engine-table Show documentation
Engine Table: Implementation and closely-coupled utilities
/**
* Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
*/
package io.deephaven.engine.table.impl.sources;
import io.deephaven.chunk.ChunkType;
import io.deephaven.engine.table.ColumnSource;
import io.deephaven.engine.table.WritableColumnSource;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.function.Consumer;
public class ReinterpretUtils {
/**
* Given a {@code byte} column source turn it into a {@link Boolean} column source, either via reinterpretation or
* wrapping.
*
* @param source the source to turn into a {@link Boolean} source
*
* @return the {@link Boolean} source
*/
@NotNull
public static ColumnSource byteToBooleanSource(@NotNull final ColumnSource source) {
if (source.allowsReinterpret(Boolean.class)) {
return source.reinterpret(Boolean.class);
} else {
return new ByteAsBooleanColumnSource(source);
}
}
/**
* Given a {@link Boolean} column source turn it into a {@code byte} column source, either via reinterpretation or
* wrapping.
*
* @param source the source to turn into a {@code byte} source
*
* @return the {@code byte} source
*/
@NotNull
public static ColumnSource booleanToByteSource(@NotNull final ColumnSource source) {
if (source.allowsReinterpret(byte.class)) {
return source.reinterpret(byte.class);
} else {
return new BooleanAsByteColumnSource(source);
}
}
/**
* Given a writable {@link Boolean} column source turn it into a writable {@code byte} column source via
* reinterpretation if possible.
*
* @param source the source to turn into a {@code byte} source
*
* @return the {@code byte} source or null if it could not be reinterpreted
*/
@Nullable
public static WritableColumnSource writableBooleanToByteSource(
@NotNull final WritableColumnSource source) {
if (source.allowsReinterpret(byte.class)) {
return (WritableColumnSource) source.reinterpret(byte.class);
}
return null;
}
/**
* Given a {@code long} column source turn it into an {@link Instant} column source, either via reinterpretation or
* wrapping.
*
* @param source the source to turn into an {@link Instant} source
*
* @return the {@code long} source
*/
@NotNull
public static ColumnSource longToInstantSource(@NotNull final ColumnSource source) {
if (source.allowsReinterpret(Instant.class)) {
return source.reinterpret(Instant.class);
} else {
return new LongAsInstantColumnSource(source);
}
}
/**
* Given an {@link Instant} column source turn it into a {@code long} column source, either via reinterpretation or
* wrapping.
*
* @param source the source to turn into a {@code long} source
*
* @return the {@code long} source
*/
@NotNull
public static ColumnSource instantToLongSource(@NotNull final ColumnSource source) {
if (source.allowsReinterpret(long.class)) {
return source.reinterpret(long.class);
} else {
return new InstantAsLongColumnSource(source);
}
}
/**
* Given a writable {@link Instant} column source turn it into a writable {@code long} column source via
* reinterpretation if possible.
*
* @param source the source to turn into a {@code long} source
*
* @return the {@code long} source or null if it could not be reinterpreted
*/
@Nullable
public static WritableColumnSource writableInstantToLongSource(
@NotNull final WritableColumnSource source) {
if (source.allowsReinterpret(long.class)) {
return (WritableColumnSource) source.reinterpret(long.class);
}
return null;
}
/**
* Given a {@code long} column source turn it into a {@link ZonedDateTime} column source, either via
* reinterpretation or wrapping.
*
* @param source the source to turn into a {@link ZonedDateTime} source
* @param wrapperTimeZone the {@link ZoneId} to use if and only if we can't apply a simple reinterpret
*
* @return the {@code long} source
*/
public static ColumnSource longToZonedDateTimeSource(
@NotNull final ColumnSource source,
@NotNull final ZoneId wrapperTimeZone) {
if (source.allowsReinterpret(ZonedDateTime.class)) {
return source.reinterpret(ZonedDateTime.class);
} else {
return new LongAsZonedDateTimeColumnSource(source, wrapperTimeZone);
}
}
/**
* Given a {@link ZonedDateTime} column source turn it into a {@code long} column source, either via
* reinterpretation or wrapping.
*
* @param source the source to turn into a {@code long} source
*
* @return the {@code long} source
*/
@NotNull
public static ColumnSource zonedDateTimeToLongSource(@NotNull final ColumnSource source) {
if (source.allowsReinterpret(long.class)) {
return source.reinterpret(long.class);
} else {
return new ZonedDateTimeAsLongSource(source);
}
}
/**
* Given a writable {@link ZonedDateTime} column source turn it into a writable {@code long} column source via
* reinterpretation if possible.
*
* @param source the source to turn into a {@code long} source
*
* @return the {@code long} source or null if it could not be reinterpreted
*/
@Nullable
public static WritableColumnSource writableZonedDateTimeToLongSource(
@NotNull final WritableColumnSource source) {
if (source.allowsReinterpret(long.class)) {
return (WritableColumnSource) source.reinterpret(long.class);
}
return null;
}
/**
* If source is something that we prefer to handle as a primitive, do the appropriate conversion.
*
* @param source The source to convert
* @return if possible, the source converted to a primitive, otherwise the source
*/
@SuppressWarnings("unchecked")
@NotNull
public static ColumnSource> maybeConvertToPrimitive(@NotNull final ColumnSource> source) {
if (source.getType() == Boolean.class || source.getType() == boolean.class) {
return booleanToByteSource((ColumnSource) source);
}
if (source.getType() == Instant.class) {
return instantToLongSource((ColumnSource) source);
}
if (source.getType() == ZonedDateTime.class) {
// We require this to be symmetrical with convertToOriginalType. This means we must restrict conversion to
// sources where we can find the time zone when we need to convert back.
// TODO (https://github.com/deephaven/deephaven-core/issues/3455): Do better with richer types
if (source instanceof ConvertibleTimeSource.Zoned) {
return zonedDateTimeToLongSource((ColumnSource) source);
}
}
return source;
}
/**
* If {@code source} is something that we prefer to handle as a primitive, do the appropriate conversion.
*
* @param source the source to convert
* @return if possible, {@code source} converted to a writable primitive, otherwise {@code source}
*/
@SuppressWarnings("unchecked")
@NotNull
public static WritableColumnSource> maybeConvertToWritablePrimitive(
@NotNull final WritableColumnSource> source) {
WritableColumnSource> result = null;
if (source.getType() == Boolean.class || source.getType() == boolean.class) {
result = writableBooleanToByteSource((WritableColumnSource) source);
} else if (source.getType() == Instant.class) {
result = writableInstantToLongSource((WritableColumnSource) source);
} else if (source.getType() == ZonedDateTime.class) {
result = writableZonedDateTimeToLongSource((WritableColumnSource) source);
}
return result == null ? source : result;
}
/**
* If {@code dataType} is something that we prefer to handle as a primitive, emit the appropriate {@link ChunkType},
* else the normal ChunkType for the data type.
*
* @param dataType The data type to convert to a {@link ChunkType}
* @return the appropriate {@link ChunkType} to use when extracting primitives from the source
*/
@NotNull
public static ChunkType maybeConvertToPrimitiveChunkType(@NotNull final Class> dataType) {
if (dataType == Boolean.class || dataType == boolean.class) {
return ChunkType.Byte;
}
if (dataType == Instant.class || dataType == ZonedDateTime.class) {
return ChunkType.Long;
}
return ChunkType.fromElementType(dataType);
}
/**
* If {@code dataType} is something that we prefer to handle as a primitive, emit the appropriate {@link ChunkType},
* else the normal ChunkType for the data type.
*
* @param dataType The data type to convert to a {@link ChunkType}
* @return the appropriate {@link ChunkType} to use when writing primitives to the destination
*/
@NotNull
public static ChunkType maybeConvertToWritablePrimitiveChunkType(@NotNull final Class> dataType) {
if (dataType == Boolean.class || dataType == boolean.class) {
return ChunkType.Byte;
}
if (dataType == Instant.class) {
// Note that storing ZonedDateTime as a primitive is lossy on the time zone.
return ChunkType.Long;
}
return ChunkType.fromElementType(dataType);
}
/**
* If {@code dataType} is something that we prefer to handle as a primitive, emit the appropriate {@link Class data
* type to use}, else return {@code dataType}.
*
* @param dataType The data type to examine
* @return the appropriate data type to use when extracting primitives from the source
*/
@NotNull
public static Class> maybeConvertToPrimitiveDataType(@NotNull final Class> dataType) {
if (dataType == Boolean.class || dataType == boolean.class) {
return byte.class;
}
if (dataType == Instant.class || dataType == ZonedDateTime.class) {
return long.class;
}
return dataType;
}
/**
* Reinterpret or box {@code sourceToConvert} back to its original type.
*
* @param originalSource The source that was reinterpreted to produce {@code sourceToConvert}, or a similarly-typed
* source for type information
* @param sourceToConvert The source to convert
* @return reinterpret or box {@code sourceToConvert} back to the original type if possible
* @throws UnsupportedOperationException for unsupported conversions
*/
@NotNull
public static ColumnSource> convertToOriginalType(
@NotNull final ColumnSource> originalSource,
@NotNull final ColumnSource> sourceToConvert) {
final Class> originalType = originalSource.getType();
final Consumer> validateSourceType = expectedType -> {
if (sourceToConvert.getType() != expectedType) {
throw new UnsupportedOperationException(String.format(
"Cannot convert column of type %s to %s", sourceToConvert.getType(), originalType));
}
};
if (originalType == Boolean.class) {
validateSourceType.accept(byte.class);
// noinspection unchecked
return byteToBooleanSource((ColumnSource) sourceToConvert);
}
if (originalType == Instant.class) {
validateSourceType.accept(long.class);
// noinspection unchecked
return longToInstantSource((ColumnSource) sourceToConvert);
}
if (originalType == ZonedDateTime.class) {
validateSourceType.accept(long.class);
if (originalSource instanceof ConvertibleTimeSource.Zoned) {
final ConvertibleTimeSource.Zoned zonedOriginal = (ConvertibleTimeSource.Zoned) originalSource;
// noinspection unchecked
return longToZonedDateTimeSource((ColumnSource) sourceToConvert, zonedOriginal.getZone());
}
throw new UnsupportedOperationException(String.format(
"Unsupported original source class %s for converting long to ZonedDateTime",
originalSource.getClass()));
}
throw new UnsupportedOperationException((String.format("Unsupported original type %s", originalType)));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy