/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.cache.lock;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Collections;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedRef;

/**
 * Provide lock ownership mapping.
 *
 * @author Ben Wang
 */
public class LockMap
{
   public static final int OWNER_ANY = 0;
   public static final int OWNER_READ = 1;
   public static final int OWNER_WRITE = 2;
   private SynchronizedRef writeOwner_;
   private List readOwnerList_;

   public LockMap()
   {
      writeOwner_ = new SynchronizedRef(null);
      readOwnerList_ = Collections.synchronizedList(new ArrayList());
   }

   /**
    * Check whether this owner has reader or writer ownership.
    *
    * @param owner
    * @param ownership Either <code>OWNER_ANY</code>, <code>OWNER_READ</code>,
    *                  or <code>OWNER_WRITE</code>.
    * @return
    */
   public synchronized boolean isOwner(Object owner, int ownership)
   {
      checkOwnerNull(owner);
      boolean hasOwner = false;
      switch (ownership) {
         case OWNER_ANY:
            if ((getWriteOwner() != null && getWriteOwner().equals(owner) ||
                  readOwnerList_.size() != 0 && readOwnerList_.contains(owner)))
               hasOwner = true;
            break;
         case OWNER_READ:
            if (readOwnerList_.size() != 0 && readOwnerList_.contains(owner))
               hasOwner = true;
            break;
         case OWNER_WRITE:
            if (getWriteOwner() != null && getWriteOwner().equals(owner)) hasOwner = true;
            break;
         default:
            break;
      }
      return hasOwner;
   }

   private void checkOwnerNull(Object owner)
   {
      if (owner == null)
         throw new IllegalArgumentException("Owner object is null");
   }

   /**
    * Adding a reader owner.
    *
    * @param owner
    */
   public synchronized void addReader(Object owner)
   {
      checkOwnerNull(owner);
      readOwnerList_.add(owner);
   }

   /**
    * Adding a writer owner.
    *
    * @param owner
    */
   public synchronized void addWriter(Object owner)
   {
      checkOwnerNull(owner);
      if(getWriteOwner() != null)
         throw new IllegalStateException("addWriter(): owner already existed");
      
      addWriteOwner(owner);
   }

   private void addWriteOwner(Object owner) {
      writeOwner_.set(owner);
   }

   private Object getWriteOwner() {
      return writeOwner_.get();
   }

   /**
    * Upgrading current reader ownership to writer one.
    *
    * @param owner
    * @return True if successful.
    */
   public synchronized boolean upgrade(Object owner) throws OwnerNotExistedException
   {
      checkOwnerNull(owner);
      if (!readOwnerList_.contains(owner))
         throw new OwnerNotExistedException("Can't upgrade lock. Read lock owner not existed");

      readOwnerList_.remove(owner);
      addWriteOwner(owner);
      return true;
   }

   /**
    * @return List of reader owner objects. Size 0 if not found.
    */
   public List readerOwners()
   {
      return readOwnerList_;
   }

   /**
    * @return Writer owner object. Null if none.
    */
   public Object writerOwner()
   {
      return getWriteOwner();
   }

   /**
    * Remove reader ownership.
    *
    * @param owner
    * @return The owner object. Null if not found.
    */
   public synchronized boolean removeReader(Object owner)
   {
      checkOwnerNull(owner);
      return readOwnerList_.remove(owner);
   }

   /**
    * Remove writer ownership.
    *
    * @param owner
    * @return The owner object. Null if not found.
    */
   public synchronized Object removeWriter(Object owner)
   {
      checkOwnerNull(owner);
      Object tmp = getWriteOwner();
      addWriteOwner(null);
      return tmp;
   }

   /**
    * Remove all ownership.
    */
   public synchronized void removeAll()
   {
      addWriteOwner(null);
      readOwnerList_.clear();
   }

   /**
    * Debugging information.
    *
    * @return
    */
   public synchronized String printInfo()
   {
      StringBuffer buf = new StringBuffer();
      buf.append("Read lock owners: ").append(readOwnerList_).append("\n");
      buf.append("Write lock owner: ").append(getWriteOwner()).append("\n");
      return buf.toString();
   }
}
