content: /**
   * Portions Copyright 2006 DFKI GmbH.
   * Portions Copyright 2001 Sun Microsystems, Inc.
   * Portions Copyright 1999-2001 Language Technologies Institute,
   * Carnegie Mellon University.
   * All Rights Reserved.  Use is subject to license terms.
   * <p/>
   * Permission is hereby granted, free of charge, to use and distribute
   * this software and its documentation without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of this work, and to
   * permit persons to whom this work is furnished to do so, subject to
   * the following conditions:
   * <p/>
   * 1. The code must retain the above copyright notice, this list of
   * conditions and the following disclaimer.
   * 2. Any modifications must be clearly marked as such.
   * 3. Original authors' names are not deleted.
   * 4. The authors' names are not used to endorse or promote products
   * derived from this software without specific prior written
   * permission.
   * <p/>
   * DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH
   * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE
   * CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
   * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
   * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
   * THIS SOFTWARE.
   */
  package marytts.util.data;
  
  import java.io.DataOutput;
  import java.io.IOException;
  import java.io.RandomAccessFile;
  import java.nio.ByteBuffer;
  
  public class Datagram {
  
      public static final int NUM_HEADER_BYTES = (Long.SIZE + Integer.SIZE) / Byte.SIZE; // the duration and the length
      /****************/
      /* DATA FIELDS  */
      /****************/
  
      /**
       * The datagram duration, in samples.
       */
      protected long duration = 0l; // The (time) duration of the datagram, in samples
      /**
       * The datagram's contents, as a generic byte array.
       */
      protected byte[] data = null;
  
      /****************/
      /* CONSTRUCTORS */
      /****************/
      /**
       * Constructor for subclasses which want to represent data in a different format.
       *
       * @param setDuration the datagram duration, in samples. Must be non-negative.
       * @throws IllegalArgumentException if duration is negative
       */
      protected Datagram(long duration) {
          if (duration < 0) {
              throw new IllegalArgumentException("Can't create a datagram with the negative duration [" + duration + "].");
          }
          this.duration = duration;
      }
  
      /**
       * Constructor from external data.
       *
       * @param setDuration the datagram duration, in samples. Must be non-negative.
       * @param setBuff     the byte buffer to use as the datagram's data field. Must not be null.
       * @throws IllegalArgumentException if duration is negative
       * @throws NullPointerException     if setData is null.
       */
      public Datagram(long setDuration, byte[] setData) {
          if (setDuration < 0) {
              throw new IllegalArgumentException("Can't create a datagram with the negative duration [" + setDuration + "].");
          }
          if (setData == null) {
              throw new NullPointerException("null argument");
          }
          duration = setDuration;
          data = setData;
      }
  
      /**
       * Constructor which reads a datagram from a random access file.
       *
       * @param raf the random access file to read the datagram from.
       * @throws IOException if there is a problem initialising the datagram from the file
       */
      public Datagram(RandomAccessFile raf) throws IOException {
          duration = raf.readLong();
          if (duration < 0) {
              throw new IOException("Can't create a datagram with a negative duration [" + duration + "].");
          }
          int len = raf.readInt();
          if (len < 0) {
              throw new IOException("Can't create a datagram with a negative data size [" + len + "].");
          }
          data = new byte[len];
          raf.readFully(data);
      }
  
      /**
       * Constructor which reads a datagram from a byte buffer.
       *
       * @param bb the byte buffer to read the datagram from.
       * @throws IOException if the datagram has wrong format or if the datagram cannot be fully read
       */
      public Datagram(ByteBuffer bb) throws IOException {
          this(bb, true);
      }
  
      /**
       * Constructor which reads a datagram from a byte buffer.
       *
       * @param bb       the byte buffer to read the datagram from.
       * @param readData whether to try and read the actual data
       * @throws IOException if the datagram has wrong format or if the datagram cannot be fully read
       */
      public Datagram(ByteBuffer bb, boolean readData) throws IOException {
          duration = bb.getLong();
          if (duration < 0) {
              throw new IOException("Can't create a datagram with a negative duration [" + duration + "].");
          }
          int len = bb.getInt();
          if (len < 0) {
              throw new IOException("Can't create a datagram with a negative data size [" + len + "].");
          }
          data = new byte[len];
          if (!readData) {
              return;
          }
          if (bb.limit() - bb.position() < len) {
              throw new IOException("Not enough data in byte buffer to read the full datagram: datagram length is " + len + ", but can read only " + (bb.limit() - bb.position()));
          }
          bb.get(data);
      }
  
      /****************/
      /* SETTERS      */
      /****************/
  
      /**
       * Write this datagram to a random access file or data output stream.
       * Must only be called if data is not null.
       *
       * @param raf the data output to write to.
       * @throws IllegalStateException if called when data is null.
       * @throws IOException           if a write error occurs.
       */
      public void write(DataOutput raf) throws IOException {
          assert duration >= 0;
          if (data == null) {
              throw new IllegalStateException("This method can only be called for data that is not null");
          }
          raf.writeLong(duration);
          raf.writeInt(data.length);
          raf.write(data);
      }
  
  
      /****************/
      /* I/O METHODS  */
      /****************/
  
      /**
       * Get the datagram duration, in samples. Note: the sample rate has to be provided  externally.
       *
       * @return the non-negative duration.
       */
      public long getDuration() {
          assert duration >= 0;
          return duration;
      }
  
      /****************/
      /* ACCESSORS    */
      /****************/
  
      /**
       * Set the new duration.
       *
       * @param setDuration the datagram duration, in samples. Must be non-negative.
       * @throws IllegalArgumentException if duration is negative
       */
      public void setDuration(long setDuration) {
          if (setDuration < 0) {
              throw new IllegalArgumentException("Can't create a datagram with the negative duration [" + setDuration + "].");
          }
          this.duration = setDuration;
      }
  
      /**
       * Get the length, in bytes, of the datagram's data field.
       * Must only be called if data is not null.
       *
       * @return a non-negative integer representing the number of bytes in the data field.
       * @throws IllegalStateException if called when data is null.
       */
      public int getLength() {
          if (data == null) {
              throw new IllegalStateException("This method must not be called if data is null");
          }
          return data.length;
      }
  
      /**
       * Get the datagram's data field.
       *
       * @return the data in this Datagram, or null if there is no such data (should be the case only for subclasses).
       */
      public byte[] getData() {
          return data;
      }
  
      /****************/
      /* MISC METHODS */
      /****************/
  
      /**
       * Tests if this datagram is equal to another datagram.
       */
      @Override
      public boolean equals(Object obj) {
          if (!(obj instanceof Datagram)) {
              return false;
          }
          Datagram other = (Datagram) obj;
          if (this.duration != other.duration) return (false);
          if (this.data.length != other.data.length) return (false);
          for (int i = 0; i < this.data.length; i++) {
              if (this.data[i] != other.data[i]) return (false);
          }
          return (true);
      }
  
  }
  