package edu.princeton.toy.lang; /** * TWordBuffer is essentially a vector for TWords. The main need for a TWordBuffer is that * java.util.Vector allows null values to be added, whereas the Visual X-TOY really needs a vector * class which does not permit null values. This class is thread-safe. * * @author Brian Tsang * @version 7.1 */ public class TWordBuffer implements Cloneable { private static Object idLock = new Object(); private static int idCtr = 0; private int id; private TWord array[]; private int size; /** * Constructs a new buffer with an initial capacity of 50. */ public TWordBuffer() { this(50); } /** * Constructs a new buffer with the given initial capacity. */ public TWordBuffer(int capacity) { size = 0; array = new TWord[capacity]; synchronized (idLock) { id = idCtr; idCtr++; } } /** * Returns a clone of this TWordBuffer (the array objects will be reallocated and copied. * * @return Another instance of TWordBuffer with the same fields as this TWordBuffer. */ public synchronized Object clone() { TWordBuffer clone = new TWordBuffer(size); clone.add(this); return clone; } /** * Sets the size of the buffer to 0. */ public synchronized void clear() { size = 0; } /** * Adds the contents of one TWordBuffer to the end of this buffer. * * @param buffer The TWordBuffer whose contents are to be added. A NullPointerException will be * thrown if this is null. */ public void add(TWordBuffer buffer) { if (buffer == null) throw new NullPointerException(); TWordBuffer firstBuffer; TWordBuffer secondBuffer; // This is how we prevent deadlock: // http://java.sun.com/docs/books/tutorial/essential/threads/deadlock.html if (this.id > buffer.id) { firstBuffer = this; secondBuffer = buffer; } else { firstBuffer = buffer; secondBuffer = this; } synchronized (firstBuffer) { synchronized (secondBuffer) { add(buffer.array, 0, buffer.size); } } } /** * Adds the contents of one TWordBuffer to the end of this buffer. * * @param array The array of TWords to add to this buffer. A NullPointerException will be * thrown if this is null. */ public synchronized void add(TWord array[]) { add(array, 0, array.length); } /** * Adds the contents of one TWordBuffer to the end of this buffer. * * @param array The array of TWords to add to this buffer. A NullPointerException will be * thrown if this is null. * @param offset The offset at which to begin copying. An invalid value will result in an * ArrayIndexOutOfBoundsException. * @param length The number of words to copy. An invalid value will result in an * ArrayIndexOutOfBoundsException. */ public synchronized void add(TWord array[], int offset, int length) { if (offset < 0 || length < 0 || offset + length > array.length) throw new ArrayIndexOutOfBoundsException(); TWord thisArray[] = this.array; if (size + length > thisArray.length) { expandArray(3 * (size + length) / 2); thisArray = this.array; } for (int ctr = 0; ctr < length; ctr++) thisArray[size + ctr] = array[offset + ctr]; size += length; } /** * Adds the specified TWord to the end of the buffer. * * @param word The TWord to be added. A NullPointerException will be thrown if this is null. */ public synchronized void add(TWord word) { if (word == null) throw new NullPointerException(); if (array.length == size) expandArray(); array[size] = word; size++; } /** * Returns the TWord at the beginning of the buffer after removing it from the buffer. * * @return The TWord which was at the beginning of the buffer. */ public synchronized TWord pop() { if (size == 0) throw new ArrayIndexOutOfBoundsException(); TWord word = array[0]; for (int ctr = 1; ctr < size; ctr++) array[ctr - 1] = array[ctr]; size--; return word; } /** * Returns the TWord at the given index. * * @param index The index of the desired word. * @return The TWord at a given index; */ public TWord getWord(int index) { if (index < 0 || index >= size) throw new ArrayIndexOutOfBoundsException(); return array[index]; } /** * Returns the number of lines we have so far. * * @return the number of lines we have so far */ public int getSize() { return size; } /** * Returns wheter or not two buffers contain the same data. * * @param obj The Object to compare this buffer to. * @return True iff obj is a TWordBuffer and contains the same data as this. */ public synchronized boolean equals(Object obj) { if (obj == this) { return true; } else if (obj instanceof TWordBuffer) { TWordBuffer buffer = (TWordBuffer)obj; int size = buffer.size; if (buffer.size != this.size) return false; TWord bufferArray[] = buffer.array; TWord thisArray[] = array; for (int ctr = 0; ctr < size; ctr++) { if (bufferArray[ctr] != thisArray[ctr]) return false; } return true; } else { return false; } } /** * Reallocates the array so it has approximately double its initial capacity. */ private void expandArray() { expandArray(2 * array.length + 1); } /** * Reallocates the array so it at least the capacity specified. * * @param minimumCapacity The minimum number of elements the new array must be able to hold. */ private void expandArray(int minimumCapacity) { int size = this.size; TWord oldArray[] = array; TWord newArray[] = array = new TWord[Math.max(minimumCapacity, size)]; for (int ctr = 0; ctr < size; ctr++) newArray[ctr] = oldArray[ctr]; } }