/*
 * Redistribution and use of this software and associated documentation ("Software"), with or
 * without modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
 * must also contain a copy of this document.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided with
 * the distribution.
 *
 * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
 * without prior written permission of Intalio, Inc. For written permission, please contact
 * info@exolab.org.
 *
 * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
 * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
 * Intalio, Inc.
 *
 * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
 *
 * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright 2002 (C) Intalio, Inc. All Rights Reserved.
 *
 * $Id$
 */
package org.exolab.castor.xml.util;

import org.exolab.castor.xml.XMLFieldDescriptor;

/**
 * A class which represents a collection of XMLFieldDescriptor instances.
 *
 * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
 * @version $Revision$ $Date: 2005-12-13 14:58:48 -0700 (Tue, 13 Dec 2005) $
 * @see java.util.List
 * @see java.util.Collection
 */
public class XMLFieldDescriptors {

  private static final int DEFAULT_SIZE = 11;

  private XMLFieldDescriptor[] _elements;

  /**
   * The next available location in the elements array.
   */
  private int _elementCount = 0;

  /**
   * Creates a new XMLFieldDescriptors with the default Size.
   */
  public XMLFieldDescriptors() {
    _elements = new XMLFieldDescriptor[DEFAULT_SIZE];
  } // -- XMLFieldDescriptors

  /**
   * Creates a new XMLFieldDescriptors with the given size.
   *
   * @param size the initial size of the internal collection.
   */
  public XMLFieldDescriptors(int size) {
    _elements = new XMLFieldDescriptor[size];
  } // -- XMLFieldDescriptors

  /**
   * Adds the specified XMLFieldDescriptor to the collection. If the specified XMLFieldDescriptor is
   * already contained in the collection, it will not be re-added, false will be returned.
   *
   * @param descriptor the XMLFieldDescriptor to add
   * @return true if the descriptor is added, false otherwise.
   */
  public boolean add(XMLFieldDescriptor descriptor) {

    for (int i = 0; i < _elementCount; i++) {
      if (_elements[i] == descriptor)
        return false;
    }
    if (_elementCount == _elements.length)
      increaseSize();
    _elements[_elementCount++] = descriptor;
    return true;
  } // -- add

  /**
   * Removes all descriptors from this collection.
   */
  public void clear() {
    for (int i = 0; i < _elementCount; i++) {
      _elements[i] = null;
    }
    _elementCount = 0;
  } // -- clear

  public Object clone() {
    Object obj = null;
    try {
      obj = super.clone();
    } catch (CloneNotSupportedException e) {
      // this should never happen
    }
    return obj;
  } // -- clone

  /**
   * Returns true if the specified descriptor is contained in this collection. If the descriptor is
   * null, then if this collection contains a null value, true will be returned.
   *
   * @param descriptor the XMLFieldDescriptor to search the list for
   * @return true if specified descriptor is contained in the list
   **/
  public boolean contains(XMLFieldDescriptor descriptor) {
    return (indexOf(descriptor) >= 0);
  } // -- contains

  /**
   * Compares the specified object with this list for equality. Returns true if and only if the
   * specified Object is a list and all of its associated elements are equal to the elements of this
   * list
   *
   * @return true if the given object is considered equal to this list.
   */
  public boolean equals(Object obj) {
    if (obj == null)
      return false;
    if (obj == this)
      return true;
    if (!(obj instanceof XMLFieldDescriptors))
      return false;
    XMLFieldDescriptors descs = (XMLFieldDescriptors) obj;
    if (descs._elementCount != _elementCount)
      return false;
    for (int i = 0; i < _elementCount; i++) {
      Object e1 = get(i);
      Object e2 = descs._elements[i];
      if (!((e1 == null) ? (e2 == null) : e1.equals(e2)))
        return false;
    }
    return true;
  } // -- equals

  /**
   * Returns the XMLFieldDescriptor at the specified position in this list.
   *
   * @param index the position of the descriptor to return
   * @exception IndexOutOfBoundsException
   */
  public XMLFieldDescriptor get(int index) throws IndexOutOfBoundsException {
    if ((index < 0) || index >= _elementCount) {
      throw new IndexOutOfBoundsException();
    }
    return _elements[index];
  } // -- get

  /**
   * As defined by the JDK 1.2 API spec:<BR>
   * Returns the hash code value for this list. The hash code of a list is defined to be the result
   * of the following calculation: <BR>
   * <code>
   * hashCode = 1;
   * Iterator i = list.iterator();
   * while (i.hasNext()) {
   *    Object obj = i.next();
   *    hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
   * }
   * </code>
   * 
   * @return the hash code value for this list
   */
  public int hashCode() {
    int hashCode = 1;
    for (int i = 0; i < _elementCount; i++) {
      Object obj = _elements[i];
      hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
    }
    return hashCode;
  } // -- hashCode

