org.hibernate.hql.internal.ast.tree.MethodNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
The core O/RM functionality as provided by Hibernate
The newest version!
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.hql.internal.ast.tree;
import java.util.Arrays;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.hql.internal.CollectionProperties;
import org.hibernate.hql.internal.antlr.SqlTokenTypes;
import org.hibernate.hql.internal.ast.TypeDiscriminatorMetadata;
import org.hibernate.hql.internal.ast.util.ASTUtil;
import org.hibernate.hql.internal.ast.util.ColumnHelper;
import org.hibernate.internal.CoreLogging;
import org.hibernate.persister.collection.CollectionPropertyMapping;
import org.hibernate.persister.collection.CollectionPropertyNames;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
import antlr.SemanticException;
import antlr.collections.AST;
import java.util.Locale;
/**
* Represents a method call.
*
* @author josh
*/
public class MethodNode extends AbstractSelectExpression implements FunctionNode {
private static final Logger LOG = CoreLogging.logger( MethodNode.class );
private String methodName;
private FromElement fromElement;
private String[] selectColumns;
private SQLFunction function;
private boolean inSelect;
@Override
public boolean isScalar() throws SemanticException {
// Method expressions in a SELECT should always be considered scalar.
return true;
}
@Override
public SQLFunction getSQLFunction() {
return function;
}
@Override
public Type getFirstArgumentType() {
AST argument = getFirstChild();
while ( argument != null ) {
if ( argument instanceof SqlNode ) {
final Type type = ( (SqlNode) argument ).getDataType();
if ( type != null ) {
return type;
}
argument = argument.getNextSibling();
}
}
return null;
}
public void resolve(boolean inSelect) throws SemanticException {
// Get the function name node.
AST nameNode = getFirstChild();
AST exprListNode = nameNode.getNextSibling();
initializeMethodNode( nameNode, inSelect );
// If the expression list has exactly one expression, and the type of the expression is a collection
// then this might be a collection function, such as index(c) or size(c).
if ( ASTUtil.hasExactlyOneChild( exprListNode ) ) {
if ( "type".equals( methodName ) ) {
typeDiscriminator( exprListNode.getFirstChild() );
return;
}
if ( isCollectionPropertyMethod() ) {
collectionProperty( exprListNode.getFirstChild(), nameNode );
return;
}
}
dialectFunction( exprListNode );
}
public void initializeMethodNode(AST name, boolean inSelect) {
name.setType( SqlTokenTypes.METHOD_NAME );
String text = name.getText();
// Use the lower case function name.
methodName = text.toLowerCase(Locale.ROOT);
// Remember whether we're in a SELECT clause or not.
this.inSelect = inSelect;
}
private void typeDiscriminator(AST path) throws SemanticException {
if ( path == null ) {
throw new SemanticException( "type() discriminator reference has no path!" );
}
FromReferenceNode pathAsFromReferenceNode = (FromReferenceNode) path;
FromElement fromElement = pathAsFromReferenceNode.getFromElement();
TypeDiscriminatorMetadata typeDiscriminatorMetadata = fromElement.getTypeDiscriminatorMetadata();
setDataType( typeDiscriminatorMetadata.getResolutionType() );
setText( typeDiscriminatorMetadata.getSqlFragment() );
setType( SqlTokenTypes.SQL_TOKEN );
}
private void dialectFunction(AST exprList) {
function = getSessionFactoryHelper().findSQLFunction( methodName );
if ( function != null ) {
AST firstChild = exprList != null ? exprList.getFirstChild() : null;
Type functionReturnType = getSessionFactoryHelper()
.findFunctionReturnType( methodName, function, firstChild );
setDataType( functionReturnType );
}
}
public boolean isCollectionPropertyMethod() {
return CollectionProperties.isAnyCollectionProperty( methodName );
}
private void collectionProperty(AST path, AST name) throws SemanticException {
if ( path == null ) {
throw new SemanticException( "Collection function " + name.getText() + " has no path!" );
}
SqlNode expr = (SqlNode) path;
Type type = expr.getDataType();
LOG.debugf( "collectionProperty() : name=%s type=%s", name, type );
resolveCollectionProperty( expr );
}
protected void resolveCollectionProperty(AST expr) throws SemanticException {
String propertyName = CollectionProperties.getNormalizedPropertyName( methodName );
if ( expr instanceof FromReferenceNode ) {
FromReferenceNode collectionNode = (FromReferenceNode) expr;
// If this is 'elements' then create a new FROM element.
if ( CollectionPropertyNames.COLLECTION_ELEMENTS.equals( propertyName ) ) {
handleElements( collectionNode, propertyName );
}
else {
// Not elements(x)
fromElement = collectionNode.getFromElement();
final CollectionPropertyReference cpr = fromElement.getCollectionPropertyReference( propertyName );
setDataType( cpr.getType() );
selectColumns = cpr.toColumns( fromElement.getTableAlias() );
// setDataType( fromElement.getPropertyType( propertyName, propertyName ) );
selectColumns = fromElement.toColumns( fromElement.getTableAlias(), propertyName, inSelect );
}
if ( collectionNode instanceof DotNode ) {
prepareAnyImplicitJoins( (DotNode) collectionNode );
}
if ( !inSelect ) {
fromElement.setText( "" );
fromElement.setUseWhereFragment( false );
}
prepareSelectColumns( selectColumns );
setText( selectColumns[0] );
setType( SqlTokenTypes.SQL_TOKEN );
}
else {
throw new SemanticException(
"Unexpected expression " + expr +
" found for collection function " + propertyName
);
}
}
private void prepareAnyImplicitJoins(DotNode dotNode) throws SemanticException {
if ( dotNode.getLhs() instanceof DotNode ) {
DotNode lhs = (DotNode) dotNode.getLhs();
FromElement lhsOrigin = lhs.getFromElement();
if ( lhsOrigin != null && "".equals( lhsOrigin.getText() ) ) {
String lhsOriginText = lhsOrigin.getQueryable().getTableName() +
" " + lhsOrigin.getTableAlias();
lhsOrigin.setText( lhsOriginText );
}
prepareAnyImplicitJoins( lhs );
}
}
private void handleElements(FromReferenceNode collectionNode, String propertyName) {
FromElement collectionFromElement = collectionNode.getFromElement();
QueryableCollection queryableCollection = collectionFromElement.getQueryableCollection();
String path = collectionNode.getPath() + "[]." + propertyName;
LOG.debugf( "Creating elements for %s", path );
fromElement = collectionFromElement;
if ( !collectionFromElement.isCollectionOfValuesOrComponents() ) {
getWalker().addQuerySpaces( queryableCollection.getElementPersister().getQuerySpaces() );
}
setDataType( queryableCollection.getElementType() );
selectColumns = collectionFromElement.toColumns( fromElement.getTableAlias(), propertyName, inSelect );
}
@Override
public void setScalarColumnText(int i) throws SemanticException {
if ( selectColumns == null ) { // Dialect function
ColumnHelper.generateSingleScalarColumn( this, i );
}
else { // Collection 'property function'
ColumnHelper.generateScalarColumns( this, selectColumns, i );
}
}
protected void prepareSelectColumns(String[] columns) {
}
@Override
public FromElement getFromElement() {
return fromElement;
}
public String getDisplayText() {
return "{" +
"method=" + methodName +
",selectColumns=" + ( selectColumns == null ?
null : Arrays.asList( selectColumns ) ) +
",fromElement=" + fromElement.getTableAlias() +
"}";
}
}