All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.ydb.table.values.PrimitiveValue Maven / Gradle / Ivy

package tech.ydb.table.values;

import java.nio.charset.Charset;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import com.google.common.escape.Escaper;
import com.google.common.escape.Escapers;
import com.google.protobuf.ByteString;
import com.google.protobuf.UnsafeByteOperations;

import tech.ydb.proto.ValueProtos;
import tech.ydb.table.utils.LittleEndian;
import tech.ydb.table.values.proto.ProtoValue;



/**
 * @author Sergey Polovko
 */
public abstract class PrimitiveValue implements Value {

    // -- unboxing --

    public boolean getBool() {
        throw new IllegalStateException("expected Bool, but was " + getClass().getSimpleName());
    }

    public byte getInt8() {
        throw new IllegalStateException("expected Int8, but was " + getClass().getSimpleName());
    }

    public int getUint8() {
        throw new IllegalStateException("expected Uint8, but was " + getClass().getSimpleName());
    }

    public short getInt16() {
        throw new IllegalStateException("expected Int16, but was " + getClass().getSimpleName());
    }

    public int getUint16() {
        throw new IllegalStateException("expected Uint16, but was " + getClass().getSimpleName());
    }

    public int getInt32() {
        throw new IllegalStateException("expected Int32, but was " + getClass().getSimpleName());
    }

    public long getUint32() {
        throw new IllegalStateException("expected Uint32, but was " + getClass().getSimpleName());
    }

    public long getInt64() {
        throw new IllegalStateException("expected Int64, but was " + getClass().getSimpleName());
    }

    /** JVM does not support unsigned long, be careful when using this method
      for numbers greater than Long.MAX_VALUE.For correct work you can use wrappers
      like {@link com.google.common.primitives.UnsignedLong#fromLongBits(long) UnsignedLong }
     * @return signed long value corresponding to a bit representation of unsigned.*/
    public long getUint64() {
        throw new IllegalStateException("expected Uint64, but was " + getClass().getSimpleName());
    }

    public float getFloat() {
        throw new IllegalStateException("expected Float, but was " + getClass().getSimpleName());
    }

    public double getDouble() {
        throw new IllegalStateException("expected Double, but was " + getClass().getSimpleName());
    }

    public byte[] getBytes() {
        throw new IllegalStateException("expected Bytes, but was " + getClass().getSimpleName());
    }

    public byte[] getBytesUnsafe() {
        throw new IllegalStateException("expected Bytes, but was " + getClass().getSimpleName());
    }

    public ByteString getBytesAsByteString() {
        throw new IllegalStateException("expected String, but was " + getClass().getSimpleName());
    }

    public String getBytesAsString(Charset charset) {
        return new String(getBytesUnsafe(), charset);
    }

    public String getText() {
        throw new IllegalStateException("expected Utf8, but was " + getClass().getSimpleName());
    }

    public byte[] getYson() {
        throw new IllegalStateException("expected Yson, but was " + getClass().getSimpleName());
    }

    public byte[] getYsonUnsafe() {
        throw new IllegalStateException("expected Yson, but was " + getClass().getSimpleName());
    }

    public ByteString getYsonBytes() {
        throw new IllegalStateException("expected Yson, but was " + getClass().getSimpleName());
    }

    public String getJson() {
        throw new IllegalStateException("expected Json, but was " + getClass().getSimpleName());
    }

    public String getJsonDocument() {
        throw new IllegalStateException("expected JsonDocument, but was " + getClass().getSimpleName());
    }

    public String getUuidString() {
        throw new IllegalStateException("expected Uuid, but was " + getClass().getSimpleName());
    }

    public long getUuidHigh() {
        throw new IllegalStateException("expected Uuid, but was " + getClass().getSimpleName());
    }

    public long getUuidLow() {
        throw new IllegalStateException("expected Uuid, but was " + getClass().getSimpleName());
    }

    public UUID getUuidJdk() {
        throw new IllegalStateException("expected Uuid, but was " + getClass().getSimpleName());
    }