  /**
   * Returns the index of the first occurrence of the specified XMLFieldDescriptor, or -1 if the
   * descriptor is not contained in the collection.
   *
   * @param descriptor the XMLFieldDescriptor to get the index of
   */
  public int indexOf(XMLFieldDescriptor descriptor) {
    if (descriptor == null) {
      for (int i = 0; i < _elementCount; i++) {
        if (_elements[i] == null)
          return i;
      }
    } else {
      for (int i = 0; i < _elementCount; i++) {
        if (descriptor.equals(_elements[i]))
          return i;
      }
    }
    return -1;
  } // -- indexOf

  /**
   * Returns true if there are no descriptors in the collection.
   *
   * @return true if the collection is empty.
   */
  public boolean isEmpty() {
    return (_elementCount == 0);
  } // -- isEmpty

  /**
   * Removes the descriptor at the specified index from the list.
   *
   * @param index the position in the list to remove the descriptor from.
   * @return the descriptor that was removed from the list.
   */
  public XMLFieldDescriptor remove(int index) {

    if ((index < 0) || (index > _elementCount))
      return null;
    XMLFieldDescriptor desc = _elements[index];
    shiftDown(index + 1);
    --_elementCount;
    return desc;
  } // -- remove

  /**
   * Removes the given XMLFieldDescriptor from the list.
   *
   * @param descriptor the XMLFieldDescriptor to remove from the list.
   * @return true if the descriptor was removed from the list.
   */
  public boolean remove(XMLFieldDescriptor descriptor) {
    int index = indexOf(descriptor);
    if (index > -1) {
      remove(index);
      return true;
    }
    return false;
  } // -- remove

  /**
   * Reduces the capacity of the internal buffer to the current size freeing up unused memory.
   */
  public void trimToSize() {
    if (_elements.length == _elementCount)
      return;
    XMLFieldDescriptor[] pointer = _elements;
    _elements = new XMLFieldDescriptor[_elementCount];
    System.arraycopy(pointer, 0, _elements, 0, _elementCount);
    pointer = null;
  } // -- trimToSize

  /**
   * Returns the number of descriptors in the list.
   *
   * @return the number of descriptors in the list.
   */
  public int size() {
    return _elementCount;
  } // -- size

  /**
   * Returns an array containing all of the descriptors in this list in proper sequence.
   *
   * @return the array of descriptors of this List
   */
  public XMLFieldDescriptor[] toArray() {
    XMLFieldDescriptor[] objArray = new XMLFieldDescriptor[_elementCount];
    System.arraycopy(_elements, 0, objArray, 0, _elementCount);
    return objArray;
  } // -- toArray

  /**
   * Returns an array containing all of the descriptors in this list in proper sequence.
   *
   * @return the array of descriptors of this list.
   */
  public XMLFieldDescriptor[] toArray(XMLFieldDescriptor[] dst) {
    return toArray(dst, 0);
  } // -- toArray

  /**
   * Returns an array containing all of the elements in this list in proper sequence.
   *
   * @return the array of descriptors of this list.
   */
  public XMLFieldDescriptor[] toArray(XMLFieldDescriptor[] dst, int offset) {

    XMLFieldDescriptor[] objArray = null;

    if (dst.length >= _elementCount)
      objArray = dst;
    else {
      objArray = new XMLFieldDescriptor[_elementCount];
    }
    System.arraycopy(_elements, 0, objArray, offset, _elementCount);
    return objArray;
  } // -- toArray


  // -------------------/
  // - Private Methods -/
  // -------------------/

  /**
   * Basically the same as a vector, increase the list by a factor of its initial size
   */
  private void increaseSize() {
    XMLFieldDescriptor[] pointer = _elements;
    int length = (pointer.length > 0) ? pointer.length : 1;
    _elements = new XMLFieldDescriptor[(length * 3) / 2 + 1];
    System.arraycopy(pointer, 0, _elements, 0, pointer.length);
    pointer = null;
  } // -- increaseSize

  /**
   * Shifts all elements at the specified index to down by 1
   */
  private void shiftDown(int index) {
    if ((index <= 0) || (index >= _elementCount))
      return;
    System.arraycopy(_elements, index, _elements, index - 1, _elementCount - index);
    // clean up for gc
    _elements[_elementCount - 1] = null;
  } // -- shiftDown

} // -- XMLFieldDescriptors
