Open Source Repository

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


org/hibernate/hql/ast/util/SessionFactoryHelper.java
// $Id: SessionFactoryHelper.java 10825 2006-11-16 19:33:19Z [email protected] $
package org.hibernate.hql.ast.util;

import java.util.HashMap;
import java.util.Map;

import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.engine.JoinSequence;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.hql.NameGenerator;
import org.hibernate.hql.ast.DetailedSemanticException;
import org.hibernate.hql.ast.QuerySyntaxException;
import org.hibernate.hql.ast.tree.SqlNode;
import org.hibernate.persister.collection.CollectionPropertyMapping;
import org.hibernate.persister.collection.CollectionPropertyNames;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;

import antlr.SemanticException;
import antlr.collections.AST;

/**
 * Helper for performing common and/or complex operations with the
 {@link SessionFactoryImplementor} during translation of an HQL query.
 *
 @author Joshua Davis
 */
public class SessionFactoryHelper {

  private SessionFactoryImplementor sfi;
  private Map collectionPropertyMappingByRole;

  /**
   * Construct a new SessionFactoryHelper instance.
   *
   @param sfi The SessionFactory impl to be encapsualted.
   */
  public SessionFactoryHelper(SessionFactoryImplementor sfi) {
    this.sfi = sfi;
    collectionPropertyMappingByRole = new HashMap();
  }

  /**
   * Get a handle to the encapsulated SessionFactory.
   *
   @return The encapsulated SessionFactory.
   */
  public SessionFactoryImplementor getFactory() {
    return sfi;
  }

  /**
   * Does the given persister define a physical discriminator column
   * for the purpose of inheritence discrimination?
   *
   @param persister The persister to be checked.
   @return True if the persister does define an actual discriminator column.
   */
  public boolean hasPhysicalDiscriminatorColumn(Queryable persister) {
    if persister.getDiscriminatorType() != null ) {
      String discrimColumnName = persister.getDiscriminatorColumnName();
      // Needed the "clazz_" check to work around union-subclasses
      // TODO : is there a way to tell whether a persister is truly discrim-column based inheritence?
      if discrimColumnName != null && !"clazz_".equalsdiscrimColumnName ) ) {
        return true;
      }
    }
    return false;
  }

  /**
   * Given a (potentially unqualified) class name, locate its imported qualified name.
   *
   @param className The potentially unqualified class name
   @return The qualified class name.
   */
  public String getImportedClassName(String className) {
    return sfi.getImportedClassNameclassName );
  }

  /**
   * Given a (potentially unqualified) class name, locate its persister.
   *
   @param className The (potentially unqualified) class name.
   @return The defined persister for this class, or null if none found.
   */
  public Queryable findQueryableUsingImports(String className) {
    return findQueryableUsingImportssfi, className );
  }


  /**
   * Given a (potentially unqualified) class name, locate its persister.
   *
   @param sfi The session factory implementor.
   @param className The (potentially unqualified) class name.
   @return The defined persister for this class, or null if none found.
   */
  public static Queryable findQueryableUsingImports(SessionFactoryImplementor sfi, String className) {
    final String importedClassName = sfi.getImportedClassNameclassName );
    if importedClassName == null ) {
      return null;
    }
    try {
      return Queryable sfi.getEntityPersisterimportedClassName );
    }
    catch MappingException me ) {
      return null;
    }
  }

  /**
   * Locate the persister by class or entity name.
   *
   @param name The class or entity name
   @return The defined persister for this entity, or null if none found.
   @throws MappingException
   */
  private EntityPersister findEntityPersisterByName(String namethrows MappingException {
    // First, try to get the persister using the given name directly.
    try {
      return sfi.getEntityPersistername );
    }
    catch MappingException ignore ) {
      // unable to locate it using this name
    }

    // If that didn't work, try using the 'import' name.
    String importedClassName = sfi.getImportedClassNamename );
    if importedClassName == null ) {
      return null;
    }
    return sfi.getEntityPersisterimportedClassName );
  }

  /**
   * Locate the persister by class or entity name, requiring that such a persister
   * exist.
   *
   @param name The class or entity name
   @return The defined persister for this entity
   @throws SemanticException Indicates the persister could not be found
   */
  public EntityPersister requireClassPersister(String namethrows SemanticException {
    EntityPersister cp;
    try {
      cp = findEntityPersisterByNamename );
      if cp == null ) {
        throw new QuerySyntaxExceptionname + " is not mapped" );
      }
    }
    catch MappingException e ) {
      throw new DetailedSemanticExceptione.getMessage(), e );
    }
    return cp;
  }

  /**
   * Locate the collection persister by the collection role.
   *
   @param role The collection role name.
   @return The defined CollectionPersister for this collection role, or null.
   */
  public QueryableCollection getCollectionPersister(String role) {
    try {
      return QueryableCollection sfi.getCollectionPersisterrole );
    }
    catch ClassCastException cce ) {
      throw new QueryException"collection is not queryable: " + role );
    }
    catch Exception e ) {
      throw new QueryException"collection not found: " + role );
    }
  }

  /**
   * Locate the collection persister by the collection role, requiring that
   * such a persister exist.
   *
   @param role The collection role name.
   @return The defined CollectionPersister for this collection role.
   @throws QueryException Indicates that the collection persister could not be found.
   */
  public QueryableCollection requireQueryableCollection(String rolethrows QueryException {
    try {
      QueryableCollection queryableCollection = QueryableCollection sfi.getCollectionPersisterrole );
      if queryableCollection != null ) {
        collectionPropertyMappingByRole.putrole, new CollectionPropertyMappingqueryableCollection ) );
      }
      return queryableCollection;
    }
    catch ClassCastException cce ) {
      throw new QueryException"collection role is not queryable: " + role );
    }
    catch Exception e ) {
      throw new QueryException"collection role not found: " + role );
    }
  }

  /**
   * Retreive a PropertyMapping describing the given collection role.
   *
   @param role The collection role for whcih to retrieve the property mapping.
   @return The property mapping.
   */
  private PropertyMapping getCollectionPropertyMapping(String role) {
    return PropertyMapping collectionPropertyMappingByRole.getrole );
  }

  /**
   * Retrieves the column names corresponding to the collection elements for the given
   * collection role.
   *
   @param role The collection role
   @param roleAlias The sql column-qualification alias (i.e., the table alias)
   @return the collection element columns
   */
  public String[] getCollectionElementColumns(String role, String roleAlias) {
    return getCollectionPropertyMappingrole ).toColumnsroleAlias, CollectionPropertyNames.COLLECTION_ELEMENTS );
  }

  /**
   * Generate an empty join sequence instance.
   *
   @return The generate join sequence.
   */
  public JoinSequence createJoinSequence() {
    return new JoinSequencesfi );
  }

  /**
   * Generate a join sequence representing the given association type.
   *
   @param implicit Should implicit joins (theta-style) or explicit joins (ANSI-style) be rendered
   @param associationType The type representing the thing to be joined into.
   @param tableAlias The table alias to use in qualifing the join conditions
   @param joinType The type of join to render (inner, outer, etc);  see {@link org.hibernate.sql.JoinFragment}
   @param columns The columns making up the condition of the join.
   @return The generated join sequence.
   */
  public JoinSequence createJoinSequence(boolean implicit, AssociationType associationType, String tableAlias, int joinType, String[] columns) {
    JoinSequence joinSequence = createJoinSequence();
    joinSequence.setUseThetaStyleimplicit );  // Implicit joins use theta style (WHERE pk = fk), explicit joins use JOIN (after from)
    joinSequence.addJoinassociationType, tableAlias, joinType, columns );
    return joinSequence;
  }

  /**
   * Create a join sequence rooted at the given collection.
   *
   @param collPersister The persister for the collection at which the join should be rooted.
   @param collectionName The alias to use for qualifying column references.
   @return The generated join sequence.
   */
  public JoinSequence createCollectionJoinSequence(QueryableCollection collPersister, String collectionName) {
    JoinSequence joinSequence = createJoinSequence();
    joinSequence.setRootcollPersister, collectionName );
    joinSequence.setUseThetaStyletrue );    // TODO: figure out how this should be set.
