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

ru.progrm_jarvis.javacommons.data.DataSerializers Maven / Gradle / Ivy

package ru.progrm_jarvis.javacommons.data;

import lombok.*;
import lombok.experimental.FieldDefaults;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;
import ru.progrm_jarvis.javacommons.util.UuidUtil;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.time.*;
import java.util.*;

/**
 * Common {@link DataSerializer data serializers}.
 */
@UtilityClass
public class DataSerializers {

    /* ********************************************** Primitive types ********************************************** */

    public @NotNull DataSerializer<@NotNull Boolean> booleanDataSerializer() {
        return BooleanDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull Byte> byteDataSerializer() {
        return ByteDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull Short> shortDataSerializer() {
        return ShortDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull Character> charDataSerializer() {
        return CharDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull Integer> intDataSerializer() {
        return IntDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull Long> longDataSerializer() {
        return LongDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull Float> floatDataSerializer() {
        return FloatDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull Double> doubleDataSerializer() {
        return DoubleDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull String> stringDataSerializer() {
        return StringDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull UUID> uuidDataSerializer() {
        return UuidDataSerializer.INSTANCE;
    }

    /* ************************************************ Collections ************************************************ */

    public , T> @NotNull DataSerializer collectionDataSerializer(
            final @NonNull DataSerializers.SizeAwareFactory<@NotNull C> collectionFactory,
            final @NonNull DataSerializer elementSerializer
    ) {
        return new CollectionDataSerializer<>(collectionFactory, elementSerializer);
    }

    public  @NotNull DataSerializer> collectionDataSerializer(
            final @NonNull DataSerializer elementSerializer) {
        return collectionDataSerializer(ArrayList::new, elementSerializer);
    }

    public  @NotNull DataSerializer> listDataSerializer(
            final @NonNull DataSerializer elementSerializer
    ) {
        return collectionDataSerializer(ArrayList::new, elementSerializer);
    }

    public  @NotNull DataSerializer> setDataSerializer(
            final @NonNull DataSerializer elementSerializer
    ) {
        return collectionDataSerializer(HashSet::new, elementSerializer);
    }

    public , K, V> @NotNull DataSerializer mapDataSerializer(
            final @NonNull DataSerializers.SizeAwareFactory<@NotNull M> mapFactory,
            final @NonNull DataSerializer keySerializer,
            final @NonNull DataSerializer valueSerializer
    ) {
        return new MapDataSerializer<>(mapFactory, keySerializer, valueSerializer);
    }

    /* *************************************************** Enums *************************************************** */

    public > DataSerializer<@NotNull E> namedEnumDataSerializer(final @NonNull Class enumType) {
        final E[] enumValues;
        final int length;
        val map = new HashMap(length = (enumValues = enumType.getEnumConstants()).length);
        for (var index = 0; index < length; index++) {
            final E value;
            map.put((value = enumValues[index]).name(), value);
        }

        return new NamedEnumDataSerializer<>(map);
    }

    public > DataSerializer<@NotNull E> ordinalEnumDataSerializer(final @NonNull Class enumType) {
        final E[] enumConstants;
        final int enumConstantsLength;
        if ((enumConstantsLength = (enumConstants = enumType.getEnumConstants()).length)
                < 1 << 8) return new ByteOrdinalEnumDataSerializer<>(enumConstants);

        if (enumConstantsLength
                < 1 << 16) return new ShortOrdinalEnumDataSerializer<>(enumConstants);

        return new IntOrdinalEnumDataSerializer<>(enumConstants);
    }

    /* ************************************************ Date & Time ************************************************ */

    public @NotNull DataSerializer<@NotNull LocalDateTime> localDateTimeDataSerializer(
            final @NotNull ZoneOffset zoneOffset
    ) {
        return new LocalDateTimeDataSerializer(zoneOffset);
    }

    public @NotNull DataSerializer<@NotNull LocalDate> localDateDataSerializer() {
        return LocalDateDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull LocalTime> localTimeDataSerializer() {
        return LocalTimeDataSerializer.INSTANCE;
    }

    public @NotNull DataSerializer<@NotNull Instant> instantDataSerializer() {
        return InstantDataSerializer.INSTANCE;
    }

    /* ******************************************* Functional interfaces ******************************************* */

    @FunctionalInterface
    public interface SizeAwareFactory {
        @NotNull C create(int initialSize);
    }

    /* ********************************************** Implementations ********************************************** */

    /* ********************************************** Primitive types ********************************************** */

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class BooleanDataSerializer implements DataSerializer<@NotNull Boolean> {

        private static final @NotNull DataSerializer<@NotNull Boolean> INSTANCE = new BooleanDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull Boolean value) throws IOException {
            out.writeBoolean(value);
        }

        @Override
        public @NotNull Boolean read(final @NotNull DataInputStream in) throws IOException {
            return in.readBoolean();
        }

        @Override
        public @NotNull Boolean fromByteArray(final byte @NotNull [] byteArray) throws IOException {
            if (byteArray.length != 1) throw new IOException("Byte array should be of length 1");

            return byteArray[0] != 0;
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Boolean value, final int expectedSize) {
            return toByteArray(value);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Boolean value) {
            return new byte[]{value ? (byte) 1 : (byte) 0};
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class ByteDataSerializer implements DataSerializer<@NotNull Byte> {

        private static final @NotNull DataSerializer<@NotNull Byte> INSTANCE = new ByteDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull Byte value) throws IOException {
            out.writeByte(value);
        }

        @Override
        public @NotNull Byte read(final @NotNull DataInputStream in) throws IOException {
            return in.readByte();
        }

        @Override
        public @NotNull Byte fromByteArray(final byte @NotNull [] byteArray) throws IOException {
            if (byteArray.length != 1) throw new IOException("Byte array should be of length 1");

            return byteArray[0];
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Byte value, final int expectedSize) {
            return toByteArray(value);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Byte value) {
            return new byte[]{value};
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class ShortDataSerializer implements DataSerializer<@NotNull Short> {

        private static final @NotNull DataSerializer<@NotNull Short> INSTANCE = new ShortDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull Short value) throws IOException {
            out.writeShort(value);
        }

        @Override
        public @NotNull Short read(final @NotNull DataInputStream in) throws IOException {
            return in.readShort();
        }

        @Override
        public @NotNull Short fromByteArray(final byte @NotNull [] byteArray) throws IOException {
            if (byteArray.length != 2) throw new IOException("Byte array should be of length 2");

            return (short) (byteArray[0] << 8 | byteArray[1] & 0xFF);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Short value, final int expectedSize) {
            return toByteArray(value);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Short value) {
            return new byte[]{(byte) (value >>> 8 & 0xFF), (byte) (value & 0xFF)};
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class CharDataSerializer implements DataSerializer<@NotNull Character> {

        private static final @NotNull DataSerializer<@NotNull Character> INSTANCE = new CharDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull Character value) throws IOException {
            out.writeChar(value);
        }

        @Override
        public @NotNull Character read(final @NotNull DataInputStream in) throws IOException {
            return in.readChar();
        }

        @Override
        public @NotNull Character fromByteArray(final byte @NotNull [] byteArray) throws IOException {
            if (byteArray.length != 2) throw new IOException("Byte array should be of length 2");

            return (char) (byteArray[0] << 8 | byteArray[1] & 0xFF);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Character value, final int expectedSize) {
            return toByteArray(value);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Character value) {
            return new byte[]{(byte) (value >>> 8 & 0xFF), (byte) (value & 0xFF)};
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class IntDataSerializer implements DataSerializer<@NotNull Integer> {

        private static final @NotNull DataSerializer<@NotNull Integer> INSTANCE = new IntDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull Integer value) throws IOException {
            out.writeInt(value);
        }

        @Override
        public @NotNull Integer read(final @NotNull DataInputStream in) throws IOException {
            return in.readInt();
        }

        @Override
        public @NotNull Integer fromByteArray(final byte @NotNull [] byteArray) throws IOException {
            if (byteArray.length != 4) throw new IOException("Byte array should be of length 4");

            return byteArray[0] << 24
                    | (byteArray[1] & 0xFF) << 16
                    | (byteArray[2] & 0xFF) << 8
                    | byteArray[3] & 0xFF;
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Integer value, final int expectedSize) {
            return toByteArray(value);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Integer value) {
            return new byte[]{
                    (byte) (value >>> 24 & 0xFF),
                    (byte) (value >>> 16 & 0xFF),
                    (byte) (value >>> 8 & 0xFF),
                    (byte) (value & 0xFF)
            };
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class LongDataSerializer implements DataSerializer<@NotNull Long> {

        private static final @NotNull DataSerializer<@NotNull Long> INSTANCE = new LongDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull Long value) throws IOException {
            out.writeLong(value);
        }

        @Override
        public @NotNull Long read(final @NotNull DataInputStream in) throws IOException {
            return in.readLong();
        }

        @Override
        public @NotNull Long fromByteArray(final byte @NotNull [] byteArray) throws IOException {
            if (byteArray.length != 8) throw new IOException("Byte array should be of length 8");

            return (long) byteArray[0] << 56
                    | (long) (byteArray[1] & 0xFF) << 48
                    | (long) (byteArray[2] & 0xFF) << 40
                    | (long) (byteArray[3] & 0xFF) << 32
                    | (byteArray[4] & 0xFF) << 24
                    | (byteArray[5] & 0xFF) << 16
                    | (byteArray[6] & 0xFF) << 8
                    | byteArray[7] & 0xFF;
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Long value, final int expectedSize) {
            return toByteArray(value);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Long value) {
            return new byte[]{
                    (byte) (value >>> 56 & 0xFF),
                    (byte) (value >>> 48 & 0xFF),
                    (byte) (value >>> 40 & 0xFF),
                    (byte) (value >>> 32 & 0xFF),
                    (byte) (value >>> 24 & 0xFF),
                    (byte) (value >>> 16 & 0xFF),
                    (byte) (value >>> 8 & 0xFF),
                    (byte) (value & 0xFF)
            };
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class FloatDataSerializer implements DataSerializer<@NotNull Float> {

        private static final @NotNull DataSerializer<@NotNull Float> INSTANCE = new FloatDataSerializer();

        private static final @NotNull DataSerializer<@NotNull Integer> INT_DATA_SERIALIZER = IntDataSerializer.INSTANCE;

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull Float value) throws IOException {
            out.writeFloat(value);
        }

        @Override
        public @NotNull Float read(final @NotNull DataInputStream in) throws IOException {
            return in.readFloat();
        }

        @Override
        public @NotNull Float fromByteArray(final byte @NotNull [] byteArray) throws IOException {
            return Float.intBitsToFloat(INT_DATA_SERIALIZER.fromByteArray(byteArray));
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Float value, final int expectedSize) throws IOException {
            return toByteArray(value);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Float value) throws IOException {
            return INT_DATA_SERIALIZER.toByteArray(Float.floatToIntBits(value));
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class DoubleDataSerializer implements DataSerializer<@NotNull Double> {

        private static final @NotNull DataSerializer<@NotNull Double> INSTANCE = new DoubleDataSerializer();

        private static final @NotNull DataSerializer<@NotNull Long> LONG_DATA_SERIALIZER = LongDataSerializer.INSTANCE;

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull Double value) throws IOException {
            out.writeDouble(value);
        }

        @Override
        public @NotNull Double read(final @NotNull DataInputStream in) throws IOException {
            return in.readDouble();
        }

        @Override
        public @NotNull Double fromByteArray(final byte @NotNull [] byteArray) throws IOException {
            return Double.longBitsToDouble(LONG_DATA_SERIALIZER.fromByteArray(byteArray));
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Double value, final int expectedSize) throws IOException {
            return toByteArray(value);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull Double value) throws IOException {
            return LONG_DATA_SERIALIZER.toByteArray(Double.doubleToLongBits(value));
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class StringDataSerializer implements DataSerializer<@NotNull String> {

        private static final @NotNull DataSerializer<@NotNull String> INSTANCE = new StringDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull String string) throws IOException {
            out.writeUTF(string);
        }

        @Override
        public @NotNull String read(final @NotNull DataInputStream in) throws IOException {
            return in.readUTF();
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class UuidDataSerializer implements DataSerializer<@NotNull UUID> {

        private static final @NotNull DataSerializer<@NotNull UUID> INSTANCE = new UuidDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull UUID uuid) throws IOException {
            out.writeLong(uuid.getMostSignificantBits());
            out.writeLong(uuid.getLeastSignificantBits());
        }

        @Override
        public @NotNull UUID read(final @NotNull DataInputStream in) throws IOException {
            return new UUID(in.readLong(), in.readLong());
        }

        @Override
        public @NotNull UUID fromByteArray(final byte @NotNull [] byteArray) throws IOException {
            if (byteArray.length != 16) throw new IOException("Byte array should be of length 16");

            return UuidUtil.uuidFromBytes(byteArray);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull UUID uuid, final int expectedSize) {
            return toByteArray(uuid);
        }

        @Override
        public byte @NotNull [] toByteArray(final @NotNull UUID uuid) {
            return UuidUtil.uuidToBytes(uuid);
        }
    }

    /* ************************************************ Collections ************************************************ */

    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
    private static final class CollectionDataSerializer, T> implements DataSerializer {

        @NotNull DataSerializers.SizeAwareFactory<@NotNull C> collectionFactory;
        @NotNull DataSerializer elementSerializer;

        @Override
        public void write(final @NotNull DataOutputStream out, final C collection) throws IOException {
            out.writeInt(collection.size());
            for (val element : collection) elementSerializer.write(out, element);
        }

        @Override
        public C read(final @NotNull DataInputStream in) throws IOException {
            final int size;
            val collection = collectionFactory.create(size = in.readInt());
            for (var i = 0; i < size; i++) collection.add(elementSerializer.read(in));

            return collection;
        }
    }

    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
    private static final class MapDataSerializer, K, V> implements DataSerializer {

        @NotNull DataSerializers.SizeAwareFactory<@NotNull M> mapFactory;
        @NotNull DataSerializer keySerializer;
        @NotNull DataSerializer valueSerializer;

        @Override
        public void write(final @NotNull DataOutputStream out, final M map) throws IOException {
            final Set> entries;
            out.writeInt((entries = map.entrySet()).size());
            for (val entry : entries) {
                keySerializer.write(out, entry.getKey());
                valueSerializer.write(out, entry.getValue());
            }
        }

        @Override
        public M read(final @NotNull DataInputStream in) throws IOException {
            final int size;
            val map = mapFactory.create(size = in.readInt());
            for (var i = 0; i < size; i++) map.put(keySerializer.read(in), valueSerializer.read(in));

            return map;
        }
    }

    /* *************************************************** Enums *************************************************** */

    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
    private static final class NamedEnumDataSerializer> implements DataSerializer<@NotNull E> {

        @NotNull Map enumsByNames;

        @Override
        public void write(final @NotNull DataOutputStream out, final E element) throws IOException {
            out.writeUTF(element.name());
        }

        @Override
        public E read(final @NotNull DataInputStream in) throws IOException {
            final E value;
            {
                final String name;
                if ((value = enumsByNames.get(name = in.readUTF()))
                        == null) throw new IOException("Invalid enum constant name: " + name);
            }

            return value;
        }
    }

    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
    private static final class ByteOrdinalEnumDataSerializer> implements DataSerializer<@NotNull E> {

        @NotNull E @NotNull [] enumConstants;

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull E element) throws IOException {
            out.writeByte(element.ordinal());
        }

        @Override
        public E read(final @NotNull DataInputStream in) throws IOException {
            final int enumConstantOrdinal;
            final E[] thisEnumConstants;
            if ((enumConstantOrdinal = in.readUnsignedByte())
                    >= (thisEnumConstants = enumConstants).length) throw new IOException(
                    "Enum constant ordinal (" + enumConstantOrdinal
                            + ") exceeds its limit (" + thisEnumConstants.length + ')'
            );

            return enumConstants[enumConstantOrdinal];
        }
    }

    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
    private static final class ShortOrdinalEnumDataSerializer> implements DataSerializer<@NotNull E> {

        @NotNull E @NotNull [] enumConstants;

        @Override
        public void write(final @NotNull DataOutputStream out, final E element) throws IOException {
            out.writeShort(element.ordinal());
        }

        @Override
        public E read(final @NotNull DataInputStream in) throws IOException {
            final int enumConstantOrdinal;
            final E[] thisEnumConstants;
            if ((enumConstantOrdinal = in.readUnsignedShort())
                    >= (thisEnumConstants = enumConstants).length) throw new IOException(
                    "Enum constant ordinal (" + enumConstantOrdinal
                            + ") exceeds its limit (" + thisEnumConstants.length + ')'
            );

            return enumConstants[enumConstantOrdinal];
        }
    }

    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
    private static final class IntOrdinalEnumDataSerializer> implements DataSerializer<@NotNull E> {

        @NotNull E @NotNull [] enumConstants;

        @Override
        public void write(final @NotNull DataOutputStream out, final E element) throws IOException {
            out.writeInt(element.ordinal());
        }

        @Override
        public E read(final @NotNull DataInputStream in) throws IOException {
            final int enumConstantOrdinal;
            final E[] thisEnumConstants;
            if ((enumConstantOrdinal = in.readInt()) // note: no need for un-signing as arrays cannot be as big
                    >= (thisEnumConstants = enumConstants).length) throw new IOException(
                    "Enum constant ordinal (" + enumConstantOrdinal
                            + ") exceeds its limit (" + thisEnumConstants.length + ')'
            );

            return enumConstants[enumConstantOrdinal];
        }
    }

    /* ************************************************ Date & Time ************************************************ */

    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class InstantDataSerializer implements DataSerializer<@NotNull Instant> {

        private static final @NotNull DataSerializer<@NotNull Instant> INSTANCE = new InstantDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull Instant object) throws IOException {
            out.writeLong(object.toEpochMilli());
        }

        @Override
        public @NotNull Instant read(final @NotNull DataInputStream in) throws IOException {
            return Instant.ofEpochMilli(in.readLong());
        }
    }

    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
    private static final class LocalDateTimeDataSerializer implements DataSerializer<@NotNull LocalDateTime> {

        @NotNull ZoneOffset zoneOffset;

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull LocalDateTime object) throws IOException {
            out.writeLong(object.toEpochSecond(zoneOffset));
            out.writeInt(object.getNano());
        }

        @Override
        public @NotNull LocalDateTime read(final @NotNull DataInputStream in) throws IOException {
            return LocalDateTime.ofEpochSecond(in.readLong(), in.readInt(), zoneOffset);
        }
    }

    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class LocalTimeDataSerializer implements DataSerializer<@NotNull LocalTime> {

        private static final @NotNull DataSerializer<@NotNull LocalTime> INSTANCE = new LocalTimeDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull LocalTime object) throws IOException {
            out.writeLong(object.toNanoOfDay());
        }

        @Override
        public @NotNull LocalTime read(final @NotNull DataInputStream in) throws IOException {
            return LocalTime.ofNanoOfDay(in.readLong());
        }
    }

    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class LocalDateDataSerializer implements DataSerializer<@NotNull LocalDate> {

        private static final @NotNull DataSerializer<@NotNull LocalDate> INSTANCE = new LocalDateDataSerializer();

        @Override
        public void write(final @NotNull DataOutputStream out, final @NotNull LocalDate object) throws IOException {
            out.writeLong(object.toEpochDay());
        }

        @Override
        public @NotNull LocalDate read(final @NotNull DataInputStream in) throws IOException {
            return LocalDate.ofEpochDay(in.readLong());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy