[hibernate-commits] Hibernate SVN: r16437 - in core/branches/antlr3/src: main/antlr3/org/hibernate/sql/ast/phase/hql/parse and 4 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Fri Apr 24 17:05:09 EDT 2009


Author: steve.ebersole at jboss.com
Date: 2009-04-24 17:05:09 -0400 (Fri, 24 Apr 2009)
New Revision: 16437

Added:
   core/branches/antlr3/src/test/java/org/hibernate/sql/ast/ordering/
   core/branches/antlr3/src/test/java/org/hibernate/sql/ast/ordering/OrderByParserTest.java
Modified:
   core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/ordering/OrderByParser.g
   core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/phase/hql/parse/HQLLexer.g
   core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateToken.java
   core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateTree.java
   core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentParser.java
   core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentTranslator.java
Log:
first phase of order-by mapping fragment processing using the new unified sql rendering

Modified: core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/ordering/OrderByParser.g
===================================================================
--- core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/ordering/OrderByParser.g	2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/ordering/OrderByParser.g	2009-04-24 21:05:09 UTC (rev 16437)
@@ -1,3 +1,12 @@
+parser grammar OrderByParser;
+
+options {
+	tokenVocab=HQLLexer;
+	output=AST;
+	ASTLabelType=CommonTree;
+}
+
+ at parser::header {
 /*
  * Hibernate, Relational Persistence for Idiomatic Java
  *
@@ -21,148 +30,65 @@
  * 51 Franklin Street, Fifth Floor
  * Boston, MA  02110-1301  USA
  *
+ * Portions of SQL grammar parsing copyright (C) 2003 by Lubos Vnuk.  All rights
+ * reserved.  These portions are distributed under license by Red Hat Middleware
+ * LLC and are covered by the above LGPL notice.  If you redistribute this material,
+ * with or without modification, you must preserve this copyright notice in its
+ * entirety.
  */
-grammar OrderByParser;
+package org.hibernate.sql.ast.ordering;
 
-options {
-	language = Java;
-	output = AST;
-	ASTLabelType = CommonTree;
+import org.hibernate.sql.Template;
 }
 
-tokens {
-    COLLATE;
-	ASCENDING;
-	DESCENDING;
-
-    ORDER_BY;
-    SORT_SPEC;
-    ORDER_SPEC;
-    SORT_KEY;
-    EXPR_LIST;
-    IDENT_LIST;
-    COLUMN_REF;
-
-	NUM_INTEGER_LITERAL;
-    NUM_LONG_LITERAL;
-    NUM_DOUBLE_LITERAL;
-    NUM_FLOAT_LITERAL;
-}
-
- at lexer::header {
-	package org.hibernate.sql.ast.ordering;
-}
-
- at lexer::members {
-}
-
- at parser::header {
-	package org.hibernate.sql.ast.ordering;
-
-	import org.hibernate.sql.Template;
-	import org.hibernate.dialect.function.SQLFunction;
-}
-
 @parser::members {
     /**
-     * Process the given node as a quote identifier.  These need to be quoted in the dialect-specific way.
+     * A check to see if the text represents a known function name (in addition to the
+     * set of known {@link #standardFunction "standard"} functions.  This is only needed in the
+     * case of seeing a dot-ident structure which is not followed by a paren; such structures
+     * which are followed by a paren are explicitly assumed to be a function name.
      *
-     * @param ident The quoted-identifier node.
+     * @param text The text to check as a  function name.
      *
-     * @return The processed node.
-     */
-    protected CommonTree quotedIdentifier(CommonTree ident) {
-        // here we assume single-quote as the identifier quote character...
-        return createTreeNode( IDENTIFIER, Template.TEMPLATE + ".'" + ident.getText() + "'" );
-    }
-
-
-    /**
-     * Process the given node as a quote string.
+     * @return True if the text is a known function name, false otherwise.
      *
-     * @param token The quoted string.  This is used from within function param recognition, and represents a
-     * SQL-quoted string.
-     *
-     * @return The processed node.
+     * @see #standardFunction
      */
-    protected CommonTree quotedString(Token token) {
-    	return createTreeNode( STRING_LITERAL, "'" + token.getText() + "'" );
+    protected boolean isFunctionName(String text) {
+        // by default, assume it is not
+    	return false;
     }
 
     /**
-     * A check to see if the text of the given node represents a known function name.
+     * A check to see if the text represents a mapped property name.
      *
-     * @param token The node whose text we want to check.
+     * @param text The text to check as a property name.
      *
-     * @return True if the node's text is a known function name, false otherwise.
-     *
-     * @see org.hibernate.dialect.function.SQLFunctionRegistry
+     * @return True if the text is a mapped property name, false otherwise.
      */
-    protected boolean isFunctionName(Token token) {
+    protected boolean isPropertyName(String text) {
+        // by default, assume it is not
     	return false;
     }
 
     /**
-     * Process the given node as a function name.  Differs from {@link #resolveFunction(org.antlr.runtime.tree.CommonTree)
-     * specifically in that here we are expecting just a function name without parens or arguments.
+     * Given a property, resolve it's {@link #COLUMN} or {@link #VECTOR_EXPR} tree.
      *
-     * @param token The token representing the function name.
+     * @param propertyTree The tree representing the property name.
      *
-     * @return The processed node.
+     * @return The column(s) tree.
      */
-    protected CommonTree resolveFunction(Token token) {
-        return resolveFunction( new CommonTree( token ) );
+    protected CommonTree buildPropertyColumns(CommonTree propertyTree) {
+        throw new UnsupportedOperationException( "must be overridden!" );
     }
 
-    /**
-     * Process the given node as a function.
-     *
-     * @param tree The node representing the function invocation (including parameters as subtree components).
-     *
-     * @return The processed node.
-     */
-    protected CommonTree resolveFunction(CommonTree tree) {
-		Tree argumentList = tree.getChild( 0 );
-		assert argumentList == null || "{param list}".equals( argumentList.getText() );
-
-        String text = tree.getText();
-        int count = argumentList == null ? 0 : argumentList.getChildCount();
-        if ( count > 0 ) {
-            text += '(';
-            for ( int i = 0; i < count; i++ ) {
-                Tree argument = argumentList.getChild( i );
-                text += argument.getText();
-                if ( i < count ) {
-                    text += ", ";
-                }
-            }
-            text += ')';
-        }
-        return createTreeNode( IDENTIFIER, text );
-    }
-
-    protected CommonTree resolveIdent(Token token) {
-        return resolveIdent( new CommonTree( token ) );
-    }
-
-    /**
-     * Process the given node as an IDENTIFIER.  May represent either a column reference or a property reference.
-     *
-     * @param ident The node whose text represents either a column or property reference.
-     *
-     * @return The processed node.
-     */
-    protected CommonTree resolveIdent(CommonTree ident) {
-        return createTreeNode( IDENTIFIER, Template.TEMPLATE + "." + ident.getText() );
-    }
-
-	private boolean validateIdentifierAsKeyword(String text) {
-		return validateLT( 1, text );
+	private boolean validateSoftKeyword(String text) {
+		return validateLT(1, text);
 	}
 
 	private boolean validateLT(int offset, String text) {
 		String text2Validate = retrieveLT( offset );
-		return text2Validate == null ? false : text2Validate.equalsIgnoreCase(text);
+		return text2Validate == null ? false : text2Validate.equalsIgnoreCase( text );
 	}
 
 	private String retrieveLT(int offset) {
@@ -172,10 +98,6 @@
 		Token token = input.LT(offset);
 		return token == null ? null : token.getText();
 	}
-
-	protected CommonTree createTreeNode(int type, String text) {
-		return new CommonTree( new CommonToken( type, text ) );
-	}
 }
 
 
@@ -185,17 +107,19 @@
 /**
  * Main recognition rule for this grammar
  */
-orderByFragment
-	: sortSpecification ( ',' sortSpecification )* -> ^( ORDER_BY sortSpecification+ )
-	;
+orderByFragment :
+    sortSpecification ( COMMA sortSpecification )*
+        -> ^( ORDER_BY sortSpecification+ )
+;
 
 
 /**
- * Reconition rule for what ANSI SQL terms the <tt>sort specification</tt>, which is essentially each thing upon which
- * the results should be sorted.
+ * Reconition rule for what ANSI SQL terms the <tt>sort specification</tt>.  These are the atomic elements of the
+ * <tt>ORDER BY</tt> list pieces.
  */
-sortSpecification
-	: sortKey collationSpecification? orderingSpecification? -> ^( SORT_SPEC sortKey collationSpecification? orderingSpecification? )
+sortSpecification :
+    sortKey collationSpecification? orderingSpecification?
+        -> ^( SORT_SPEC sortKey collationSpecification? orderingSpecification? )
 ;
 
 
@@ -203,213 +127,441 @@
  * Reconition rule for what ANSI SQL terms the <tt>sort key</tt> which is the expression (column, function, etc) upon
  * which to base the sorting.
  */
-sortKey
-	: expression -> ^( SORT_KEY expression )
+sortKey :
+    expression
 ;
 
 /**
  * Reconition rule what this grammar recognizes as valid <tt>sort key</tt>.
  */
 expression
-	: hardQuoteExpression
-	| ( IDENTIFIER ('.' IDENTIFIER)* OPEN_PAREN ) => functionCall
-    | simplePropertyPath
-    | IDENTIFIER 	-> {isFunctionName($IDENTIFIER)}? 	{ resolveFunction( $IDENTIFIER ) }
-    			-> 							{ resolveIdent( $IDENTIFIER ) }
+    : QUOTED_IDENTIFIER -> ^( COLUMN[$QUOTED_IDENTIFIER] ALIAS_REF[Template.TEMPLATE] QUOTED_IDENTIFIER[$QUOTED_IDENTIFIER] )
+    // we treat the so-called standard functions differently because they are handled differently by the HQL lexer which we also use here...
+    | standardFunction
+    // not identDotIdentStructure because we dont want QUOTED_IDENTIFIERs is here
+    | ( IDENTIFIER ( DOT IDENTIFIER )* LEFT_PAREN ) => generalFunction
+    // otherwise we fully expect a dot-identifier series, and then we just need to decode the semantic of that structure
+    | identDotIdentStructure
+        -> { ( isFunctionName($identDotIdentStructure.text) ) }?
+              // we have a function with parens (thus no args)
+              ^( GENERAL_FUNCTION_CALL[$identDotIdentStructure.start,$identDotIdentStructure.text] )
+        -> { ( isPropertyName($identDotIdentStructure.text) ) }?
+              // we have a reference to a mapped property
+              { buildPropertyColumns( $identDotIdentStructure.tree ) }
+        -> { ( $identDotIdentStructure.tree.getType() == DOT ) }?
+              // we have a reference to a column which is already qualified
+              identDotIdentStructure
+        ->
+              // we have a reference to a column which is not qualified
+              ^( COLUMN ALIAS_REF[Template.TEMPLATE] IDENTIFIER[$identDotIdentStructure.start,$identDotIdentStructure.text] )
+    ;
+
+fragment
+identifier
+    : IDENTIFIER
+    | QUOTED_IDENTIFIER
+    ;
+
+fragment
+identDotIdentStructure
+    : IDENTIFIER ( DOT^ identifier )*
+    ;
+
+standardFunction
+	:	castFunction
+	|	concatFunction
+	|	substringFunction
+	|	trimFunction
+	|	upperFunction
+	|	lowerFunction
+	|	lengthFunction
+	|	locateFunction
+	|	absFunction
+	|	sqrtFunction
+	|	modFunction
+	|	currentDateFunction
+	|	currentTimeFunction
+	|	currentTimestampFunction
+	|	extractFunction
+	|	positionFunction
+	|	charLengthFunction
+	|	octetLengthFunction
+	|	bitLengthFunction
 	;
 
-hardQuoteExpression
- at after { $tree = quotedIdentifier( $tree ); }
-	: HARD_QUOTE IDENTIFIER HARD_QUOTE -> IDENTIFIER
+castFunction
+	:	cast_keyword^ LEFT_PAREN! expression as_keyword! dataType RIGHT_PAREN!
 	;
 
-/**
- * Recognition rule for a function call
- */
-functionCall
- at after { $tree = resolveFunction( $tree ); }
-	: functionName OPEN_PAREN functionParameterList CLOSE_PAREN -> ^( functionName functionParameterList )
+fragment
+dataType
+	:	IDENTIFIER
 	;
 
-/**
- * A function-name is an IDENTIFIER followed by zero or more (DOT IDENTIFIER) sequences
- */
-functionName returns [String nameText]
-	: i=IDENTIFIER { $nameText = $i.text; } ( '.' i=IDENTIFIER { $nameText += ( '.' + $i.text ); } )+
+concatFunction
+	:	concat_keyword^ LEFT_PAREN! expression ( COMMA! expression )+ RIGHT_PAREN!
 	;
 
+substringFunction
+	:	substring_keyword^ LEFT_PAREN! expression COMMA! expression ( COMMA! expression)? RIGHT_PAREN!
+	;
+
+trimFunction
+	:	trim_keyword LEFT_PAREN trimOperands RIGHT_PAREN
+		-> ^(trim_keyword trimOperands)
+	;
+
+fragment
+trimOperands
+options{
+k=2;
+}
+ at init {boolean hasSecondExpression = false;}
+	:	trimSpecification from_keyword expression -> ^(trimSpecification STRING_LITERAL[" "] expression)
+	|	trimSpecification expression from_keyword expression -> ^(trimSpecification expression+)
+	|	from_keyword expression -> ^(BOTH STRING_LITERAL[" "] expression)
+	|	cn=expression ( from_keyword expression {hasSecondExpression = true;} )?
+		-> {hasSecondExpression}? ^(BOTH expression+)
+		-> ^(BOTH STRING_LITERAL[" "] $cn)
+	;
+
+fragment
+trimSpecification
+	:	leading_keyword
+	|	trailing_keyword
+	|	both_keyword
+	;
+
+upperFunction
+	:	upper_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+	;
+
+lowerFunction
+	:	lower_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+	;
+
+lengthFunction
+	:	length_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+	;
+
+locateFunction
+	:	locate_keyword^ LEFT_PAREN! expression COMMA! expression ( COMMA! expression )? RIGHT_PAREN!
+	;
+
+absFunction
+	:	abs_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+	;
+
+sqrtFunction
+	:	sqrt_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+	;
+
+modFunction
+	:	mod_keyword^ LEFT_PAREN! expression COMMA! expression RIGHT_PAREN!
+	;
+
+currentDateFunction
+	:	current_date_keyword ( LEFT_PAREN! RIGHT_PAREN! )?
+	;
+
+currentTimeFunction
+	:	current_time_keyword ( LEFT_PAREN! RIGHT_PAREN! )?
+	;
+
+currentTimestampFunction
+	:	current_timestamp_keyword ( LEFT_PAREN! RIGHT_PAREN! )?
+	;
+
+extractFunction
+	:	extract_keyword^ LEFT_PAREN! extractField from_keyword! expression RIGHT_PAREN!
+	;
+
+extractField
+	:	datetimeField
+	|	timeZoneField
+	;
+
+datetimeField
+	:	nonSecondDatetimeField
+	|	second_keyword
+	;
+
+nonSecondDatetimeField
+	:	year_keyword
+	|	month_keyword
+	|	day_keyword
+	|	hour_keyword
+	|	minute_keyword
+	;
+
+timeZoneField
+	:	timezone_hour_keyword
+	|	timezone_minute_keyword
+	;
+
+positionFunction
+	:	position_keyword^ LEFT_PAREN! expression in_keyword! expression RIGHT_PAREN!
+	;
+
+charLengthFunction
+	:	character_length_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+	;
+
+octetLengthFunction
+	:	octet_length_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+	;
+
+bitLengthFunction
+	:	bit_length_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+	;
+
+
+generalFunction
+    : generalFunctionName LEFT_PAREN functionArgumentList RIGHT_PAREN
+          -> ^( GENERAL_FUNCTION_CALL[$generalFunctionName.start,$generalFunctionName.text] functionArgumentList )
+    ;
+
+generalFunctionName :
+    IDENTIFIER ( DOT IDENTIFIER )+
+;
+
 /**
- * Recognition rule used to "wrap" all function parameters into an EXPR_LIST node
+ * Recognition rule used to "wrap" all function arguments into a GENERAL_FUNCTION_ARGUMENTS node
  */
-functionParameterList
-	: functionParameter ( COMMA functionParameter )* -> ^( EXPR_LIST functionParameter+ )
-	;
+functionArgumentList :
+    functionArgument ( COMMA functionArgument )*
+        -> ^( GENERAL_FUNCTION_ARGUMENTS functionArgument+ )
+;
 
 
 /**
  * Recognized function parameters.
  */
-functionParameter :
+functionArgument :
     expression
-    | numericLiteral
-    | qs=STRING_LITERAL -> { quotedString( $qs ) }
+    | literal
 ;
 
-numericLiteral
-	: HEX_LITERAL
-	| OCTAL_LITERAL
-	| DECIMAL_LITERAL
-	| FLOATING_POINT_LITERAL
+literal
+	:	numeric_literal
+	|	HEX_LITERAL
+	|	OCTAL_LITERAL
+	|	CHARACTER_LITERAL
+	|	STRING_LITERAL
 	;
 
+numeric_literal
+	:	INTEGER_LITERAL
+	|	DECIMAL_LITERAL
+	|	FLOATING_POINT_LITERAL
+	;
 
 /**
  * Reconition rule for what ANSI SQL terms the <tt>collation specification</tt> used to allow specifying that sorting for
  * the given {@link #sortSpecification} be treated within a specific character-set.
  */
-collationSpecification! :
-    collateKeyword collationName -> { createTreeNode(COLLATE, $collationName.text) }
+collationSpecification!
+    : collateKeyword collationName
+        -> COLLATE[$collateKeyword.start,$collationName.text]
 ;
 
-collateKeyword
-	: {(validateIdentifierAsKeyword("collate"))}?=>  id=IDENTIFIER
+collateKeyword :
+    {(validateSoftKeyword("collate"))}?=>  id=IDENTIFIER
 		->	COLLATE[$id]
+;
 
-	;
-
 /**
  * The collation name wrt {@link #collationSpecification}.  Namely, the character-set.
  */
-collationName
-	: IDENTIFIER
-	;
+collationName :
+    IDENTIFIER
+;
 
 /**
  * Reconition rule for what ANSI SQL terms the <tt>ordering specification</tt>; <tt>ASCENDING</tt> or
  * <tt>DESCENDING</tt>.
  */
-orderingSpecification!
-	: ( 'asc' | 'ascending' ) -> { createTreeNode(ORDER_SPEC,"asc") }
-    | ( 'desc' | 'descending') -> { createTreeNode(ORDER_SPEC,"desc" ) }
+orderingSpecification! :
+    ascending_keyword
+        -> ORDER_SPEC[$ascending_keyword.start,"asc"]
+    | descending_keyword
+        -> ORDER_SPEC[$descending_keyword.start,"desc"]
+;
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Soft-keyword handling rules
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+abs_keyword
+	:	{(validateSoftKeyword("abs"))}?=>  id=IDENTIFIER
+		->	ABS[$id]
 	;
 
-/**
- * A simple-property-path is an IDENTIFIER followed by one or more (DOT IDENTIFIER) sequences
- */
-simplePropertyPath
- at after { $tree = resolveIdent($tree); }
-	: p=simplePropertyPathText -> { createTreeNode(IDENTIFIER, $p.pathText) }
+as_keyword
+	:	{(validateSoftKeyword("as"))}?=>  id=IDENTIFIER
+		->	AS[$id]
 	;
 
-simplePropertyPathText returns [String pathText]
-	: i=IDENTIFIER { $pathText = $i.text; } ( '.' i=IDENTIFIER { $pathText += ( '.' + $i.text ); } )+
+ascending_keyword :
+    {(validateSoftKeyword("ascending") || validateSoftKeyword("asc"))}?=>  IDENTIFIER
+;
+
+bit_length_keyword
+	:	{(validateSoftKeyword("bit_length"))}?=>  id=IDENTIFIER
+		->	BIT_LENGTH[$id]
 	;
 
+both_keyword
+	:	{(validateSoftKeyword("both"))}?=>  id=IDENTIFIER
+		->	BOTH[$id]
+	;
 
+cast_keyword
+	:	{(validateSoftKeyword("cast"))}?=>  id=IDENTIFIER
+		->	CAST[$id]
+	;
 
-// Lexer rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+character_length_keyword
+	:	{(validateSoftKeyword("character_length") || validateSoftKeyword("char_length"))}?=>  id=IDENTIFIER
+		->	CHARACTER_LENGTH[$id]
+	;
 
-WS
-    : (SPACE | EOL | '\u000C') { $channel=HIDDEN; }
-    ;
+concat_keyword
+	:	{(validateSoftKeyword("concat"))}?=>  id=IDENTIFIER
+		->	CONCAT[$id]
+	;
 
-fragment
-EOL
-    : ( '\r' (options{greedy=true;}: '\n')? | '\n' )
-    ;
+current_date_keyword
+	:	{(validateSoftKeyword("current_date"))}?=>  id=IDENTIFIER
+		->	CURRENT_DATE[$id]
+	;
 
-fragment
-SPACE
-    : ' '
-    | '\t'
-    ;
+current_time_keyword
+	:	{(validateSoftKeyword("current_time"))}?=>  id=IDENTIFIER
+		->	CURRENT_TIME[$id]
+	;
 
-OPEN_PAREN
-    : '('
-    ;
-CLOSE_PAREN
-    : ')'
-    ;
+current_timestamp_keyword
+	:	{(validateSoftKeyword("current_timestamp"))}?=>  id=IDENTIFIER
+		->	CURRENT_TIMESTAMP[$id]
+	;
 
-COMMA
-    : ','
-    ;
+day_keyword
+	:	{(validateSoftKeyword("day"))}?=>  id=IDENTIFIER
+		->	DAY[$id]
+	;
 
-HARD_QUOTE
-    : '`'
-    ;
+descending_keyword :
+    {(validateSoftKeyword("descending") || validateSoftKeyword("desc"))}?=>  IDENTIFIER
+;
 
-INTEGER_LITERAL
-    : (
-        '0'
-        | '1'..'9' ('0'..'9')*
-    )
-    ;
+extract_keyword
+	:	{(validateSoftKeyword("extract"))}?=>  id=IDENTIFIER
+		->	EXTRACT[$id]
+	;
 
-DECIMAL_LITERAL : ('0' | '1'..'9' '0'..'9'*) INTEGER_TYPE_SUFFIX ;
+from_keyword
+	:	{(validateSoftKeyword("from"))}?=>  id=IDENTIFIER
+		->	FROM[$id]
+	;
 
-HEX_LITERAL
-    : '0' ('x'|'X') HEX_DIGIT+ INTEGER_TYPE_SUFFIX?
-    ;
+hour_keyword
+	:	{(validateSoftKeyword("hour"))}?=>  id=IDENTIFIER
+		->	HOUR[$id]
+	;
 
-OCTAL_LITERAL : '0' ('0'..'7')+ INTEGER_TYPE_SUFFIX? ;
+in_keyword
+	:	{(validateSoftKeyword("in"))}?=>  id=IDENTIFIER
+		->	IN[$id]
+	;
+leading_keyword
+	:	{(validateSoftKeyword("leading"))}?=>  id=IDENTIFIER
+		->	LEADING[$id]
+	;
 
+length_keyword
+	:	{(validateSoftKeyword("length"))}?=>  id=IDENTIFIER
+		->	LENGTH[$id]
+	;
 
-fragment
-HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
+locate_keyword
+	:	{(validateSoftKeyword("locate"))}?=>  id=IDENTIFIER
+		->	LOCATE[$id]
+	;
 
-fragment
-INTEGER_TYPE_SUFFIX : ('l'|'L') ;
+lower_keyword
+	:	{(validateSoftKeyword("lower"))}?=>  id=IDENTIFIER
+		->	LOWER[$id]
+	;
 
-FLOATING_POINT_LITERAL
-    :   ('0'..'9')+ '.' ('0'..'9')* EXPONENT? FLOAT_TYPE_SUFFIX?
-    |   '.' ('0'..'9')+ EXPONENT? FLOAT_TYPE_SUFFIX?
-    |   ('0'..'9')+ EXPONENT FLOAT_TYPE_SUFFIX?
-    |   ('0'..'9')+ FLOAT_TYPE_SUFFIX
-    ;
+minute_keyword
+	:	{(validateSoftKeyword("minute"))}?=>  id=IDENTIFIER
+		->	MINUTE[$id]
+	;
 
-fragment
-EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
+mod_keyword
+	:	{(validateSoftKeyword("mod"))}?=>  id=IDENTIFIER
+		->	MOD[$id]
+	;
 
-fragment
-FLOAT_TYPE_SUFFIX : ('f'|'F'|'d'|'D') ;
+month_keyword
+	:	{(validateSoftKeyword("month"))}?=>  id=IDENTIFIER
+		->	MONTH[$id]
+	;
 
+octet_length_keyword
+	:	{(validateSoftKeyword("octet_length"))}?=>  id=IDENTIFIER
+		->	OCTET_LENGTH[$id]
+	;
 
-STRING_LITERAL
-    :   '\'' ( ESCAPE_SEQUENCE | ~('\''|'\\') ) '\''
-    ;
+position_keyword
+	:	{(validateSoftKeyword("position"))}?=>  id=IDENTIFIER
+		->	POSITION[$id]
+	;
 
-fragment
-ESCAPE_SEQUENCE
-    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
-    |   UNICODE_ESCAPE
-    |   OCTAL_ESCAPE
-    ;
+second_keyword
+	:	{(validateSoftKeyword("second"))}?=>  id=IDENTIFIER
+		->	SECOND[$id]
+	;
 
-fragment
-OCTAL_ESCAPE
-    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
-    |   '\\' ('0'..'7') ('0'..'7')
-    |   '\\' ('0'..'7')
-    ;
+sqrt_keyword
+	:	{(validateSoftKeyword("sqrt"))}?=>  id=IDENTIFIER
+		->	SQRT[$id]
+	;
 
-fragment
-UNICODE_ESCAPE
-    :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
-    ;
+substring_keyword
+	:	{(validateSoftKeyword("substring"))}?=>  id=IDENTIFIER
+		->	SUBSTRING[$id]
+	;
 
-IDENTIFIER
-	: IDENTIFIER_START_FRAGMENT (IDENTIFER_FRAGMENT)*
+timezone_hour_keyword
+	:	{(validateSoftKeyword("timezone_hour"))}?=>  id=IDENTIFIER
+		->	TIMEZONE_HOUR[$id]
 	;
 
-fragment
-IDENTIFIER_START_FRAGMENT
-    : ('a'..'z'|'A'..'Z'|'_'|'$'|'\u0080'..'\ufffe')
-    ;
+timezone_minute_keyword
+	:	{(validateSoftKeyword("timezone_minute"))}?=>  id=IDENTIFIER
+		->	TIMEZONE_MINUTE[$id]
+	;
 
-fragment
-IDENTIFER_FRAGMENT
-    : IDENTIFIER_START_FRAGMENT
-    | '0'..'9'
-    ;
+trailing_keyword
+	:	{(validateSoftKeyword("trailing"))}?=>  id=IDENTIFIER
+		->	TRAILING[$id]
+	;
 
+trim_keyword
+	:	{(validateSoftKeyword("trim"))}?=>  id=IDENTIFIER
+		->	TRIM[$id]
+	;
+
+upper_keyword
+	:	{(validateSoftKeyword("upper"))}?=>  id=IDENTIFIER
+		->	UPPER[$id]
+	;
+
+year_keyword
+	:	{(validateSoftKeyword("year"))}?=>  id=IDENTIFIER
+		->	YEAR[$id]
+	;
+

Modified: core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/phase/hql/parse/HQLLexer.g
===================================================================
--- core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/phase/hql/parse/HQLLexer.g	2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/phase/hql/parse/HQLLexer.g	2009-04-24 21:05:09 UTC (rev 16437)
@@ -19,6 +19,7 @@
 	ENTITY_PERSISTER_REF;
 	FILTER;
 	GENERAL_FUNCTION_CALL;
+	GENERAL_FUNCTION_ARGUMENTS;
 	GROUPING_VALUE;
 	IN_LIST;
 	INSERTABILITY_SPEC;
@@ -32,6 +33,7 @@
 	NOT_IN;
 	NOT_LIKE;
 	NOT_MEMBER_OF;
+	ORDER_SPEC;
 	PATH;
 	PERSISTER_JOIN;
 	PERSISTER_SPACE;
@@ -122,7 +124,6 @@
 	ON;
 	OR;
 	ORDER_BY;
-	ORDER_SPEC;
 	OUTER;
 	POSITION;
 	PROPERTIES;
@@ -233,7 +234,6 @@
 STRING_LITERAL
     :  '"' ( ESCAPE_SEQUENCE | ~('\\'|'"') )* '"'
     |  ('\'' ( ESCAPE_SEQUENCE | ~('\\'|'\'') )* '\'')+
-    |  '`' ( ESCAPE_SEQUENCE | ~('\\'|'`') )* '`'
     ;
 
 fragment
@@ -320,6 +320,10 @@
 	:	('a'..'z'|'A'..'Z'|'_'|'$'|'\u0080'..'\ufffe')('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|'\u0080'..'\ufffe')*
 	;
 
+QUOTED_IDENTIFIER
+    : '`' ( ESCAPE_SEQUENCE | ~('\\'|'`') )* '`'
+    ;
+
 LEFT_PAREN
         :	'('
         ;

Modified: core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateToken.java
===================================================================
--- core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateToken.java	2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateToken.java	2009-04-24 21:05:09 UTC (rev 16437)
@@ -26,7 +26,6 @@
  * 51 Franklin Street, Fifth Floor
  * Boston, MA  02110-1301  USA
  */
-
 package org.hibernate.sql.ast.common;
 
 import org.antlr.runtime.CommonToken;
@@ -37,34 +36,34 @@
  * Models the token-type/text portion of an Antlr tree for a specific node in said tree.
  *
  * @author Steve Ebersole
+ * @author Alexandre Porcelli
  */
 public class HibernateToken extends CommonToken {
-       public HibernateToken(int type) {
-               super(type);
-       }
+	public HibernateToken(int type) {
+		super( type );
+	}
 
-       public HibernateToken(CharStream input, int type, int channel, int start,
-                       int stop) {
-               super(input, type, channel, start, stop);
-       }
+	public HibernateToken(CharStream input, int type, int channel, int start, int stop) {
+		super( input, type, channel, start, stop );
+	}
 
-       public HibernateToken(int type, String text) {
-               super(type, text);
-       }
+	public HibernateToken(int type, String text) {
+		super( type, text );
+	}
 
-       /**
-        * Constructor that preserves the char offset
-        *
-        * @param oldToken
-        */
-       public HibernateToken(Token oldToken) {
-               super(oldToken);
-               if (null != oldToken
-                               && (oldToken.getClass().equals(CommonToken.class) || oldToken
-                                               .getClass().equals(HibernateToken.class))) {
-                       start = ((CommonToken) oldToken).getStartIndex();
-                       stop = ((CommonToken) oldToken).getStopIndex();
-               }
-       }
+	/**
+	 * Constructor that preserves the char offset
+	 *
+	 * @param oldToken A token to use to template the creation of this new one.
+	 */
+	public HibernateToken(Token oldToken) {
+		super( oldToken );
+		if ( null != oldToken
+				&& ( oldToken.getClass().equals( CommonToken.class ) || oldToken
+				.getClass().equals( HibernateToken.class ) ) ) {
+			start = ( ( CommonToken ) oldToken ).getStartIndex();
+			stop = ( ( CommonToken ) oldToken ).getStopIndex();
+		}
+	}
 
 }

Modified: core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateTree.java
===================================================================
--- core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateTree.java	2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateTree.java	2009-04-24 21:05:09 UTC (rev 16437)
@@ -36,6 +36,7 @@
  * todo : javadocs
  * 
  * @author Steve Ebersole
+ * @author Alexandre Porcelli
  */
 public class HibernateTree extends CommonTree {
 	/**

Modified: core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentParser.java
===================================================================
--- core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentParser.java	2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentParser.java	2009-04-24 21:05:09 UTC (rev 16437)
@@ -24,172 +24,86 @@
  */
 package org.hibernate.sql.ast.ordering;
 
-import java.util.ArrayList;
-
-import org.hibernate.sql.Template;
-import org.hibernate.dialect.function.SQLFunction;
-import org.hibernate.util.StringHelper;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.antlr.runtime.TokenStream;
-import org.antlr.runtime.CommonToken;
 import org.antlr.runtime.tree.CommonTree;
-import org.antlr.runtime.tree.Tree;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import org.hibernate.sql.ast.common.HibernateTree;
+import org.hibernate.sql.Template;
+
 /**
  * Extension of the Antlr-generated parser for the purpose of adding our custom parsing behavior.
  *
  * @author Steve Ebersole
  */
-public class OrderByFragmentParser extends OrderByParserParser {
+public class OrderByFragmentParser extends OrderByParser {
 	private static final Logger log = LoggerFactory.getLogger( OrderByFragmentParser.class );
-
 	private final TranslationContext context;
-	private int traceDepth = 0;
 
+	private final String openQuoteChar;
+	private final String closeQuoteChar;
+
 	public OrderByFragmentParser(TokenStream lexer, TranslationContext context) {
 		super( lexer );
 		this.context = context;
+
+		this.openQuoteChar = Character.toString( context.getDialect().openQuote() );
+		this.closeQuoteChar = Character.toString( context.getDialect().closeQuote() );
 	}
-//
-//
-//	// handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//
-//	public void traceIn(String ruleName) throws TokenStreamException {
-//		if ( inputState.guessing > 0 ) {
-//			return;
-//		}
-//		String prefix = StringHelper.repeat( "-", (traceDepth++ * 2) ) + "->";
-//		trace( prefix + ruleName );
-//	}
-//
-//	public void traceOut(String ruleName) throws TokenStreamException {
-//		if ( inputState.guessing > 0 ) {
-//			return;
-//		}
-//		String prefix = "<-" + StringHelper.repeat( "-", (--traceDepth * 2) );
-//		trace( prefix + ruleName );
-//	}
-//
-//    private void trace(String msg) {
-//		log.trace( msg );
-//	}
 
-protected CommonTree quotedIdentifier(CommonTree ident) {
-    return createTreeNode( IDENTIFIER, Template.TEMPLATE + "." + context.getDialect().quote( '`' + ident.getText() + '`' ) );
-}
-
-    protected CommonTree quotedString(CommonTree ident) {
-		return createTreeNode( IDENTIFIER, context.getDialect().quote( ident.getText() ) );
+	@Override
+	protected boolean isFunctionName(String text) {
+		log.trace( "Checking function name [" + text + "]" );
+		return context.getSqlFunctionRegistry().hasFunction( text );
 	}
 
-    protected boolean isFunctionName(CommonToken token) {
-        return context.getSqlFunctionRegistry().hasFunction( token.getText() );
-    }
-
-    protected CommonTree resolveFunction(CommonTree tree) {
-		Tree argumentList = tree.getChild( 0 );
-		assert "{param list}".equals( argumentList.getText() );
-
-		final String functionName = tree.getText();
-		final SQLFunction function = context.getSqlFunctionRegistry().findSQLFunction( functionName );
-
-		if ( function == null ) {
-            // If the function is not registered with the session factory we just need to render it as-is
-            // including its arguments...
-			String text = functionName;
-            int count = argumentList.getChildCount();
-            if ( count > 0 ) {
-                text += '(';
-                for ( int i = 0; i < count; i++ ) {
-                    Tree argument = argumentList.getChild( i );
-                    text += argument.getText();
-                    if ( i < count ) {
-                        text += ", ";
-                    }
-                }
-                text += ')';
-            }
-            return createTreeNode( IDENTIFIER, text );
+	@Override
+	protected boolean isPropertyName(String text) {
+		log.trace( "Checking property name [" + text + "]" );
+		try {
+			return context.getColumnMapper().map( text ) != null;
 		}
-		else {
-			ArrayList expressions = new ArrayList();
-            for ( int i = 0; i < argumentList.getChildCount(); i++ ) {
-                expressions.add( argumentList.getChild( i ).getText() );
-            }
-			final String text = function.render( expressions, context.getSessionFactory() );
-			return createTreeNode( IDENTIFIER, text );
+		catch ( Throwable t ) {
+			return false;
 		}
 	}
 
-    protected CommonTree resolveIdent(CommonTree ident) {
-		String text = ident.getText();
-		String[] replacements;
-		try {
-			replacements = context.getColumnMapper().map( text );
+	@Override
+	protected CommonTree buildPropertyColumns(CommonTree propertyTree) {
+		final String text = extractPropertyName( propertyTree );
+		String[] columns = context.getColumnMapper().map( text );
+		if ( columns.length == 1 ) {
+			return buildColumn( columns[0] );
 		}
-		catch( Throwable t ) {
-			replacements = null;
+		else {
+			HibernateTree vector = new HibernateTree( VECTOR_EXPR );
+			for ( String column : columns ) {
+				vector.addChild( buildColumn( column ) );
+			}
+			return vector;
 		}
+	}
 
-		if ( replacements == null || replacements.length == 0 ) {
-			return createTreeNode( IDENTIFIER, Template.TEMPLATE + "." + text );
+	private CommonTree buildColumn(String columnName) {
+		// ugh
+		HibernateTree columnNameNode;
+		if ( columnName.startsWith( openQuoteChar ) && columnName.endsWith( closeQuoteChar ) ) {
+			columnName = columnName.substring( 1, columnName.length() - 1 );
+			columnNameNode = new HibernateTree( QUOTED_IDENTIFIER, columnName );
 		}
-		else if ( replacements.length == 1 ) {
-			return createTreeNode( IDENTIFIER, Template.TEMPLATE + "." + replacements[0] );
-		}
 		else {
-            final CommonTree root = createTreeNode( IDENT_LIST, "{ident list}" );
-			for ( int i = 0; i < replacements.length; i++ ) {
-				final String identText = Template.TEMPLATE + '.' + replacements[i];
-				root.addChild( createTreeNode( IDENTIFIER, identText ) );
-			}
-			return root;
+			columnNameNode = new HibernateTree( IDENTIFIER, columnName );
 		}
+
+		HibernateTree tree = new HibernateTree( COLUMN );
+		tree.addChild( new HibernateTree( ALIAS_REF, Template.TEMPLATE ) );
+		tree.addChild( columnNameNode );
+
+		return tree;
 	}
-//
-//	/**
-//	 * {@inheritDoc}
-//	 */
-//	protected AST postProcessSortSpecification(AST sortSpec) {
-//		assert SORT_SPEC == sortSpec.getType();
-//		SortSpecification sortSpecification = ( SortSpecification ) sortSpec;
-//		AST sortKey = sortSpecification.getSortKey();
-//		if ( IDENT_LIST == sortKey.getFirstChild().getType() ) {
-//			AST identList = sortKey.getFirstChild();
-//			AST ident = identList.getFirstChild();
-//			AST holder = new CommonAST();
-//			do {
-//				holder.addChild(
-//						createSortSpecification(
-//								ident,
-//								sortSpecification.getCollation(),
-//								sortSpecification.getOrdering()
-//						)
-//				);
-//				ident = ident.getNextSibling();
-//			} while ( ident != null );
-//			sortSpec = holder.getFirstChild();
-//		}
-//		return sortSpec;
-//	}
-//
-//	private SortSpecification createSortSpecification(
-//			AST ident,
-//			CollationSpecification collationSpecification,
-//			OrderingSpecification orderingSpecification) {
-//		AST sortSpecification = getASTFactory().create( SORT_SPEC, "{{sort specification}}" );
-//		AST sortKey = getASTFactory().create( SORT_KEY, "{{sort key}}" );
-//		AST newIdent = getASTFactory().create( ident.getType(), ident.getText() );
-//		sortKey.setFirstChild( newIdent );
-//		sortSpecification.setFirstChild( sortKey );
-//		if ( collationSpecification != null ) {
-//			sortSpecification.addChild( collationSpecification );
-//		}
-//		if ( orderingSpecification != null ) {
-//			sortSpecification.addChild( orderingSpecification );
-//		}
-//		return ( SortSpecification ) sortSpecification;
-//	}
+
+	private String extractPropertyName(CommonTree propertyTree) {
+		return propertyTree.getText();
+	}
 }

Modified: core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentTranslator.java
===================================================================
--- core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentTranslator.java	2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentTranslator.java	2009-04-24 21:05:09 UTC (rev 16437)
@@ -24,15 +24,13 @@
  */
 package org.hibernate.sql.ast.ordering;
 
-import java.io.StringReader;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.hibernate.HibernateException;
 import org.hibernate.sql.ast.util.TreePrinter;
-import org.antlr.runtime.CharStream;
-import org.antlr.runtime.TokenStream;
+import org.hibernate.sql.ast.phase.hql.parse.HQLLexer;
+
 import org.antlr.runtime.ANTLRStringStream;
 import org.antlr.runtime.CommonTokenStream;
 import org.antlr.runtime.tree.Tree;
@@ -59,13 +57,13 @@
 	 * @return The translated fragment.
 	 */
 	public String render(String fragment) {
-		OrderByParserLexer lexer = new OrderByParserLexer( new ANTLRStringStream( fragment ) );
+		HQLLexer lexer = new HQLLexer( new ANTLRStringStream( fragment ) );
 		OrderByFragmentParser parser = new OrderByFragmentParser( new CommonTokenStream( lexer ), context );
 		try {
             Tree tree = ( Tree ) parser.orderByFragment().getTree();
 
             if ( log.isTraceEnabled() ) {
-                TreePrinter printer = new TreePrinter( OrderByParserParser.class );
+                TreePrinter printer = new TreePrinter( OrderByParser.class );
                 log.trace( printer.renderAsString( tree, "--- {order-by fragment} ---" ) );
             }
 		}

Added: core/branches/antlr3/src/test/java/org/hibernate/sql/ast/ordering/OrderByParserTest.java
===================================================================
--- core/branches/antlr3/src/test/java/org/hibernate/sql/ast/ordering/OrderByParserTest.java	                        (rev 0)
+++ core/branches/antlr3/src/test/java/org/hibernate/sql/ast/ordering/OrderByParserTest.java	2009-04-24 21:05:09 UTC (rev 16437)
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.sql.ast.ordering;
+
+import java.util.Collections;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.QueryException;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.sql.ast.phase.hql.parse.HQLLexer;
+import org.hibernate.sql.ast.util.ASTUtil;
+import org.hibernate.sql.ast.util.TreePrinter;
+import org.hibernate.sql.Template;
+import org.hibernate.type.Type;
+
+import org.antlr.runtime.ANTLRStringStream;
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.tree.Tree;
+import org.antlr.runtime.tree.CommonTree;
+
+/**
+ * Assertions against the structures generated by various paths of the "order-by" mapping fragment parser.
+ *
+ * @author Steve Ebersole
+ */
+public class OrderByParserTest extends TestCase {
+	public static final PropertyMapping STANDARD_PROPERTY_MAPPING = new PropertyMapping() {
+		public String[] toColumns(String propertyName) throws QueryException, UnsupportedOperationException {
+			if ( "component".equals( propertyName ) ) {
+				return new String[] { "comp_1", "comp_2" };
+			}
+			else if ( "component.prop1".equals( propertyName ) ) {
+				return new String[] { "comp_1" };
+			}
+			else if ( "component.prop2".equals( propertyName ) ) {
+				return new String[] { "comp_2" };
+			}
+			else if ( "property".equals( propertyName ) ) {
+				return new String[] { "prop" };
+			}
+			throw new QueryException( "could not resolve property: " + propertyName );
+		}
+
+		public Type toType(String propertyName) throws QueryException {
+			throw new UnsupportedOperationException( "toType(propertyName) not supported here" );
+		}
+
+		public String[] toColumns(String alias, String propertyName) throws QueryException {
+			throw new UnsupportedOperationException( "toColumns(alias,propertyName) not supported here" );
+		}
+
+		public Type getType() {
+			throw new UnsupportedOperationException( "getType() not supported here" );
+		}
+	};
+
+	public static final ColumnMapper STANDARD_MAPPER = new ColumnMapper() {
+		public String[] map(String reference) {
+			return STANDARD_PROPERTY_MAPPING.toColumns( reference );
+		}
+	};
+
+	public OrderByParserTest() {
+	}
+
+	public OrderByParserTest(String name) {
+		super( name );
+	}
+
+    public void testSQLReferences() {
+        String fragment = "sql asc, sql desc";
+        CommonTree tree = parse( fragment );
+
+		assertTokenType( HQLLexer.ORDER_BY, tree );
+		assertEquals( 2, tree.getChildCount() );
+
+		Tree firstSortSpec = tree.getChild( 0 );
+		assertTokenType( HQLLexer.SORT_SPEC, firstSortSpec );
+		assertEquals( 2, firstSortSpec.getChildCount() );
+		assertTokenType( HQLLexer.COLUMN, firstSortSpec.getChild( 0 ) );
+		assertTokenType( HQLLexer.ALIAS_REF, firstSortSpec.getChild( 0 ).getChild( 0 ) );
+		assertEquals( Template.TEMPLATE, firstSortSpec.getChild( 0 ).getChild( 0 ).getText() );
+		assertTokenType( HQLLexer.IDENTIFIER, firstSortSpec.getChild( 0 ).getChild( 1 ) );
+		assertEquals( "sql", firstSortSpec.getChild( 0 ).getChild( 1 ).getText() );
+		assertTokenType( HQLLexer.ORDER_SPEC, firstSortSpec.getChild( 1 ) );
+		assertEquals( "asc", firstSortSpec.getChild( 1 ).getText() );
+
+		Tree secondSortSpec = tree.getChild( 1 );
+		assertTokenType( HQLLexer.SORT_SPEC, secondSortSpec.getType() );
+		assertEquals( 2, secondSortSpec.getChildCount() );
+		assertTokenType( HQLLexer.COLUMN, secondSortSpec.getChild( 0 ) );
+		assertTokenType( HQLLexer.ALIAS_REF, secondSortSpec.getChild( 0 ).getChild( 0 ) );
+		assertEquals( Template.TEMPLATE, secondSortSpec.getChild( 0 ).getChild( 0 ).getText() );
+		assertTokenType( HQLLexer.IDENTIFIER, secondSortSpec.getChild( 0 ).getChild( 1 ) );
+		assertEquals( "sql", secondSortSpec.getChild( 0 ).getChild( 1 ).getText() );
+		assertTokenType( HQLLexer.ORDER_SPEC, secondSortSpec.getChild( 1 ) );
+		assertEquals( "desc", secondSortSpec.getChild( 1 ).getText() );
+    }
+
+    public void testQuotedSQLReferences() {
+        String fragment = "`sql` asc, `sql` desc";
+        CommonTree tree = parse( fragment );
+
+		assertTokenType( HQLLexer.ORDER_BY, tree.getType() );
+		assertEquals( 2, tree.getChildCount() );
+
+		Tree firstSortSpec = tree.getChild( 0 );
+		assertTokenType( HQLLexer.SORT_SPEC, firstSortSpec.getType() );
+		assertEquals( 2, firstSortSpec.getChildCount() );
+		assertTokenType( HQLLexer.COLUMN, firstSortSpec.getChild( 0 ) );
+		assertTokenType( HQLLexer.ALIAS_REF, firstSortSpec.getChild( 0 ).getChild( 0 ) );
+		assertEquals( Template.TEMPLATE, firstSortSpec.getChild( 0 ).getChild( 0 ).getText() );
+		assertTokenType( HQLLexer.QUOTED_IDENTIFIER, firstSortSpec.getChild( 0 ).getChild( 1 ) );
+		assertEquals( "`sql`", firstSortSpec.getChild( 0 ).getChild( 1 ).getText() );
+		assertTokenType( HQLLexer.ORDER_SPEC, firstSortSpec.getChild( 1 ) );
+		assertEquals( "asc", firstSortSpec.getChild( 1 ).getText() );
+
+		Tree secondSortSpec = tree.getChild( 1 );
+		assertTokenType( HQLLexer.SORT_SPEC, secondSortSpec.getType() );
+		assertEquals( 2, secondSortSpec.getChildCount() );
+		assertTokenType( HQLLexer.COLUMN, secondSortSpec.getChild( 0 ) );
+		assertTokenType( HQLLexer.ALIAS_REF, secondSortSpec.getChild( 0 ).getChild( 0 ) );
+		assertEquals( Template.TEMPLATE, secondSortSpec.getChild( 0 ).getChild( 0 ).getText() );
+		assertTokenType( HQLLexer.QUOTED_IDENTIFIER, secondSortSpec.getChild( 0 ).getChild( 1 ) );
+		assertEquals( "`sql`", secondSortSpec.getChild( 0 ).getChild( 1 ).getText() );
+		assertTokenType( HQLLexer.ORDER_SPEC, secondSortSpec.getChild( 1 ) );
+		assertEquals( "desc", secondSortSpec.getChild( 1 ).getText() );
+    }
+
+    public void testPropertyReference() {
+		String fragment = "property asc, property desc";
+        CommonTree tree = parse( fragment );
+
+		assertTokenType( HQLLexer.ORDER_BY, tree.getType() );
+		assertEquals( 2, tree.getChildCount() );
+
+		Tree firstSortSpec = tree.getChild( 0 );
+		assertTokenType( HQLLexer.SORT_SPEC, firstSortSpec.getType() );
+		assertEquals( 2, firstSortSpec.getChildCount() );
+		assertTokenType( HQLLexer.COLUMN, firstSortSpec.getChild( 0 ) );
+		assertTokenType( HQLLexer.ALIAS_REF, firstSortSpec.getChild( 0 ).getChild( 0 ) );
+		assertEquals( Template.TEMPLATE, firstSortSpec.getChild( 0 ).getChild( 0 ).getText() );
+		assertTokenType( HQLLexer.IDENTIFIER, firstSortSpec.getChild( 0 ).getChild( 1 ) );
+		assertEquals( "prop", firstSortSpec.getChild( 0 ).getChild( 1 ).getText() );
+		assertTokenType( HQLLexer.ORDER_SPEC, firstSortSpec.getChild( 1 ) );
+		assertEquals( "asc", firstSortSpec.getChild( 1 ).getText() );
+	}
+
+	protected CommonTree parse(String orderByFragment) {
+		final Dialect dialect = new HSQLDialect();
+		final SQLFunction myFunction = new SQLFunctionTemplate( Hibernate.STRING, "myFunction($1)" );
+		final SQLFunctionRegistry functionRegistry = new SQLFunctionRegistry( dialect, Collections.singletonMap( "myFunction", myFunction ) );
+		return parse(
+				orderByFragment,
+				new TranslationContext() {
+					public SessionFactoryImplementor getSessionFactory() {
+						return null;
+					}
+
+					public Dialect getDialect() {
+						return dialect;
+					}
+
+					public SQLFunctionRegistry getSqlFunctionRegistry() {
+						return functionRegistry;
+					}
+
+					public ColumnMapper getColumnMapper() {
+						return STANDARD_MAPPER;
+					}
+				}
+		);
+	}
+
+	private static final TreePrinter TREE_PRINTER = new TreePrinter( HQLLexer.class );
+
+	protected CommonTree parse(String orderByFragment, TranslationContext context) {
+		try {
+			HQLLexer lexer = new HQLLexer( new ANTLRStringStream( orderByFragment ) );
+			OrderByFragmentParser parser = new OrderByFragmentParser( new CommonTokenStream( lexer ), context );
+			CommonTree rtn = parser.orderByFragment().tree;
+			// temp
+			System.out.println(
+					TREE_PRINTER.renderAsString( rtn, "fragment" )
+			);
+			return rtn;
+		}
+		catch ( HibernateException e ) {
+			throw e;
+		}
+		catch ( Throwable t ) {
+			throw new HibernateException( "Unable to parse order-by fragment", t );
+		}
+	}
+
+	private static final Map<Integer,String> tokenNameMap = ( Map<Integer,String> ) ASTUtil.generateTokenNameCache( HQLLexer.class );
+
+	public void assertTokenType(int expected, int actual) {
+		if ( expected != actual ) {
+			throw new AssertionFailedError(
+					"expected:<"+ tokenNameMap.get( expected ) +"> but was:<"+ tokenNameMap.get( actual ) +">"
+			);
+		}
+	}
+
+	public void assertTokenType(int expected, Tree tree) {
+		if ( tree == null ) {
+			throw new AssertionFailedError(
+					"expected:<"+ tokenNameMap.get( expected ) +"> but was:<null>"
+			);
+		}
+		assertTokenType( expected, tree.getType() );
+	}
+}




More information about the hibernate-commits mailing list