/**
 * GUI Commands
 * Copyright 2004 Andrew Pietsch
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * $Id: WeakSet.java,v 1.5 2005/09/18 00:37:18 pietschy Exp $
 */

package org.pietschy.command;

import java.lang.ref.WeakReference;
import java.util.*;

/**
 * A private class based on the HashSet but that only keeps weak references to
 * its collection. It provides access only via an iterator.
 */
class WeakSet
{
   private HashSet referenceSet;

   WeakIterator iterator = new WeakIterator();

   /**
    * A lightweight empty iterator for when we don't have any attachments
    */
   private Iterator emptyIterator = new Iterator()
   {
      public boolean hasNext()
      {
         return false;
      }

      public Object next()
      {
         return null;
      }

      public void remove()
      {
      }
   };

   /**
    * Creats a new AttachmentSet with the default size of 10.
    */
   protected WeakSet()
   {
      this(10);
   }

   /**
    *
    * @param size
    */
   public WeakSet(int size)
   {
      if (size < 0)
         throw new IllegalArgumentException("Size must be zero or greater");

      referenceSet = new HashSet(size);
   }

   public boolean isEmpty()
   {
      return (referenceSet.size() == 0);
   }

   /**
    * Add the specified object to the list.  If the object already exists in the list, nothing
    * is done.
    * @param o the object to installFace.
    * @return <code>true</code> if the object was added.
    */
   public boolean add(Object o)
   {
      if (o == null)
         return false;

      if (!contains(o))
      {
         referenceSet.add(new WeakReference(o));
         return true;
      }

      return false;
   }

   /**
    *
    * @param o
    * @return
    */
   public boolean remove(Object o)
   {
      Iterator iter = referenceSet.iterator();

      while (iter.hasNext())
      {
         WeakReference reference = (WeakReference) iter.next();
         Object target = reference.get();
         if (target == null)
         {
            iter.remove();
         }
         else
         {
            if (target.equals(o))
            {
               iter.remove();
               return true;
            }
         }
      }

      return false;
   }

   public void removeAll()
   {
      referenceSet.clear();
   }

   /**
    *
    * @param o
    * @return
    */
   public boolean contains(Object o)
   {
      Iterator iter = referenceSet.iterator();

      while (iter.hasNext())
      {
         WeakReference reference = (WeakReference) iter.next();
         Object target = reference.get();
         if (target == null)
         {
            iter.remove();
         }
         else
         {
            if (o.equals(target))
               return true;
         }
      }

      return false;
   }

   public Iterator iterator()
   {
      if (referenceSet.size() < 1)
         return emptyIterator;

      return iterator.restart(referenceSet.iterator());
   }

   public List
   asList()
   {
      ArrayList list = new ArrayList(referenceSet.size());
      for (Iterator iter = iterator(); iter.hasNext();)
      {
         list.add(iter.next());
      }

      return list;
   }


   /**
    *
    */
   private class
   WeakIterator
   implements Iterator
   {
      Iterator parentIterator;
      Object nextObject = null;

      protected WeakIterator()
      {
      }

      public Iterator restart(Iterator parent)
      {
         parentIterator = parent;
         nextObject = null;
         return this;
      }

      /**
       * Returns <tt>true</tt> if the iteration has more elements. (In other
       * words, returns <tt>true</tt> if <tt>next</tt> would return an element
       * rather than throwing an exception.)
       *
       * @return <tt>true</tt> if the iterator has more elements.
       */
      public boolean hasNext()
      {
         if (parentIterator == null)
            return false;

         // check that the last one has been collected...
         if (nextObject != null)
            return true;

         nextObject = prepareNext();

         if (nextObject == null)
            return false;

         return true;
      }

      /**
       * Returns the next element in the interation.
       *
       * @return the next element in the iteration.
       * @exception java.util.NoSuchElementException iteration has no more elements.
       */
      public Object next()
      {
         if (!hasNext())
         {
            throw new NoSuchElementException();
         }

         // return the next object but make sure we loose our reference to
         // it on the way out so it can be collected.
         Object o = nextObject;
         nextObject = null;
         return o;
      }

      /**
       *
       * Removes from the underlying collection the last element returned by the
       * iterator (optional operation).  This method can be called only once per
       * call to <tt>next</tt>.  The behavior of an iterator is unspecified if
       * the underlying collection is modified while the iteration is in
       * progress in any way other than by calling this method.
       *
       * @exception UnsupportedOperationException if the <tt>remove</tt>
       *		  operation is not supported by this Iterator.

       * @exception IllegalStateException if the <tt>next</tt> method has not
       *		  yet been called, or the <tt>remove</tt> method has already
       *		  been called after the last call to the <tt>next</tt>
       *		  method.
       */
      public void remove()
      {
         throw new UnsupportedOperationException("Method not  supported by this Iterator");
      }

      /**
       * Gets the next non null referenced object.
       * @return the Object or <code>null</code> if there are no more left.
       */
      private Object prepareNext()
      {
         Object o = null;
         while (o == null && parentIterator.hasNext())
         {
            WeakReference ref = (WeakReference) parentIterator.next();
            o = ref.get();
            if (o == null)
            {
               parentIterator.remove();
            }
         }
         return o;
      }
   }

}


