package org.hibernate.hql.internal.ast.tree;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
import org.hibernate.hql.internal.ast.util.ASTIterator;
import org.hibernate.hql.internal.ast.util.ASTUtil;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import antlr.SemanticException;
import antlr.collections.AST;
public class FromClause extends HqlSqlWalkerNode implements HqlSqlTokenTypes, DisplayableNode {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( FromClause.class );
public static final int ROOT_LEVEL = 1;
private int level = ROOT_LEVEL;
private Set<FromElement> fromElements = new HashSet<FromElement>();
private Map<String,FromElement> fromElementByClassAlias = new HashMap<String,FromElement>();
private Map<String,FromElement> fromElementByTableAlias = new HashMap<String,FromElement>();
private Map<String,FromElement> fromElementsByPath = new HashMap<String,FromElement>();
private Map collectionJoinFromElementsByPath = new HashMap();
private FromClause parentFromClause;
private Set<FromClause> childFromClauses;
private int fromElementCounter;
private List impliedElements = new LinkedList();
private List<EntityJoinFromElement> entityJoinFromElements;
public FromElement addFromElement(String path, AST alias) throws SemanticException {
String classAlias = ( alias == null ) ? null : alias.getText();
checkForDuplicateClassAlias( classAlias );
FromElementFactory factory = new FromElementFactory( this, null, path, classAlias, null, false );
return factory.addFromElement();
}
void registerFromElement(FromElement element) {
fromElements.add( element );
String classAlias = element.getClassAlias();
if ( classAlias != null ) {
fromElementByClassAlias.put( classAlias, element );
}
String tableAlias = element.getTableAlias();
if ( tableAlias != null ) {
fromElementByTableAlias.put( tableAlias, element );
}
if ( element instanceof EntityJoinFromElement ) {
if ( entityJoinFromElements == null ) {
entityJoinFromElements = new ArrayList<EntityJoinFromElement>();
}
entityJoinFromElements.add( (EntityJoinFromElement) element );
}
}
public void finishInit() {
if ( entityJoinFromElements == null ) {
return;
}
for ( EntityJoinFromElement entityJoinFromElement : entityJoinFromElements ) {
ASTUtil.appendChild( this, entityJoinFromElement );
}
entityJoinFromElements.clear();
}
void addDuplicateAlias(String alias, FromElement element) {
if ( alias != null ) {
fromElementByClassAlias.put( alias, element );
}
}
private void checkForDuplicateClassAlias(String classAlias) throws SemanticException {
if ( classAlias != null && fromElementByClassAlias.containsKey( classAlias ) ) {
throw new SemanticException( "Duplicate definition of alias '" + classAlias + "'" );
}
}
public FromElement getFromElement(String aliasOrClassName) {
FromElement fromElement = fromElementByClassAlias.get( aliasOrClassName );
if ( fromElement == null && getSessionFactoryHelper().isStrictJPAQLComplianceEnabled() ) {
fromElement = findIntendedAliasedFromElementBasedOnCrazyJPARequirements( aliasOrClassName );
}
if ( fromElement == null && parentFromClause != null ) {
fromElement = parentFromClause.getFromElement( aliasOrClassName );
}
return fromElement;
}
public FromElement findFromElementBySqlAlias(String sqlAlias) {
FromElement fromElement = fromElementByTableAlias.get( sqlAlias );
if ( fromElement == null && parentFromClause != null ) {
fromElement = parentFromClause.getFromElement( sqlAlias );
}
return fromElement;
}
public FromElement findFromElementByUserOrSqlAlias(String userAlias, String sqlAlias) {
FromElement fromElement = null;
if ( userAlias != null ) {
fromElement = getFromElement( userAlias );
}
if ( fromElement == null ) {
fromElement = findFromElementBySqlAlias( sqlAlias );
}
return fromElement;
}
private FromElement findIntendedAliasedFromElementBasedOnCrazyJPARequirements(String specifiedAlias) {
for ( Map.Entry<String, FromElement> entry : fromElementByClassAlias.entrySet() ) {
final String alias = entry.getKey();
if ( alias.equalsIgnoreCase( specifiedAlias ) ) {
return entry.getValue();
}
}
return null;
}
public boolean isFromElementAlias(String possibleAlias) {
boolean isAlias = containsClassAlias( possibleAlias );
if ( !isAlias && parentFromClause != null ) {
isAlias = parentFromClause.isFromElementAlias( possibleAlias );
}
return isAlias;
}
public List getFromElements() {
return ASTUtil.collectChildren( this, fromElementPredicate );
}
public FromElement getFromElement() {
return (FromElement) getFromElements().get( 0 );
}
public List getProjectionList() {
return ASTUtil.collectChildren( this, projectionListPredicate );
}
public List getCollectionFetches() {
return ASTUtil.collectChildren( this, collectionFetchPredicate );
}
public boolean hasCollectionFecthes() {
return getCollectionFetches().size() > 0;
}
public List getExplicitFromElements() {
return ASTUtil.collectChildren( this, explicitFromPredicate );
}
private static ASTUtil.FilterPredicate fromElementPredicate = new ASTUtil.IncludePredicate() {
@Override
public boolean include(AST node) {
FromElement fromElement = (FromElement) node;
return fromElement.isFromOrJoinFragment();
}
};
private static ASTUtil.FilterPredicate projectionListPredicate = new ASTUtil.IncludePredicate() {
@Override
public boolean include(AST node) {
FromElement fromElement = (FromElement) node;
return fromElement.inProjectionList();
}
};
private static ASTUtil.FilterPredicate collectionFetchPredicate = new ASTUtil.IncludePredicate() {
@Override
public boolean include(AST node) {
FromElement fromElement = (FromElement) node;
return fromElement.isFetch() && fromElement.getQueryableCollection() != null;
}
};
private static ASTUtil.FilterPredicate explicitFromPredicate = new ASTUtil.IncludePredicate() {
@Override
public boolean include(AST node) {
final FromElement fromElement = (FromElement) node;
return !fromElement.isImplied();
}
};
FromElement findCollectionJoin(String path) {
return (FromElement) collectionJoinFromElementsByPath.get( path );
}
FromElement findJoinByPath(String path) {
FromElement elem = findJoinByPathLocal( path );
if ( elem == null && parentFromClause != null ) {
elem = parentFromClause.findJoinByPath( path );
}
return elem;
}
FromElement findJoinByPathLocal(String path) {
Map joinsByPath = fromElementsByPath;
return (FromElement) joinsByPath.get( path );
}
void addJoinByPathMap(String path, FromElement destination) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "addJoinByPathMap() : %s -> %s", path, destination.getDisplayText() );
}
fromElementsByPath.put( path, destination );
}
public boolean containsClassAlias(String alias) {
boolean isAlias = fromElementByClassAlias.containsKey( alias );
if ( !isAlias && getSessionFactoryHelper().isStrictJPAQLComplianceEnabled() ) {
isAlias = findIntendedAliasedFromElementBasedOnCrazyJPARequirements( alias ) != null;
}
return isAlias;
}
public boolean containsTableAlias(String alias) {
return fromElementByTableAlias.keySet().contains( alias );
}
public String getDisplayText() {
return "FromClause{" +
"level=" + level +
", fromElementCounter=" + fromElementCounter +
", fromElements=" + fromElements.size() +
", fromElementByClassAlias=" + fromElementByClassAlias.keySet() +
", fromElementByTableAlias=" + fromElementByTableAlias.keySet() +
", fromElementsByPath=" + fromElementsByPath.keySet() +
", collectionJoinFromElementsByPath=" + collectionJoinFromElementsByPath.keySet() +
", impliedElements=" + impliedElements +
"}";
}
public void setParentFromClause(FromClause parentFromClause) {
this.parentFromClause = parentFromClause;
if ( parentFromClause != null ) {
level = parentFromClause.getLevel() + 1;
parentFromClause.addChild( this );
}
}
private void addChild(FromClause fromClause) {
if ( childFromClauses == null ) {
childFromClauses = new HashSet<FromClause>();
}
childFromClauses.add( fromClause );
}
public FromClause locateChildFromClauseWithJoinByPath(String path) {
if ( childFromClauses != null && !childFromClauses.isEmpty() ) {
for ( FromClause child : childFromClauses ) {
if ( child.findJoinByPathLocal( path ) != null ) {
return child;
}
}
}
return null;
}
public void promoteJoin(FromElement elem) {
LOG.debugf( "Promoting [%s] to [%s]", elem, this );
}
public boolean isSubQuery() {
return parentFromClause != null;
}
void addCollectionJoinFromElementByPath(String path, FromElement destination) {
LOG.debugf( "addCollectionJoinFromElementByPath() : %s -> %s", path, destination );
collectionJoinFromElementsByPath.put(
path,
destination
);
}
public FromClause getParentFromClause() {
return parentFromClause;
}
public int getLevel() {
return level;
}
public int nextFromElementCounter() {
return fromElementCounter++;
}
public void resolve() {
ASTIterator iter = new ASTIterator( this.getFirstChild() );
Set childrenInTree = new HashSet();
while ( iter.hasNext() ) {
childrenInTree.add( iter.next() );
}
for ( FromElement fromElement : fromElements ) {
if ( !childrenInTree.contains( fromElement ) ) {
throw new IllegalStateException( "Element not in AST: " + fromElement );
}
}
}
public void addImpliedFromElement(FromElement element) {
impliedElements.add( element );
}
@Override
public String toString() {
return "FromClause{level=" + level + "}";
}
}