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

import org.jboss.cache.Fqn;
import org.jboss.cache.aop.AOPInstance;
import org.jboss.cache.aop.TreeCacheAop;

import java.util.Set;
import java.util.Iterator;

/**
 * LRUAlgorithm specific to TreeCacheAop. Overriding couple of hooks to customize
 * the algorithm such that it works correctly when using TreeCacheAop.
 * The basic strategy for the AOP-specific case are:
 * <ul>
 * <li>When a node is visited, it will check if it is an AOPInstance node. If it
 * is, then it is an AOP node. In that case, we will update all children nodes'
 * time stamp to synchronize with parent node.</li>
 * <li>When a node is to be evicted, it will check if it an AOP node. If it is,
 * we will traverse through the children nodes to see if their timestamp is younger.
 * If it is younger, then we must not evict the whol aop node (i.e., parent node is
 * not evicted either). Furthermore, we should synchronize the whole tree.
 * </ul>
 *
 * @author Ben Wang, Feb 17, 2004
 */
public class AopLRUAlgorithm extends LRUAlgorithm
{
   static final long TOLERANCE = 10;    // millis


   /**
    * Hook for evict.
    * @param fqn
    * @return Set of associated node to evict as well in string
    */
   protected Set getAssociatedEvictNode(Fqn fqn) {
      // That means even is child is touched, once the parent
      // node is evicted, it will evict the children as well regardless of
      // the time stamp.
      return getChildrenNames(fqn);
   }

   /**
    * Hook for processAddedNodes.
    */
   protected boolean preAddedNodes(Fqn fqn) {
      // If this is a JBoss_Internal nodes. Don't evict.
      if(fqn.getFqnChild(0).equals(TreeCacheAop.JBOSS_INTERNAL)) return false;
      return true;
   }

   private boolean isAopNode(Fqn fqn) {
      EvictionPolicy policy = region_.getEvictionPolicy();
      return (policy.getCacheData(fqn, AOPInstance.KEY) == null);
   }

   /**
    *
    * @param fqn
    * @return Set of Objects
    */
   private Set getChildrenNames(Fqn fqn) {
      EvictionPolicy policy = region_.getEvictionPolicy();
      return policy.getChildrenNames(fqn);
   }

   /**
    * Hook for processVisitedNodes
    * @param fqn
    * @return true if it is successful
    */
   protected boolean preVisitedNodes(Fqn fqn, long stamp) {
      // If AOPInstance exists, update all the children's time stamp.
      if(isAopNode(fqn)) {
         Set set = getChildrenNames(fqn);
         Iterator it = set.iterator();
         while(it.hasNext()) {
            Object child = it.next();
            Fqn childFqn = new Fqn(child);
            if( !preVisitedNodes(childFqn, stamp) ) return false;
            try {
               updateChildTimeStamp(childFqn, stamp);
            } catch (EvictionException e) {
               e.printStackTrace();
               return false;
            }
         }
      }
      return true;
   }

   private void updateChildTimeStamp(Fqn childFqn, long stamp) throws EvictionException
   {
      NodeEntry ne = (NodeEntry)nodeMap_.get(childFqn);
      ne.setModifiedTimeStamp(stamp);
      demote(childFqn);
   }
}