package org.hibernate.hql.internal.ast.tree;
import org.hibernate.engine.internal.JoinSequence;
import org.hibernate.hql.internal.antlr.SqlTokenTypes;
import org.hibernate.hql.internal.ast.util.ASTUtil;
import org.hibernate.hql.internal.ast.util.AliasGenerator;
import org.hibernate.hql.internal.ast.util.PathHelper;
import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import antlr.ASTFactory;
import antlr.SemanticException;
import antlr.collections.AST;
public class FromElementFactory implements SqlTokenTypes {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( FromElementFactory.class );
private FromClause fromClause;
private FromElement origin;
private String path;
private String classAlias;
private String[] columns;
private boolean implied;
private boolean inElementsFunction;
private boolean collection;
private QueryableCollection queryableCollection;
private CollectionType collectionType;
public FromElementFactory(FromClause fromClause, FromElement origin, String path) {
this.fromClause = fromClause;
this.origin = origin;
this.path = path;
collection = false;
}
public FromElementFactory(
FromClause fromClause,
FromElement origin,
String path,
String classAlias,
String[] columns,
boolean implied) {
this( fromClause, origin, path );
this.classAlias = classAlias;
this.columns = columns;
this.implied = implied;
collection = true;
}
FromElement addFromElement() throws SemanticException {
final FromClause parentFromClause = fromClause.getParentFromClause();
if ( parentFromClause != null ) {
final String pathAlias = PathHelper.getAlias( path );
final FromElement parentFromElement = parentFromClause.getFromElement( pathAlias );
if ( parentFromElement != null ) {
return createFromElementInSubselect( path, pathAlias, parentFromElement, classAlias );
}
}
final EntityPersister entityPersister = fromClause.getSessionFactoryHelper().requireClassPersister( path );
final FromElement elem = createAndAddFromElement(
path,
classAlias,
entityPersister,
(EntityType) ( (Queryable) entityPersister ).getType(),
null
);
fromClause.getWalker().addQuerySpaces( entityPersister.getQuerySpaces() );
return elem;
}
private FromElement createFromElementInSubselect(
String path,
String pathAlias,
FromElement parentFromElement,
String classAlias) throws SemanticException {
LOG.debugf( "createFromElementInSubselect() : path = %s", path );
FromElement fromElement = evaluateFromElementPath( path, classAlias );
EntityPersister entityPersister = fromElement.getEntityPersister();
String tableAlias = null;
boolean correlatedSubselect = pathAlias.equals( parentFromElement.getClassAlias() );
if ( correlatedSubselect ) {
tableAlias = fromElement.getTableAlias();
}
else {
tableAlias = null;
}
if ( fromElement.getFromClause() != fromClause ) {
LOG.debug( "createFromElementInSubselect() : creating a new FROM element..." );
fromElement = createFromElement( entityPersister );
initializeAndAddFromElement(
fromElement,
path,
classAlias,
entityPersister,
(EntityType) ( (Queryable) entityPersister ).getType(),
tableAlias
);
}
LOG.debugf( "createFromElementInSubselect() : %s -> %s", path, fromElement );
return fromElement;
}
private FromElement evaluateFromElementPath(String path, String classAlias) throws SemanticException {
ASTFactory factory = fromClause.getASTFactory();
FromReferenceNode pathNode = (FromReferenceNode) PathHelper.parsePath( path, factory );
pathNode.recursiveResolve(
FromReferenceNode.ROOT_LEVEL,
false,
classAlias,
null
);
if ( pathNode.getImpliedJoin() != null ) {
return pathNode.getImpliedJoin();
}
return pathNode.getFromElement();
}
FromElement createCollectionElementsJoin(
QueryableCollection queryableCollection,
String collectionName) throws SemanticException {
JoinSequence collectionJoinSequence = fromClause.getSessionFactoryHelper()
.createCollectionJoinSequence( queryableCollection, collectionName );
this.queryableCollection = queryableCollection;
return createCollectionJoin( collectionJoinSequence, null );
}
public FromElement createCollection(
QueryableCollection queryableCollection,
String role,
JoinType joinType,
boolean fetchFlag,
boolean indexed)
throws SemanticException {
if ( !collection ) {
throw new IllegalStateException( "FromElementFactory not initialized for collections!" );
}
this.inElementsFunction = indexed;
FromElement elem;
this.queryableCollection = queryableCollection;
collectionType = queryableCollection.getCollectionType();
String roleAlias = fromClause.getAliasGenerator().createName( role );
boolean explicitSubqueryFromElement = fromClause.isSubQuery() && !implied;
if ( explicitSubqueryFromElement ) {
String pathRoot = StringHelper.root( path );
FromElement origin = fromClause.getFromElement( pathRoot );
if ( origin == null || origin.getFromClause() != fromClause ) {
implied = true;
}
}
if ( explicitSubqueryFromElement && DotNode.useThetaStyleImplicitJoins ) {
implied = true;
}
Type elementType = queryableCollection.getElementType();
if ( elementType.isEntityType() ) {
elem = createEntityAssociation( role, roleAlias, joinType );
}
else if ( elementType.isComponentType() ) {
JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
elem = createCollectionJoin( joinSequence, roleAlias );
}
else {
JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
elem = createCollectionJoin( joinSequence, roleAlias );
}
elem.setRole( role );
elem.setQueryableCollection( queryableCollection );
if ( implied ) {
elem.setIncludeSubclasses( false );
}
if ( explicitSubqueryFromElement ) {
elem.setInProjectionList( true );
}
if ( fetchFlag ) {
elem.setFetch( true );
}
return elem;
}
public FromElement createEntityJoin(
String entityClass,
String tableAlias,
JoinSequence joinSequence,
boolean fetchFlag,
boolean inFrom,
EntityType type,
String role,
String joinPath) throws SemanticException {
FromElement elem = createJoin( entityClass, tableAlias, joinSequence, type, false );
elem.setFetch( fetchFlag );
if ( joinPath != null ) {
elem.applyTreatAsDeclarations( fromClause.getWalker().getTreatAsDeclarationsByPath( joinPath ) );
}
EntityPersister entityPersister = elem.getEntityPersister();
int numberOfTables = entityPersister.getQuerySpaces().length;
if ( numberOfTables > 1 && implied && !elem.useFromFragment() ) {
LOG.debug( "createEntityJoin() : Implied multi-table entity join" );
elem.setUseFromFragment( true );
}
if ( implied && inFrom ) {
joinSequence.setUseThetaStyle( false );
elem.setUseFromFragment( true );
elem.setImpliedInFromClause( true );
}
if ( elem.getWalker().isSubQuery() ) {
if ( elem.getFromClause() != elem.getOrigin().getFromClause() ||
DotNode.useThetaStyleImplicitJoins ) {
elem.setType( FROM_FRAGMENT );
joinSequence.setUseThetaStyle( true );
elem.setUseFromFragment( false );
}
}
elem.setRole( role );
return elem;
}
public FromElement createComponentJoin(CompositeType type) {
return new ComponentJoin( fromClause, origin, classAlias, path, type );
}
FromElement createElementJoin(QueryableCollection queryableCollection) throws SemanticException {
FromElement elem;
implied = true;
inElementsFunction = true;
Type elementType = queryableCollection.getElementType();
if ( !elementType.isEntityType() ) {
throw new IllegalArgumentException( "Cannot create element join for a collection of non-entities!" );
}
this.queryableCollection = queryableCollection;
SessionFactoryHelper sfh = fromClause.getSessionFactoryHelper();
FromElement destination = null;
String tableAlias = null;
EntityPersister entityPersister = queryableCollection.getElementPersister();
tableAlias = fromClause.getAliasGenerator().createName( entityPersister.getEntityName() );
String associatedEntityName = entityPersister.getEntityName();
EntityPersister targetEntityPersister = sfh.requireClassPersister( associatedEntityName );
destination = createAndAddFromElement(
associatedEntityName,
classAlias,
targetEntityPersister,
(EntityType) queryableCollection.getElementType(),
tableAlias
);
if ( implied ) {
destination.setIncludeSubclasses( false );
}
fromClause.addCollectionJoinFromElementByPath( path, destination );
fromClause.getWalker().addQuerySpaces( entityPersister.getQuerySpaces() );
CollectionType type = queryableCollection.getCollectionType();
String role = type.getRole();
String roleAlias = origin.getTableAlias();
String[] targetColumns = sfh.getCollectionElementColumns( role, roleAlias );
AssociationType elementAssociationType = sfh.getElementAssociationType( type );
JoinType joinType = JoinType.INNER_JOIN;
JoinSequence joinSequence = sfh.createJoinSequence(
implied,
elementAssociationType,
tableAlias,
joinType,
targetColumns
);
elem = initializeJoin( path, destination, joinSequence, targetColumns, origin, false );
elem.setUseFromFragment( true );
elem.setCollectionTableAlias( roleAlias );
return elem;
}
private FromElement createCollectionJoin(JoinSequence collectionJoinSequence, String tableAlias)
throws SemanticException {
String text = queryableCollection.getTableName();
AST ast = createFromElement( text );
FromElement destination = (FromElement) ast;
Type elementType = queryableCollection.getElementType();
if ( elementType.isCollectionType() ) {
throw new SemanticException( "Collections of collections are not supported!" );
}
destination.initializeCollection( fromClause, classAlias, tableAlias );
destination.setType( JOIN_FRAGMENT );
destination.setIncludeSubclasses( false );
destination.setCollectionJoin( true );
destination.setJoinSequence( collectionJoinSequence );
destination.setOrigin( origin, false );
destination.setCollectionTableAlias( tableAlias );
origin.setText( "" );
origin.setCollectionJoin( true );
fromClause.addCollectionJoinFromElementByPath( path, destination );
fromClause.getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() );
return destination;
}
private FromElement createEntityAssociation(
String role,
String roleAlias,
JoinType joinType) throws SemanticException {
FromElement elem;
Queryable entityPersister = (Queryable) queryableCollection.getElementPersister();
String associatedEntityName = entityPersister.getEntityName();
if ( queryableCollection.isOneToMany() ) {
LOG.debugf(
"createEntityAssociation() : One to many - path = %s role = %s associatedEntityName = %s",
path,
role,
associatedEntityName
);
JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
elem = createJoin(
associatedEntityName,
roleAlias,
joinSequence,
(EntityType) queryableCollection.getElementType(),
false
);
}
else {
LOG.debugf(
"createManyToMany() : path = %s role = %s associatedEntityName = %s",
path,
role,
associatedEntityName
);
elem = createManyToMany(
role, associatedEntityName,
roleAlias, entityPersister, (EntityType) queryableCollection.getElementType(), joinType
);
fromClause.getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() );
}
elem.setCollectionTableAlias( roleAlias );
return elem;
}
private FromElement createJoin(
String entityClass,
String tableAlias,
JoinSequence joinSequence,
EntityType type,
boolean manyToMany) throws SemanticException {
EntityPersister entityPersister = fromClause.getSessionFactoryHelper().requireClassPersister( entityClass );
FromElement destination = createAndAddFromElement(
entityClass,
classAlias,
entityPersister,
type,
tableAlias
);
return initializeJoin( path, destination, joinSequence, getColumns(), origin, manyToMany );
}
private FromElement createManyToMany(
String role,
String associatedEntityName,
String roleAlias,
Queryable entityPersister,
EntityType type,
JoinType joinType) throws SemanticException {
FromElement elem;
SessionFactoryHelper sfh = fromClause.getSessionFactoryHelper();
if ( inElementsFunction ) {
JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
elem = createJoin( associatedEntityName, roleAlias, joinSequence, type, true );
}
else {
String tableAlias = fromClause.getAliasGenerator().createName( entityPersister.getEntityName() );
String[] secondJoinColumns = sfh.getCollectionElementColumns( role, roleAlias );
JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
joinSequence.addJoin(
sfh.getElementAssociationType( collectionType ),
tableAlias,
joinType,
secondJoinColumns
);
elem = createJoin( associatedEntityName, tableAlias, joinSequence, type, false );
elem.setUseFromFragment( true );
}
return elem;
}
private JoinSequence createJoinSequence(String roleAlias, JoinType joinType) {
SessionFactoryHelper sessionFactoryHelper = fromClause.getSessionFactoryHelper();
String[] joinColumns = getColumns();
if ( collectionType == null ) {
throw new IllegalStateException( "collectionType is null!" );
}
return sessionFactoryHelper.createJoinSequence( implied, collectionType, roleAlias, joinType, joinColumns );
}
private FromElement createAndAddFromElement(
String className,
String classAlias,
EntityPersister entityPersister,
EntityType type,
String tableAlias) {
if ( !( entityPersister instanceof Joinable ) ) {
throw new IllegalArgumentException( "EntityPersister " + entityPersister + " does not implement Joinable!" );
}
FromElement element = createFromElement( entityPersister );
initializeAndAddFromElement( element, className, classAlias, entityPersister, type, tableAlias );
return element;
}
private void initializeAndAddFromElement(
FromElement element,
String className,
String classAlias,
EntityPersister entityPersister,
EntityType type,
String tableAlias) {
if ( tableAlias == null ) {
AliasGenerator aliasGenerator = fromClause.getAliasGenerator();
tableAlias = aliasGenerator.createName( entityPersister.getEntityName() );
}
element.initializeEntity( fromClause, className, entityPersister, type, classAlias, tableAlias );
}
private FromElement createFromElement(EntityPersister entityPersister) {
Joinable joinable = (Joinable) entityPersister;
String text = joinable.getTableName();
AST ast = createFromElement( text );
FromElement element = (FromElement) ast;
return element;
}
private AST createFromElement(String text) {
AST ast = ASTUtil.create(
fromClause.getASTFactory(),
implied ? IMPLIED_FROM : FROM_FRAGMENT,
text
);
ast.setType( FROM_FRAGMENT );
return ast;
}
private FromElement initializeJoin(
String path,
FromElement destination,
JoinSequence joinSequence,
String[] columns,
FromElement origin,
boolean manyToMany) {
destination.setType( JOIN_FRAGMENT );
destination.setJoinSequence( joinSequence );
destination.setColumns( columns );
destination.setOrigin( origin, manyToMany );
fromClause.addJoinByPathMap( path, destination );
return destination;
}
private String[] getColumns() {
if ( columns == null ) {
throw new IllegalStateException( "No foriegn key columns were supplied!" );
}
return columns;
}
}