    public LocalDate getDate() {
        throw new IllegalStateException("expected Date, but was " + getClass().getSimpleName());
    }

    public LocalDateTime getDatetime() {
        throw new IllegalStateException("expected Datetime, but was " + getClass().getSimpleName());
    }

    public Instant getTimestamp() {
        throw new IllegalStateException("expected Timestamp, but was " + getClass().getSimpleName());
    }

    public Duration getInterval() {
        throw new IllegalStateException("expected Interval, but was " + getClass().getSimpleName());
    }

    public ZonedDateTime getTzDate() {
        throw new IllegalStateException("expected TzDate, but was " + getClass().getSimpleName());
    }

    public ZonedDateTime getTzDatetime() {
        throw new IllegalStateException("expected TzDatetime, but was " + getClass().getSimpleName());
    }

    public ZonedDateTime getTzTimestamp() {
        throw new IllegalStateException("expected TzTimestamp, but was " + getClass().getSimpleName());
    }

    // -- constructors --

    public static PrimitiveValue newBool(boolean value) {
        return value ? Bool.TRUE : Bool.FALSE;
    }

    public static PrimitiveValue newInt8(byte value) {
        return new Int8(value);
    }

    public static PrimitiveValue newUint8(int value) {
        return new Uint8(value);
    }

    public static PrimitiveValue newInt16(short value) {
        return new Int16(value);
    }

    public static PrimitiveValue newUint16(int value) {
        return new Uint16(value);
    }

    public static PrimitiveValue newInt32(int value) {
        return new Int32(value);
    }

    public static PrimitiveValue newUint32(long value) {
        return new Uint32(value);
    }

    public static PrimitiveValue newInt64(long value) {
        return new Int64(value);
    }

    public static PrimitiveValue newUint64(long value) {
        return new Uint64(value);
    }

    public static PrimitiveValue newFloat(float value) {
        return new FloatValue(value);
    }

    public static PrimitiveValue newDouble(double value) {
        return new DoubleValue(value);
    }

    public static PrimitiveValue newBytes(byte[] value) {
        return value.length == 0 ? Bytes.EMPTY_STRING : new Bytes(PrimitiveType.Bytes, value.clone());
    }

    public static PrimitiveValue newBytes(ByteString value) {
        return value.isEmpty() ? Bytes.EMPTY_STRING : new Bytes(PrimitiveType.Bytes, value);
    }

    public static PrimitiveValue newBytesOwn(byte[] value) {
        return value.length == 0 ? Bytes.EMPTY_STRING : new Bytes(PrimitiveType.Bytes, value);
    }

    public static PrimitiveValue newText(String value) {
        return value.isEmpty() ? Text.EMPTY_TEXT : new Text(PrimitiveType.Text, value);
    }

    public static PrimitiveValue newYson(byte[] value) {
        return value.length == 0 ? Bytes.EMPTY_YSON : new Bytes(PrimitiveType.Yson, value.clone());
    }

    public static PrimitiveValue newYson(ByteString value) {
        return value.isEmpty() ? Bytes.EMPTY_YSON : new Bytes(PrimitiveType.Yson, value);
    }

    public static PrimitiveValue newYsonOwn(byte[] value) {
        return value.length == 0 ? Bytes.EMPTY_YSON : new Bytes(PrimitiveType.Yson, value);
    }

    public static PrimitiveValue newJson(String value) {
        return value.isEmpty() ? Text.EMPTY_JSON : new Text(PrimitiveType.Json, value);
    }

    public static PrimitiveValue newJsonDocument(String value) {
        return value.isEmpty() ? Text.EMPTY_JSON_DOCUMENT : new Text(PrimitiveType.JsonDocument, value);
    }

    public static PrimitiveValue newUuid(long high, long low) {
        return new Uuid(high, low);
    }

    public static PrimitiveValue newUuid(UUID uuid) {
        return new Uuid(uuid);
    }

    public static PrimitiveValue newUuid(String uuid) {
        return new Uuid(uuid);
    }

