diff --git a/core/pom.xml b/core/pom.xml
index 3255511cb2..5cea86da94 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -77,7 +77,7 @@
antlr-maven-plugin
${antlrPluginVersion}
- hql.g,hql-sql.g,sql-gen.g
+ hql.g,hql-sql.g,sql-gen.g,order-by.g,order-by-render.g
diff --git a/core/src/main/antlr/order-by-render.g b/core/src/main/antlr/order-by-render.g
new file mode 100644
index 0000000000..bba7bddcf6
--- /dev/null
+++ b/core/src/main/antlr/order-by-render.g
@@ -0,0 +1,92 @@
+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 org.hibernate.sql.ordering.antlr;
+}
+/**
+ * Antlr grammar for rendering ORDER_BY trees as described by the {@link OrderByFragmentParser}
+
+ * @author Steve Ebersole
+ */
+class GeneratedOrderByFragmentRenderer extends TreeParser;
+
+options {
+ importVocab=OrderByTemplate;
+ buildAST=false;
+}
+
+{
+ // the buffer to which we write the resulting SQL.
+ private StringBuffer buffer = new StringBuffer();
+
+ protected void out(String text) {
+ buffer.append( text );
+ }
+
+ protected void out(AST ast) {
+ buffer.append( ast.getText() );
+ }
+
+ /*package*/ String getRenderedFragment() {
+ return buffer.toString();
+ }
+}
+
+orderByFragment
+ : #(
+ ORDER_BY sortSpecification ( {out(", ");} sortSpecification)*
+ )
+ ;
+
+sortSpecification
+ : #(
+ SORT_SPEC sortKeySpecification (collationSpecification)? (orderingSpecification)?
+ )
+ ;
+
+sortKeySpecification
+ : #(SORT_KEY sortKey)
+ ;
+
+sortKey
+ : i:IDENT {
+ out( #i );
+ }
+ ;
+
+collationSpecification
+ : c:COLLATE {
+ out( " collate " );
+ out( c );
+ }
+ ;
+
+orderingSpecification
+ : o:ORDER_SPEC {
+ out( " " );
+ out( #o );
+ }
+ ;
\ No newline at end of file
diff --git a/core/src/main/antlr/order-by.g b/core/src/main/antlr/order-by.g
new file mode 100644
index 0000000000..48d3017a36
--- /dev/null
+++ b/core/src/main/antlr/order-by.g
@@ -0,0 +1,440 @@
+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 org.hibernate.sql.ordering.antlr;
+}
+/**
+ * Antlr grammar for dealing with order-by 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;
+ SORT_KEY;
+ EXPR_LIST;
+ DOT;
+ IDENT_LIST;
+ COLUMN_REF;
+
+ COLLATE="collate";
+ ASCENDING="asc";
+ DESCENDING="desc";
+}
+
+
+{
+ /**
+ * 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 somtimes 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 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 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 sort specification
+ *
+ * @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 );
+ }
+ ;
+
+/**
+ * Reconition rule for what ANSI SQL terms the sort specification, which is essentially each thing upon which
+ * the results should be sorted.
+ */
+sortSpecification { trace("sortSpecification"); }
+ : sortKey (collationSpecification)? (orderingSpecification)? {
+ #sortSpecification = #( [SORT_SPEC, "{sort specification}"], #sortSpecification );
+ #sortSpecification = postProcessSortSpecification( #sortSpecification );
+ }
+ ;
+
+/**
+ * Reconition rule for what ANSI SQL terms the sort key which is the expression (column, function, etc) upon
+ * which to base the sorting.
+ */
+sortKey! { trace("sortKey"); }
+ : e:expression {
+ #sortKey = #( [SORT_KEY, "sort key"], #e );
+ }
+ ;
+
+/**
+ * Reconition rule what this grammar recognizes as valid sort key.
+ */
+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");
+ StringBuffer buffer = new StringBuffer();
+ }
+ : 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 );
+ }
+ ;
+
+/**
+ * Reconition rule for what ANSI SQL terms the collation specification 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
+ ;
+
+/**
+ * Reconition rule for what ANSI SQL terms the ordering specification; ASCENDING or
+ * DESCENDING.
+ */
+orderingSpecification! { trace("orderingSpecification"); }
+ : ( "asc" | "ascending" ) {
+ #orderingSpecification = #( [ORDER_SPEC, "asc"] );
+ }
+ | ( "desc" | "descending") {
+ #orderingSpecification = #( [ORDER_SPEC, "desc"] );
+ }
+ ;
+
+/**
+ * A simple-property-path is an IDENT followed by one or more (DOT IDENT) sequences
+ */
+simplePropertyPath {
+ trace("simplePropertyPath");
+ StringBuffer buffer = new StringBuffer();
+ }
+ : i:IDENT { buffer.append( i.getText() ); }
+ ( DOT i2:IDENT { buffer.append( '.').append( i2.getText() ); } )+ {
+ #simplePropertyPath = #( [IDENT,buffer.toString()] );
+ }
+ ;
+
+
+// **** LEXER ******************************************************************
+
+/**
+ * Lexer for the order-by 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
+ ;
diff --git a/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java
index 880e21d116..482c79af83 100644
--- a/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java
+++ b/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java
@@ -80,12 +80,12 @@ import org.hibernate.sql.Alias;
import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.Template;
+import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.type.AbstractComponentType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.util.ArrayHelper;
-import org.hibernate.util.CollectionHelper;
import org.hibernate.util.FilterHelper;
import org.hibernate.util.StringHelper;
@@ -113,7 +113,6 @@ public abstract class AbstractCollectionPersister
private final String sqlDetectRowByIndexString;
private final String sqlDetectRowByElementString;
- private final String sqlOrderByString;
protected final String sqlWhereString;
private final String sqlOrderByStringTemplate;
private final String sqlWhereStringTemplate;
@@ -197,7 +196,7 @@ public abstract class AbstractCollectionPersister
private final String manyToManyWhereString;
private final String manyToManyWhereTemplate;
- private final String manyToManyOrderByString;
+ private final boolean hasManyToManyOrder;
private final String manyToManyOrderByTemplate;
// custom sql
@@ -266,12 +265,7 @@ public abstract class AbstractCollectionPersister
for ( int i = 1; i < spacesSize; i++ ) {
spaces[i] = (String) iter.next();
}
-
- sqlOrderByString = collection.getOrderBy();
- hasOrder = sqlOrderByString != null;
- sqlOrderByStringTemplate = hasOrder ?
- Template.renderOrderByStringTemplate(sqlOrderByString, dialect, factory.getSqlFunctionRegistry()) :
- null;
+
sqlWhereString = StringHelper.isNotEmpty( collection.getWhere() ) ? "( " + collection.getWhere() + ") " : null;
hasWhere = sqlWhereString != null;
sqlWhereStringTemplate = hasWhere ?
@@ -540,7 +534,26 @@ public abstract class AbstractCollectionPersister
);
}
}
-
+
+ hasOrder = collection.getOrderBy() != null;
+ if ( hasOrder ) {
+ ColumnMapper mapper = new ColumnMapper() {
+ public String[] map(String reference) {
+ return elementPropertyMapping.toColumns( reference );
+ }
+ };
+ sqlOrderByStringTemplate = Template.renderOrderByStringTemplate(
+ collection.getOrderBy(),
+ mapper,
+ factory,
+ dialect,
+ factory.getSqlFunctionRegistry()
+ );
+ }
+ else {
+ sqlOrderByStringTemplate = null;
+ }
+
// Handle any filters applied to this collection
filterHelper = new FilterHelper( collection.getFilterMap(), dialect, factory.getSqlFunctionRegistry() );
@@ -552,10 +565,25 @@ public abstract class AbstractCollectionPersister
manyToManyWhereTemplate = manyToManyWhereString == null ?
null :
Template.renderWhereStringTemplate( manyToManyWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() );
- manyToManyOrderByString = collection.getManyToManyOrdering();
- manyToManyOrderByTemplate = manyToManyOrderByString == null
- ? null
- : Template.renderOrderByStringTemplate( manyToManyOrderByString, factory.getDialect(), factory.getSqlFunctionRegistry() );
+
+ hasManyToManyOrder = collection.getManyToManyOrdering() != null;
+ if ( hasManyToManyOrder ) {
+ ColumnMapper mapper = new ColumnMapper() {
+ public String[] map(String reference) {
+ return elementPropertyMapping.toColumns( reference );
+ }
+ };
+ manyToManyOrderByTemplate = Template.renderOrderByStringTemplate(
+ collection.getManyToManyOrdering(),
+ mapper,
+ factory,
+ dialect,
+ factory.getSqlFunctionRegistry()
+ );
+ }
+ else {
+ manyToManyOrderByTemplate = null;
+ }
initCollectionPropertyMap();
}
@@ -658,17 +686,15 @@ public abstract class AbstractCollectionPersister
}
public String getSQLOrderByString(String alias) {
- return hasOrdering() ?
- StringHelper.replace( sqlOrderByStringTemplate, Template.TEMPLATE, alias ) : "";
+ return hasOrdering()
+ ? StringHelper.replace( sqlOrderByStringTemplate, Template.TEMPLATE, alias )
+ : "";
}
public String getManyToManyOrderByString(String alias) {
- if ( isManyToMany() && manyToManyOrderByString != null ) {
- return StringHelper.replace( manyToManyOrderByTemplate, Template.TEMPLATE, alias );
- }
- else {
- return "";
- }
+ return hasManyToManyOrdering()
+ ? StringHelper.replace( manyToManyOrderByTemplate, Template.TEMPLATE, alias )
+ : "";
}
public FetchMode getFetchMode() {
return fetchMode;
@@ -679,7 +705,7 @@ public abstract class AbstractCollectionPersister
}
public boolean hasManyToManyOrdering() {
- return isManyToMany() && manyToManyOrderByTemplate != null;
+ return isManyToMany() && hasManyToManyOrder;
}
public boolean hasWhere() {
diff --git a/core/src/main/java/org/hibernate/sql/Template.java b/core/src/main/java/org/hibernate/sql/Template.java
index bed3c7d5d6..68cb61e7ea 100644
--- a/core/src/main/java/org/hibernate/sql/Template.java
+++ b/core/src/main/java/org/hibernate/sql/Template.java
@@ -30,6 +30,10 @@ import java.util.StringTokenizer;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.util.StringHelper;
+import org.hibernate.sql.ordering.antlr.ColumnMapper;
+import org.hibernate.sql.ordering.antlr.TranslationContext;
+import org.hibernate.sql.ordering.antlr.OrderByFragmentTranslator;
+import org.hibernate.engine.SessionFactoryImplementor;
/**
* Parses SQL fragments specified in mapping documents
@@ -233,93 +237,79 @@ public final class Template {
return result.toString();
}
- /**
- * Takes order by clause provided in the mapping attribute and interpolates the alias.
- * Handles asc, desc, SQL functions, quoted identifiers.
- */
- public static String renderOrderByStringTemplate(String sqlOrderByString, Dialect dialect, SQLFunctionRegistry functionRegistry) {
- //TODO: make this a bit nicer
- String symbols = new StringBuffer()
- .append("=>ORDER BY template
+ * has all column references "qualified" with a placeholder identified by {@link Template#TEMPLATE}
+ *
+ * @param orderByFragment The order-by fragment to render.
+ * @param dialect The SQL dialect being used.
+ * @param functionRegistry The SQL function registry
+ *
+ * @return The rendered ORDER BY template.
+ *
+ * @see #renderOrderByStringTemplate(String,ColumnMapper,SessionFactoryImplementor,Dialect,SQLFunctionRegistry)
+ */
+ public static String renderOrderByStringTemplate(
+ String orderByFragment,
+ Dialect dialect,
+ SQLFunctionRegistry functionRegistry) {
+ return renderOrderByStringTemplate(
+ orderByFragment,
+ NoOpColumnMapper.INSTANCE,
+ null,
+ dialect,
+ functionRegistry
+ );
+ }
+
+ /**
+ * Performs order-by template rendering allowing {@link ColumnMapper column mapping}. An ORDER BY template
+ * has all column references "qualified" with a placeholder identified by {@link Template#TEMPLATE} which can later
+ * be used to easily inject the SQL alias.
+ *
+ * @param orderByFragment The order-by fragment to render.
+ * @param columnMapper The column mapping strategy to use.
+ * @param sessionFactory The session factory.
+ * @param dialect The SQL dialect being used.
+ * @param functionRegistry The SQL function registry
+ *
+ * @return The rendered ORDER BY template.
+ */
+ public static String renderOrderByStringTemplate(
+ String orderByFragment,
+ final ColumnMapper columnMapper,
+ final SessionFactoryImplementor sessionFactory,
+ final Dialect dialect,
+ final SQLFunctionRegistry functionRegistry) {
+ TranslationContext context = new TranslationContext() {
+ public SessionFactoryImplementor getSessionFactory() {
+ return sessionFactory;
+ }
+
+ public Dialect getDialect() {
+ return dialect;
+ }
+
+ public SQLFunctionRegistry getSqlFunctionRegistry() {
+ return functionRegistry;
+ }
+
+ public ColumnMapper getColumnMapper() {
+ return columnMapper;
+ }
+ };
+
+ OrderByFragmentTranslator translator = new OrderByFragmentTranslator( context );
+ return translator.render( orderByFragment );
+ }
+
private static boolean isNamedParameter(String token) {
return token.startsWith(":");
}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/CollationSpecification.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/CollationSpecification.java
new file mode 100644
index 0000000000..b119d99097
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/CollationSpecification.java
@@ -0,0 +1,34 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+/**
+ * Models a collation specification (COLLATE using a specific character-set) within a
+ * {@link SortSpecification}.
+ *
+ * @author Steve Ebersole
+ */
+public class CollationSpecification extends NodeSupport {
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/ColumnMapper.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/ColumnMapper.java
new file mode 100644
index 0000000000..af3d4848c9
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/ColumnMapper.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Contract for mapping a (an assumed) property reference to its columns.
+ *
+ * @author Steve Ebersole
+ */
+public interface ColumnMapper {
+ /**
+ * Resolve the property reference to its underlying columns.
+ *
+ * @param reference The property reference name.
+ *
+ * @return The underlying columns, or null if the property reference is unknown.
+ *
+ * @throws HibernateException Generally indicates that the property reference is unkown; interpretation is the
+ * same as a null return.
+ */
+ public String[] map(String reference) throws HibernateException;
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/Factory.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/Factory.java
new file mode 100644
index 0000000000..c6343436ed
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/Factory.java
@@ -0,0 +1,54 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+import antlr.ASTFactory;
+
+/**
+ * Acts as a {@link ASTFactory} for injecting our specific AST node classes into the Antlr generated trees.
+ *
+ * @author Steve Ebersole
+ */
+public class Factory extends ASTFactory implements OrderByTemplateTokenTypes {
+ /**
+ * {@inheritDoc}
+ */
+ public Class getASTNodeType(int i) {
+ switch ( i ) {
+ case ORDER_BY:
+ return OrderByFragment.class;
+ case SORT_SPEC:
+ return SortSpecification.class;
+ case ORDER_SPEC:
+ return OrderingSpecification.class;
+ case COLLATE:
+ return CollationSpecification.class;
+ case SORT_KEY:
+ return SortKey.class;
+ default:
+ return NodeSupport.class;
+ }
+ }
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/Node.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/Node.java
new file mode 100644
index 0000000000..4f8a6adfd7
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/Node.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+/**
+ * General contract for AST nodes.
+ *
+ * @author Steve Ebersole
+ */
+public interface Node {
+ /**
+ * Get the intrinsic text of this node.
+ *
+ * @return The node's text.
+ */
+ public String getText();
+
+ /**
+ * Get a string representation of this node usable for debug logging or similar.
+ *
+ * @return The node's debugging text.
+ */
+ public String getDebugText();
+
+ /**
+ * Build the node's representation for use in the resulting rendering.
+ *
+ * @return The renderable text.
+ */
+ public String getRenderableText();
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/NodeSupport.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/NodeSupport.java
new file mode 100644
index 0000000000..0abe56e67c
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/NodeSupport.java
@@ -0,0 +1,48 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+import antlr.CommonAST;
+
+/**
+ * Basic implementation of a {@link Node}.
+ *
+ * @author Steve Ebersole
+ */
+public class NodeSupport extends CommonAST implements Node {
+ /**
+ * {@inheritDoc}
+ */
+ public String getDebugText() {
+ return getText();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getRenderableText() {
+ return getText();
+ }
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragment.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragment.java
new file mode 100644
index 0000000000..00d86a1a46
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragment.java
@@ -0,0 +1,33 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+/**
+ * Represents a parsed order-by mapping fragment. This holds the tree of all {@link SortSpecification}s.
+ *
+ * @author Steve Ebersole
+ */
+public class OrderByFragment extends NodeSupport {
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java
new file mode 100644
index 0000000000..059bb91c30
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java
@@ -0,0 +1,194 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+import java.util.ArrayList;
+
+import antlr.TokenStream;
+import antlr.CommonAST;
+import antlr.collections.AST;
+
+import org.hibernate.sql.Template;
+import org.hibernate.dialect.function.SQLFunction;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extension of the Antlr-generated parser for the purpose of adding our custom parsing behavior.
+ *
+ * @author Steve Ebersole
+ */
+public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
+ private static final Logger log = LoggerFactory.getLogger( OrderByFragmentParser.class );
+
+ private final TranslationContext context;
+
+ public OrderByFragmentParser(TokenStream lexer, TranslationContext context) {
+ super( lexer );
+ super.setASTFactory( new Factory() );
+ this.context = context;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected void trace(String msg) {
+ log.trace( msg );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected AST quotedIdentifier(AST ident) {
+ return getASTFactory().create(
+ OrderByTemplateTokenTypes.IDENT,
+ Template.TEMPLATE + "." + context.getDialect().quote( '`' + ident.getText() + '`' )
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected AST quotedString(AST ident) {
+ return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, context.getDialect().quote( ident.getText() ) );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected boolean isFunctionName(AST ast) {
+ return context.getSqlFunctionRegistry().hasFunction( ast.getText() );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected AST resolveFunction(AST ast) {
+ AST child = ast.getFirstChild();
+ assert "{param list}".equals( child.getText() );
+ child = child.getFirstChild();
+
+ final String functionName = ast.getText();
+ final SQLFunction function = context.getSqlFunctionRegistry().findSQLFunction( functionName );
+ if ( function == null ) {
+ String text = functionName;
+ if ( child != null ) {
+ text += '(';
+ while ( child != null ) {
+ text += child.getText();
+ child = child.getNextSibling();
+ if ( child != null ) {
+ text += ", ";
+ }
+ }
+ text += ')';
+ }
+ return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, text );
+ }
+ else {
+ ArrayList expressions = new ArrayList();
+ while ( child != null ) {
+ expressions.add( child.getText() );
+ child = child.getNextSibling();
+ }
+ final String text = function.render( expressions, context.getSessionFactory() );
+ return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, text );
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected AST resolveIdent(AST ident) {
+ String text = ident.getText();
+ String[] replacements;
+ try {
+ replacements = context.getColumnMapper().map( text );
+ }
+ catch( Throwable t ) {
+ replacements = null;
+ }
+
+ if ( replacements == null || replacements.length == 0 ) {
+ return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, Template.TEMPLATE + "." + ident );
+ }
+ else if ( replacements.length == 1 ) {
+ return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, Template.TEMPLATE + "." + replacements[0] );
+ }
+ else {
+ final AST root = getASTFactory().create( OrderByTemplateTokenTypes.IDENT_LIST, "{ident list}" );
+ for ( int i = 0; i < replacements.length; i++ ) {
+ final String identText = Template.TEMPLATE + '.' + replacements[i];
+ root.addChild( getASTFactory().create( OrderByTemplateTokenTypes.IDENT, identText ) );
+ }
+ return root;
+ }
+ }
+
+ /**
+ * {@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;
+ }
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentRenderer.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentRenderer.java
new file mode 100644
index 0000000000..741ef77055
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentRenderer.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+import antlr.collections.AST;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public class OrderByFragmentRenderer extends GeneratedOrderByFragmentRenderer {
+ protected void out(AST ast) {
+ out( ( ( Node ) ast ).getRenderableText() );
+ }
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentTranslator.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentTranslator.java
new file mode 100644
index 0000000000..e85b703bdd
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentTranslator.java
@@ -0,0 +1,87 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+import java.io.StringReader;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.hql.ast.util.ASTPrinter;
+
+/**
+ * A translator which coordinates translation of an order-by mapping.
+ *
+ * @author Steve Ebersole
+ */
+public class OrderByFragmentTranslator {
+ private static final Logger log = LoggerFactory.getLogger( OrderByFragmentTranslator.class );
+
+ public final TranslationContext context;
+
+ public OrderByFragmentTranslator(TranslationContext context) {
+ this.context = context;
+ }
+
+ /**
+ * The main contract, performing the transaction.
+ *
+ * @param fragment The order-by mapping fragment to be translated.
+ *
+ * @return The translated fragment.
+ */
+ public String render(String fragment) {
+ GeneratedOrderByLexer lexer = new GeneratedOrderByLexer( new StringReader( fragment ) );
+ OrderByFragmentParser parser = new OrderByFragmentParser( lexer, context );
+ try {
+ parser.orderByFragment();
+ }
+ catch ( HibernateException e ) {
+ throw e;
+ }
+ catch ( Throwable t ) {
+ throw new HibernateException( "Unable to parse order-by fragment", t );
+ }
+
+ if ( log.isTraceEnabled() ) {
+ ASTPrinter printer = new ASTPrinter( OrderByTemplateTokenTypes.class );
+ log.trace( printer.showAsString( parser.getAST(), "--- {order-by fragment} ---" ) );
+ }
+
+ GeneratedOrderByFragmentRenderer renderer = new GeneratedOrderByFragmentRenderer();
+ try {
+ renderer.orderByFragment( parser.getAST() );
+ }
+ catch ( HibernateException e ) {
+ throw e;
+ }
+ catch ( Throwable t ) {
+ throw new HibernateException( "Unable to render parsed order-by fragment", t );
+ }
+
+ return renderer.getRenderedFragment();
+ }
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderingSpecification.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderingSpecification.java
new file mode 100644
index 0000000000..4eaf4017ba
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderingSpecification.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+/**
+ * Models an ordering specification (ASCENDING or DESCENDING) within a {@link SortSpecification}.
+ *
+ * @author Steve Ebersole
+ */
+public class OrderingSpecification extends NodeSupport {
+ public static class Ordering {
+ public static final Ordering ASCENDING = new Ordering( "asc" );
+ public static final Ordering DESCENDING = new Ordering( "desc" );
+
+ private final String name;
+
+ private Ordering(String name) {
+ this.name = name;
+ }
+ }
+
+ private boolean resolved;
+ private Ordering ordering;
+
+ public Ordering getOrdering() {
+ if ( !resolved ) {
+ ordering = resolve( getText() );
+ resolved = true;
+ }
+ return ordering;
+ }
+
+ private static Ordering resolve(String text) {
+ if ( Ordering.ASCENDING.name.equals( text ) ) {
+ return Ordering.ASCENDING;
+ }
+ else if ( Ordering.DESCENDING.name.equals( text ) ) {
+ return Ordering.DESCENDING;
+ }
+ else {
+ throw new IllegalStateException( "Unknown ordering [" + text + "]" );
+ }
+ }
+
+ public String getRenderableText() {
+ return getOrdering().name;
+ }
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/SortKey.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/SortKey.java
new file mode 100644
index 0000000000..aee743ee48
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/SortKey.java
@@ -0,0 +1,34 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+/**
+ * Models the container node for the sort key, which is the term given by the ANSI SQL specification to the
+ * expression upon which to sort for each {@link SortSpecification}
+ *
+ * @author Steve Ebersole
+ */
+public class SortKey extends NodeSupport {
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/SortSpecification.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/SortSpecification.java
new file mode 100644
index 0000000000..29ef97b40d
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/SortSpecification.java
@@ -0,0 +1,80 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+import antlr.collections.AST;
+
+/**
+ * Models each sorting exprersion.
+ *
+ * @author Steve Ebersole
+ */
+public class SortSpecification extends NodeSupport {
+ /**
+ * Locate the specified {@link SortKey}.
+ *
+ * @return The sort key.
+ */
+ public SortKey getSortKey() {
+ return ( SortKey ) getFirstChild();
+ }
+
+ /**
+ * Locate the specified collation specification, if one.
+ *
+ * @return The collation specification, or null if none was specified.
+ */
+ public CollationSpecification getCollation() {
+ AST possible = getSortKey().getNextSibling();
+ return possible != null && OrderByTemplateTokenTypes.COLLATE == possible.getType()
+ ? ( CollationSpecification ) possible
+ : null;
+ }
+
+ /**
+ * Locate the specified ordering specification, if one.
+ *
+ * @return The ordering specification, or null if none was specified.
+ */
+ public OrderingSpecification getOrdering() {
+ // IMPL NOTE : the ordering-spec would be either the 2nd or 3rd child (of the overall sort-spec), if it existed,
+ // depending on whether a collation-spec was specified.
+
+ AST possible = getSortKey().getNextSibling();
+ if ( possible == null ) {
+ // There was no sort-spec parts specified other then the sort-key so there can be no ordering-spec...
+ return null;
+ }
+
+ if ( OrderByTemplateTokenTypes.COLLATE == possible.getType() ) {
+ // the 2nd child was a collation-spec, so we need to check the 3rd child instead.
+ possible = possible.getNextSibling();
+ }
+
+ return possible != null && OrderByTemplateTokenTypes.ORDER_SPEC == possible.getType()
+ ? ( OrderingSpecification ) possible
+ : null;
+ }
+}
diff --git a/core/src/main/java/org/hibernate/sql/ordering/antlr/TranslationContext.java b/core/src/main/java/org/hibernate/sql/ordering/antlr/TranslationContext.java
new file mode 100644
index 0000000000..01348440de
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/TranslationContext.java
@@ -0,0 +1,64 @@
+/*
+ * 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 org.hibernate.sql.ordering.antlr;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+
+/**
+ * Contract for contextual information required to perform translation.
+*
+* @author Steve Ebersole
+*/
+public interface TranslationContext {
+ /**
+ * Retrieves the session factory for this context.
+ *
+ * @return The session factory
+ */
+ public SessionFactoryImplementor getSessionFactory();
+
+ /**
+ * Retrieves the dialect for this context.
+ *
+ * @return The dialect
+ */
+ public Dialect getDialect();
+
+ /**
+ * Retrieves the SQL function registry/tt> for this context.
+ *
+ * @return The SQL function registry.
+ */
+ public SQLFunctionRegistry getSqlFunctionRegistry();
+
+ /**
+ * Retrieves the column mapper for this context.
+ *
+ * @return The column mapper
+ */
+ public ColumnMapper getColumnMapper();
+}
diff --git a/core/src/test/java/org/hibernate/sql/TemplateTest.java b/core/src/test/java/org/hibernate/sql/TemplateTest.java
new file mode 100644
index 0000000000..464b195690
--- /dev/null
+++ b/core/src/test/java/org/hibernate/sql/TemplateTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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 org.hibernate.sql;
+
+import java.util.Collections;
+
+import junit.framework.TestCase;
+
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.type.Type;
+import org.hibernate.QueryException;
+import org.hibernate.sql.ordering.antlr.ColumnMapper;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public class TemplateTest extends TestCase {
+ private static final PropertyMapping PROPERTY_MAPPING = new PropertyMapping() {
+ public String[] toColumns(String propertyName) throws QueryException, UnsupportedOperationException {
+ if ( "sql".equals( propertyName ) ) {
+ return new String[] { "sql" };
+ }
+ else 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 {
+ return null;
+ }
+
+ public String[] toColumns(String alias, String propertyName) throws QueryException {
+ return new String[0];
+ }
+
+ public Type getType() {
+ return null;
+ }
+ };
+
+ private static final ColumnMapper MAPPER = new ColumnMapper() {
+ public String[] map(String reference) {
+ return PROPERTY_MAPPING.toColumns( reference );
+ }
+ };
+
+ private static final Dialect DIALECT = new HSQLDialect();
+
+ private static final SQLFunctionRegistry FUNCTION_REGISTRY = new SQLFunctionRegistry( DIALECT, Collections.EMPTY_MAP );
+
+ public void testSQLReferences() {
+ String fragment = "sql asc, sql desc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( Template.TEMPLATE + ".sql asc, " + Template.TEMPLATE + ".sql desc", template );
+ }
+
+ public void testQuotedSQLReferences() {
+ String fragment = "`sql` asc, `sql` desc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( Template.TEMPLATE + ".\"sql\" asc, " + Template.TEMPLATE + ".\"sql\" desc", template );
+ }
+
+ public void testPropertyReference() {
+ String fragment = "property asc, property desc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( Template.TEMPLATE + ".prop asc, " + Template.TEMPLATE + ".prop desc", template );
+ }
+
+ public void testFunctionReference() {
+ String fragment = "upper(sql) asc, lower(sql) desc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( "upper(" + Template.TEMPLATE + ".sql) asc, lower(" + Template.TEMPLATE + ".sql) desc", template );
+ }
+
+ public void testQualifiedFunctionReference() {
+ String fragment = "qual.upper(property) asc, qual.lower(property) desc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( "qual.upper(" + Template.TEMPLATE + ".prop) asc, qual.lower(" + Template.TEMPLATE + ".prop) desc", template );
+ }
+
+ public void testDoubleQualifiedFunctionReference() {
+ String fragment = "qual1.qual2.upper(property) asc, qual1.qual2.lower(property) desc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( "qual1.qual2.upper(" + Template.TEMPLATE + ".prop) asc, qual1.qual2.lower(" + Template.TEMPLATE + ".prop) desc", template );
+ }
+
+ public void testFunctionWithPropertyReferenceAsParam() {
+ String fragment = "upper(property) asc, lower(property) desc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( "upper(" + Template.TEMPLATE + ".prop) asc, lower(" + Template.TEMPLATE + ".prop) desc", template );
+ }
+
+ public void testNestedFunctionReferences() {
+ String fragment = "upper(lower(sql)) asc, lower(upper(sql)) desc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( "upper(lower(" + Template.TEMPLATE + ".sql)) asc, lower(upper(" + Template.TEMPLATE + ".sql)) desc", template );
+ }
+
+ public void testComplexNestedFunctionReferences() {
+ String fragment = "mod(mod(sql,2),3) asc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( "mod(mod(" + Template.TEMPLATE + ".sql, 2), 3) asc", template );
+ }
+
+ public void testCollation() {
+ String fragment = "`sql` COLLATE my_collation, `sql` COLLATE your_collation";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( Template.TEMPLATE + ".\"sql\" collate my_collation, " + Template.TEMPLATE + ".\"sql\" collate your_collation", template );
+ }
+
+ public void testCollationAndOrdering() {
+ String fragment = "sql COLLATE my_collation, upper(prop) COLLATE your_collation asc, `sql` desc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( Template.TEMPLATE + ".sql collate my_collation, upper(" + Template.TEMPLATE + ".prop) collate your_collation asc, " + Template.TEMPLATE + ".\"sql\" desc", template );
+
+ }
+
+ public void testComponentReferences() {
+ String fragment = "component asc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( Template.TEMPLATE + ".comp_1 asc, " + Template.TEMPLATE + ".comp_2 asc", template );
+
+ }
+
+ public void testComponentDerefReferences() {
+ String fragment = "component.prop1 asc";
+ String template = doStandardRendering( fragment );
+
+ assertEquals( Template.TEMPLATE + ".comp_1 asc", template );
+ }
+
+ public String doStandardRendering(String fragment) {
+ return Template.renderOrderByStringTemplate( fragment, MAPPER, null, DIALECT, FUNCTION_REGISTRY );
+ }
+}
\ No newline at end of file
diff --git a/core/src/test/resources/log4j.properties b/core/src/test/resources/log4j.properties
index adaf3f2a02..3522e064dd 100644
--- a/core/src/test/resources/log4j.properties
+++ b/core/src/test/resources/log4j.properties
@@ -21,7 +21,6 @@
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301 USA
#
-#
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
@@ -31,3 +30,4 @@ log4j.rootLogger=info, stdout
log4j.logger.org.hibernate.test=info
log4j.logger.org.hibernate.tool.hbm2ddl=debug
+log4j.logger.org.hibernate.sql.ordering.antlr.OrderByFragmentTranslator=trace
\ No newline at end of file