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

ru.curs.celesta.dbutils.BLOB Maven / Gradle / Ivy

The newest version!
package ru.curs.celesta.dbutils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * Class for working with BLOB fields.
 */
public final class BLOB implements Cloneable {
    private DataPage data;
    private boolean isModified;
    private int size;

    /**
     * Empty (NULL) BLOB.
     */
    public BLOB() {
    }

    /**
     * BLOB based on data from stream.
     *
     * @param source  Stream from which the data is being read to BLOB
     * @throws IOException  on reading error
     */
    BLOB(final InputStream source) throws IOException {
        InputStream counter = new InputStream() {
            @Override
            public int read() throws IOException {
                int result = source.read();
                if (result >= 0) {
                    size++;
                }
                return result;
            }
        };
        int buf = counter.read();
        data = buf < 0 ? new DataPage(0) : new DataPage(buf, counter);
    }

    /**
     * Returns a BLOB clone pointing at the same data page.
     *
     * @return
     */
    @Override
    public BLOB clone() {
        BLOB result = new BLOB();
        result.data = data;
        result.size = size;
        return result;
    }

    /**
     * Whether BLOB data has been changed.
     *
     * @return
     */
    public boolean isModified() {
        return isModified;
    }

    /**
     * Returns a stream for data reading.
     *
     * @return
     */
    public InputStream getInStream() {
        return data == null ? null : data.getInStream();
    }

    /**
     * Returns a stream for data writing, at the same time dropping off current data from BLOB.
     *
     * @return
     */
    public OutputStream getOutStream() {
        isModified = true;
        data = new DataPage();
        size = 0;
        return new OutputStream() {
            private DataPage tail = data;

            @Override
            public void write(int b) {
                tail = tail.write(b);
                size++;
            }
        };
    }

    /**
     * Whether current field accepts a value of {@code NULL}.
     *
     * @return
     */
    public boolean isNull() {
        return data == null;
    }

    /**
     * Resets BLOB to {@code NULL}.
     */
    public void setNull() {
        isModified = isModified || data != null;
        size = 0;
        data = null;
    }

    /**
     * Returns data size.
     *
     * @return
     */
    public int size() {
        return size;
    }

    /**
     * Data of BLOB field.
     */
    private static final class DataPage {
        private static final int DEFAULT_PAGE_SIZE = 0xFFFF;
        private static final int BYTE_MASK = 0xFF;

        private final byte[] data;
        private DataPage nextPage;
        private int pos;

        DataPage() {
            this(DEFAULT_PAGE_SIZE);
        }

        private DataPage(int size) {
            data = new byte[size];
        }

        private DataPage(int firstByte, InputStream source) throws IOException {
            this();
            int buf = firstByte;
            while (pos < data.length && buf >= 0) {
                data[pos++] = (byte) buf;
                buf = source.read();
            }
            nextPage = buf < 0 ? null : new DataPage(buf, source);
        }

        DataPage write(int b) {
            if (pos < data.length) {
                data[pos++] = (byte) (b & BYTE_MASK);
                return this;
            } else {
                DataPage result = new DataPage();
                nextPage = result;
                return result.write(b);
            }
        }

        InputStream getInStream() {
            return new InputStream() {
                private int i = 0;
                private DataPage currentPage = DataPage.this;

                @Override
                public int read() {
                    if (i < currentPage.pos) {
                        return (int) currentPage.data[i++] & BYTE_MASK;
                    } else if (currentPage.nextPage != null) {
                        i = 0;
                        currentPage = currentPage.nextPage;
                        return read();
                    } else {
                        return -1;
                    }
                }
            };
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy