/*
 * Created on Mar 12, 2004
 */
package org.apache.cocoon.generation;

import java.io.IOException;
import java.io.Serializable;
import java.util.Map;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.caching.CacheableProcessingComponent;
import org.apache.cocoon.caching.CheckIntervalValidity;
import org.apache.cocoon.components.source.SourceUtil;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceException;
import org.apache.excalibur.source.SourceValidity;
import org.xml.sax.SAXException;

/**
 * @author Oscar Picasso
 * @version Mar 12, 2004 - 2:18:23 PM
 */
public abstract class AbstractCacheableVelocityGenerator
	extends VelocityGenerator
	implements CacheableProcessingComponent
{
	/** The input source */
	protected Source inputSource;
	private int checkInterval;
	private boolean useVelocityCache;

	/**
	 * Recycle this component.
	 * All instance variables are set to <code>null</code>.
	 */
	public void recycle()
	{
		if (null != this.inputSource)
		{
			super.resolver.release(this.inputSource);
			this.inputSource = null;
		}
		super.recycle();
	}

	/**
	 * Setup the file generator.
	 * Try to get the last modification date of the source for caching.
	 */
	public void setup(SourceResolver resolver, Map objectModel, String src, Parameters params)
		throws ProcessingException, SAXException, IOException
	{
		getLogger().debug("CacheableVelocityGenerator setup");
		super.setup(resolver, objectModel, src, params);
		try
		{
			this.inputSource = super.resolver.resolveURI(src);
			if (getLogger().isDebugEnabled())
			{
				getLogger().debug("validity: " + this.inputSource.getValidity());
			}
		}
		catch (SourceException se)
		{
			throw SourceUtil.handle("Error during resolving of '" + src + "'.", se);
		}
	}

	/**
	 * Generate the unique key.
	 * This key must be unique inside the space of this component.
	 *
	 * @return The generated key 
	 */
	public abstract Serializable getKey();

	/**
	 * Generate the validity object.
	 *
	 * @return The generated validity object or <code>null</code> if the
	 *         component is currently not cacheable.
	 */
	public SourceValidity getValidity()
	{
		//
		// Key is generated using parameter/value pairs,
		// so this information does not need to be verified again
		// (if parameter added/removed or value changed, key should
		// change also), only source validity is included.
		// 
		// If not valid the caching mechanisme of the velocity engine
		// should be used.
		//
		SourceValidity validity = null;
		if (useVelocityCache)
		{
			/*
			 * USE CheckIntervalValidity to solve the following issue:
			 * - when using usecache="true" and checkInterval="interval"
			 * if we request a generation after the last modification 
			 * but before it has been checked, the cocoon uses the cached 
			 * template and caches it. Results the last modification is then
			 * never used. 
			 */
			validity = new CheckIntervalValidity(this.inputSource.getValidity(), checkInterval);
		}
		else
		{
			validity = this.inputSource.getValidity();
		}
		if (getLogger().isDebugEnabled())
		{
			getLogger().debug("cacheable parameters validity: " + validity);
		}
		return validity;
	}

	/**
	 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
	 */
	public void configure(Configuration configuration) throws ConfigurationException
	{
		super.configure(configuration);
		useVelocityCache = configuration.getAttributeAsBoolean("usecache", false);
		checkInterval = configuration.getAttributeAsInteger("checkInterval", 0);
		if (getLogger().isDebugEnabled())
		{
			getLogger().debug("usecache: " + this.useVelocityCache);
			getLogger().debug("checkInterval: " + this.checkInterval);
		}
	}


}