    public static PrimitiveValue newDate(long daysSinceEpoch) {
        if (daysSinceEpoch < 0) {
            throw new IllegalArgumentException("negative daysSinceEpoch: " + daysSinceEpoch);
        }
        return new InstantValue(PrimitiveType.Date, TimeUnit.DAYS.toMicros(daysSinceEpoch));
    }

    public static PrimitiveValue newDate(LocalDate value) {
        return newDate(value.toEpochDay());
    }

    public static PrimitiveValue newDate(Instant value) {
        return newDate(TimeUnit.SECONDS.toDays(value.getEpochSecond()));
    }

    public static PrimitiveValue newDatetime(long secondsSinceEpoch) {
        if (secondsSinceEpoch < 0) {
            throw new IllegalArgumentException("negative secondsSinceEpoch: " + secondsSinceEpoch);
        }
        return new InstantValue(PrimitiveType.Datetime, TimeUnit.SECONDS.toMicros(secondsSinceEpoch));
    }

    public static PrimitiveValue newDatetime(Instant value) {
        return newDatetime(value.getEpochSecond());
    }

    public static PrimitiveValue newDatetime(LocalDateTime value) {
        return newDatetime(value.toEpochSecond(ZoneOffset.UTC));
    }

    public static PrimitiveValue newTimestamp(long microsSinceEpoch) {
        if (microsSinceEpoch < 0) {
            throw new IllegalArgumentException("negative microsSinceEpoch: " + microsSinceEpoch);
        }
        return new InstantValue(PrimitiveType.Timestamp, microsSinceEpoch);
    }

    public static PrimitiveValue newTimestamp(Instant value) {
        long micros = TimeUnit.SECONDS.toMicros(value.getEpochSecond()) +
            TimeUnit.NANOSECONDS.toMicros(value.getNano());
        return new InstantValue(PrimitiveType.Timestamp, micros);
    }

    public static PrimitiveValue newInterval(long micros) {
        return new IntervalValue(micros);
    }

    public static PrimitiveValue newInterval(Duration value) {
        return newInterval(TimeUnit.NANOSECONDS.toMicros(value.toNanos()));
    }

    public static PrimitiveValue newTzDate(ZonedDateTime dateTime) {
        return new TzDatetime(PrimitiveType.TzDate, dateTime);
    }

    public static PrimitiveValue newTzDatetime(ZonedDateTime dateTime) {
        return new TzDatetime(PrimitiveType.TzDatetime, dateTime);
    }

    public static PrimitiveValue newTzTimestamp(ZonedDateTime dateTime) {
        return new TzDatetime(PrimitiveType.TzTimestamp, dateTime);
    }

    // -- helpers --

    private static void checkType(PrimitiveType expected, PrimitiveType actual) {
        if (expected != actual) {
            throw new IllegalStateException("types mismatch, expected " + expected + ", but was " + actual);
        }
    }

    // -- implementations --

    private static final class Bool extends PrimitiveValue {
        private static final Bool TRUE = new Bool(true);
        private static final Bool FALSE = new Bool(false);

        private final boolean value;

        private Bool(boolean value) {
            this.value = value;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Bool;
        }

        @Override
        public boolean getBool() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            return value == ((Bool) o).value;
        }

        @Override
        public int hashCode() {
            return Boolean.hashCode(value);
        }

        @Override
        public String toString() {
            return Boolean.toString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromBool(value);
        }
    }

    private static final class Int8 extends PrimitiveValue {
        private final byte value;

        Int8(byte value) {
            this.value = value;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Int8;
        }

        @Override
        public byte getInt8() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            return value == ((Int8) o).value;
        }

        @Override
        public int hashCode() {
            return Byte.hashCode(value);
        }

        @Override
        public String toString() {
            return Byte.toString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromInt8(value);
        }
    }

    private static final class Uint8 extends PrimitiveValue {
        private final int value;

        Uint8(int value) {
            this.value = value & 0xFF;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Uint8;
        }

        @Override
        public int getUint8() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            return value == ((Uint8) o).value;
        }

        @Override
        public int hashCode() {
            return Integer.hashCode(value);
        }

        @Override
        public String toString() {
            return Integer.toUnsignedString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromUint8(value);
        }
    }

    private static final class Int16 extends PrimitiveValue {
        private final short value;

        Int16(short value) {
            this.value = value;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Int16;
        }

        @Override
        public short getInt16() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            return value == ((Int16) o).value;
        }

        @Override
        public int hashCode() {
            return Short.hashCode(value);
        }

        @Override
        public String toString() {
            return Short.toString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromInt16(value);
        }
    }

    private static final class Uint16 extends PrimitiveValue {
        private final int value;

        Uint16(int value) {
            this.value = value & 0xFFFF;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Uint16;
        }

        @Override
        public int getUint16() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            return value == ((Uint16) o).value;
        }

        @Override
        public int hashCode() {
            return Integer.hashCode(value);
        }

        @Override
        public String toString() {
            return Integer.toUnsignedString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromUint16(value);
        }
    }

    private static final class Int32 extends PrimitiveValue {
        private final int value;

        Int32(int value) {
            this.value = value;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Int32;
        }

        @Override
        public int getInt32() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            return value == ((Int32) o).value;
        }

        @Override
        public int hashCode() {
            return Integer.hashCode(value);
        }

        @Override
        public String toString() {
            return Integer.toString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromInt32(value);
        }
    }

    private static final class Uint32 extends PrimitiveValue {
        private final long value;

        Uint32(long value) {
            this.value = value & 0xFFFFFFFFL;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Uint32;
        }

        @Override
        public long getUint32() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            return value == ((Uint32) o).value;
        }

        @Override
        public int hashCode() {
            return Long.hashCode(value);
        }

        @Override
        public String toString() {
            return Long.toUnsignedString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromUint32(value);
        }
    }

    private static final class Int64 extends PrimitiveValue {
        private final long value;

        Int64(long value) {
            this.value = value;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Int64;
        }

        @Override
        public long getInt64() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            return value == ((Int64) o).value;
        }

        @Override
        public int hashCode() {
            return Long.hashCode(value);
        }

        @Override
        public String toString() {
            return Long.toString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromInt64(value);
        }
    }

    private static final class Uint64 extends PrimitiveValue {
        private final long value;

        Uint64(long value) {
            this.value = value;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Uint64;
        }

        @Override
        public long getUint64() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            return value == ((Uint64) o).value;
        }

        @Override
        public int hashCode() {
            return Long.hashCode(value);
        }

        @Override
        public String toString() {
            return Long.toUnsignedString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromUint64(value);
        }
    }

    private static final class FloatValue extends PrimitiveValue {
        private final float value;

        FloatValue(float value) {
            this.value = value;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Float;
        }

        @Override
        public float getFloat() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            FloatValue that = (FloatValue) o;
            return Float.compare(that.value, value) == 0;
        }

        @Override
        public int hashCode() {
            return Float.hashCode(value);
        }

        @Override
        public String toString() {
            return Float.toString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromFloat(value);
        }
    }

    private static final class DoubleValue extends PrimitiveValue {
        private final double value;

        DoubleValue(double value) {
            this.value = value;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Double;
        }

        @Override
        public double getDouble() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            DoubleValue that = (DoubleValue) o;
            return Double.compare(that.value, value) == 0;
        }

        @Override
        public int hashCode() {
            return Double.hashCode(value);
        }

        @Override
        public String toString() {
            return Double.toString(value);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromDouble(value);
        }
    }

    private static final class Bytes extends PrimitiveValue {
        private static final Bytes EMPTY_STRING = new Bytes(PrimitiveType.Bytes, new byte[0]);
        private static final Bytes EMPTY_YSON = new Bytes(PrimitiveType.Yson, new byte[0]);

        private final PrimitiveType type;
        private final Object value;

        private Bytes(PrimitiveType type, byte[] value) {
            this.type = type;
            this.value = value;
        }

        private Bytes(PrimitiveType type, ByteString value) {
            this.type = type;
            this.value = value;
        }

        @Override
        public PrimitiveType getType() {
            return type;
        }

        @Override
        public byte[] getBytes() {
            return getBytes(PrimitiveType.Bytes);
        }

        @Override
        public byte[] getBytesUnsafe() {
            return getBytesUnsafe(PrimitiveType.Bytes);
        }

        @Override
        public ByteString getBytesAsByteString() {
            return getByteString(PrimitiveType.Bytes);
        }

        @Override
        public byte[] getYson() {
            return getBytes(PrimitiveType.Yson);
        }

        @Override
        public byte[] getYsonUnsafe() {
            return getBytesUnsafe(PrimitiveType.Yson);
        }

        @Override
        public ByteString getYsonBytes() {
            return getByteString(PrimitiveType.Yson);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            Bytes that = (Bytes) o;
            if (type != that.type) {
                return false;
            }

            if (value instanceof byte[]) {
                if (that.value instanceof byte[]) {
                    return Arrays.equals((byte[]) value, (byte[]) that.value);
                }
                return that.value.equals(UnsafeByteOperations.unsafeWrap((byte[]) value));
            }

            if (that.value instanceof byte[]) {
                return value.equals(UnsafeByteOperations.unsafeWrap((byte[]) that.value));
            }
            return value.equals(that.value);
        }

        @Override
        public int hashCode() {
            int result = type.hashCode();

            if (value instanceof byte[]) {
                for (byte b : (byte[]) value) {
                    result = 31 * result + b;
                }
            } else {
                ByteString v = (ByteString) this.value;
                for (int i = 0; i < v.size(); i++) {
                    byte b = v.byteAt(i);
                    result = 31 * result + b;
                }
            }

            return result;
        }

        @Override
        public String toString() {
            final int length = (value instanceof byte[])
                ? ((byte[]) value).length
                : ((ByteString) value).size();

            if (length == 0) {
                return "\"\"";
            }

            // bytes are escaped as \nnn (octal value)
            StringBuilder sb = new StringBuilder(length * 4 + 2);
            sb.append('\"');

            if (value instanceof byte[]) {
                for (byte b : (byte[]) value) {
                    encodeAsOctal(sb, b);
                }
            } else {
                ByteString bytes = (ByteString) this.value;
                for (int i = 0; i < bytes.size(); i++) {
                    encodeAsOctal(sb, bytes.byteAt(i));
                }
            }
            sb.append('\"');
            return sb.toString();
        }

        private static void encodeAsOctal(StringBuilder sb, byte b) {
            final int i = Byte.toUnsignedInt(b);
            sb.append('\\');
            if (i < 64) {
                sb.append('0');
                if (i < 8) {
                    sb.append('0');
                }
            }
            sb.append(Integer.toString(i, 8));
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromBytes(getByteString(type));
        }

        private byte[] getBytes(PrimitiveType expected) {
            checkType(expected, type);

            if (value instanceof byte[]) {
                return ((byte[]) value).clone();
            }
            return ((ByteString) value).toByteArray();
        }

        private byte[] getBytesUnsafe(PrimitiveType expected) {
            checkType(expected, type);

            if (value instanceof byte[]) {
                return (byte[]) value;
            }
            return ((ByteString) value).toByteArray();
        }

        private ByteString getByteString(PrimitiveType expected) {
            checkType(expected, type);

            if (value instanceof byte[]) {
                return UnsafeByteOperations.unsafeWrap((byte[]) value);
            }
            return (ByteString) value;
        }
    }

    private static final class Text extends PrimitiveValue {
        private static final Text EMPTY_TEXT = new Text(PrimitiveType.Text, "");
        private static final Text EMPTY_JSON = new Text(PrimitiveType.Json, "");
        private static final Text EMPTY_JSON_DOCUMENT = new Text(PrimitiveType.JsonDocument, "");

        private static final Escaper ESCAPER = Escapers.builder()
            .addEscape('\\', "\\\\")
            .addEscape('\"', "\\\"")
            .build();

        private final PrimitiveType type;
        private final String value;

        Text(PrimitiveType type, String value) {
            this.type = type;
            this.value = value;
        }

        @Override
        public PrimitiveType getType() {
            return type;
        }

        @Override
        public String getText() {
            checkType(PrimitiveType.Text, type);
            return value;
        }

        @Override
        public String getJson() {
            checkType(PrimitiveType.Json, type);
            return value;
        }

        @Override
        public String getJsonDocument() {
            checkType(PrimitiveType.JsonDocument, type);
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            Text that = (Text) o;
            if (type != that.type) {
                return false;
            }
            return that.value.equals(value);
        }

        @Override
        public int hashCode() {
            return 31 * type.hashCode() + value.hashCode();
        }

        @Override
        public String toString() {
            if (value.isEmpty()) {
                return "\"\"";
            }

            return '\"' + ESCAPER.escape(value) + '\"';
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromText(value);
        }
    }

    private static final class Uuid extends PrimitiveValue {
        private final long high;
        private final long low;

        Uuid(long high, long low) {
            this.high = high;
            this.low = low;
        }

        Uuid(String value) {
            String[] components = value.split("-");
            if (components.length != 5) {
                throw new IllegalArgumentException("invalid UUID string: " + value);
            }

            long timeLow = Long.parseLong(components[0], 16);
            long timeMid = Long.parseLong(components[1], 16) << 32;
            long timeHighAndVersion = Long.parseLong(components[2], 16) << 48;
            this.low = timeLow | timeMid | timeHighAndVersion;

            long lsb = Long.parseLong(components[3], 16) << 48;
            lsb |= Long.parseLong(components[4], 16);
            this.high = LittleEndian.bswap(lsb);
        }

        Uuid(UUID uuid) {
            long msb = uuid.getMostSignificantBits();
            long timeLow  = (msb & 0xffffffff00000000L) >>> 32;
            long timeMid = (msb & 0x00000000ffff0000L) << 16;
            long timeHighAndVersion = (msb & 0x000000000000ffffL) << 48;

            this.low = timeLow | timeMid | timeHighAndVersion;
            this.high = LittleEndian.bswap(uuid.getLeastSignificantBits());
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Uuid;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            Uuid uuid = (Uuid) o;
            return high == uuid.high && low == uuid.low;
        }

        @Override
        public int hashCode() {
            int result = (int) (high ^ (high >>> 32));
            result = 31 * result + (int) (low ^ (low >>> 32));
            return result;
        }

        @Override
        public String toString() {
            return '\"' + getUuidString() + '\"';
        }

        @Override
        public String getUuidString() {
            long hiBe = LittleEndian.bswap(high);
            return
                digits(low, 8) + "-" + digits(low >>> 32, 4) + "-" + digits(low >>> 48, 4) + "-" +
                digits(hiBe >> 48, 4) + "-" + digits(hiBe, 12);
        }

        @Override
        public long getUuidHigh() {
            return high;
        }

        @Override
        public long getUuidLow() {
            return low;
        }

        @Override
        public UUID getUuidJdk() {
            long timeLow = (low & 0x00000000ffffffffL) << 32;
            long timeMid = (low & 0x0000ffff00000000L) >>> 16;
            long timeHighAndVersion = (low & 0xffff000000000000L) >>> 48;

            long hiBe = LittleEndian.bswap(high);
            return new UUID(timeLow | timeMid | timeHighAndVersion, hiBe);
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromUuid(high, low);
        }

        /** Returns val represented by the specified number of hex digits. */
        private static String digits(long val, int digits) {
            long high = 1L << (digits * 4);
            return Long.toHexString(high | (val & (high - 1))).substring(1);
        }
    }

    private static final class InstantValue extends PrimitiveValue {
        private final PrimitiveType type;
        private final long microsSinceEpoch;

        InstantValue(PrimitiveType type, long microsSinceEpoch) {
            this.type = type;
            this.microsSinceEpoch = microsSinceEpoch;
        }

        @Override
        public PrimitiveType getType() {
            return type;
        }

        @Override
        public LocalDate getDate() {
            checkType(PrimitiveType.Date, type);
            return ProtoValue.toDate(TimeUnit.MICROSECONDS.toDays(microsSinceEpoch));
        }

        @Override
        public LocalDateTime getDatetime() {
            checkType(PrimitiveType.Datetime, type);
            return ProtoValue.toDatetime(TimeUnit.MICROSECONDS.toSeconds(microsSinceEpoch));
        }

        @Override
        public Instant getTimestamp() {
            checkType(PrimitiveType.Timestamp, type);
            return ProtoValue.toTimestamp(microsSinceEpoch);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            InstantValue that = (InstantValue) o;
            return microsSinceEpoch == that.microsSinceEpoch && type == that.type;
        }

        @Override
        public int hashCode() {
            return 31 * type.hashCode() + (int) (microsSinceEpoch ^ (microsSinceEpoch >>> 32));
        }

        @Override
        public String toString() {
            switch (type) {
                case Date: return DateTimeFormatter.ISO_DATE.format(getDate());
                case Datetime: return DateTimeFormatter.ISO_DATE_TIME.format(getDatetime());
                case Timestamp: return DateTimeFormatter.ISO_INSTANT.format(getTimestamp());
                default:
                    throw new IllegalStateException("unsupported type: " + type);
            }
        }

        @Override
        public ValueProtos.Value toPb() {
            switch (type) {
                case Date: return ProtoValue.fromDate(TimeUnit.MICROSECONDS.toDays(microsSinceEpoch));
                case Datetime: return ProtoValue.fromDatetime(TimeUnit.MICROSECONDS.toSeconds(microsSinceEpoch));
                case Timestamp: return ProtoValue.fromTimestamp(microsSinceEpoch);
                default:
                    throw new IllegalStateException("unsupported type: " + type);
            }
        }
    }

    private static final class IntervalValue extends PrimitiveValue {
        private final long micros;

        IntervalValue(long micros) {
            this.micros = micros;
        }

        @Override
        public PrimitiveType getType() {
            return PrimitiveType.Interval;
        }

        @Override
        public Duration getInterval() {
            return Duration.of(micros, ChronoUnit.MICROS);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            IntervalValue that = (IntervalValue) o;
            return micros == that.micros;
        }

        @Override
        public int hashCode() {
            return (int) (micros ^ (micros >>> 32));
        }

        @Override
        public String toString() {
            return getInterval().toString();
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromInterval(micros);
        }
    }

    private static final class TzDatetime extends PrimitiveValue {
        private final PrimitiveType type;
        private final ZonedDateTime dateTime;

        TzDatetime(PrimitiveType type, ZonedDateTime dateTime) {
            this.type = type;
            this.dateTime = dateTime;
        }

        @Override
        public PrimitiveType getType() {
            return type;
        }

        @Override
        public ZonedDateTime getTzDate() {
            checkType(PrimitiveType.TzDate, type);
            return dateTime;
        }

        @Override
        public ZonedDateTime getTzDatetime() {
            checkType(PrimitiveType.TzDatetime, type);
            return dateTime;
        }

        @Override
        public ZonedDateTime getTzTimestamp() {
            checkType(PrimitiveType.TzTimestamp, type);
            return dateTime;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            TzDatetime that = (TzDatetime) o;
            return type == that.type && dateTime.equals(that.dateTime);
        }

        @Override
        public int hashCode() {
            int result = type.hashCode();
            result = 31 * result + dateTime.hashCode();
            return result;
        }

        @Override
        public String toString() {
            String timeStr = (type == PrimitiveType.TzDate)
                ? dateTime.toLocalDate().toString()
                : dateTime.toLocalDateTime().toString();

            return timeStr + ',' + dateTime.getZone().getId();
        }

        @Override
        public ValueProtos.Value toPb() {
            return ProtoValue.fromText(toString());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy