Open Source Repository

Home /hibernate/hibernate-3.2.7.ga | Repository Home


org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java
package org.hibernate.hql.ast.tree;

import org.hibernate.type.Type;
import org.hibernate.Hibernate;
import org.hibernate.TypeMismatchException;
import org.hibernate.HibernateException;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.util.StringHelper;
import org.hibernate.hql.antlr.HqlSqlTokenTypes;
import org.hibernate.engine.SessionFactoryImplementor;
import antlr.SemanticException;
import antlr.collections.AST;

/**
 * Contract for nodes representing binary operators.
 *
 @author Steve Ebersole
 */
public class BinaryLogicOperatorNode extends HqlSqlWalkerNode implements BinaryOperatorNode {
  /**
   * Performs the operator node initialization by seeking out any parameter
   * nodes and setting their expected type, if possible.
   */
  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" );
    }

    Type lhsType = extractDataTypelhs );
    Type rhsType = extractDataTyperhs );

    if lhsType == null ) {
      lhsType = rhsType;
    }
    if rhsType == null ) {
      rhsType = lhsType;
    }

    if ExpectedTypeAwareNode.class.isAssignableFromlhs.getClass() ) ) {
      ( ( ExpectedTypeAwareNode lhs ).setExpectedTyperhsType );
    }
    if ExpectedTypeAwareNode.class.isAssignableFromrhs.getClass() ) ) {
      ( ( ExpectedTypeAwareNode rhs ).setExpectedTypelhsType );
    }

    mutateRowValueConstructorSyntaxesIfNecessarylhsType, rhsType );
  }

  protected final void mutateRowValueConstructorSyntaxesIfNecessary(Type lhsType, Type rhsType) {
    // TODO : this really needs to be delayed unitl after we definitively know all node types
    // where this is currently a problem is parameters for which where we cannot unequivocally
    // resolve an expected type
    SessionFactoryImplementor sessionFactory = getSessionFactoryHelper().getFactory();
    if lhsType != null && rhsType != null ) {
      int lhsColumnSpan = lhsType.getColumnSpansessionFactory );
      if lhsColumnSpan != rhsType.getColumnSpansessionFactory ) ) {
        throw new TypeMismatchException(
            "left and right hand sides of a binary logic operator were incompatibile [" +
            lhsType.getName() " : "+ rhsType.getName() "]"
        );
      }
      if lhsColumnSpan > ) {
        // for dialects which are known to not support ANSI-SQL row-value-constructor syntax,
        // we should mutate the tree.
        if !sessionFactory.getDialect().supportsRowValueConstructorSyntax() ) {
          mutateRowValueConstructorSyntaxlhsColumnSpan );
        }
      }
    }
  }

  /**
   * Mutate the subtree relating to a row-value-constructor to instead use
   * a series of ANDed predicates.  This allows multi-column type comparisons
   * and explicit row-value-constructor syntax even on databases which do
   * not support row-value-constructor.
   <p/>
   * For example, here we'd mutate "... where (col1, col2) = ('val1', 'val2) ..." to
   * "... where col1 = 'val1' and col2 = 'val2' ..."
   *
   @param valueElements The number of elements in the row value constructor list.
   */
  private void mutateRowValueConstructorSyntax(int valueElements) {
    // mutation depends on the types of nodes invloved...
    int comparisonType = getType();
    String comparisonText = getText();
    setTypeHqlSqlTokenTypes.AND );
    setText"AND" );
    String[] lhsElementTexts = extractMutationTextsgetLeftHandOperand(), valueElements );
    String[] rhsElementTexts = extractMutationTextsgetRightHandOperand(), valueElements );

    ParameterSpecification lhsEmbeddedCompositeParameterSpecification =
        getLeftHandOperand() == null || !ParameterNode.class.isInstancegetLeftHandOperand() ) )
            null
            ( ( ParameterNode getLeftHandOperand() ).getHqlParameterSpecification();

    ParameterSpecification rhsEmbeddedCompositeParameterSpecification =
        getRightHandOperand() == null || !ParameterNode.class.isInstancegetRightHandOperand() ) )
            null
            ( ( ParameterNode getRightHandOperand() ).getHqlParameterSpecification();

    AST container = this;
    for int i = valueElements - 1; i > 0; i-- ) {
      if i == ) {
        AST op1 = getASTFactory().createcomparisonType, comparisonText );
        AST lhs1 = getASTFactory().createHqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[0] );
        AST rhs1 = getASTFactory().createHqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[0] );
        op1.setFirstChildlhs1 );
        lhs1.setNextSiblingrhs1 );
        container.setFirstChildop1 );
        AST op2 = getASTFactory().createcomparisonType, comparisonText );
        AST lhs2 = getASTFactory().createHqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[1] );
        AST rhs2 = getASTFactory().createHqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[1] );
        op2.setFirstChildlhs2 );
        lhs2.setNextSiblingrhs2 );
        op1.setNextSiblingop2 );

        // "pass along" our initial embedded parameter node(s) to the first generated
        // sql fragment so that it can be handled later for parameter binding...
        SqlFragment fragment = SqlFragment lhs1;
        if lhsEmbeddedCompositeParameterSpecification != null ) {
          fragment.addEmbeddedParameterlhsEmbeddedCompositeParameterSpecification );
        }
        if rhsEmbeddedCompositeParameterSpecification != null ) {
          fragment.addEmbeddedParameterrhsEmbeddedCompositeParameterSpecification );
        }
      }
      else {
        AST op = getASTFactory().createcomparisonType, comparisonText );
        AST lhs = getASTFactory().createHqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[i] );
        AST rhs = getASTFactory().createHqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[i] );
        op.setFirstChildlhs );
        lhs.setNextSiblingrhs );
        AST newContainer = getASTFactory().createHqlSqlTokenTypes.AND, "AND" );
        container.setFirstChildnewContainer );
        newContainer.setNextSiblingop );
        container = newContainer;
      }
    }
  }

  private static String[] extractMutationTexts(Node operand, int count) {
    if operand instanceof ParameterNode ) {
      String[] rtn = new String[count];
      for int i = 0; i < count; i++ ) {
        rtn[i"?";
      }
      return rtn;
    }
    else if operand.getType() == HqlSqlTokenTypes.VECTOR_EXPR ) {
      String[] rtn = new Stringoperand.getNumberOfChildren() ];
      int x = 0;
      AST node = operand.getFirstChild();
      while node != null ) {
        rtnx++ = node.getText();
        node = node.getNextSibling();
      }
      return rtn;
    }
    else if operand instanceof SqlNode ) {
      String nodeText = operand.getText();
      if nodeText.startsWith"(" ) ) {
        nodeText = nodeText.substring);
      }
      if nodeText.endsWith")" ) ) {
        nodeText = nodeText.substring0, nodeText.length() );
      }
      String[] splits = StringHelper.split", ", nodeText );
      if count != splits.length ) {
        throw new HibernateException"SqlNode's text did not reference expected number of columns" );
      }
      return splits;
    }
    else {
      throw new HibernateException"dont know how to extract row value elements from node : " + operand );
    }
  }

  protected Type extractDataType(Node operand) {
    Type type = null;
    if operand instanceof SqlNode ) {
      type = ( ( SqlNode operand ).getDataType();
    }
    if type == null && operand instanceof ExpectedTypeAwareNode ) {
      type = ( ( ExpectedTypeAwareNode operand ).getExpectedType();
    }
    return type;
  }

  public Type getDataType() {
    // logic operators by definition resolve to booleans
    return Hibernate.BOOLEAN;
  }

  /**
   * 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();
  }
}