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

com.segment.analytics.Tape Maven / Gradle / Ivy

package com.segment.analytics;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.AbstractQueue;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

class Tape extends AbstractQueue {
  /** Backing storage implementation. */
  private final QueueFile queueFile;
  /** Keep file around for error reporting. */
  private final File file;
  /** Reusable byte output buffer. */
  private final DirectByteArrayOutputStream bytes = new DirectByteArrayOutputStream();
  /** Arbitrer to convert from bytes to concrete types and vice versa. */
  private final Converter converter;

  Tape(File file, Converter converter) throws IOException {
    this.file = file;
    this.queueFile = new QueueFile(file);
    this.converter = converter;
  }

  @Override public Iterator iterator() {
    final List elements = new ArrayList();
    try {
      queueFile.forEach(new QueueFile.ElementReader() {
        @Override
        public void read(InputStream in, int length) throws IOException {
          byte[] data = new byte[length];
          in.read(data, 0, length);
          elements.add(converter.from(data));
        }
      });
    } catch (IOException e) {
      throw new FileException("Unable to iterate over QueueFile contents.", e, file);
    }
    return elements.iterator();
  }

  @Override public int size() {
    return queueFile.size();
  }

  @Override public boolean offer(E e) {
    if (e == null) {
      throw new IllegalArgumentException("null element may not be inserted.");
    }
    try {
      bytes.reset();
      converter.toStream(e, bytes);
      queueFile.add(bytes.getArray(), 0, bytes.size());
    } catch (IOException exception) {
      throw new FileException("Failed to add entry.", exception, file);
    }
    return true;
  }

  @Override public E poll() {
    E e = peek();
    if (e == null) {
      return null;
    } else {
      try {
        queueFile.remove();
        return e;
      } catch (IOException exception) {
        throw new FileException("Failed to poll.", exception, file);
      }
    }
  }

  @Override public E peek() {
    try {
      byte[] bytes = queueFile.peek();
      if (bytes == null) return null;
      return converter.from(bytes);
    } catch (IOException e) {
      throw new FileException("Failed to peek.", e, file);
    }
  }

  /**
   * Convert a byte stream to and from a concrete type.
   *
   * @param  Object type.
   */
  interface Converter {
    /** Converts bytes to an object. */
    T from(byte[] bytes) throws IOException;

    /** Converts o to bytes written to the specified stream. */
    void toStream(T o, OutputStream bytes) throws IOException;
  }

  /** Enables direct access to the internal array. Avoids unnecessary copying. */
  private static class DirectByteArrayOutputStream extends ByteArrayOutputStream {
    DirectByteArrayOutputStream() {
      super();
    }

    /**
     * Gets a reference to the internal byte array.  The {@link #size()} method indicates how many
     * bytes contain actual data added since the last {@link #reset()} call.
     */
    byte[] getArray() {
      return buf;
    }
  }

  /** Encapsulates an {@link IOException} in an extension of {@link RuntimeException}. */
  static class FileException extends RuntimeException {
    private final File file;

    FileException(String message, IOException e, File file) {
      super(message, e);
      this.file = file;
    }

    File getFile() {
      return file;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy