/******************************************************************************* * Copyright (c) 2016 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Semantum Oy - initial API and implementation *******************************************************************************/ package org.simantics.databoard.util.binary; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; /** * This class is a custom write-only wrapper for {@link BinaryFile} and * {@link BinaryMemory} that implements a {@link RandomAccessBinary} which is * contained fully in memory until its size reaches a user-specified threshold. * When that size threshold is exceeded, the {@link BinaryMemory} contents are * flushed into a BinaryFile and from there on this implementation will continue * to write to the BinaryFile. *

* Since it is based {@link BinaryFile} and {@link BinaryMemory}, this * implementation is also buffered and not thread-safe. *

* The {@link SeekableBinaryReadable} part of the {@link RandomAccessBinary} * interface is not implemented and will throw * {@link UnsupportedOperationException}. * * @author Tuukka Lehtonen * @since 1.22.1 & 1.24.0 * @see BinaryFile * @see BinaryMemory */ public class DeferredBinaryFile implements RandomAccessBinary { File file; int threshold; int fileBufferSize; BinaryMemory memory; RandomAccessBinary backend; public DeferredBinaryFile(File file, int threshold, int fileBufferSize) throws IOException { this.memory = new BinaryMemory(threshold+10000); this.backend = memory; this.threshold = threshold; this.file = file; this.fileBufferSize = fileBufferSize; } /** * Closes the object. Note, this will close the input random access file. * This method may be called several times. * * @throws IOException */ @Override public synchronized void close() throws IOException { if (backend == null) return; backend.close(); backend = null; } @Override public synchronized boolean isOpen() { return backend != null; } @Override public byte readByte() throws IOException { throw new UnsupportedOperationException(); } @Override public char readChar() throws IOException { throw new UnsupportedOperationException(); } @Override public int readUnsignedByte() throws IOException { throw new UnsupportedOperationException(); } @Override public boolean readBoolean() throws IOException { throw new UnsupportedOperationException(); } @Override public void readFully(byte[] dst, int offset, int length) throws IOException { throw new UnsupportedOperationException(); } @Override public void readFully(byte[] dst) throws IOException { throw new UnsupportedOperationException(); } @Override public void readFully(ByteBuffer buf) throws IOException { throw new UnsupportedOperationException(); } @Override public void readFully(ByteBuffer buf, int length) throws IOException { throw new UnsupportedOperationException(); } @Override public double readDouble() throws IOException { throw new UnsupportedOperationException(); } @Override public float readFloat() throws IOException { throw new UnsupportedOperationException(); } @Override public int readInt() throws IOException { throw new UnsupportedOperationException(); } @Override public long readLong() throws IOException { throw new UnsupportedOperationException(); } @Override public short readShort() throws IOException { throw new UnsupportedOperationException(); } @Override public int readUnsignedShort() throws IOException { throw new UnsupportedOperationException(); } @Override public String readUTF() throws IOException { throw new UnsupportedOperationException(); } public final String readLine() throws IOException { throw new UnsupportedOperationException(); } @Override public long position() throws IOException { return backend.position(); } @Override public void position(long newPosition) throws IOException { backend.position(newPosition); } /** * Flushes internal buffer */ @Override public void flush() throws IOException { backend.flush(); } /** * Clears read&write buffer. The file can be modified elsewhere after this. * * @throws IOException */ @Override public void reset() throws IOException { backend.reset(); } @Override public long skipBytes(long bytes) throws IOException { return backend.skipBytes(bytes); } @Override public int skipBytes(int bytes) throws IOException { return backend.skipBytes(bytes); } // WRITE @Override public void write(int b) throws IOException { backend.write(b); if (memory != null) threshold(); } @Override public void writeByte(int b) throws IOException { backend.writeByte(b); if (memory != null) threshold(); } @Override public void writeBoolean(boolean v) throws IOException { backend.writeBoolean(v); if (memory != null) threshold(); } @Override public void writeFully(ByteBuffer src) throws IOException { backend.writeFully(src); if (memory != null) threshold(); } @Override public void writeFully(ByteBuffer src, int length) throws IOException { backend.writeFully(src, length); if (memory != null) threshold(); } @Override public void write(byte[] src, int offset, int length) throws IOException { backend.write(src, offset, length); } @Override public void write(byte[] src) throws IOException { backend.write(src); if (memory != null) threshold(); } @Override public void writeDouble(double value) throws IOException { backend.writeDouble(value); if (memory != null) threshold(); } @Override public void writeFloat(float value) throws IOException { backend.writeFloat(value); if (memory != null) threshold(); } @Override public void writeInt(int value) throws IOException { backend.writeInt(value); if (memory != null) threshold(); } @Override public void writeLong(long value) throws IOException { backend.writeLong(value); if (memory != null) threshold(); } @Override public void writeShort(int value) throws IOException { backend.writeShort(value); if (memory != null) threshold(); } @Override public void writeChar(int value) throws IOException { backend.writeChar(value); if (memory != null) threshold(); } @Override public void writeBytes(String s) throws IOException { backend.writeBytes(s); if (memory != null) threshold(); } @Override public void writeChars(String s) throws IOException { backend.writeChars(s); if (memory != null) threshold(); } @Override public void writeUTF(String s) throws IOException { backend.writeUTF(s); if (memory != null) threshold(); } @Override public void insertBytes(long bytes, ByteSide side) throws IOException { throw new UnsupportedOperationException(); } @Override public void removeBytes(long bytes, ByteSide side) throws IOException { throw new UnsupportedOperationException(); } @Override public long length() throws IOException { return backend.length(); } @Override public void setLength(long newLength) throws IOException { throw new UnsupportedOperationException(); } @Override public String toString() { try { return "DeferredBinaryFile(file="+file.getName()+", size="+length()+")"; } catch (IOException e) { return "DeferredBinaryFile()"; } } private void threshold() throws IOException { if (backend.position() >= threshold) { backend = new BinaryFile(file, fileBufferSize); long length = memory.position(); memory.position(0); memory.toByteBuffer().position(0); backend.writeFully(memory.toByteBuffer(), (int) length); memory = null; } } public boolean isInMemory() { return memory != null; } public RandomAccessBinary getMemory() { return memory; } public RandomAccessBinary getBackend() { return backend; } }