///////////////////////////////////////////////////////////////////////////////
// This was the reason for failures regarding INDEX_OP and subclass joins on
// theta-join dialects; not sure what behaviour we were trying to emulate ;)
//    joinSequence = joinSequence.getFromPart();  // Emulate the old addFromOnly behavior.
    return joinSequence;
  }

  /**
   * Determine the name of the property for the entity encapsulated by the
   * given type which represents the id or unique-key.
   *
   @param entityType The type representing the entity.
   @return The corresponding property name
   @throws QueryException Indicates such a property could not be found.
   */
  public String getIdentifierOrUniqueKeyPropertyName(EntityType entityType) {
    try {
      return entityType.getIdentifierOrUniqueKeyPropertyNamesfi );
    }
    catch MappingException me ) {
      throw new QueryExceptionme );
    }
  }

  /**
   * Retreive the number of columns represented by this type.
   *
   @param type The type.
   @return The number of columns.
   */
  public int getColumnSpan(Type type) {
    return type.getColumnSpansfi );
  }

  /**
   * Given a collection type, determine the entity name of the elements
   * contained within instance of that collection.
   *
   @param collectionType The collection type to check.
   @return The entity name of the elements of this collection.
   */
  public String getAssociatedEntityName(CollectionType collectionType) {
    return collectionType.getAssociatedEntityNamesfi );
  }

  /**
   * Given a collection type, determine the Type representing elements
   * within instances of that collection.
   *
   @param collectionType The collection type to be checked.
   @return The Type of the elements of the collection.
   */
  private Type getElementType(CollectionType collectionType) {
    return collectionType.getElementTypesfi );
  }

  /**
   * Essentially the same as {@link #getElementType}, but requiring that the
   * element type be an association type.
   *
   @param collectionType The collection type to be checked.
   @return The AssociationType of the elements of the collection.
   */
  public AssociationType getElementAssociationType(CollectionType collectionType) {
    return AssociationType getElementTypecollectionType );
  }

  /**
   * Locate a registered sql function by name.
   *
   @param functionName The name of the function to locate
   @return The sql function, or null if not found.
   */
  public SQLFunction findSQLFunction(String functionName) {
    return sfi.getSqlFunctionRegistry().findSQLFunctionfunctionName.toLowerCase() );
  }

  /**
   * Locate a registered sql function by name, requiring that such a registered function exist.
   *
   @param functionName The name of the function to locate
   @return The sql function.
   @throws QueryException Indicates no matching sql functions could be found.
   */
  private SQLFunction requireSQLFunction(String functionName) {
    SQLFunction f = findSQLFunctionfunctionName );
    if f == null ) {
      throw new QueryException"Unable to find SQL function: " + functionName );
    }
    return f;
  }

  /**
   * Find the function return type given the function name and the first argument expression node.
   *
   @param functionName The function name.
   @param first        The first argument expression.
   @return the function return type given the function name and the first argument expression node.
   */
  public Type findFunctionReturnType(String functionName, AST first) {
    // locate the registered function by the given name
    SQLFunction sqlFunction = requireSQLFunctionfunctionName );

    // determine the type of the first argument...
    Type argumentType = null;
    if first != null ) {
      if "cast".equals(functionName) ) {
        argumentType = TypeFactory.heuristicTypefirst.getNextSibling().getText() );
      }
      else if first instanceof SqlNode ) {
        argumentType = ( (SqlNodefirst ).getDataType();
      }
    }

    return sqlFunction.getReturnTypeargumentType, sfi );
  }

  public String[][] generateColumnNames(Type[] sqlResultTypes) {
    return NameGenerator.generateColumnNamessqlResultTypes, sfi );
  }

  public boolean isStrictJPAQLComplianceEnabled() {
    return sfi.getSettings().isStrictJPAQLCompliance();
  }
}