diff --git a/core/pom.xml b/core/pom.xml
index c6b0a539b5..f1696542e9 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -76,6 +76,8 @@
${antlrPluginVersion}
hql.g,hql-sql.g,sql-gen.g,order-by.g,order-by-render.g
+ true
+ true
diff --git a/core/src/main/antlr/hql-sql.g b/core/src/main/antlr/hql-sql.g
index 03314bc470..40cee752e4 100644
--- a/core/src/main/antlr/hql-sql.g
+++ b/core/src/main/antlr/hql-sql.g
@@ -230,6 +230,10 @@ tokens
protected void prepareLogicOperator(AST operator) throws SemanticException { }
protected void prepareArithmeticOperator(AST operator) throws SemanticException { }
+
+ protected void processMapComponentReference(AST node) throws SemanticException { }
+
+ protected void validateMapPropertyExpression(AST node) throws SemanticException { }
}
// The main statement rule.
@@ -640,7 +644,11 @@ propertyName
;
propertyRef!
- : #(d:DOT lhs:propertyRefLhs rhs:propertyName ) {
+ : mcr:mapComponentReference {
+ resolve( #mcr );
+ #propertyRef = #mcr;
+ }
+ | #(d:DOT lhs:propertyRefLhs rhs:propertyName ) {
// This gives lookupProperty() a chance to transform the tree to process collection properties (.elements, etc).
#propertyRef = #(#d, #lhs, #rhs);
#propertyRef = lookupProperty(#propertyRef,false,true);
@@ -672,6 +680,18 @@ aliasRef!
}
;
+mapComponentReference
+ : #( KEY mapPropertyExpression )
+ | #( VALUE mapPropertyExpression )
+ | #( ENTRY mapPropertyExpression )
+ ;
+
+mapPropertyExpression
+ : e:expr {
+ validateMapPropertyExpression( #e );
+ }
+ ;
+
parameter!
: #(c:COLON a:identifier) {
// Create a NAMED_PARAM node instead of (COLON IDENT).
diff --git a/core/src/main/antlr/hql.g b/core/src/main/antlr/hql.g
index 3e3cd581fe..06996541ec 100644
--- a/core/src/main/antlr/hql.g
+++ b/core/src/main/antlr/hql.g
@@ -99,6 +99,9 @@ tokens
OBJECT="object";
OF="of";
TRAILING="trailing";
+ KEY="key";
+ VALUE="value";
+ ENTRY="entry";
// -- Synthetic token types --
AGGREGATE; // One of the aggregate functions (e.g. min, max, avg)
@@ -633,7 +636,8 @@ vectorExpr
// NOTE: handleDotIdent() is called immediately after the first IDENT is recognized because
// the method looks a head to find keywords after DOT and turns them into identifiers.
identPrimary
- : identifier { handleDotIdent(); }
+ : mapComponentReference
+ | identifier { handleDotIdent(); }
( options { greedy=true; } : DOT^ ( identifier | ELEMENTS | o:OBJECT { #o.setType(IDENT); } ) )*
( options { greedy=true; } :
( op:OPEN^ { #op.setType(METHOD_CALL);} exprList CLOSE! )
@@ -642,11 +646,9 @@ identPrimary
| aggregate
;
-//## aggregate:
-//## ( aggregateFunction OPEN path CLOSE ) | ( COUNT OPEN STAR CLOSE ) | ( COUNT OPEN (DISTINCT | ALL) path CLOSE );
-
-//## aggregateFunction:
-//## COUNT | 'sum' | 'avg' | 'max' | 'min';
+mapComponentReference
+ : ( KEY^ | VALUE^ | ENTRY^ ) OPEN! path CLOSE!
+ ;
aggregate
: ( SUM^ | AVG^ | MAX^ | MIN^ ) OPEN! additiveExpression CLOSE! { #aggregate.setType(AGGREGATE); }
diff --git a/core/src/main/antlr/sql-gen.g b/core/src/main/antlr/sql-gen.g
index 9e860b574a..a617a4077a 100644
--- a/core/src/main/antlr/sql-gen.g
+++ b/core/src/main/antlr/sql-gen.g
@@ -207,6 +207,7 @@ selectColumn
selectExpr
: e:selectAtom { out(e); }
+ | mcr:mapComponentReference { out(mcr); }
| count
| #(CONSTRUCTOR (DOT | IDENT) ( selectColumn )+ )
| methodCall
@@ -240,6 +241,12 @@ selectAtom
| SELECT_EXPR
;
+mapComponentReference
+ : KEY
+ | VALUE
+ | ENTRY
+ ;
+
// The from-clause piece is all goofed up. Currently, nodes of type FROM_FRAGMENT
// and JOIN_FRAGMENT can occur at any level in the FromClause sub-tree. We really
// should come back and clean this up at some point; which I think will require
diff --git a/core/src/main/java/org/hibernate/hql/ast/HqlParser.java b/core/src/main/java/org/hibernate/hql/ast/HqlParser.java
index 80f0734ce0..a7bea13c04 100644
--- a/core/src/main/java/org/hibernate/hql/ast/HqlParser.java
+++ b/core/src/main/java/org/hibernate/hql/ast/HqlParser.java
@@ -42,6 +42,7 @@ import org.hibernate.hql.antlr.HqlTokenTypes;
import org.hibernate.hql.ast.util.ASTPrinter;
import org.hibernate.hql.ast.util.ASTUtil;
import org.hibernate.QueryException;
+import org.hibernate.util.StringHelper;
/**
* Implements the semantic action methods defined in the HQL base parser to keep the grammar
@@ -73,6 +74,28 @@ public final class HqlParser extends HqlBaseParser {
initialize();
}
+
+ // handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private int traceDepth = 0;
+
+
+ public void traceIn(String ruleName) {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = StringHelper.repeat( '-', (traceDepth++ * 2) ) + "-> ";
+ log.trace( prefix + ruleName );
+ }
+
+ public void traceOut(String ruleName) {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = "<-" + StringHelper.repeat( '-', (--traceDepth * 2) ) + " ";
+ log.trace( prefix + ruleName );
+ }
+
public void reportError(RecognitionException e) {
parseErrorHandler.reportError( e ); // Use the delegate.
}
diff --git a/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java b/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java
index 4dc1456c80..e88094a393 100644
--- a/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java
+++ b/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java
@@ -94,6 +94,7 @@ import org.hibernate.type.VersionType;
import org.hibernate.type.DbTimestampType;
import org.hibernate.usertype.UserVersionType;
import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.StringHelper;
import antlr.ASTFactory;
import antlr.RecognitionException;
@@ -169,6 +170,34 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
}
+ // handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private int traceDepth = 0;
+
+ public void traceIn(String ruleName, AST tree) {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = StringHelper.repeat( '-', (traceDepth++ * 2) ) + "-> ";
+ String traceText = ruleName + " (" + buildTraceNodeName(tree) + ")";
+ log.trace( prefix + traceText );
+ }
+
+ private String buildTraceNodeName(AST tree) {
+ return tree == null
+ ? "???"
+ : tree.getText() + " [" + printer.getTokenTypeName( tree.getType() ) + "]";
+ }
+
+ public void traceOut(String ruleName, AST tree) {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = "<-" + StringHelper.repeat( '-', (--traceDepth * 2) ) + " ";
+ log.trace( prefix + ruleName );
+ }
+
+
protected void prepareFromClauseInputTree(AST fromClauseInput) {
if ( !isSubQuery() ) {
// // inject param specifications to account for dynamic filter param values
@@ -826,11 +855,12 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
// This is called when it's time to fully resolve a path expression.
int type = node.getType();
switch ( type ) {
- case DOT:
+ case DOT: {
DotNode dot = ( DotNode ) node;
dot.resolveSelectExpression();
break;
- case ALIAS_REF:
+ }
+ case ALIAS_REF: {
// Notify the FROM element that it is being referenced by the select.
FromReferenceNode aliasRefNode = ( FromReferenceNode ) node;
//aliasRefNode.resolve( false, false, aliasRefNode.getText() ); //TODO: is it kosher to do it here?
@@ -839,8 +869,11 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
if ( fromElement != null ) {
fromElement.setIncludeSubclasses( true );
}
- default:
break;
+ }
+ default: {
+ break;
+ }
}
}
@@ -1101,6 +1134,22 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
( ( OperatorNode ) operator ).initialize();
}
+ protected void validateMapPropertyExpression(AST node) throws SemanticException {
+ try {
+ FromReferenceNode fromReferenceNode = (FromReferenceNode) node;
+ QueryableCollection collectionPersister = fromReferenceNode.getFromElement().getQueryableCollection();
+ if ( ! Map.class.isAssignableFrom( collectionPersister.getCollectionType().getReturnedClass() ) ) {
+ throw new SemanticException( "node did not reference a map" );
+ }
+ }
+ catch ( SemanticException se ) {
+ throw se;
+ }
+ catch ( Throwable t ) {
+ throw new SemanticException( "node did not reference a map" );
+ }
+ }
+
public static void panic() {
throw new QueryException( "TreeWalker: panic" );
}
diff --git a/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java b/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java
index 9b503463a0..311820ff2a 100644
--- a/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java
+++ b/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java
@@ -288,7 +288,6 @@ public class QueryTranslatorImpl implements FilterTranslator {
void showHqlAst(AST hqlAst) {
if ( AST_LOG.isDebugEnabled() ) {
ASTPrinter printer = new ASTPrinter( HqlTokenTypes.class );
- printer.setShowClassNames( false ); // The class names aren't interesting in the first tree.
AST_LOG.debug( printer.showAsString( hqlAst, "--- HQL AST ---" ) );
}
}
diff --git a/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java b/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java
index 2e357f0fa0..2fc9a7365e 100644
--- a/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java
+++ b/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java
@@ -65,6 +65,9 @@ import org.hibernate.hql.ast.tree.SessionFactoryAwareNode;
import org.hibernate.hql.ast.tree.BooleanLiteralNode;
import org.hibernate.hql.ast.tree.IsNullLogicOperatorNode;
import org.hibernate.hql.ast.tree.IsNotNullLogicOperatorNode;
+import org.hibernate.hql.ast.tree.MapKeyNode;
+import org.hibernate.hql.ast.tree.MapValueNode;
+import org.hibernate.hql.ast.tree.MapEntryNode;
import java.lang.reflect.Constructor;
@@ -187,6 +190,15 @@ public class SqlASTFactory extends ASTFactory implements HqlSqlTokenTypes {
return IsNotNullLogicOperatorNode.class;
case EXISTS:
return UnaryLogicOperatorNode.class;
+ case KEY: {
+ return MapKeyNode.class;
+ }
+ case VALUE: {
+ return MapValueNode.class;
+ }
+ case ENTRY: {
+ return MapEntryNode.class;
+ }
default:
return SqlNode.class;
} // switch
diff --git a/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java b/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java
index 3dfbd6aebd..22174d08cd 100644
--- a/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java
+++ b/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java
@@ -32,15 +32,21 @@ import java.util.Arrays;
import antlr.RecognitionException;
import antlr.collections.AST;
import org.hibernate.QueryException;
+import org.hibernate.util.StringHelper;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.hql.antlr.SqlGeneratorBase;
+import org.hibernate.hql.antlr.SqlTokenTypes;
import org.hibernate.hql.ast.tree.MethodNode;
import org.hibernate.hql.ast.tree.FromElement;
import org.hibernate.hql.ast.tree.Node;
import org.hibernate.hql.ast.tree.ParameterNode;
import org.hibernate.hql.ast.tree.ParameterContainer;
+import org.hibernate.hql.ast.util.ASTPrinter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Generates SQL by overriding callback methods in the base class, which does
@@ -50,10 +56,7 @@ import org.hibernate.hql.ast.tree.ParameterContainer;
* @author Steve Ebersole
*/
public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter {
- /**
- * Handles parser errors.
- */
- private ParseErrorHandler parseErrorHandler;
+ private static final Logger log = LoggerFactory.getLogger( SqlGenerator.class );
/**
* all append invocations on the buf should go through this Output instance variable.
@@ -64,12 +67,40 @@ public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter {
*/
private SqlWriter writer = new DefaultWriter();
+ private ParseErrorHandler parseErrorHandler;
private SessionFactoryImplementor sessionFactory;
-
private LinkedList outputStack = new LinkedList();
-
+ private final ASTPrinter printer = new ASTPrinter( SqlTokenTypes.class );
private List collectedParameters = new ArrayList();
+
+ // handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private int traceDepth = 0;
+
+ public void traceIn(String ruleName, AST tree) {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = StringHelper.repeat( '-', (traceDepth++ * 2) ) + "-> ";
+ String traceText = ruleName + " (" + buildTraceNodeName(tree) + ")";
+ log.trace( prefix + traceText );
+ }
+
+ private String buildTraceNodeName(AST tree) {
+ return tree == null
+ ? "???"
+ : tree.getText() + " [" + printer.getTokenTypeName( tree.getType() ) + "]";
+ }
+
+ public void traceOut(String ruleName, AST tree) {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = "<-" + StringHelper.repeat( '-', (--traceDepth * 2) ) + " ";
+ log.trace( prefix + ruleName );
+ }
+
public List getCollectedParameters() {
return collectedParameters;
}
diff --git a/core/src/main/java/org/hibernate/hql/ast/tree/AbstractMapComponentNode.java b/core/src/main/java/org/hibernate/hql/ast/tree/AbstractMapComponentNode.java
new file mode 100644
index 0000000000..74e7098e27
--- /dev/null
+++ b/core/src/main/java/org/hibernate/hql/ast/tree/AbstractMapComponentNode.java
@@ -0,0 +1,106 @@
+/*
+ * 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.hql.ast.tree;
+
+import java.util.Map;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.util.StringHelper;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractMapComponentNode extends FromReferenceNode implements HqlSqlTokenTypes {
+ private String[] columns;
+
+ public FromReferenceNode getMapReference() {
+ return ( FromReferenceNode ) getFirstChild();
+ }
+
+ public String[] getColumns() {
+ return columns;
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ ColumnHelper.generateScalarColumns( this, getColumns(), i );
+ }
+
+ public void resolve(
+ boolean generateJoin,
+ boolean implicitJoin,
+ String classAlias,
+ AST parent) throws SemanticException {
+ if ( parent != null ) {
+ throw attemptedDereference();
+ }
+
+ FromReferenceNode mapReference = getMapReference();
+ mapReference.resolve( true, true );
+ if ( mapReference.getDataType().isCollectionType() ) {
+ CollectionType collectionType = (CollectionType) mapReference.getDataType();
+ if ( Map.class.isAssignableFrom( collectionType.getReturnedClass() ) ) {
+ FromElement sourceFromElement = mapReference.getFromElement();
+ setFromElement( sourceFromElement );
+ setDataType( resolveType( sourceFromElement.getQueryableCollection() ) );
+ this.columns = resolveColumns( sourceFromElement.getQueryableCollection() );
+ initText( this.columns );
+ setFirstChild( null );
+ return;
+ }
+ }
+
+ throw nonMap();
+ }
+
+ private void initText(String[] columns) {
+ String text = StringHelper.join( ", ", columns );
+ if ( columns.length > 1 && getWalker().isComparativeExpressionClause() ) {
+ text = "(" + text + ")";
+ }
+ setText( text );
+ }
+
+ protected abstract String expressionDescription();
+ protected abstract String[] resolveColumns(QueryableCollection collectionPersister);
+ protected abstract Type resolveType(QueryableCollection collectionPersister);
+
+ protected SemanticException attemptedDereference() {
+ return new SemanticException( expressionDescription() + " expression cannot be further de-referenced" );
+ }
+
+ protected SemanticException nonMap() {
+ return new SemanticException( expressionDescription() + " expression did not reference map property" );
+ }
+
+ public void resolveIndex(AST parent) throws SemanticException {
+ throw new UnsupportedOperationException( expressionDescription() + " expression cannot be the source for an index operation" );
+ }
+}
diff --git a/core/src/main/java/org/hibernate/hql/ast/tree/AggregatedSelectExpression.java b/core/src/main/java/org/hibernate/hql/ast/tree/AggregatedSelectExpression.java
new file mode 100644
index 0000000000..8ce21f94ec
--- /dev/null
+++ b/core/src/main/java/org/hibernate/hql/ast/tree/AggregatedSelectExpression.java
@@ -0,0 +1,55 @@
+/*
+ * 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.hql.ast.tree;
+
+import java.util.List;
+
+import org.hibernate.transform.ResultTransformer;
+
+/**
+ * Contract for a select expression which aggregates other select expressions together into a single return
+ *
+ * @author Steve Ebersole
+ */
+public interface AggregatedSelectExpression extends SelectExpression {
+ /**
+ * Retrieves a list of the selection {@link org.hibernate.type.Type types} being aggregated
+ *
+ * @return The list of types.
+ */
+ public List getAggregatedSelectionTypeList();
+
+ /**
+ * Retrieve the aliases for the columns aggregated here.
+ *
+ * @return The column aliases.
+ */
+ public String[] getAggregatedAliases();
+
+ /**
+ * Retrieve the {@link ResultTransformer} responsible for building aggregated select expression results into their
+ * aggregated form.
+ *
+ * @return The appropriate transformer
+ */
+ public ResultTransformer getResultTransformer();
+}
diff --git a/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java b/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java
index cd60514ce5..5547904a9a 100644
--- a/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java
+++ b/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java
@@ -29,6 +29,10 @@ import java.util.Arrays;
import java.util.List;
import org.hibernate.PropertyNotFoundException;
+import org.hibernate.QueryException;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.transform.AliasToBeanConstructorResultTransformer;
+import org.hibernate.transform.Transformers;
import org.hibernate.hql.ast.DetailedSemanticException;
import org.hibernate.type.Type;
import org.hibernate.util.ReflectHelper;
@@ -42,13 +46,25 @@ import antlr.collections.AST;
*
* @author josh
*/
-public class ConstructorNode extends SelectExpressionList implements SelectExpression {
-
+public class ConstructorNode extends SelectExpressionList implements AggregatedSelectExpression {
private Constructor constructor;
private Type[] constructorArgumentTypes;
private boolean isMap;
private boolean isList;
-
+
+ public ResultTransformer getResultTransformer() {
+ if ( constructor != null ) {
+ return new AliasToBeanConstructorResultTransformer( constructor );
+ }
+ else if ( isMap ) {
+ return Transformers.ALIAS_TO_ENTITY_MAP;
+ }
+ else if ( isList ) {
+ return Transformers.TO_LIST;
+ }
+ throw new QueryException( "Unable to determine proper dynamic-instantiation tranformer to use." );
+ }
+
public boolean isMap() {
return isMap;
}
@@ -56,8 +72,17 @@ public class ConstructorNode extends SelectExpressionList implements SelectExpre
public boolean isList() {
return isList;
}
-
- public String[] getAliases() {
+
+ private String[] aggregatedAliases;
+
+ public String[] getAggregatedAliases() {
+ if ( aggregatedAliases == null ) {
+ aggregatedAliases = buildAggregatedAliases();
+ }
+ return aggregatedAliases;
+ }
+
+ private String[] buildAggregatedAliases() {
SelectExpression[] selectExpressions = collectSelectExpressions();
String[] aliases = new String[selectExpressions.length] ;
for ( int i=0; i
+ * Delegates to {@link #ASTPrinter(Class, boolean)} with {@link #isShowClassNames showClassNames} as true
*
- * @param tokenTypeConstants The class with token type constants in it.
+ * @param tokenTypeConstants The token types to use during printing; typically the {vocabulary}TokenTypes.java
+ * interface generated by ANTLR.
*/
public ASTPrinter(Class tokenTypeConstants) {
- this.tokenTypeConstants = tokenTypeConstants;
+ this( ASTUtil.generateTokenNameCache( tokenTypeConstants ), true );
+ }
+
+ public ASTPrinter(boolean showClassNames) {
+ this( ( Map ) null, showClassNames );
}
/**
- * Returns true if the node class names will be displayed.
+ * Constructs a printer.
*
- * @return true if the node class names will be displayed.
+ * @param tokenTypeConstants The token types to use during printing; typically the {vocabulary}TokenTypes.java
+ * interface generated by ANTLR.
+ * @param showClassNames Should the AST class names be shown.
+ */
+ public ASTPrinter(Class tokenTypeConstants, boolean showClassNames) {
+ this( ASTUtil.generateTokenNameCache( tokenTypeConstants ), showClassNames );
+ }
+
+ private ASTPrinter(Map tokenTypeNameCache, boolean showClassNames) {
+ this.tokenTypeNameCache = tokenTypeNameCache;
+ this.showClassNames = showClassNames;
+ }
+
+ /**
+ * Getter for property 'showClassNames'.
+ *
+ * @return Value for property 'showClassNames'.
*/
public boolean isShowClassNames() {
return showClassNames;
}
/**
- * Enables or disables AST node class name display.
+ * Renders the AST into 'ASCII art' form and returns that string representation.
*
- * @param showClassNames true to enable class name display, false to disable
- */
- public void setShowClassNames(boolean showClassNames) {
- this.showClassNames = showClassNames;
- }
-
- /**
- * Prints the AST in 'ASCII art' tree form to the specified print stream.
- *
- * @param ast The AST to print.
- * @param out The print stream.
- */
- private void showAst(AST ast, PrintStream out) {
- showAst( ast, new PrintWriter( out ) );
- }
-
- /**
- * Prints the AST in 'ASCII art' tree form to the specified print writer.
- *
- * @param ast The AST to print.
- * @param pw The print writer.
- */
- public void showAst(AST ast, PrintWriter pw) {
- ArrayList parents = new ArrayList();
- showAst( parents, pw, ast );
- pw.flush();
- }
-
- /**
- * Prints the AST in 'ASCII art' tree form into a string.
- *
- * @param ast The AST to display.
+ * @param ast The AST to display.
* @param header The header for the display.
+ *
* @return The AST in 'ASCII art' form, as a string.
*/
public String showAsString(AST ast, String header) {
@@ -116,53 +104,25 @@ public class ASTPrinter {
}
/**
- * Get a single token type name in the specified set of token type constants (interface).
+ * Prints the AST in 'ASCII art' form to the specified print stream.
*
- * @param tokenTypeConstants Token type constants interface (e.g. HqlSqlTokenTypes.class).
- * @param type The token type ( typically from ast.getType() ).
- * @return The token type name, *or* the integer value if the name could not be found for some reason.
+ * @param ast The AST to print.
+ * @param out The print stream to which the AST should be printed.
*/
- public static String getConstantName(Class tokenTypeConstants, int type) {
- String tokenTypeName = null;
- if ( tokenTypeConstants != null ) {
- Field[] fields = tokenTypeConstants.getFields();
- for ( int i = 0; i < fields.length; i++ ) {
- Field field = fields[i];
- tokenTypeName = getTokenTypeName( field, type, true );
- if ( tokenTypeName != null ) {
- break; // Stop if found.
- }
- } // for
- } // if type constants were provided
-
- // Use the integer value if no token type name was found
- if ( tokenTypeName == null ) {
- tokenTypeName = Integer.toString( type );
- }
-
- return tokenTypeName;
+ public void showAst(AST ast, PrintStream out) {
+ showAst( ast, new PrintWriter( out ) );
}
- private static String getTokenTypeName(Field field, int type, boolean checkType) {
- if ( Modifier.isStatic( field.getModifiers() ) ) {
- try {
- Object value = field.get( null );
- if ( !checkType ) {
- return field.getName();
- }
- else if ( value instanceof Integer ) {
- Integer integer = ( Integer ) value;
- if ( integer.intValue() == type ) {
- return field.getName();
- }
- } // if value is an integer
- } // try
- catch ( IllegalArgumentException ignore ) {
- }
- catch ( IllegalAccessException ignore ) {
- }
- } // if the field is static
- return null;
+ /**
+ * Prints the AST in 'ASCII art' tree form to the specified print writer.
+ *
+ * @param ast The AST to print.
+ * @param pw The print writer to which the AST should be written.
+ */
+ public void showAst(AST ast, PrintWriter pw) {
+ ArrayList parents = new ArrayList();
+ showAst( parents, pw, ast );
+ pw.flush();
}
/**
@@ -172,33 +132,16 @@ public class ASTPrinter {
* @return String - The token type name from the token type constant class,
* or just the integer as a string if none exists.
*/
- private String getTokenTypeName(int type) {
- // If the class with the constants in it was not supplied, just
- // use the integer token type as the token type name.
- if ( tokenTypeConstants == null ) {
- return Integer.toString( type );
+ public String getTokenTypeName(int type) {
+ final Integer typeInteger = new Integer( type );
+ String value = null;
+ if ( tokenTypeNameCache != null ) {
+ value = ( String ) tokenTypeNameCache.get( typeInteger );
}
-
- // Otherwise, create a type id -> name map from the class if it
- // hasn't already been created.
- if ( tokenTypeNamesByTokenType == null ) {
- Field[] fields = tokenTypeConstants.getFields();
- tokenTypeNamesByTokenType = new HashMap();
- String tokenTypeName = null;
- for ( int i = 0; i < fields.length; i++ ) {
- Field field = fields[i];
- tokenTypeName = getTokenTypeName( field, type, false );
- if ( tokenTypeName != null ) {
- try {
- tokenTypeNamesByTokenType.put( field.get( null ), field.getName() );
- }
- catch ( IllegalAccessException ignore ) {
- }
- }
- } // for
- } // if the map hasn't been created.
-
- return ( String ) tokenTypeNamesByTokenType.get( new Integer( type ) );
+ if ( value == null ) {
+ value = typeInteger.toString();
+ }
+ return value;
}
private void showAst(ArrayList parents, PrintWriter pw, AST ast) {
@@ -242,7 +185,7 @@ public class ASTPrinter {
public String nodeToString(AST ast, boolean showClassName) {
if ( ast == null ) {
- return "{null}";
+ return "{node:null}";
}
StringBuffer buf = new StringBuffer();
buf.append( "[" ).append( getTokenTypeName( ast.getType() ) ).append( "] " );
@@ -252,15 +195,17 @@ public class ASTPrinter {
buf.append( "'" );
String text = ast.getText();
- appendEscapedMultibyteChars(text, buf);
+ if ( text == null ) {
+ text = "{text:null}";
+ }
+ appendEscapedMultibyteChars(text, buf);
buf.append( "'" );
if ( ast instanceof DisplayableNode ) {
DisplayableNode displayableNode = ( DisplayableNode ) ast;
// Add a space before the display text.
buf.append( " " ).append( displayableNode.getDisplayText() );
}
- String s = buf.toString();
- return s;
+ return buf.toString();
}
public static void appendEscapedMultibyteChars(String text, StringBuffer buf) {
@@ -276,10 +221,9 @@ public class ASTPrinter {
}
}
- public static String escapeMultibyteChars(String text)
- {
+ public static String escapeMultibyteChars(String text) {
StringBuffer buf = new StringBuffer();
appendEscapedMultibyteChars(text,buf);
return buf.toString();
}
-}
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java b/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java
index cf60c11614..8febf2b3dd 100644
--- a/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java
+++ b/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java
@@ -26,6 +26,10 @@ package org.hibernate.hql.ast.util;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import antlr.ASTFactory;
import antlr.collections.AST;
@@ -35,46 +39,62 @@ import antlr.collections.impl.ASTArray;
* Provides utility methods for AST traversal and manipulation.
*
* @author Joshua Davis
+ * @author Steve Ebersole
*/
public final class ASTUtil {
/**
- * Private empty constructor.
- * (or else checkstyle says: 'warning: Utility classes should not have a public or default constructor.')
+ * Disallow instantiation.
*
- * @deprecated (tell clover to ignore this)
+ * @deprecated (tellclovertoignorethis)
*/
private ASTUtil() {
}
/**
* Creates a single node AST.
+ *
+ * TODO : this is silly, remove it...
*
* @param astFactory The factory.
- * @param type The node type.
- * @param text The node text.
+ * @param type The node type.
+ * @param text The node text.
+ *
* @return AST - A single node tree.
+ *
+ * @deprecated silly
*/
public static AST create(ASTFactory astFactory, int type, String text) {
- AST node = astFactory.create( type, text );
- return node;
+ return astFactory.create( type, text );
}
/**
- * Creates a single node AST as a sibling.
+ * Creates a single node AST as a sibling of the passed prevSibling,
+ * taking care to reorganize the tree correctly to account for this
+ * newly created node.
*
- * @param astFactory The factory.
- * @param type The node type.
- * @param text The node text.
+ * @param astFactory The factory.
+ * @param type The node type.
+ * @param text The node text.
* @param prevSibling The previous sibling.
- * @return AST - A single node tree.
+ *
+ * @return The created AST node.
*/
public static AST createSibling(ASTFactory astFactory, int type, String text, AST prevSibling) {
AST node = astFactory.create( type, text );
- node.setNextSibling( prevSibling.getNextSibling() );
- prevSibling.setNextSibling( node );
- return node;
+ return insertSibling( node, prevSibling );
}
+ /**
+ * Inserts a node into a child subtree as a particularly positioned
+ * sibling taking care to properly reorganize the tree to account for this
+ * new addition.
+ *
+ * @param node The node to insert
+ * @param prevSibling The previous node at the sibling position
+ * where we want this node inserted.
+ *
+ * @return The return is the same as the node parameter passed in.
+ */
public static AST insertSibling(AST node, AST prevSibling) {
node.setNextSibling( prevSibling.getNextSibling() );
prevSibling.setNextSibling( node );
@@ -85,11 +105,12 @@ public final class ASTUtil {
* Creates a 'binary operator' subtree, given the information about the
* parent and the two child nodex.
*
- * @param factory The AST factory.
+ * @param factory The AST factory.
* @param parentType The type of the parent node.
* @param parentText The text of the parent node.
- * @param child1 The first child.
- * @param child2 The second child.
+ * @param child1 The first child.
+ * @param child2 The second child.
+ *
* @return AST - A new sub-tree of the form "(parent child1 child2)"
*/
public static AST createBinarySubtree(ASTFactory factory, int parentType, String parentText, AST child1, AST child2) {
@@ -102,10 +123,11 @@ public final class ASTUtil {
* Creates a single parent of the specified child (i.e. a 'unary operator'
* subtree).
*
- * @param factory The AST factory.
+ * @param factory The AST factory.
* @param parentType The type of the parent node.
* @param parentText The text of the parent node.
- * @param child The child.
+ * @param child The child.
+ *
* @return AST - A new sub-tree of the form "(parent child)"
*/
public static AST createParent(ASTFactory factory, int parentType, String parentText, AST child) {
@@ -126,100 +148,6 @@ public final class ASTUtil {
return array[0];
}
- /**
- * Finds the first node of the specified type in the chain of children.
- *
- * @param parent The parent
- * @param type The type to find.
- * @return The first node of the specified type, or null if not found.
- */
- public static AST findTypeInChildren(AST parent, int type) {
- AST n = parent.getFirstChild();
- while ( n != null && n.getType() != type ) {
- n = n.getNextSibling();
- }
- return n;
- }
-
- /**
- * Returns the last direct child of 'n'.
- *
- * @param n The parent
- * @return The last direct child of 'n'.
- */
- public static AST getLastChild(AST n) {
- return getLastSibling( n.getFirstChild() );
- }
-
- /**
- * Returns the last sibling of 'a'.
- *
- * @param a The sibling.
- * @return The last sibling of 'a'.
- */
- private static AST getLastSibling(AST a) {
- AST last = null;
- while ( a != null ) {
- last = a;
- a = a.getNextSibling();
- }
- return last;
- }
-
- /**
- * Returns the 'list' representation with some brackets around it for debugging.
- *
- * @param n The tree.
- * @return The list representation of the tree.
- */
- public static String getDebugString(AST n) {
- StringBuffer buf = new StringBuffer();
- buf.append( "[ " );
- buf.append( ( n == null ) ? "{null}" : n.toStringTree() );
- buf.append( " ]" );
- return buf.toString();
- }
-
- /**
- * Find the previous sibling in the parent for the given child.
- *
- * @param parent the parent node
- * @param child the child to find the previous sibling of
- * @return the previous sibling of the child
- */
- public static AST findPreviousSibling(AST parent, AST child) {
- AST prev = null;
- AST n = parent.getFirstChild();
- while ( n != null ) {
- if ( n == child ) {
- return prev;
- }
- prev = n;
- n = n.getNextSibling();
- }
- throw new IllegalArgumentException( "Child not found in parent!" );
- }
-
- /**
- * Determine if a given node (test) is a direct (throtle to one level down)
- * child of another given node (fixture).
- *
- * @param fixture The node against which to testto be checked for children.
- * @param test The node to be tested as being a child of the parent.
- * @return True if test is contained in the fixtures's direct children;
- * false otherwise.
- */
- public static boolean isDirectChild(AST fixture, AST test) {
- AST n = fixture.getFirstChild();
- while ( n != null ) {
- if ( n == test ) {
- return true;
- }
- n = n.getNextSibling();
- }
- return false;
- }
-
/**
* Determine if a given node (test) is contained anywhere in the subtree
* of another given node (fixture).
@@ -242,11 +170,90 @@ public final class ASTUtil {
return false;
}
+ /**
+ * Finds the first node of the specified type in the chain of children.
+ *
+ * @param parent The parent
+ * @param type The type to find.
+ *
+ * @return The first node of the specified type, or null if not found.
+ */
+ public static AST findTypeInChildren(AST parent, int type) {
+ AST n = parent.getFirstChild();
+ while ( n != null && n.getType() != type ) {
+ n = n.getNextSibling();
+ }
+ return n;
+ }
+
+ /**
+ * Returns the last direct child of 'n'.
+ *
+ * @param n The parent
+ *
+ * @return The last direct child of 'n'.
+ */
+ public static AST getLastChild(AST n) {
+ return getLastSibling( n.getFirstChild() );
+ }
+
+ /**
+ * Returns the last sibling of 'a'.
+ *
+ * @param a The sibling.
+ *
+ * @return The last sibling of 'a'.
+ */
+ private static AST getLastSibling(AST a) {
+ AST last = null;
+ while ( a != null ) {
+ last = a;
+ a = a.getNextSibling();
+ }
+ return last;
+ }
+
+ /**
+ * Returns the 'list' representation with some brackets around it for debugging.
+ *
+ * @param n The tree.
+ *
+ * @return The list representation of the tree.
+ */
+ public static String getDebugString(AST n) {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "[ " );
+ buf.append( ( n == null ) ? "{null}" : n.toStringTree() );
+ buf.append( " ]" );
+ return buf.toString();
+ }
+
+ /**
+ * Find the previous sibling in the parent for the given child.
+ *
+ * @param parent the parent node
+ * @param child the child to find the previous sibling of
+ *
+ * @return the previous sibling of the child
+ */
+ public static AST findPreviousSibling(AST parent, AST child) {
+ AST prev = null;
+ AST n = parent.getFirstChild();
+ while ( n != null ) {
+ if ( n == child ) {
+ return prev;
+ }
+ prev = n;
+ n = n.getNextSibling();
+ }
+ throw new IllegalArgumentException( "Child not found in parent!" );
+ }
+
/**
* Makes the child node a sibling of the parent, reconnecting all siblings.
*
* @param parent the parent
- * @param child the child
+ * @param child the child
*/
public static void makeSiblingOfParent(AST parent, AST child) {
AST prev = findPreviousSibling( parent, child );
@@ -295,7 +302,7 @@ public final class ASTUtil {
* Inserts the child as the first child of the parent, all other children are shifted over to the 'right'.
*
* @param parent the parent
- * @param child the new first child
+ * @param child the new first child
*/
public static void insertChild(AST parent, AST child) {
if ( parent.getFirstChild() == null ) {
@@ -308,6 +315,13 @@ public final class ASTUtil {
}
}
+ private static ASTArray createAstArray(ASTFactory factory, int size, int parentType, String parentText, AST child1) {
+ ASTArray array = new ASTArray( size );
+ array.add( factory.create( parentType, parentText ) );
+ array.add( child1 );
+ return array;
+ }
+
/**
* Filters nodes out of a tree.
*/
@@ -316,6 +330,7 @@ public final class ASTUtil {
* Returns true if the node should be filtered out.
*
* @param n The node.
+ *
* @return true if the node should be filtered out, false to keep the node.
*/
boolean exclude(AST n);
@@ -332,17 +347,7 @@ public final class ASTUtil {
public abstract boolean include(AST node);
}
- private static ASTArray createAstArray(ASTFactory factory, int size, int parentType, String parentText, AST child1) {
- ASTArray array = new ASTArray( size );
- array.add( factory.create( parentType, parentText ) );
- array.add( child1 );
- return array;
- }
-
public static List collectChildren(AST root, FilterPredicate predicate) {
-// List children = new ArrayList();
-// collectChildren( children, root, predicate );
-// return children;
return new CollectingNodeVisitor( predicate ).collect( root );
}
@@ -371,13 +376,89 @@ public final class ASTUtil {
}
}
- private static void collectChildren(List children, AST root, FilterPredicate predicate) {
- for ( AST n = root.getFirstChild(); n != null; n = n.getNextSibling() ) {
- if ( predicate == null || !predicate.exclude( n ) ) {
- children.add( n );
+ /**
+ * Method to generate a map of token type names, keyed by their token type values.
+ *
+ * @param tokenTypeInterface The *TokenTypes interface (or implementor of said interface).
+ * @return The generated map.
+ */
+ public static Map generateTokenNameCache(Class tokenTypeInterface) {
+ final Field[] fields = tokenTypeInterface.getFields();
+ Map cache = new HashMap( (int)( fields.length * .75 ) + 1 );
+ for ( int i = 0; i < fields.length; i++ ) {
+ final Field field = fields[i];
+ if ( Modifier.isStatic( field.getModifiers() ) ) {
+ try {
+ cache.put( field.get( null ), field.getName() );
+ }
+ catch ( Throwable ignore ) {
+ }
}
- collectChildren( children, n, predicate );
}
+ return cache;
}
+ /**
+ * Get the name of a constant defined on the given class which has the given value.
+ *
+ * Note, if multiple constants have this value, the first will be returned which is known to be different
+ * on different JVM implementations.
+ *
+ * @param owner The class which defines the constant
+ * @param value The value of the constant.
+ *
+ * @return The token type name, *or* the integer value if the name could not be found.
+ *
+ * @deprecated Use #getTokenTypeName instead
+ */
+ public static String getConstantName(Class owner, int value) {
+ return getTokenTypeName( owner, value );
+ }
+
+ /**
+ * Intended to retrieve the name of an AST token type based on the token type interface. However, this
+ * method can be used to look up the name of any constant defined on a class/interface based on the constant value.
+ * Note that if multiple constants have this value, the first will be returned which is known to be different
+ * on different JVM implementations.
+ *
+ * @param tokenTypeInterface The *TokenTypes interface (or one of its implementors).
+ * @param tokenType The token type value.
+ *
+ * @return The corresponding name.
+ */
+ public static String getTokenTypeName(Class tokenTypeInterface, int tokenType) {
+ String tokenTypeName = Integer.toString( tokenType );
+ if ( tokenTypeInterface != null ) {
+ Field[] fields = tokenTypeInterface.getFields();
+ for ( int i = 0; i < fields.length; i++ ) {
+ final Integer fieldValue = extractIntegerValue( fields[i] );
+ if ( fieldValue != null && fieldValue.intValue() == tokenType ) {
+ tokenTypeName = fields[i].getName();
+ break;
+ }
+ }
+ }
+ return tokenTypeName;
+ }
+
+ private static Integer extractIntegerValue(Field field) {
+ Integer rtn = null;
+ try {
+ Object value = field.get( null );
+ if ( value instanceof Integer ) {
+ rtn = ( Integer ) value;
+ }
+ else if ( value instanceof Short ) {
+ rtn = new Integer( ( ( Short ) value ).intValue() );
+ }
+ else if ( value instanceof Long ) {
+ if ( ( ( Long ) value ).longValue() <= Integer.MAX_VALUE ) {
+ rtn = new Integer( ( ( Long ) value ).intValue() );
+ }
+ }
+ }
+ catch ( IllegalAccessException ignore ) {
+ }
+ return rtn;
+ }
}
diff --git a/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java b/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java
index 5ed86db81b..3e27c0d2ea 100644
--- a/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java
+++ b/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java
@@ -47,6 +47,7 @@ import org.hibernate.hql.ast.QueryTranslatorImpl;
import org.hibernate.hql.ast.tree.FromElement;
import org.hibernate.hql.ast.tree.SelectClause;
import org.hibernate.hql.ast.tree.QueryNode;
+import org.hibernate.hql.ast.tree.AggregatedSelectExpression;
import org.hibernate.impl.IteratorImpl;
import org.hibernate.loader.BasicLoader;
import org.hibernate.param.ParameterSpecification;
@@ -96,7 +97,7 @@ public class QueryLoader extends BasicLoader {
private int selectLength;
- private ResultTransformer selectNewTransformer;
+ private ResultTransformer implicitResultTransformer;
private String[] queryReturnAliases;
private LockMode[] defaultLockModes;
@@ -128,10 +129,10 @@ public class QueryLoader extends BasicLoader {
//sqlResultTypes = selectClause.getSqlResultTypes();
queryReturnTypes = selectClause.getQueryReturnTypes();
- selectNewTransformer = HolderInstantiator.createSelectNewTransformer(
- selectClause.getConstructor(),
- selectClause.isMap(),
- selectClause.isList());
+ AggregatedSelectExpression aggregatedSelectExpression = selectClause.getAggregatedSelectExpression();
+ implicitResultTransformer = aggregatedSelectExpression == null
+ ? null
+ : aggregatedSelectExpression.getResultTransformer();
queryReturnAliases = selectClause.getQueryReturnAliases();
List collectionFromElements = selectClause.getCollectionFromElements();
@@ -341,7 +342,7 @@ public class QueryLoader extends BasicLoader {
}
private boolean hasSelectNew() {
- return selectNewTransformer!=null;
+ return implicitResultTransformer != null;
}
protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
@@ -374,7 +375,7 @@ public class QueryLoader extends BasicLoader {
protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
// meant to handle dynamic instantiation queries...
- HolderInstantiator holderInstantiator = HolderInstantiator.getHolderInstantiator(selectNewTransformer, resultTransformer, queryReturnAliases);
+ HolderInstantiator holderInstantiator = buildHolderInstantiator( resultTransformer );
if ( holderInstantiator.isRequired() ) {
for ( int i = 0; i < results.size(); i++ ) {
Object[] row = ( Object[] ) results.get( i );
@@ -382,16 +383,25 @@ public class QueryLoader extends BasicLoader {
results.set( i, result );
}
- if(!hasSelectNew() && resultTransformer!=null) {
+ if ( !hasSelectNew() && resultTransformer != null ) {
return resultTransformer.transformList(results);
- } else {
+ }
+ else {
return results;
}
- } else {
+ }
+ else {
return results;
}
}
+ private HolderInstantiator buildHolderInstantiator(ResultTransformer queryLocalResultTransformer) {
+ return HolderInstantiator.getHolderInstantiator(
+ implicitResultTransformer,
+ queryLocalResultTransformer,
+ queryReturnAliases
+ );
+ }
// --- Query translator methods ---
public List list(
@@ -418,10 +428,8 @@ public class QueryLoader extends BasicLoader {
}
try {
-
final PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
-
- if(queryParameters.isCallable()) {
+ if ( queryParameters.isCallable() ) {
throw new QueryException("iterate() not supported for callable statements");
}
final ResultSet rs = getResultSet(st, queryParameters.hasAutoDiscoverScalarTypes(), false, queryParameters.getRowSelection(), session);
@@ -431,8 +439,8 @@ public class QueryLoader extends BasicLoader {
session,
queryReturnTypes,
queryTranslator.getColumnNames(),
- HolderInstantiator.getHolderInstantiator(selectNewTransformer, queryParameters.getResultTransformer(), queryReturnAliases)
- );
+ buildHolderInstantiator( queryParameters.getResultTransformer() )
+ );
if ( stats ) {
session.getFactory().getStatisticsImplementor().queryExecuted(
@@ -461,7 +469,12 @@ public class QueryLoader extends BasicLoader {
final QueryParameters queryParameters,
final SessionImplementor session) throws HibernateException {
checkQuery( queryParameters );
- return scroll( queryParameters, queryReturnTypes, HolderInstantiator.getHolderInstantiator(selectNewTransformer, queryParameters.getResultTransformer(), queryReturnAliases), session );
+ return scroll(
+ queryParameters,
+ queryReturnTypes,
+ buildHolderInstantiator( queryParameters.getResultTransformer() ),
+ session
+ );
}
// -- Implementation private methods --
diff --git a/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
index b80c30a17a..69b8e5ed4e 100644
--- a/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
+++ b/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
@@ -99,6 +99,7 @@ import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.Template;
import org.hibernate.sql.Update;
+import org.hibernate.sql.AliasGenerator;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.tuple.Tuplizer;
@@ -982,8 +983,14 @@ public abstract class AbstractEntityPersister
}
- public String propertySelectFragment(String name, String suffix, boolean allProperties) {
+ public String propertySelectFragment(String tableAlias, String suffix, boolean allProperties) {
+ return propertySelectFragmentFragment( tableAlias, suffix, allProperties ).toFragmentString();
+ }
+ public SelectFragment propertySelectFragmentFragment(
+ String tableAlias,
+ String suffix,
+ boolean allProperties) {
SelectFragment select = new SelectFragment()
.setSuffix( suffix )
.setUsedAliases( getIdentifierAliases() );
@@ -996,7 +1003,7 @@ public abstract class AbstractEntityPersister
!isSubclassTableSequentialSelect( columnTableNumbers[i] ) &&
subclassColumnSelectableClosure[i];
if ( selectable ) {
- String subalias = generateTableAlias( name, columnTableNumbers[i] );
+ String subalias = generateTableAlias( tableAlias, columnTableNumbers[i] );
select.addColumn( subalias, columns[i], columnAliases[i] );
}
}
@@ -1008,20 +1015,20 @@ public abstract class AbstractEntityPersister
boolean selectable = ( allProperties || !subclassFormulaLazyClosure[i] )
&& !isSubclassTableSequentialSelect( formulaTableNumbers[i] );
if ( selectable ) {
- String subalias = generateTableAlias( name, formulaTableNumbers[i] );
+ String subalias = generateTableAlias( tableAlias, formulaTableNumbers[i] );
select.addFormula( subalias, formulaTemplates[i], formulaAliases[i] );
}
}
if ( entityMetamodel.hasSubclasses() ) {
- addDiscriminatorToSelect( select, name, suffix );
+ addDiscriminatorToSelect( select, tableAlias, suffix );
}
if ( hasRowId() ) {
- select.addColumn( name, rowIdName, ROWID_ALIAS );
+ select.addColumn( tableAlias, rowIdName, ROWID_ALIAS );
}
- return select.toFragmentString();
+ return select;
}
public Object[] getDatabaseSnapshot(Serializable id, SessionImplementor session)
diff --git a/core/src/main/java/org/hibernate/persister/entity/Queryable.java b/core/src/main/java/org/hibernate/persister/entity/Queryable.java
index ddf9498a51..ba6ec2ceb1 100644
--- a/core/src/main/java/org/hibernate/persister/entity/Queryable.java
+++ b/core/src/main/java/org/hibernate/persister/entity/Queryable.java
@@ -24,6 +24,8 @@
*/
package org.hibernate.persister.entity;
+import org.hibernate.sql.SelectFragment;
+
/**
* Extends the generic EntityPersister contract to add
* operations required by the Hibernate Query Language
@@ -60,6 +62,7 @@ public interface Queryable extends Loadable, PropertyMapping, Joinable {
*/
public String propertySelectFragment(String alias, String suffix, boolean allProperties);
+ public SelectFragment propertySelectFragmentFragment(String alias, String suffix, boolean allProperties);
/**
* Get the names of columns used to persist the identifier
*/
diff --git a/core/src/main/java/org/hibernate/sql/AliasGenerator.java b/core/src/main/java/org/hibernate/sql/AliasGenerator.java
new file mode 100644
index 0000000000..b3e5830dba
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/AliasGenerator.java
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public interface AliasGenerator {
+ public String generateAlias(String sqlExpression);
+}
diff --git a/core/src/main/java/org/hibernate/sql/SelectExpression.java b/core/src/main/java/org/hibernate/sql/SelectExpression.java
new file mode 100644
index 0000000000..a511ff9d4d
--- /dev/null
+++ b/core/src/main/java/org/hibernate/sql/SelectExpression.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public interface SelectExpression {
+ public String getExpression();
+ public String getAlias();
+}
diff --git a/core/src/main/java/org/hibernate/sql/SelectFragment.java b/core/src/main/java/org/hibernate/sql/SelectFragment.java
index ceeff052f7..e0b67b4b60 100644
--- a/core/src/main/java/org/hibernate/sql/SelectFragment.java
+++ b/core/src/main/java/org/hibernate/sql/SelectFragment.java
@@ -46,7 +46,15 @@ public class SelectFragment {
private String[] usedAliases;
public SelectFragment() {}
-
+
+ public List getColumns() {
+ return columns;
+ }
+
+ public String getExtraSelectList() {
+ return extraSelectList;
+ }
+
public SelectFragment setUsedAliases(String[] aliases) {
usedAliases = aliases;
return this;
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
index 7db8b527f4..f2775f01e1 100644
--- a/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java
@@ -32,6 +32,7 @@ import antlr.collections.AST;
import org.hibernate.sql.Template;
import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,6 +53,28 @@ public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
this.context = context;
}
+
+ // handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private int traceDepth = 0;
+
+
+ public void traceIn(String ruleName) {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = StringHelper.repeat( '-', (traceDepth++ * 2) ) + "-> ";
+ log.trace( prefix + ruleName );
+ }
+
+ public void traceOut(String ruleName) {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = "<-" + StringHelper.repeat( '-', (--traceDepth * 2) ) + " ";
+ log.trace( prefix + ruleName );
+ }
+
/**
* {@inheritDoc}
*/
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
index 741ef77055..163ec4b1df 100644
--- a/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentRenderer.java
+++ b/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentRenderer.java
@@ -26,13 +26,50 @@ package org.hibernate.sql.ordering.antlr;
import antlr.collections.AST;
+import org.hibernate.util.StringHelper;
+import org.hibernate.hql.ast.util.ASTPrinter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class OrderByFragmentRenderer extends GeneratedOrderByFragmentRenderer {
+ private static final Logger log = LoggerFactory.getLogger( OrderByFragmentRenderer.class );
+ private static final ASTPrinter printer = new ASTPrinter( GeneratedOrderByFragmentRendererTokenTypes.class );
+
protected void out(AST ast) {
out( ( ( Node ) ast ).getRenderableText() );
}
+
+
+ // handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private int traceDepth = 0;
+
+ public void traceIn(String ruleName, AST tree) {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = StringHelper.repeat( '-', (traceDepth++ * 2) ) + "-> ";
+ String traceText = ruleName + " (" + buildTraceNodeName(tree) + ")";
+ log.trace( prefix + traceText );
+ }
+
+ private String buildTraceNodeName(AST tree) {
+ return tree == null
+ ? "???"
+ : tree.getText() + " [" + printer.getTokenTypeName( tree.getType() ) + "]";
+ }
+
+ public void traceOut(String ruleName, AST tree) {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = "<-" + StringHelper.repeat( '-', (--traceDepth * 2) ) + " ";
+ log.trace( prefix + ruleName );
+ }
}
diff --git a/core/src/main/java/org/hibernate/util/ArrayHelper.java b/core/src/main/java/org/hibernate/util/ArrayHelper.java
index 4a7c14a525..48a527bfa3 100644
--- a/core/src/main/java/org/hibernate/util/ArrayHelper.java
+++ b/core/src/main/java/org/hibernate/util/ArrayHelper.java
@@ -84,7 +84,7 @@ public final class ArrayHelper {
}
public static String[] toStringArray(Collection coll) {
- return (String[]) coll.toArray(EMPTY_STRING_ARRAY);
+ return (String[]) coll.toArray( new String[coll.size()] );
}
public static String[][] to2DStringArray(Collection coll) {
@@ -96,7 +96,7 @@ public final class ArrayHelper {
}
public static Type[] toTypeArray(Collection coll) {
- return (Type[]) coll.toArray(EMPTY_TYPE_ARRAY);
+ return (Type[]) coll.toArray( new Type[coll.size()] );
}
public static int[] toIntArray(Collection coll) {
@@ -136,17 +136,13 @@ public final class ArrayHelper {
public static String[] slice(String[] strings, int begin, int length) {
String[] result = new String[length];
- for ( int i=0; i