/* FastReadArray.java

	Purpose:
		
	Description:
		
	History:
		Tue Jun 17 19:19:19     2008, Created by tomyeh

Copyright (C) 2008 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
	This program is distributed under LGPL Version 2.1 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.util;

import java.util.List;
import java.util.LinkedList;
import java.lang.reflect.Array;

import org.zkoss.lang.Objects;

/**
 * An array of objects that are fast to read but a bit slower to add and
 * remove.
 *
 * <p>It is thread-safe.
 *
 * <p>Typical use:
 * <pre>{@code Object[] ary = fra.toArray();
 *for (int j = 0; j < ary.length; ++j)
 *  <i>whatever</i>;}</pre>
 *
 * @author tomyeh
 * @since 3.0.6
 */
public class FastReadArray<T> implements java.io.Serializable, Cloneable {
	private Object[] _ary;

	/** Constructs an array of the specified class.
	 */
	public FastReadArray(Class<?> klass) {
		_ary = (Object[])Array.newInstance(klass, 0);
	}
	/** Returns the array (never null).
	 * To read, you shall store the return value in a local variable
	 * and then iterate thru it.
	 *
	 * <p>Note: the return array is readonly. Don't modify the value
	 * of any element.
	 */
	@SuppressWarnings("unchecked")
	public T[] toArray() {
		return (T[])_ary;
	}
	/** Returns if it is empty.
	 */
	public boolean isEmpty() {
		return _ary.length == 0;
	}
	/** Returns the size.
	 */
	public int size() {
		return _ary.length;
	}
	/** Removes all of the elements for this array.
	 * @since 6.0.0
	 */
	public void clear() {
		_ary = (Object[])Array.newInstance(_ary.getClass().getComponentType(), 0);
	}

	/** Adds an object.
	 */
	synchronized public void add(T val) {
		Object[] ary = (Object[])ArraysX.resize(_ary, _ary.length + 1);
		ary[_ary.length] = val;
		_ary = ary;
	}
	/** Removes an object.
	 * @return whether the object is removed successfully (i.e., found).
	 */
	@SuppressWarnings("unchecked")
	synchronized public boolean remove(T val) {
		final List l = new LinkedList();
		boolean found = false;
		for (int j = 0; j < _ary.length; ++j)
			if (found || !Objects.equals(val, _ary[j]))
				l.add(_ary[j]);
			else
				found = true;

		if (found)
			_ary = (Object[])l.toArray((Object[])
				Array.newInstance(_ary.getClass().getComponentType(), l.size()));
		return found;
	}
	/** Removes the object(s) that matches the specified condition.
	 * By match we mean {@link Comparable#compareTo} returns 0.
	 * In other words, this method invokes val.compareTo() against
	 * each element in this array.
	 *
	 * @param atMostOne whether to remove the first matched object only.
	 * If true, only the first matched object, if any, is removed.
	 * If false, all matched object are removed.
	 */
	@SuppressWarnings("unchecked")
	synchronized public boolean removeBy(Comparable val, boolean atMostOne) {
		final List l = new LinkedList();
		boolean found = false;
		for (int j = 0; j < _ary.length; ++j)
			if ((atMostOne && found) || val.compareTo(_ary[j]) != 0)
				l.add(_ary[j]);
			else
				found = true;

		if (found)
			_ary = (Object[])l.toArray((Object[])
				Array.newInstance(_ary.getClass().getComponentType(), l.size()));
		return found;
	}

	public Object clone() {
		//No need to deep copy _ary since it is considered as readonly
		try {
			return super.clone();
		}catch(CloneNotSupportedException e) {
			throw new InternalError();
		}
	}

	//Object//
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (!(o instanceof FastReadArray))
			return false;

		return Objects.equals(_ary, ((FastReadArray)o)._ary);
	}
	public int hashCode() {
		return Objects.hashCode(_ary);
	}
	public String toString() {
		return Objects.toString(_ary);
	}
}
