package org.mandarax.util.resultsetfilters.aggregationfunctions;

/*
 * Copyright (C) 1999-2004 <a href="mailto:mandarax@jbdietrich.com">Jens Dietrich</a>
 *
 * This library 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 of the License, or (at your option) any later version.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import org.mandarax.kernel.LogicFactory;
import org.mandarax.kernel.ResultSet;
import org.mandarax.kernel.VariableTerm;
import org.mandarax.util.resultsetfilters.AggregationException;
import org.mandarax.util.resultsetfilters.AggregationFunction;

/**
 * Abstract superclass for aggregation functions.
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 3.0
 */
public abstract class AbstractAggregationFunction implements AggregationFunction {
	private List values = new ArrayList();
	private Object computedValue = null;
	protected Class type = null;
	protected VariableTerm var = null;
	/**
	 * Constructor.
	 * @param var the variable
	 */
	public AbstractAggregationFunction(VariableTerm var) throws AggregationException  {
		super();
		this.var = var;
		setType(var.getType());
	}
	/**
	 * Add a value. May throw an IllegalArgumentException
	 * indicating that this type of value cannot be aggregated.
	 * @param rs a result set
	 */
	public void add(ResultSet rs) throws AggregationException {
		Object value = null;
		try {
			value = rs.getResult(var);
		}
		catch (Exception x) {
			throw new AggregationException("Cannot fetch value from result set for variable",x);
		}
		if (type==null) setType(value.getClass());
		if (!(type.isAssignableFrom(value.getClass()))) throw new AggregationException("Cannot use this function with this type of value: " + (value==null?"null":value.getClass().getName()));
		if (computedValue!=null) throw new AggregationException("Cannot add values to an aggregation function after the value has been computed"); 
		values.add(value);
	}
	/**
	 * Get the aggregated value.
	 * @return a value
	 */
	public Object getAggregatedValue() throws AggregationException {
		if (computedValue==null) computedValue = compute();
		return computedValue;
	}
	/**
	 * Set the type.
	 * @param clazz a type.
	 */
	public void setType(Class clazz) throws AggregationException {
		Class[] supportedTypes = getSupportedTypes();
		for (int i=0;i<supportedTypes.length;i++) {
			if (supportedTypes[i].isAssignableFrom(clazz)) {
				type = clazz;
				return;
			}
		}
		throw new AggregationException("The type " + clazz + " is not supported by the aggregation function " + getName());
	}
	/**
	 * Get an array of supported types.
	 * @return an array of types
	 */
	protected abstract Class[] getSupportedTypes();
	/**
	 * Compute the aggregated value.
	 * @return the computed object
	 */
	protected abstract Object compute() throws AggregationException ;
	/**
	 * Get the size of the value list.
	 * @return the size of the list of values
	 */
	protected int getNumberOfValues() {
		return values.size();
	} 
	/**
	 * Get the value (as object) at a position.
	 * @param pos the position of the value
	 * @return the value as object
	 */
	protected Object getValue(int pos) {
		return values.get(pos);
	}
	/**
	 * Get the value as int at a position.
	 * @param pos the position of the value
	 * @return the value as int
	 */
	protected int getValueAsInt(int pos) {
		return ((Integer)values.get(pos)).intValue();
	}
	/**
	 * Get the value as double at a position.
	 * @param pos the position of the value
	 * @return the value as double
	 */
	protected double getValueAsDouble(int pos) {
		return ((Double)values.get(pos)).doubleValue();
	}
	/**
	 * Get the value as float at a position.
	 * @param pos the position of the value
	 * @return the value as float
	 */
	protected float getValueAsFloat(int pos) {
		return ((Float)values.get(pos)).floatValue();
	}
	/**
	 * Get the value as long at a position.
	 * @param pos the position of the value
	 * @return the value as long
	 */
	protected long getValueAsLong(int pos) {
		return ((Long)values.get(pos)).longValue();
	}
	/**
	 * Get the value as short at a position.
	 * @param pos the position of the value
	 * @return the value as short
	 */
	protected short getValueAsShort(int pos) {
		return ((Short)values.get(pos)).shortValue();
	}
	/**
	 * Get the value as big integer at a position.
	 * @param pos the position of the value
	 * @return the value as big integer
	 */
	protected BigInteger getValueAsBigInteger(int pos) {
		return (BigInteger)values.get(pos);
	}
	/**
	 * Get the value as big decimal at a position.
	 * @param pos the position of the value
	 * @return the value as big decimal
	 */
	protected BigDecimal getValueAsBigDecimal(int pos) {
		return (BigDecimal)values.get(pos);
	}
	/**
	 * Get the value as string at a position.
	 * @param pos the position of the value
	 * @return the value as string
	 */
	protected String getValueAsString(int pos) {
		return String.valueOf(values.get(pos));
	}
	/**
	 * Get the value as comparable at a position.
	 * @param pos the position of the value
	 * @return the value as comparable
	 */
	protected Comparable getValueAsComparable(int pos) {
		return (Comparable)values.get(pos);
	}
	/**
	 * Get the result type. This type might differ from the input type (e.g. when counting records).
	 * @return the result type
	 */
	public Class getResultType() {
		return type;
	}
	/**
	 * Get the variable that can be used to retrieve the value from
	 * the filtered result set.
	 * @return a variable name
	 */
	public VariableTerm getVariableInResult() {
		String varName = getName() + "(" + var.getName()+ ")";
		return LogicFactory.getDefaultFactory().createVariableTerm(varName,getResultType());		
	}
}
