package org.jboss.cache.util;

import net.jcip.annotations.Immutable;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * This is based on an ImmutableListCopy, with the assumption that the set passed in would ensure uniqueness of elements.
 * <p/>
 * The constructor takes in a collection so the onus is on the caller to ensure that the collection passed in adheres to
 * Set semantics.
 * <p/>
 * Typically used in place of the common idiom:
 * <code>
 * return Collections.unmodifiableSet(new HashSet( myInternalSet ));
 * </code>
 *
 * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
 * @see org.jboss.cache.util.ImmutableListCopy
 * @since 3.0
 */
@Immutable
public class ImmutableSetCopy<E> extends AbstractSet<E> implements Externalizable
{
   private static final long serialVersionUID = 11929568968766L;
   private E[] elements;
   private int size;

   @SuppressWarnings("unchecked")
   public ImmutableSetCopy(Collection<E> set)
   {
      size = set.size();
      E[] tempElements = (E[]) new Object[size]; // no room for growth
      elements = set.toArray(tempElements);
   }

   /**
    * Assumes that the array passed in is "safe", i.e., is not referenced from elsewhere.  Also assumes the array contains
    * elements such that the uniqueness required by a set is adhered to.  Use with care!
    *
    * @param array to reference
    */
   public ImmutableSetCopy(E[] array)
   {
      elements = array;
      size = elements.length;
   }

   @Override
   public boolean remove(Object o)
   {
      throw new UnsupportedOperationException();
   }

   @Override
   public boolean addAll(Collection<? extends E> c)
   {
      throw new UnsupportedOperationException();
   }

   @Override
   public boolean retainAll(Collection<?> c)
   {
      throw new UnsupportedOperationException();
   }

   @Override
   public boolean removeAll(Collection<?> c)
   {
      throw new UnsupportedOperationException();
   }

   @Override
   public void clear()
   {
      throw new UnsupportedOperationException();
   }

   public Iterator<E> iterator()
   {
      return new Iterator<E>()
      {
         int cursor = 0;

         public boolean hasNext()
         {
            return cursor < size;
         }

         public E next()
         {
            if (cursor >= size) throw new NoSuchElementException();
            return elements[cursor++];
         }

         public void remove()
         {
            throw new UnsupportedOperationException();
         }
      };
   }

   public int size()
   {
      return size;
   }

   /**
    * Format:
    * - entry array size (int)
    * - elements (Object)
    *
    * @param out stream to write to
    * @throws IOException
    */
   public void writeExternal(ObjectOutput out) throws IOException
   {
      out.writeInt(size);
      for (E e : elements) out.writeObject(e);
   }

   /**
    * See {@link #writeExternal(java.io.ObjectOutput)} for serialization format
    *
    * @param in stream
    * @throws IOException
    * @throws ClassNotFoundException
    */
   @SuppressWarnings("unchecked")
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
   {
      size = in.readInt();
      elements = (E[]) new Object[size];
      for (int i = 0; i < size; i++) elements[i] = (E) in.readObject();
   }
}
