package org.hibernate.hql.internal.classic;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.hibernate.QueryException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.hql.internal.QuerySplitter;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
public class SelectParser implements Parser {
private static final Set<String> COUNT_MODIFIERS = new HashSet<String>();
static {
COUNT_MODIFIERS.add( "distinct" );
COUNT_MODIFIERS.add( "all" );
COUNT_MODIFIERS.add( "*" );
}
private LinkedList<String> aggregateFuncTokenList = new LinkedList<String>();
private boolean ready;
private boolean aggregate;
private boolean first;
private boolean afterNew;
private boolean insideNew;
private boolean aggregateAddSelectScalar;
private Class holderClass;
private final SelectPathExpressionParser pathExpressionParser;
private final PathExpressionParser aggregatePathExpressionParser;
{
pathExpressionParser = new SelectPathExpressionParser();
aggregatePathExpressionParser = new PathExpressionParser();
pathExpressionParser.setUseThetaStyleJoin( true );
aggregatePathExpressionParser.setUseThetaStyleJoin( true );
}
public void token(String token, QueryTranslatorImpl q) throws QueryException {
String lctoken = token.toLowerCase(Locale.ROOT);
if ( first ) {
first = false;
if ( "distinct".equals( lctoken ) ) {
q.setDistinct( true );
return;
}
else if ( "all".equals( lctoken ) ) {
q.setDistinct( false );
return;
}
}
if ( afterNew ) {
afterNew = false;
try {
holderClass = q.getFactory().getServiceRegistry()
.getService( ClassLoaderService.class )
.classForName( QuerySplitter.getImportedClass( token, q.getFactory() ) );
}
catch ( ClassLoadingException e ) {
throw new QueryException( e );
}
if ( holderClass == null ) {
throw new QueryException( "class not found: " + token );
}
q.setHolderClass( holderClass );
insideNew = true;
}
else if ( token.equals( "," ) ) {
if ( !aggregate && ready ) {
throw new QueryException( "alias or expression expected in SELECT" );
}
q.appendScalarSelectToken( ", " );
ready = true;
}
else if ( "new".equals( lctoken ) ) {
afterNew = true;
ready = false;
}
else if ( "(".equals( token ) ) {
if ( insideNew && !aggregate && !ready ) {
ready = true;
}
else if ( aggregate ) {
q.appendScalarSelectToken( token );
}
else {
throw new QueryException( "aggregate function expected before ( in SELECT" );
}
ready = true;
}
else if ( ")".equals( token ) ) {
if ( insideNew && !aggregate && !ready ) {
insideNew = false;
}
else if ( aggregate && ready ) {
q.appendScalarSelectToken( token );
aggregateFuncTokenList.removeLast();
if ( aggregateFuncTokenList.size() < 1 ) {
aggregate = false;
ready = false;
}
}
else {
throw new QueryException( "( expected before ) in select" );
}
}
else if ( COUNT_MODIFIERS.contains( lctoken ) ) {
if ( !ready || !aggregate ) {
throw new QueryException( token + " only allowed inside aggregate function in SELECT" );
}
q.appendScalarSelectToken( token );
if ( "*".equals( token ) ) {
q.addSelectScalar( getFunction( "count", q ).getReturnType( StandardBasicTypes.LONG, q.getFactory() ) );
}
}
else if ( getFunction( lctoken, q ) != null && token.equals( q.unalias( token ) ) ) {
if ( !ready ) {
throw new QueryException( ", expected before aggregate function in SELECT: " + token );
}
aggregate = true;
aggregateAddSelectScalar = true;
aggregateFuncTokenList.add( lctoken );
ready = false;
q.appendScalarSelectToken( token );
if ( !aggregateHasArgs( lctoken, q ) ) {
q.addSelectScalar( aggregateType( aggregateFuncTokenList, null, q ) );
if ( !aggregateFuncNoArgsHasParenthesis( lctoken, q ) ) {
aggregateFuncTokenList.removeLast();
if ( aggregateFuncTokenList.size() < 1 ) {
aggregate = false;
ready = false;
}
else {
ready = true;
}
}
}
}
else if ( aggregate ) {
boolean constantToken = false;
if ( !ready ) {
throw new QueryException( "( expected after aggregate function in SELECT" );
}
try {
ParserHelper.parse( aggregatePathExpressionParser, q.unalias( token ), ParserHelper.PATH_SEPARATORS, q );
}
catch ( QueryException qex ) {
constantToken = true;
}
if ( constantToken ) {
q.appendScalarSelectToken( token );
}
else {
if ( aggregatePathExpressionParser.isCollectionValued() ) {
q.addCollection( aggregatePathExpressionParser.getCollectionName(),
aggregatePathExpressionParser.getCollectionRole() );
}
q.appendScalarSelectToken( aggregatePathExpressionParser.getWhereColumn() );
if ( aggregateAddSelectScalar ) {
q.addSelectScalar( aggregateType( aggregateFuncTokenList, aggregatePathExpressionParser.getWhereColumnType(), q ) );
aggregateAddSelectScalar = false;
}
aggregatePathExpressionParser.addAssociation( q );
}
}
else {
if ( !ready ) {
throw new QueryException( ", expected in SELECT" );
}
ParserHelper.parse( pathExpressionParser, q.unalias( token ), ParserHelper.PATH_SEPARATORS, q );
if ( pathExpressionParser.isCollectionValued() ) {
q.addCollection( pathExpressionParser.getCollectionName(),
pathExpressionParser.getCollectionRole() );
}
else if ( pathExpressionParser.getWhereColumnType().isEntityType() ) {
q.addSelectClass( pathExpressionParser.getSelectName() );
}
q.appendScalarSelectTokens( pathExpressionParser.getWhereColumns() );
q.addSelectScalar( pathExpressionParser.getWhereColumnType() );
pathExpressionParser.addAssociation( q );
ready = false;
}
}
public boolean aggregateHasArgs(String funcToken, QueryTranslatorImpl q) {
return getFunction( funcToken, q ).hasArguments();
}
public boolean aggregateFuncNoArgsHasParenthesis(String funcToken, QueryTranslatorImpl q) {
return getFunction( funcToken, q ).hasParenthesesIfNoArguments();
}
public Type aggregateType(List funcTokenList, Type type, QueryTranslatorImpl q) throws QueryException {
Type retType = type;
Type argType;
for ( int i = funcTokenList.size() - 1; i >= 0; i-- ) {
argType = retType;
String funcToken = ( String ) funcTokenList.get( i );
retType = getFunction( funcToken, q ).getReturnType( argType, q.getFactory() );
}
return retType;
}
private SQLFunction getFunction(String name, QueryTranslatorImpl q) {
return q.getFactory().getSqlFunctionRegistry().findSQLFunction( name );
}
public void start(QueryTranslatorImpl q) {
ready = true;
first = true;
aggregate = false;
afterNew = false;
insideNew = false;
holderClass = null;
aggregateFuncTokenList.clear();
}
public void end(QueryTranslatorImpl q) {
}
}