//$Id: BinaryArithmeticOperatorNode.java,v 1.1 2005/10/14 17:18:38 steveebersole Exp $
package org.hibernate.hql.ast.tree;

import org.hibernate.Hibernate;
import org.hibernate.hql.ast.util.ColumnHelper;
import org.hibernate.type.Type;

import antlr.SemanticException;

/**
 * Nodes which represent binary arithmetic operators.
 *
 * @author Gavin King
 */
public class BinaryArithmeticOperatorNode extends AbstractSelectExpression implements BinaryOperatorNode {

	public void initialize() throws SemanticException {
		Node lhs = getLeftHandOperand();
		if ( lhs == null ) {
			throw new SemanticException( "left-hand operand of a binary operator was null" );
		}
		Node rhs = getRightHandOperand();
		if ( rhs == null ) {
			throw new SemanticException( "right-hand operand of a binary operator was null" );
		}
		if ( ParameterNode.class.isAssignableFrom( lhs.getClass() )
		     && SqlNode.class.isAssignableFrom( rhs.getClass() ) ) {
			( ( ParameterNode ) lhs ).getHqlParameterSpecification().setExpectedType(
					( ( SqlNode ) rhs ).getDataType()
			);
		}
		else if ( ParameterNode.class.isAssignableFrom( rhs.getClass() )
		          && SqlNode.class.isAssignableFrom( lhs.getClass() ) ) {
			( ( ParameterNode ) rhs ).getHqlParameterSpecification().setExpectedType(
					( ( SqlNode ) lhs ).getDataType()
			);
		}
	}

	/**
	 * Figure out the type of the binary expression by looking at
	 * the types of the operands. Sometimes we don't know both types,
	 * if, for example, one is a parameter.
	 */
	public Type getDataType() {
		Node left = getLeftHandOperand();
		Node right = getRightHandOperand();
		if ( !( right instanceof SqlNode ) ) {
			if ( left instanceof SqlNode ) {
				//we only know the type of the left operand
				return ( ( SqlNode ) left ).getDataType();
			}
			else {
				return Hibernate.DOUBLE; //BLIND GUESS!
			}
		}
		else {
			if ( left instanceof SqlNode ) {
				//both have known type
				Type x = ( (SqlNode) left ).getDataType();
				Type y = ( (SqlNode) right ).getDataType();
				if ( x==Hibernate.DOUBLE || y==Hibernate.DOUBLE ) return Hibernate.DOUBLE;
				if ( x==Hibernate.FLOAT || y==Hibernate.FLOAT ) return Hibernate.FLOAT;
				if ( x==Hibernate.BIG_DECIMAL || y==Hibernate.BIG_DECIMAL ) return Hibernate.BIG_DECIMAL;
				if ( x==Hibernate.BIG_INTEGER || y==Hibernate.BIG_INTEGER ) return Hibernate.BIG_INTEGER;
				if ( x==Hibernate.LONG || y==Hibernate.LONG ) return Hibernate.LONG;
				if ( x==Hibernate.INTEGER || y==Hibernate.INTEGER ) return Hibernate.INTEGER;
				return x;
			}
			else {
				//we only know the type of the right
				return ( ( SqlNode ) right ).getDataType();
			}
		}
	}

	public void setScalarColumnText(int i) throws SemanticException {
		ColumnHelper.generateSingleScalarColumn( this, i );
	}

	/**
	 * Retrieves the left-hand operand of the operator.
	 *
	 * @return The left-hand operand
	 */
	public Node getLeftHandOperand() {
		return ( Node ) getFirstChild();
	}

	/**
	 * Retrieves the right-hand operand of the operator.
	 *
	 * @return The right-hand operand
	 */
	public Node getRightHandOperand() {
		return ( Node ) getFirstChild().getNextSibling();
	}
}
