/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.cache.loader;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.jboss.cache.TreeCache;
import org.jboss.cache.Fqn;
import org.jboss.cache.Node;
import org.jboss.cache.Modification;
import org.jboss.logging.Logger;

/**
 * CacheLoader implementation which delegates to another TreeCache. This allows to stack caches on top of each
 * other, allowing for hierarchical cache levels. For example, first level cache delegates to a second level cache,
 * which delegates to a persistent cache.
 * @author Bela Ban
 * @author Daniel Gredler
 * @version $Id: DelegatingCacheLoader.java,v 1.1.2.4 2005/12/01 19:48:41 dimitris Exp $
 */
public abstract class DelegatingCacheLoader implements CacheLoader {
   Logger    log=Logger.getLogger(getClass());
   /** HashMap<Object,List<Modification>>. List of open transactions. Note that this is purely transient, as
    * we don't use a log, recovery is not available */
   HashMap   transactions=new HashMap();

   public abstract void setConfig(Properties props);

   public abstract void setCache(TreeCache c);

   public Set getChildrenNames(Fqn fqn) throws Exception {
      Set retval=delegateGetChildrenNames(fqn);
      return retval == null? null : (retval.size() == 0? null : retval);
   }

   public Object get(Fqn name, Object key) throws Exception {
      return delegateGet(name, key);
   }

   public Map get(Fqn name) throws Exception {
      Map attributes=null;
      Node n=delegateGet(name);
      if(n == null || (attributes=n.getData()) == null || attributes.size() == 0)
         return null;
      else
         return new HashMap(attributes);
   }

   public boolean exists(Fqn name) throws Exception {
      return delegateExists(name);
   }

   public Object put(Fqn name, Object key, Object value) throws Exception {
      return delegatePut(name, key, value);
   }

   public void put(Fqn name, Map attributes) throws Exception {
      delegatePut(name, attributes);
   }


   public void put(Fqn fqn, Map attributes, boolean erase) throws Exception {
      if(erase)
         removeData(fqn);
      put(fqn, attributes);
   }

   public void put(List modifications) throws Exception {
      if(modifications == null) return;
      for(Iterator it=modifications.iterator(); it.hasNext();) {
         Modification m=(Modification)it.next();
         switch(m.getType()) {
            case Modification.PUT_DATA:
               put(m.getFqn(), m.getData());
               break;
            case Modification.PUT_DATA_ERASE:
               put(m.getFqn(), m.getData(), true);
               break;
            case Modification.PUT_KEY_VALUE:
               put(m.getFqn(), m.getKey(), m.getValue());
               break;
            case Modification.REMOVE_DATA:
               removeData(m.getFqn());
               break;
            case Modification.REMOVE_KEY_VALUE:
               remove(m.getFqn(), m.getKey());
               break;
            case Modification.REMOVE_NODE:
               remove(m.getFqn());
               break;
            default:
               log.error("modification type " + m.getType() + " not known");
               break;
         }
      }
   }

   public Object remove(Fqn name, Object key) throws Exception {
      return delegateRemove(name, key);
   }

   public void remove(Fqn name) throws Exception {
      delegateRemove(name);
   }

   public void removeData(Fqn name) throws Exception {
      delegateRemoveData(name);
   }

   public void prepare(Object tx, List modifications, boolean one_phase) throws Exception {
      if(one_phase)
         put(modifications);
      else
         transactions.put(tx, modifications);
   }

   public void commit(Object tx) throws Exception {
      List modifications=(List)transactions.get(tx);
      if(modifications == null)
         throw new Exception("transaction " + tx + " not found in transaction table");
      put(modifications);
   }

   public void rollback(Object tx) {
      transactions.remove(tx);
   }

   public byte[] loadEntireState() throws Exception {
      return delegateLoadEntireState();
   }

   public void storeEntireState(byte[] state) throws Exception {
      delegateStoreEntireState(state);
   }

   public void create() throws Exception {
      // Empty.
   }

   public void start() throws Exception {
      // Empty.
   }

   public void stop() {
      // Empty.
   }

   public void destroy() {
      // Empty.
   }

   /*------------------------------ Delegating Methods ------------------------------*/

   protected abstract Set delegateGetChildrenNames(Fqn fqn) throws Exception;

   protected abstract Object delegateGet(Fqn name, Object key) throws Exception;

   protected abstract Node delegateGet(Fqn name) throws Exception;

   protected abstract boolean delegateExists(Fqn name) throws Exception;

   protected abstract Object delegatePut(Fqn name, Object key, Object value) throws Exception;

   protected abstract void delegatePut(Fqn name, Map attributes) throws Exception;

   protected abstract Object delegateRemove(Fqn name, Object key) throws Exception;

   protected abstract void delegateRemove(Fqn name) throws Exception;

   protected abstract void delegateRemoveData(Fqn name) throws Exception;

   protected abstract byte[] delegateLoadEntireState() throws Exception;

   protected abstract void delegateStoreEntireState(byte[] state) throws Exception;

}
