/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package org.jboss.cache.pojo.impl;

import java.lang.reflect.Field;
import java.util.List;

import org.jboss.aop.Advised;
import org.jboss.aop.Domain;
import org.jboss.aop.InstanceAdvisor;
import org.jboss.aop.advice.Interceptor;
import org.jboss.cache.Fqn;
import org.jboss.cache.pojo.PojoCacheException;
import org.jboss.cache.pojo.annotation.TxUndo;
import org.jboss.cache.pojo.interceptors.dynamic.CacheFieldInterceptor;

/**
 * Utility class for method wrappers that we are interested to rollback (i.e., rollback).
 *
 * @author Ben Wang
 * @version $Id: PojoUtil.java 4012 2007-06-13 00:08:59Z jgreene $
 */
public class PojoUtil
{
   private static final String BindingName = "ReentrancyStopperInterceptor.";

   @TxUndo
   public void attachInterceptor(Object pojo, InstanceAdvisor advisor, Interceptor interceptor)
   {
      _attachInterceptor(pojo, advisor, interceptor);
   }

   @TxUndo
   public void detachInterceptor(InstanceAdvisor advisor, Interceptor interceptor)
   {
      _detachInterceptor(advisor, interceptor);
   }

   public void undoAttachInterceptor(Object pojo, InstanceAdvisor advisor, Interceptor interceptor)
   {
      _detachInterceptor(advisor, interceptor);
   }

   public void undoDetachInterceptor(InstanceAdvisor advisor, Interceptor interceptor)
   {
      Object pojo = ((CacheFieldInterceptor) interceptor).getAopInstance().get();
      if (pojo == null)
      {
         throw new PojoCacheException("PojoUtil.detachInterceptor(): null pojo");
      }

      _attachInterceptor(pojo, advisor, interceptor);
   }

   @TxUndo
   public void inMemorySubstitution(Object obj, Field field, Object newValue)
   {
      _inMemorySubstitution(obj, field, newValue);
   }

   public void undoInMemorySubstitution(Object obj, Field field, Object oldValue)
   {
      _inMemorySubstitution(obj, field, oldValue);
   }

   private void _attachInterceptor(Object pojo, InstanceAdvisor advisor, Interceptor interceptor)
   {
      advisor.appendInterceptor(interceptor);
   }

   private Domain getInstanceDomain(Object obj)
   {
      Advised advised = ((Advised) obj);
      InstanceAdvisor advisor = advised._getInstanceAdvisor();
      return advisor.getDomain();
   }

   private void _inMemorySubstitution(Object obj, Field field, Object newValue)
   {
      try
      {
         field.set(obj, newValue);
      }
      catch (IllegalAccessException e)
      {
         throw new PojoCacheException(
               "PojoUtil.inMemorySubstitution(): Can't swap out the class of field \" " +
               "+field.getLastElementAsString()," + e);
      }
   }

   private void _detachInterceptor(InstanceAdvisor advisor, Interceptor interceptor)
   {
      advisor.removeInterceptor(interceptor.getName());
      // retrieve pojo
      Object pojo = ((CacheFieldInterceptor) interceptor).getAopInstance().get();

      if (pojo == null)
      {
         throw new PojoCacheException("PojoUtil.detachInterceptor(): null pojo");
      }
   }

   @TxUndo
   public int incrementReferenceCount(Fqn sourceFqn, int count, List refList)
   {
      return _incrementReferenceCount(sourceFqn, count, refList);
   }

   public int undoIncrementReferenceCount(Fqn sourceFqn, int count, List refList)
   {
      return _decrementReferenceCount(sourceFqn, count, refList);
   }

   private int _incrementReferenceCount(Fqn sourceFqn, int count, List refList)
   {
      refList.add(sourceFqn);
      return count + 1;
   }

   @TxUndo
   public int decrementReferenceCount(Fqn sourceFqn, int count, List refList)
   {
      return _decrementReferenceCount(sourceFqn, count, refList);
   }

   public int undoDecrementReferenceCount(Fqn sourceFqn, int count, List refList)
   {
      return _incrementReferenceCount(sourceFqn, count, refList);
   }

   private int _decrementReferenceCount(Fqn sourceFqn, int count, List refList)
   {
      refList.remove(sourceFqn);
      return count - 1;
   }
}
