You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
468 lines
12 KiB
468 lines
12 KiB
header |
|
{ |
|
/* |
|
* Hibernate, Relational Persistence for Idiomatic Java |
|
* |
|
* Copyright (c) 2008, 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 com.fr.third.org.hibernate.sql.ordering.antlr; |
|
} |
|
/** |
|
* Antlr grammar for dealing with <tt>order-by</tt> mapping fragments. |
|
|
|
* @author Steve Ebersole |
|
*/ |
|
class GeneratedOrderByFragmentParser extends Parser; |
|
|
|
options |
|
{ |
|
exportVocab=OrderByTemplate; |
|
buildAST=true; |
|
k=3; |
|
} |
|
|
|
tokens |
|
{ |
|
// synthetic tokens |
|
ORDER_BY; |
|
SORT_SPEC; |
|
ORDER_SPEC; |
|
NULL_ORDER; |
|
SORT_KEY; |
|
EXPR_LIST; |
|
DOT; |
|
IDENT_LIST; |
|
COLUMN_REF; |
|
|
|
COLLATE="collate"; |
|
ASCENDING="asc"; |
|
DESCENDING="desc"; |
|
NULLS="nulls"; |
|
FIRST; |
|
LAST; |
|
} |
|
|
|
|
|
{ |
|
/** |
|
* Method for logging execution trace information. |
|
* |
|
* @param msg The trace message. |
|
*/ |
|
protected void trace(String msg) { |
|
System.out.println( msg ); |
|
} |
|
|
|
/** |
|
* Extract a node's text. |
|
* |
|
* @param ast The node |
|
* |
|
* @return The text. |
|
*/ |
|
protected final String extractText(AST ast) { |
|
// for some reason, within AST creation blocks "[]" I am sometimes unable to refer to the AST.getText() method |
|
// using #var (the #var is not interpreted as the rule's output AST). |
|
return ast.getText(); |
|
} |
|
|
|
/** |
|
* Process the given node as a quote identifier. These need to be quoted in the dialect-specific way. |
|
* |
|
* @param ident The quoted-identifier node. |
|
* |
|
* @return The processed node. |
|
* |
|
* @see com.fr.third.org.hibernate.dialect.Dialect#quote |
|
*/ |
|
protected AST quotedIdentifier(AST ident) { |
|
return ident; |
|
} |
|
|
|
/** |
|
* Process the given node as a quote string. |
|
* |
|
* @param ident The quoted string. This is used from within function param recognition, and represents a |
|
* SQL-quoted string. |
|
* |
|
* @return The processed node. |
|
*/ |
|
protected AST quotedString(AST ident) { |
|
return ident; |
|
} |
|
|
|
/** |
|
* A check to see if the text of the given node represents a known function name. |
|
* |
|
* @param ast The node whose text we want to check. |
|
* |
|
* @return True if the node's text is a known function name, false otherwise. |
|
* |
|
* @see com.fr.third.org.hibernate.dialect.function.SQLFunctionRegistry |
|
*/ |
|
protected boolean isFunctionName(AST ast) { |
|
return false; |
|
} |
|
|
|
/** |
|
* Process the given node as a function. |
|
* |
|
* @param The node representing the function invocation (including parameters as subtree components). |
|
* |
|
* @return The processed node. |
|
*/ |
|
protected AST resolveFunction(AST ast) { |
|
return ast; |
|
} |
|
|
|
/** |
|
* Process the given node as an IDENT. 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 AST resolveIdent(AST ident) { |
|
return ident; |
|
} |
|
|
|
/** |
|
* Allow post processing of each <tt>sort specification</tt> |
|
* |
|
* @param The grammar-built sort specification subtree. |
|
* |
|
* @return The processed sort specification subtree. |
|
*/ |
|
protected AST postProcessSortSpecification(AST sortSpec) { |
|
return sortSpec; |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Main recognition rule for this grammar |
|
*/ |
|
orderByFragment { trace("orderByFragment"); } |
|
: sortSpecification ( COMMA! sortSpecification )* { |
|
#orderByFragment = #( [ORDER_BY, "order-by"], #orderByFragment ); |
|
} |
|
; |
|
|
|
/** |
|
* Recognition rule for what ANSI SQL terms the <tt>sort specification</tt>, which is essentially each thing upon which |
|
* the results should be sorted. |
|
*/ |
|
sortSpecification { trace("sortSpecification"); } |
|
: sortKey (collationSpecification)? (orderingSpecification)? (nullOrdering)? { |
|
#sortSpecification = #( [SORT_SPEC, "{sort specification}"], #sortSpecification ); |
|
#sortSpecification = postProcessSortSpecification( #sortSpecification ); |
|
} |
|
; |
|
|
|
/** |
|
* Recognition 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! { trace("sortKey"); } |
|
: e:expression { |
|
#sortKey = #( [SORT_KEY, "sort key"], #e ); |
|
} |
|
; |
|
|
|
/** |
|
* Recognition rule what this grammar recognizes as valid <tt>sort key</tt>. |
|
*/ |
|
expression! { trace("expression"); } |
|
: HARD_QUOTE qi:IDENT HARD_QUOTE { |
|
#expression = quotedIdentifier( #qi ); |
|
} |
|
| ( IDENT (DOT IDENT)* OPEN_PAREN ) => f:functionCall { |
|
#expression = #f; |
|
} |
|
| p:simplePropertyPath { |
|
#expression = resolveIdent( #p ); |
|
} |
|
| i:IDENT { |
|
if ( isFunctionName( #i ) ) { |
|
#expression = resolveFunction( #i ); |
|
} |
|
else { |
|
#expression = resolveIdent( #i ); |
|
} |
|
} |
|
; |
|
|
|
/** |
|
* Intended for use as a syntactic predicate to determine whether an IDENT represents a known SQL function name. |
|
*/ |
|
functionCallCheck! { trace("functionCallCheck"); } |
|
: IDENT (DOT IDENT)* OPEN_PAREN { true }? |
|
; |
|
|
|
/** |
|
* Recognition rule for a function call |
|
*/ |
|
functionCall! { trace("functionCall"); } |
|
: fn:functionName OPEN_PAREN pl:functionParameterList CLOSE_PAREN { |
|
#functionCall = #( [IDENT, extractText( #fn )], #pl ); |
|
#functionCall = resolveFunction( #functionCall ); |
|
} |
|
; |
|
|
|
/** |
|
* A function-name is an IDENT followed by zero or more (DOT IDENT) sequences |
|
*/ |
|
functionName { |
|
trace("functionName"); |
|
StringBuilder buffer = new StringBuilder(); |
|
} |
|
: i:IDENT { buffer.append( i.getText() ); } |
|
( DOT i2:IDENT { buffer.append( '.').append( i2.getText() ); } )* { |
|
#functionName = #( [IDENT,buffer.toString()] ); |
|
} |
|
; |
|
|
|
/** |
|
* Recognition rule used to "wrap" all function parameters into an EXPR_LIST node |
|
*/ |
|
functionParameterList { trace("functionParameterList"); } |
|
: functionParameter ( COMMA! functionParameter )* { |
|
#functionParameterList = #( [EXPR_LIST, "{param list}"], #functionParameterList ); |
|
} |
|
; |
|
|
|
/** |
|
* Recognized function parameters. |
|
*/ |
|
functionParameter { trace("functionParameter"); } |
|
: expression |
|
| NUM_DOUBLE |
|
| NUM_FLOAT |
|
| NUM_INT |
|
| NUM_LONG |
|
| QUOTED_STRING { |
|
#functionParameter = quotedString( #functionParameter ); |
|
} |
|
; |
|
|
|
/** |
|
* Recognition 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! { trace("collationSpecification"); } |
|
: c:COLLATE cn:collationName { |
|
#collationSpecification = #( [COLLATE, extractText( #cn )] ); |
|
} |
|
; |
|
|
|
/** |
|
* The collation name wrt {@link #collationSpecification}. Namely, the character-set. |
|
*/ |
|
collationName { trace("collationSpecification"); } |
|
: IDENT |
|
; |
|
|
|
/** |
|
* Recognition rule for what ANSI SQL terms the <tt>ordering specification</tt>; <tt>ASCENDING</tt> or |
|
* <tt>DESCENDING</tt>. |
|
*/ |
|
orderingSpecification! { trace("orderingSpecification"); } |
|
: ( "asc" | "ascending" ) { |
|
#orderingSpecification = #( [ORDER_SPEC, "asc"] ); |
|
} |
|
| ( "desc" | "descending") { |
|
#orderingSpecification = #( [ORDER_SPEC, "desc"] ); |
|
} |
|
; |
|
|
|
/** |
|
* Recognition rule for what SQL-2003 terms the <tt>null ordering</tt>; <tt>NULLS FIRST</tt> or |
|
* <tt>NULLS LAST</tt>. |
|
*/ |
|
nullOrdering! { trace("nullOrdering"); } |
|
: NULLS n:nullPrecedence { |
|
#nullOrdering = #( [NULL_ORDER, extractText( #n )] ); |
|
} |
|
; |
|
|
|
nullPrecedence { trace("nullPrecedence"); } |
|
: IDENT { |
|
if ( "first".equalsIgnoreCase( #nullPrecedence.getText() ) ) { |
|
#nullPrecedence.setType( FIRST ); |
|
} |
|
else if ( "last".equalsIgnoreCase( #nullPrecedence.getText() ) ) { |
|
#nullPrecedence.setType( LAST ); |
|
} |
|
else { |
|
throw new SemanticException( "Expecting 'first' or 'last', but found '" + #nullPrecedence.getText() + "' as null ordering precedence." ); |
|
} |
|
} |
|
; |
|
|
|
/** |
|
* A simple-property-path is an IDENT followed by one or more (DOT IDENT) sequences |
|
*/ |
|
simplePropertyPath { |
|
trace("simplePropertyPath"); |
|
StringBuilder buffer = new StringBuilder(); |
|
} |
|
: i:IDENT { buffer.append( i.getText() ); } |
|
( DOT i2:IDENT { buffer.append( '.').append( i2.getText() ); } )+ { |
|
#simplePropertyPath = #( [IDENT,buffer.toString()] ); |
|
} |
|
; |
|
|
|
|
|
// **** LEXER ****************************************************************** |
|
|
|
/** |
|
* Lexer for the <tt>order-by</tt> fragment parser |
|
|
|
* @author Steve Ebersole |
|
* @author Joshua Davis |
|
*/ |
|
class GeneratedOrderByLexer extends Lexer; |
|
|
|
options { |
|
exportVocab=OrderByTemplate; |
|
testLiterals = false; |
|
k=2; |
|
charVocabulary='\u0000'..'\uFFFE'; // Allow any char but \uFFFF (16 bit -1, ANTLR's EOF character) |
|
caseSensitive = false; |
|
caseSensitiveLiterals = false; |
|
} |
|
|
|
// -- Keywords -- |
|
|
|
OPEN_PAREN: '('; |
|
CLOSE_PAREN: ')'; |
|
|
|
COMMA: ','; |
|
|
|
HARD_QUOTE: '`'; |
|
|
|
IDENT options { testLiterals=true; } |
|
: ID_START_LETTER ( ID_LETTER )* |
|
; |
|
|
|
protected |
|
ID_START_LETTER |
|
: '_' |
|
| '$' |
|
| 'a'..'z' |
|
| '\u0080'..'\ufffe' // HHH-558 : Allow unicode chars in identifiers |
|
; |
|
|
|
protected |
|
ID_LETTER |
|
: ID_START_LETTER |
|
| '0'..'9' |
|
; |
|
|
|
QUOTED_STRING |
|
: '\'' ( (ESCqs)=> ESCqs | ~'\'' )* '\'' |
|
; |
|
|
|
protected |
|
ESCqs |
|
: |
|
'\'' '\'' |
|
; |
|
|
|
//--- From the Java example grammar --- |
|
// a numeric literal |
|
NUM_INT |
|
{boolean isDecimal=false; Token t=null;} |
|
: '.' {_ttype = DOT;} |
|
( ('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})? |
|
{ |
|
if (t != null && t.getText().toUpperCase().indexOf('F')>=0) |
|
{ |
|
_ttype = NUM_FLOAT; |
|
} |
|
else |
|
{ |
|
_ttype = NUM_DOUBLE; // assume double |
|
} |
|
} |
|
)? |
|
| ( '0' {isDecimal = true;} // special case for just '0' |
|
( ('x') |
|
( // hex |
|
// the 'e'|'E' and float suffix stuff look |
|
// like hex digits, hence the (...)+ doesn't |
|
// know when to stop: ambig. ANTLR resolves |
|
// it correctly by matching immediately. It |
|
// is therefore ok to hush warning. |
|
options { warnWhenFollowAmbig=false; } |
|
: HEX_DIGIT |
|
)+ |
|
| ('0'..'7')+ // octal |
|
)? |
|
| ('1'..'9') ('0'..'9')* {isDecimal=true;} // non-zero decimal |
|
) |
|
( ('l') { _ttype = NUM_LONG; } |
|
|
|
// only check to see if it's a float if looks like decimal so far |
|
| {isDecimal}? |
|
( '.' ('0'..'9')* (EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;})? |
|
| EXPONENT (f3:FLOAT_SUFFIX {t=f3;})? |
|
| f4:FLOAT_SUFFIX {t=f4;} |
|
) |
|
{ |
|
if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0) |
|
{ |
|
_ttype = NUM_FLOAT; |
|
} |
|
else |
|
{ |
|
_ttype = NUM_DOUBLE; // assume double |
|
} |
|
} |
|
)? |
|
; |
|
|
|
// hexadecimal digit (again, note it's protected!) |
|
protected |
|
HEX_DIGIT |
|
: ('0'..'9'|'a'..'f') |
|
; |
|
|
|
// a couple protected methods to assist in matching floating point numbers |
|
protected |
|
EXPONENT |
|
: ('e') ('+'|'-')? ('0'..'9')+ |
|
; |
|
|
|
protected |
|
FLOAT_SUFFIX |
|
: 'f'|'d' |
|
; |
|
|
|
WS : ( ' ' |
|
| '\t' |
|
| '\r' '\n' { newline(); } |
|
| '\n' { newline(); } |
|
| '\r' { newline(); } |
|
) |
|
{$setType(Token.SKIP);} //ignore this token |
|
;
|
|
|