package org.hibernate.hql.internal.ast.tree;
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
import org.hibernate.hql.internal.ast.util.ColumnHelper;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
import antlr.SemanticException;
public class BinaryArithmeticOperatorNode extends AbstractSelectExpression
implements BinaryOperatorNode, DisplayableNode {
@Override
public void initialize() throws SemanticException {
final Node lhs = getLeftHandOperand();
if ( lhs == null ) {
throw new SemanticException( "left-hand operand of a binary operator was null" );
}
final Node rhs = getRightHandOperand();
if ( rhs == null ) {
throw new SemanticException( "right-hand operand of a binary operator was null" );
}
final Type lhType = ( lhs instanceof SqlNode ) ? ( (SqlNode) lhs ).getDataType() : null;
final Type rhType = ( rhs instanceof SqlNode ) ? ( (SqlNode) rhs ).getDataType() : null;
if ( ExpectedTypeAwareNode.class.isAssignableFrom( lhs.getClass() ) && rhType != null ) {
Type expectedType = null;
if ( isDateTimeType( rhType ) ) {
expectedType = getType() == HqlSqlTokenTypes.PLUS ? StandardBasicTypes.DOUBLE : rhType;
}
else {
expectedType = rhType;
}
( (ExpectedTypeAwareNode) lhs ).setExpectedType( expectedType );
}
else if ( ParameterNode.class.isAssignableFrom( rhs.getClass() ) && lhType != null ) {
Type expectedType = null;
if ( isDateTimeType( lhType ) ) {
if ( getType() == HqlSqlTokenTypes.PLUS ) {
expectedType = StandardBasicTypes.DOUBLE;
}
}
else {
expectedType = lhType;
}
( (ExpectedTypeAwareNode) rhs ).setExpectedType( expectedType );
}
}
@Override
public Type getDataType() {
if ( super.getDataType() == null ) {
super.setDataType( resolveDataType() );
}
return super.getDataType();
}
private Type resolveDataType() {
final Node lhs = getLeftHandOperand();
final Node rhs = getRightHandOperand();
final Type lhType = ( lhs instanceof SqlNode ) ? ( (SqlNode) lhs ).getDataType() : null;
final Type rhType = ( rhs instanceof SqlNode ) ? ( (SqlNode) rhs ).getDataType() : null;
if ( isDateTimeType( lhType ) || isDateTimeType( rhType ) ) {
return resolveDateTimeArithmeticResultType( lhType, rhType );
}
else {
if ( lhType == null ) {
if ( rhType == null ) {
return StandardBasicTypes.DOUBLE;
}
else {
return rhType;
}
}
else {
if ( rhType == null ) {
return lhType;
}
else {
if ( lhType == StandardBasicTypes.DOUBLE || rhType == StandardBasicTypes.DOUBLE ) {
return StandardBasicTypes.DOUBLE;
}
if ( lhType == StandardBasicTypes.FLOAT || rhType == StandardBasicTypes.FLOAT ) {
return StandardBasicTypes.FLOAT;
}
if ( lhType == StandardBasicTypes.BIG_DECIMAL || rhType == StandardBasicTypes.BIG_DECIMAL ) {
return StandardBasicTypes.BIG_DECIMAL;
}
if ( lhType == StandardBasicTypes.BIG_INTEGER || rhType == StandardBasicTypes.BIG_INTEGER ) {
return StandardBasicTypes.BIG_INTEGER;
}
if ( lhType == StandardBasicTypes.LONG || rhType == StandardBasicTypes.LONG ) {
return StandardBasicTypes.LONG;
}
if ( lhType == StandardBasicTypes.INTEGER || rhType == StandardBasicTypes.INTEGER ) {
return StandardBasicTypes.INTEGER;
}
return lhType;
}
}
}
}
private boolean isDateTimeType(Type type) {
return type != null
&& ( java.util.Date.class.isAssignableFrom( type.getReturnedClass() )
|| java.util.Calendar.class.isAssignableFrom( type.getReturnedClass() ) );
}
private Type resolveDateTimeArithmeticResultType(Type lhType, Type rhType) {
boolean lhsIsDateTime = isDateTimeType( lhType );
boolean rhsIsDateTime = isDateTimeType( rhType );
if ( getType() == HqlSqlTokenTypes.PLUS ) {
return lhsIsDateTime ? lhType : rhType;
}
else if ( getType() == HqlSqlTokenTypes.MINUS ) {
if ( lhsIsDateTime && !rhsIsDateTime ) {
return lhType;
}
if ( lhsIsDateTime && rhsIsDateTime ) {
return StandardBasicTypes.DOUBLE;
}
}
return null;
}
@Override
public void setScalarColumnText(int i) throws SemanticException {
ColumnHelper.generateSingleScalarColumn( this, i );
}
@Override
public Node getLeftHandOperand() {
return (Node) getFirstChild();
}
@Override
public Node getRightHandOperand() {
return (Node) getFirstChild().getNextSibling();
}
@Override
public String getDisplayText() {
return "{dataType=" + getDataType() + "}";
}
}