Open Source Repository

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


org/hibernate/loader/criteria/CriteriaQueryTranslator.java
//$Id: CriteriaQueryTranslator.java 9636 2006-03-16 14:14:48Z [email protected] $
package org.hibernate.loader.criteria;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import org.apache.commons.collections.SequencedHashMap;
import org.hibernate.Criteria;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.hql.ast.util.SessionFactoryHelper;
import org.hibernate.criterion.CriteriaQuery;
import org.hibernate.criterion.Projection;
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.RowSelection;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.TypedValue;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.type.AssociationType;
import org.hibernate.type.Type;
import org.hibernate.type.NullableType;
import org.hibernate.util.ArrayHelper;
import org.hibernate.util.StringHelper;

/**
 @author Gavin King
 */
public class CriteriaQueryTranslator implements CriteriaQuery {

  public static final String ROOT_SQL_ALIAS = Criteria.ROOT_ALIAS + '_';

  private CriteriaQuery outerQueryTranslator;

  private final CriteriaImpl rootCriteria;
  private final String rootEntityName;
  private final String rootSQLAlias;
  private int aliasCount = 0;

  private final Map criteriaEntityNames = new SequencedHashMap();
  private final Map criteriaSQLAliasMap = new HashMap();
  private final Map aliasCriteriaMap = new HashMap();
  private final Map associationPathCriteriaMap = new SequencedHashMap();
  private final Map associationPathJoinTypesMap = new SequencedHashMap();

  private final SessionFactoryImplementor sessionFactory;

  public CriteriaQueryTranslator(
      final SessionFactoryImplementor factory,
          final CriteriaImpl criteria,
          final String rootEntityName,
          final String rootSQLAlias,
          CriteriaQuery outerQuerythrows HibernateException {
    thisfactory, criteria, rootEntityName, rootSQLAlias );
    outerQueryTranslator = outerQuery;
  }

  public CriteriaQueryTranslator(
      final SessionFactoryImplementor factory,
          final CriteriaImpl criteria,
          final String rootEntityName,
          final String rootSQLAliasthrows HibernateException {
    this.rootCriteria = criteria;
    this.rootEntityName = rootEntityName;
    this.sessionFactory = factory;
    this.rootSQLAlias = rootSQLAlias;
    createAliasCriteriaMap();
    createAssociationPathCriteriaMap();
    createCriteriaEntityNameMap();
    createCriteriaSQLAliasMap();
  }

  public String generateSQLAlias() {
    return StringHelper.generateAliasCriteria.ROOT_ALIAS, aliasCount '_';
  }

  public String getRootSQLALias() {
    return rootSQLAlias;
  }

  private Criteria getAliasedCriteria(String alias) {
    return Criteria aliasCriteriaMap.getalias );
  }

  public boolean isJoin(String path) {
    return associationPathCriteriaMap.containsKeypath );
  }

  public int getJoinType(String path) {
    Integer result = Integer associationPathJoinTypesMap.getpath );
    return result == null ? Criteria.INNER_JOIN : result.intValue() );
  }

  public Criteria getCriteria(String path) {
    return Criteria associationPathCriteriaMap.getpath );
  }

  public Set getQuerySpaces() {
    Set result = new HashSet();
    Iterator iter = criteriaEntityNames.values().iterator();
    while iter.hasNext() ) {
      String entityName = String iter.next();
      result.addAllArrays.asListgetFactory().getEntityPersisterentityName ).getQuerySpaces() ) );
    }
    return result;
  }

  private void createAliasCriteriaMap() {
    aliasCriteriaMap.putrootCriteria.getAlias(), rootCriteria );
    Iterator iter = rootCriteria.iterateSubcriteria();
    while iter.hasNext() ) {
      Criteria subcriteria = Criteria iter.next();
      if subcriteria.getAlias() != null ) {
        Object old = aliasCriteriaMap.putsubcriteria.getAlias(), subcriteria );
        if old != null ) {
          throw new QueryException"duplicate alias: " + subcriteria.getAlias() );
        }
      }
    }
  }

  private void createAssociationPathCriteriaMap() {
    Iterator iter = rootCriteria.iterateSubcriteria();
    while iter.hasNext() ) {
      CriteriaImpl.Subcriteria crit = CriteriaImpl.Subcriteria iter.next();
      String wholeAssociationPath = getWholeAssociationPathcrit );
      Object old = associationPathCriteriaMap.putwholeAssociationPath, crit );
      if old != null ) {
        throw new QueryException"duplicate association path: " + wholeAssociationPath );
      }
      int joinType = crit.getJoinType();
      old = associationPathJoinTypesMap.putwholeAssociationPath, new IntegerjoinType ) );
      if old != null ) {
        // TODO : not so sure this is needed...
        throw new QueryException"duplicate association path: " + wholeAssociationPath );
      }
    }
  }

  private String getWholeAssociationPath(CriteriaImpl.Subcriteria subcriteria) {
    String path = subcriteria.getPath();

    // some messy, complex stuff here, since createCriteria() can take an
    // aliased path, or a path rooted at the creating criteria instance
    Criteria parent = null;
    if path.indexOf'.' ) {
      // if it is a compound path
      String testAlias = StringHelper.rootpath );
      if !testAlias.equalssubcriteria.getAlias() ) ) {
        // and the qualifier is not the alias of this criteria
        //      -> check to see if we belong to some criteria other
        //          than the one that created us
        parent = Criteria aliasCriteriaMap.gettestAlias );
      }
    }
    if parent == null ) {
      // otherwise assume the parent is the the criteria that created us
      parent = subcriteria.getParent();
    }
    else {
      path = StringHelper.unrootpath );
    }

    if parent.equalsrootCriteria ) ) {
      // if its the root criteria, we are done
      return path;
    }
    else {
      // otherwise, recurse
      return getWholeAssociationPath( ( CriteriaImpl.Subcriteria parent '.' + path;
    }
  }

  private void createCriteriaEntityNameMap() {
    criteriaEntityNames.putrootCriteria, rootEntityName );
    Iterator iter = associationPathCriteriaMap.entrySet().iterator();
    while iter.hasNext() ) {
      Map.Entry me = Map.Entry iter.next();
      criteriaEntityNames.put(
          me.getValue()//the criteria instance
              getPathEntityName( ( String me.getKey() )
      );
    }
  }

  private String getPathEntityName(String path) {
    Queryable persister = Queryable sessionFactory.getEntityPersisterrootEntityName );
    StringTokenizer tokens = new StringTokenizerpath, "." );
    String componentPath = "";
    while tokens.hasMoreTokens() ) {
      componentPath += tokens.nextToken();
      Type type = persister.toTypecomponentPath );
      if type.isAssociationType() ) {
        AssociationType atype = AssociationType type;
        persister = Queryable sessionFactory.getEntityPersister(
            atype.getAssociatedEntityNamesessionFactory )
        );
        componentPath = "";
      }
      else if type.isComponentType() ) {
        componentPath += '.';
      }
      else {
        throw new QueryException"not an association: " + componentPath );
      }
    }
    return persister.getEntityName();
  }

  public int getSQLAliasCount() {
    return criteriaSQLAliasMap.size();
  }

  private void createCriteriaSQLAliasMap() {
    int i = 0;
    Iterator criteriaIterator = criteriaEntityNames.entrySet().iterator();
    while criteriaIterator.hasNext() ) {
      Map.Entry me = Map.Entry criteriaIterator.next();
      Criteria crit = Criteria me.getKey();
      String alias = crit.getAlias();
      if alias == null ) {
        alias = String me.getValue()// the entity name
      }
      criteriaSQLAliasMap.putcrit, StringHelper.generateAliasalias, i++ ) );
    }
    criteriaSQLAliasMap.putrootCriteria, rootSQLAlias );
  }

  public CriteriaImpl getRootCriteria() {
    return rootCriteria;
  }

  public QueryParameters getQueryParameters() {
    List values = new ArrayList();
    List types = new ArrayList();
    Iterator iter = rootCriteria.iterateExpressionEntries();
    while iter.hasNext() ) {
      CriteriaImpl.CriterionEntry ce = CriteriaImpl.CriterionEntry iter.next();
      TypedValue[] tv = ce.getCriterion().getTypedValuesce.getCriteria()this );
      for int i = 0; i < tv.length; i++ ) {
        values.addtv[i].getValue() );
        types.addtv[i].getType() );
      }
    }
    Object[] valueArray = values.toArray();
    Type[] typeArray = ArrayHelper.toTypeArraytypes );

    RowSelection selection = new RowSelection();
    selection.setFirstRowrootCriteria.getFirstResult() );
    selection.setMaxRowsrootCriteria.getMaxResults() );
    selection.setTimeoutrootCriteria.getTimeout() );
    selection.setFetchSizerootCriteria.getFetchSize() );

    Map lockModes = new HashMap();
    iter = rootCriteria.getLockModes().entrySet().iterator();
    while iter.hasNext() ) {
      Map.Entry me = Map.Entry iter.next();
      final Criteria subcriteria = getAliasedCriteria( ( String me.getKey() );
      lockModes.putgetSQLAliassubcriteria ), me.getValue() );
    }
    iter = rootCriteria.iterateSubcriteria();
    while iter.hasNext() ) {
      CriteriaImpl.Subcriteria subcriteria = CriteriaImpl.Subcriteria iter.next();
      LockMode lm = subcriteria.getLockMode();
      if lm != null ) {
        lockModes.putgetSQLAliassubcriteria ), lm );
      }
    }

    return new QueryParameters(
        typeArray,
            valueArray,
            lockModes,
            selection,
            rootCriteria.getCacheable(),
            rootCriteria.getCacheRegion(),
            rootCriteria.getComment(),
            rootCriteria.isLookupByNaturalKey(),
            rootCriteria.getResultTransformer()
    );
  }

  public boolean hasProjection() {
    return rootCriteria.getProjection() != null;
  }

  public String getGroupBy() {
    if rootCriteria.getProjection().isGrouped() ) {
      return rootCriteria.getProjection()
          .toGroupSqlStringrootCriteria.getProjectionCriteria()this );
    }
    else {
      return "";
    }
  }

  public String getSelect() {
    return rootCriteria.getProjection().toSqlString(
        rootCriteria.getProjectionCriteria(),
            0,
            this
    );
  }

  public Type[] getProjectedTypes() {
    return rootCriteria.getProjection().getTypesrootCriteria, this );
  }

  public String[] getProjectedColumnAliases() {
    return rootCriteria.getProjection().getColumnAliases);
  }

  public String[] getProjectedAliases() {
    return rootCriteria.getProjection().getAliases();
  }

  public String getWhereCondition() {
    StringBuffer condition = new StringBuffer30 );
    Iterator criterionIterator = rootCriteria.iterateExpressionEntries();
    while criterionIterator.hasNext() ) {
      CriteriaImpl.CriterionEntry entry = CriteriaImpl.CriterionEntry criterionIterator.next();
      String sqlString = entry.getCriterion().toSqlStringentry.getCriteria()this );
      condition.appendsqlString );
      if criterionIterator.hasNext() ) {
        condition.append" and " );
      }
    }
    return condition.toString();
  }

  public String getOrderBy() {
    StringBuffer orderBy = new StringBuffer30 );
    Iterator criterionIterator = rootCriteria.iterateOrderings();
    while criterionIterator.hasNext() ) {
      CriteriaImpl.OrderEntry oe = CriteriaImpl.OrderEntry criterionIterator.next();
      orderBy.appendoe.getOrder().toSqlStringoe.getCriteria()this ) );
      if criterionIterator.hasNext() ) {
        orderBy.append", " );
      }
    }
    return orderBy.toString();
  }

  public SessionFactoryImplementor getFactory() {
    return sessionFactory;
  }

  public String getSQLAlias(Criteria criteria) {
    return String criteriaSQLAliasMap.getcriteria );
  }

  public String getEntityName(Criteria criteria) {
    return String criteriaEntityNames.getcriteria );
  }

  public String getColumn(Criteria criteria, String propertyName) {
    String[] cols = getColumnspropertyName, criteria );
    if cols.length != ) {
      throw new QueryException"property does not map to a single column: " + propertyName );
    }
    return cols[0];
  }

  /**
   * Get the names of the columns constrained
   * by this criterion.
   */
  public String[] getColumnsUsingProjection(
      Criteria subcriteria,
          String propertyNamethrows HibernateException {

    //first look for a reference to a projection alias
    final Projection projection = rootCriteria.getProjection();
    String[] projectionColumns = projection == null ?
                                 null :
                                 projection.getColumnAliasespropertyName, );

    if projectionColumns == null ) {
      //it does not refer to an alias of a projection,
      //look for a property
      try {
        return getColumnspropertyName, subcriteria );
      }
      catch HibernateException he ) {
        //not found in inner query , try the outer query
        if outerQueryTranslator != null ) {
          return outerQueryTranslator.getColumnsUsingProjectionsubcriteria, propertyName );
        }
        else {
          throw he;
        }
      }
    }
    else {
      //it refers to an alias of a projection
      return projectionColumns;
    }
  }

  public String[] getIdentifierColumns(Criteria subcriteria) {
    String[] idcols =
        ( ( Loadable getPropertyMappinggetEntityNamesubcriteria ) ) ).getIdentifierColumnNames();
    return StringHelper.qualifygetSQLAliassubcriteria ), idcols );
  }

  public Type getIdentifierType(Criteria subcriteria) {
    return ( ( Loadable getPropertyMappinggetEntityNamesubcriteria ) ) ).getIdentifierType();
  }

  public TypedValue getTypedIdentifierValue(Criteria subcriteria, Object value) {
    final Loadable loadable = Loadable getPropertyMappinggetEntityNamesubcriteria ) );
    return new TypedValue(
        loadable.getIdentifierType(),
            value,
            EntityMode.POJO
    );
  }

  private String[] getColumns(
      String propertyName,
          Criteria subcriteriathrows HibernateException {
    return getPropertyMappinggetEntityNamesubcriteria, propertyName ) )
        .toColumns(
            getSQLAliassubcriteria, propertyName ),
                getPropertyNamepropertyName )
        );
  }

  public Type getTypeUsingProjection(Criteria subcriteria, String propertyName)
      throws HibernateException {

    //first look for a reference to a projection alias
    final Projection projection = rootCriteria.getProjection();
    Type[] projectionTypes = projection == null ?
                             null :
                             projection.getTypespropertyName, subcriteria, this );

    if projectionTypes == null ) {
      try {
        //it does not refer to an alias of a projection,
        //look for a property
        return getTypesubcriteria, propertyName );
      }
      catch HibernateException he ) {
        //not found in inner query , try the outer query
        if outerQueryTranslator != null ) {
          return outerQueryTranslator.getTypesubcriteria, propertyName );
        }
        else {
          throw he;
        }
      }
    }
    else {
      if projectionTypes.length != ) {
        //should never happen, i think
        throw new QueryException"not a single-length projection: " + propertyName );
      }
      return projectionTypes[0];
    }
  }

  public Type getType(Criteria subcriteria, String propertyName)
      throws HibernateException {
    return getPropertyMappinggetEntityNamesubcriteria, propertyName ) )
        .toTypegetPropertyNamepropertyName ) );
  }

  /**
   * Get the a typed value for the given property value.
   */
  public TypedValue getTypedValue(Criteria subcriteria, String propertyName, Object value)
      throws HibernateException {
    // Detect discriminator values...
    if value instanceof Class ) {
      Class entityClass = Class value;
      Queryable q = SessionFactoryHelper.findQueryableUsingImportssessionFactory, entityClass.getName() );
      if q != null ) {
        Type type = q.getDiscriminatorType();
        String stringValue = q.getDiscriminatorSQLValue();
        // Convert the string value into the proper type.
        if type instanceof NullableType ) {
          NullableType nullableType = NullableType type;
          value = nullableType.fromStringValuestringValue );
        }
        else {
          throw new QueryException"Unsupported discriminator type " + type );
        }
        return new TypedValue(
            type,
                value,
                EntityMode.POJO
        );
      }
    }
    // Otherwise, this is an ordinary value.
    return new TypedValue(
        getTypeUsingProjectionsubcriteria, propertyName ),
            value,
            EntityMode.POJO
    );
  }

  private PropertyMapping getPropertyMapping(String entityName)
      throws MappingException {
    return PropertyMapping sessionFactory.getEntityPersisterentityName );
  }

  //TODO: use these in methods above

  public String getEntityName(Criteria subcriteria, String propertyName) {
    if propertyName.indexOf'.' ) {
      String root = StringHelper.rootpropertyName );
      Criteria crit = getAliasedCriteriaroot );
      if crit != null ) {
        return getEntityNamecrit );
      }
    }
    return getEntityNamesubcriteria );
  }

  public String getSQLAlias(Criteria criteria, String propertyName) {
    if propertyName.indexOf'.' ) {
      String root = StringHelper.rootpropertyName );
      Criteria subcriteria = getAliasedCriteriaroot );
      if subcriteria != null ) {
        return getSQLAliassubcriteria );
      }
    }
    return getSQLAliascriteria );
  }

  public String getPropertyName(String propertyName) {
    if propertyName.indexOf'.' ) {
      String root = StringHelper.rootpropertyName );
      Criteria crit = getAliasedCriteriaroot );
      if crit != null ) {
        return propertyName.substringroot.length() );
      }
    }
    return propertyName;
  }

}