[hibernate-commits] Hibernate SVN: r17300 - in core/trunk: core/src/main/antlr and 10 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Thu Aug 13 15:30:58 EDT 2009


Author: steve.ebersole at jboss.com
Date: 2009-08-13 15:30:57 -0400 (Thu, 13 Aug 2009)
New Revision: 17300

Added:
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/AbstractMapComponentNode.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/AggregatedSelectExpression.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapEntryNode.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapKeyNode.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapValueNode.java
   core/trunk/core/src/main/java/org/hibernate/sql/AliasGenerator.java
   core/trunk/core/src/main/java/org/hibernate/sql/SelectExpression.java
Modified:
   core/trunk/core/pom.xml
   core/trunk/core/src/main/antlr/hql-sql.g
   core/trunk/core/src/main/antlr/hql.g
   core/trunk/core/src/main/antlr/sql-gen.g
   core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlParser.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/DotNode.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/SelectClause.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/util/ASTPrinter.java
   core/trunk/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java
   core/trunk/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java
   core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
   core/trunk/core/src/main/java/org/hibernate/persister/entity/Queryable.java
   core/trunk/core/src/main/java/org/hibernate/sql/SelectFragment.java
   core/trunk/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java
   core/trunk/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentRenderer.java
   core/trunk/core/src/main/java/org/hibernate/util/ArrayHelper.java
   core/trunk/core/src/main/java/org/hibernate/util/StringHelper.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java
   core/trunk/testsuite/src/test/resources/log4j.properties
Log:
HHH-4081 - Support for JPA 2.0 "qualified identification variables" (KEY, VALUE and ENTRY)


Modified: core/trunk/core/pom.xml
===================================================================
--- core/trunk/core/pom.xml	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/pom.xml	2009-08-13 19:30:57 UTC (rev 17300)
@@ -76,6 +76,8 @@
                 <version>${antlrPluginVersion}</version>
                 <configuration>
                     <grammars>hql.g,hql-sql.g,sql-gen.g,order-by.g,order-by-render.g</grammars>
+                    <traceParser>true</traceParser>
+                    <traceTreeParser>true</traceTreeParser>
                 </configuration>
                 <executions>
                     <execution>

Modified: core/trunk/core/src/main/antlr/hql-sql.g
===================================================================
--- core/trunk/core/src/main/antlr/hql-sql.g	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/antlr/hql-sql.g	2009-08-13 19:30:57 UTC (rev 17300)
@@ -230,6 +230,10 @@
 	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 @@
 	;
 
 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 @@
 		}
 	;
 
+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).

Modified: core/trunk/core/src/main/antlr/hql.g
===================================================================
--- core/trunk/core/src/main/antlr/hql.g	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/antlr/hql.g	2009-08-13 19:30:57 UTC (rev 17300)
@@ -99,6 +99,9 @@
 	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 @@
 // 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,12 +646,10 @@
 	| aggregate
 	;
 
-//## aggregate:
-//##     ( aggregateFunction OPEN path CLOSE ) | ( COUNT OPEN STAR CLOSE ) | ( COUNT OPEN (DISTINCT | ALL) path CLOSE );
+mapComponentReference
+    : ( KEY^ | VALUE^ | ENTRY^ ) OPEN! path CLOSE!
+    ;
 
-//## aggregateFunction:
-//##     COUNT | 'sum' | 'avg' | 'max' | 'min';
-
 aggregate
 	: ( SUM^ | AVG^ | MAX^ | MIN^ ) OPEN! additiveExpression CLOSE! { #aggregate.setType(AGGREGATE); }
 	// Special case for count - It's 'parameters' can be keywords.

Modified: core/trunk/core/src/main/antlr/sql-gen.g
===================================================================
--- core/trunk/core/src/main/antlr/sql-gen.g	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/antlr/sql-gen.g	2009-08-13 19:30:57 UTC (rev 17300)
@@ -207,6 +207,7 @@
 
 selectExpr
 	: e:selectAtom { out(e); }
+	| mcr:mapComponentReference { out(mcr); }
 	| count
 	| #(CONSTRUCTOR (DOT | IDENT) ( selectColumn )+ )
 	| methodCall
@@ -240,6 +241,12 @@
 	| 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

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlParser.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlParser.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlParser.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -42,6 +42,7 @@
 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 @@
 		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.
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -94,6 +94,7 @@
 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 @@
 	}
 
 
+	// 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 @@
 		// 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 @@
 				if ( fromElement != null ) {
 					fromElement.setIncludeSubclasses( true );
 				}
-			default:
 				break;
+			}
+			default: {
+				break;
+			}
 		}
 	}
 
@@ -1101,6 +1134,22 @@
 		( ( 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" );
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -288,7 +288,6 @@
 	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 ---" ) );
 		}
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -65,6 +65,9 @@
 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 @@
 				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

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -32,16 +32,22 @@
 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
  * the actual SQL AST walking.
@@ -50,10 +56,7 @@
  * @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 @@
 	 */
 	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;
 	}

Added: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/AbstractMapComponentNode.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/AbstractMapComponentNode.java	                        (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/AbstractMapComponentNode.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -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" );
+	}
+}

Added: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/AggregatedSelectExpression.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/AggregatedSelectExpression.java	                        (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/AggregatedSelectExpression.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -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();
+}

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -29,6 +29,10 @@
 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 @@
  *
  * @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 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<selectExpressions.length; i++ ) {
@@ -154,6 +179,10 @@
 		return Arrays.asList( constructorArgumentTypes );
 	}
 
+	public List getAggregatedSelectionTypeList() {
+		return getConstructorArgumentTypeList();
+	}
+
 	public FromElement getFromElement() {
 		return null;
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/DotNode.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/DotNode.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/DotNode.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -28,7 +28,6 @@
 import org.hibernate.engine.JoinSequence;
 import org.hibernate.hql.CollectionProperties;
 import org.hibernate.hql.antlr.SqlTokenTypes;
-import org.hibernate.hql.ast.util.ASTPrinter;
 import org.hibernate.hql.ast.util.ASTUtil;
 import org.hibernate.hql.ast.util.ColumnHelper;
 import org.hibernate.persister.collection.QueryableCollection;
@@ -140,7 +139,7 @@
 		StringBuffer buf = new StringBuffer();
 		FromElement fromElement = getFromElement();
 		buf.append( "{propertyName=" ).append( propertyName );
-		buf.append( ",dereferenceType=" ).append( ASTPrinter.getConstantName( getClass(), dereferenceType ) );
+		buf.append( ",dereferenceType=" ).append( getWalker().getASTPrinter().getTokenTypeName( dereferenceType ) );
 		buf.append( ",propertyPath=" ).append( propertyPath );
 		buf.append( ",path=" ).append( getPath() );
 		if ( fromElement != null ) {

Added: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapEntryNode.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapEntryNode.java	                        (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapEntryNode.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -0,0 +1,273 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Iterator;
+
+import antlr.SemanticException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.sql.SelectExpression;
+import org.hibernate.sql.AliasGenerator;
+import org.hibernate.sql.SelectFragment;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.hql.NameGenerator;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.transform.BasicTransformerAdapter;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.Type;
+import org.hibernate.type.EntityType;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public class MapEntryNode extends AbstractMapComponentNode implements AggregatedSelectExpression {
+	private static class LocalAliasGenerator implements AliasGenerator {
+		private final int base;
+		private int counter = 0;
+
+		private LocalAliasGenerator(int base) {
+			this.base = base;
+		}
+
+		public String generateAlias(String sqlExpression) {
+			return NameGenerator.scalarName( base, counter++ );
+		}
+	}
+
+	protected String expressionDescription() {
+		return "entry(*)";
+	}
+
+	protected Type resolveType(QueryableCollection collectionPersister) {
+		Type keyType = collectionPersister.getIndexType();
+		Type valueType = collectionPersister.getElementType();
+		types.add( keyType );
+		types.add( valueType );
+		mapEntryBuilder = new MapEntryBuilder();
+
+		// an entry (as an aggregated select expression) does not have a type...
+		return null;
+	}
+
+	protected String[] resolveColumns(QueryableCollection collectionPersister) {
+		List selections = new ArrayList();
+		determineKeySelectExpressions( collectionPersister, selections );
+		determineValueSelectExpressions( collectionPersister, selections );
+
+		String text = "";
+		String[] columns = new String[selections.size()];
+		for ( int i = 0; i < selections.size(); i++ ) {
+			SelectExpression selectExpression = (SelectExpression) selections.get(i);
+			text += ( ", " + selectExpression.getExpression() + " as " + selectExpression.getAlias() );
+			columns[i] = selectExpression.getExpression();
+		}
+
+		text = text.substring( 2 ); //strip leading ", "
+		setText( text );
+		setResolved();
+		return columns;
+	}
+
+	private void determineKeySelectExpressions(QueryableCollection collectionPersister, List selections) {
+		AliasGenerator aliasGenerator = new LocalAliasGenerator( 0 );
+		appendSelectExpressions( collectionPersister.getIndexColumnNames(), selections, aliasGenerator );
+		Type keyType = collectionPersister.getIndexType();
+		if ( keyType.isAssociationType() ) {
+			EntityType entityType = (EntityType) keyType;
+			Queryable keyEntityPersister = ( Queryable ) sfi().getEntityPersister(
+					entityType.getAssociatedEntityName( sfi() )
+			);
+			SelectFragment fragment = keyEntityPersister.propertySelectFragmentFragment(
+					collectionTableAlias(),
+					null,
+					false
+			);
+			appendSelectExpressions( fragment, selections, aliasGenerator );
+		}
+	}
+
+	private void appendSelectExpressions(String[] columnNames, List selections, AliasGenerator aliasGenerator) {
+		for ( int i = 0; i < columnNames.length; i++ ) {
+			selections.add(
+					new BasicSelectExpression(
+							collectionTableAlias() + '.' + columnNames[i],
+							aliasGenerator.generateAlias( columnNames[i] )
+					)
+			);
+		}
+	}
+
+	private void appendSelectExpressions(SelectFragment fragment, List selections, AliasGenerator aliasGenerator) {
+		Iterator itr = fragment.getColumns().iterator();
+		while ( itr.hasNext() ) {
+			final String column = (String) itr.next();
+			selections.add(
+					new BasicSelectExpression( column, aliasGenerator.generateAlias( column ) )
+			);
+		}
+	}
+
+	private void determineValueSelectExpressions(QueryableCollection collectionPersister, List selections) {
+		AliasGenerator aliasGenerator = new LocalAliasGenerator( 1 );
+		appendSelectExpressions( collectionPersister.getElementColumnNames(), selections, aliasGenerator );
+		Type valueType = collectionPersister.getElementType();
+		if ( valueType.isAssociationType() ) {
+			EntityType valueEntityType = (EntityType) valueType;
+			Queryable valueEntityPersister = ( Queryable ) sfi().getEntityPersister(
+					valueEntityType.getAssociatedEntityName( sfi() )
+			);
+			SelectFragment fragment = valueEntityPersister.propertySelectFragmentFragment(
+					elementTableAlias(),
+					null,
+					false
+			);
+			appendSelectExpressions( fragment, selections, aliasGenerator );
+		}
+	}
+
+	private String collectionTableAlias() {
+		return getFromElement().getCollectionTableAlias() != null
+				? getFromElement().getCollectionTableAlias()
+				: getFromElement().getTableAlias();
+	}
+
+	private String elementTableAlias() {
+		return getFromElement().getTableAlias();
+	}
+
+	private static class BasicSelectExpression implements SelectExpression {
+		private final String expression;
+		private final String alias;
+
+		private BasicSelectExpression(String expression, String alias) {
+			this.expression = expression;
+			this.alias = alias;
+		}
+
+		public String getExpression() {
+			return expression;
+		}
+
+		public String getAlias() {
+			return alias;
+		}
+	}
+
+	public SessionFactoryImplementor sfi() {
+		return getSessionFactoryHelper().getFactory();
+	}
+
+	public void setText(String s) {
+		if ( isResolved() ) {
+			return;
+		}
+		super.setText( s );
+	}
+
+	public void setScalarColumnText(int i) throws SemanticException {
+	}
+
+	public boolean isScalar() {
+		// Constructors are always considered scalar results.
+		return true;
+	}
+
+	private List types = new ArrayList(4); // size=4 to prevent resizing
+
+	public List getAggregatedSelectionTypeList() {
+		return types;
+	}
+
+	private static final String[] ALIASES = { null, null };
+
+	public String[] getAggregatedAliases() {
+		return ALIASES;
+	}
+
+	private MapEntryBuilder mapEntryBuilder;
+
+	public ResultTransformer getResultTransformer() {
+		return mapEntryBuilder;
+	}
+
+	private static class MapEntryBuilder extends BasicTransformerAdapter {
+		public Object transformTuple(Object[] tuple, String[] aliases) {
+			if ( tuple.length != 2 ) {
+				throw new HibernateException( "Expecting exactly 2 tuples to transform into Map.Entry" );
+			}
+			return new EntryAdapter( tuple[0], tuple[1] );
+		}
+	}
+
+	private static class EntryAdapter implements Map.Entry {
+		private final Object key;
+		private Object value;
+
+		private EntryAdapter(Object key, Object value) {
+			this.key = key;
+			this.value = value;
+		}
+
+		public Object getValue() {
+			return value;
+		}
+
+		public Object getKey() {
+			return key;
+		}
+
+		public Object setValue(Object value) {
+			Object old = this.value;
+			this.value = value;
+			return old;
+		}
+
+		public boolean equals(Object o) {
+			// IMPL NOTE : nulls are considered equal for keys and values according to Map.Entry contract
+			if ( this == o ) {
+				return true;
+			}
+			if ( o == null || getClass() != o.getClass() ) {
+				return false;
+			}
+			EntryAdapter that = ( EntryAdapter ) o;
+
+			// make sure we have the same types...
+			return ( key == null ? that.key == null : key.equals( that.key ) )
+					&& ( value == null ? that.value == null : value.equals( that.value ) );
+
+		}
+
+		public int hashCode() {
+			int keyHash = key == null ? 0 : key.hashCode();
+			int valueHash = value == null ? 0 : value.hashCode();
+			return keyHash ^ valueHash;
+		}
+	}
+}

Added: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapKeyNode.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapKeyNode.java	                        (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapKeyNode.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -0,0 +1,44 @@
+/*
+ * 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 org.hibernate.type.Type;
+import org.hibernate.persister.collection.QueryableCollection;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public class MapKeyNode extends AbstractMapComponentNode {
+	protected String expressionDescription() {
+		return "key(*)";
+	}
+
+	protected String[] resolveColumns(QueryableCollection collectionPersister) {
+		return collectionPersister.getIndexColumnNames();
+	}
+
+	protected Type resolveType(QueryableCollection collectionPersister) {
+		return collectionPersister.getIndexType();
+	}
+}

Added: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapValueNode.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapValueNode.java	                        (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/MapValueNode.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -0,0 +1,44 @@
+/*
+ * 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 org.hibernate.type.Type;
+import org.hibernate.persister.collection.QueryableCollection;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public class MapValueNode extends AbstractMapComponentNode {
+	protected String expressionDescription() {
+		return "value(*)";
+	}
+
+	protected String[] resolveColumns(QueryableCollection collectionPersister) {
+		return collectionPersister.getElementColumnNames();
+	}
+
+	protected Type resolveType(QueryableCollection collectionPersister) {
+		return collectionPersister.getElementType();
+	}
+}

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/SelectClause.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/SelectClause.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/tree/SelectClause.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -24,7 +24,6 @@
  */
 package org.hibernate.hql.ast.tree;
 
-import java.lang.reflect.Constructor;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -53,10 +52,12 @@
 	//private Type[] sqlResultTypes;
 	private Type[] queryReturnTypes;
 	private String[][] columnNames;
-	private ConstructorNode constructorNode;
 	private List collectionFromElements;
 	private String[] aliases;
 
+	// Currently we can only have one...
+	private AggregatedSelectExpression aggregatedSelectExpression;
+
 	/**
 	 * Does this SelectClause represent a scalar query
 	 *
@@ -99,6 +100,8 @@
 	
 	/**
 	 * The HQL aliases, or generated aliases
+	 *
+	 * @return the aliases
 	 */
 	public String[] getQueryReturnAliases() {
 		return aliases;
@@ -113,29 +116,15 @@
 		return columnNames;
 	}
 
-	/**
-	 * The constructor to use for dynamic instantiation queries.
-	 *
-	 * @return The appropriate Constructor reference, or null if not a
-	 *         dynamic instantiation query.
-	 */
-	public Constructor getConstructor() {
-		return constructorNode == null ? null : constructorNode.getConstructor();
+	public AggregatedSelectExpression getAggregatedSelectExpression() {
+		return aggregatedSelectExpression;
 	}
-	
-	public boolean isMap() {
-		return constructorNode == null ? false : constructorNode.isMap();
-	}
-	
-	public boolean isList() {
-		return constructorNode == null ? false : constructorNode.isList();
-	}
-	
+
 	/**
 	 * Prepares an explicitly defined select clause.
 	 *
 	 * @param fromClause The from clause linked to this select clause.
-	 * @throws SemanticException
+	 * @throws SemanticException indicates a semntic issue with the explicit select clause.
 	 */
 	public void initializeExplicitSelectClause(FromClause fromClause) throws SemanticException {
 		if ( prepared ) {
@@ -152,30 +141,28 @@
 		SelectExpression[] selectExpressions = collectSelectExpressions();
 		
 		for ( int i = 0; i < selectExpressions.length; i++ ) {
-			SelectExpression expr = selectExpressions[i];
+			SelectExpression selectExpression = selectExpressions[i];
 
-			if ( expr.isConstructor() ) {
-				constructorNode = ( ConstructorNode ) expr;
-				List constructorArgumentTypeList = constructorNode.getConstructorArgumentTypeList();
-				//sqlResultTypeList.addAll( constructorArgumentTypeList );
-				queryReturnTypeList.addAll( constructorArgumentTypeList );
+			if ( AggregatedSelectExpression.class.isInstance( selectExpression ) ) {
+				aggregatedSelectExpression = (AggregatedSelectExpression) selectExpression;
+				queryReturnTypeList.addAll( aggregatedSelectExpression.getAggregatedSelectionTypeList() );
 				scalarSelect = true;
 			}
 			else {
-				Type type = expr.getDataType();
+				Type type = selectExpression.getDataType();
 				if ( type == null ) {
-					throw new IllegalStateException( "No data type for node: " + expr.getClass().getName() + " "
-							+ new ASTPrinter( SqlTokenTypes.class ).showAsString( ( AST ) expr, "" ) );
+					throw new IllegalStateException( "No data type for node: " + selectExpression.getClass().getName() + " "
+							+ new ASTPrinter( SqlTokenTypes.class ).showAsString( ( AST ) selectExpression, "" ) );
 				}
 				//sqlResultTypeList.add( type );
 
 				// If the data type is not an association type, it could not have been in the FROM clause.
-				if ( expr.isScalar() ) {
+				if ( selectExpression.isScalar() ) {
 					scalarSelect = true;
 				}
 
-				if ( isReturnableEntity( expr ) ) {
-					fromElementsForLoad.add( expr.getFromElement() );
+				if ( isReturnableEntity( selectExpression ) ) {
+					fromElementsForLoad.add( selectExpression.getFromElement() );
 				}
 
 				// Always add the type to the return type list.
@@ -253,8 +240,7 @@
 		finishInitialization( /*sqlResultTypeList,*/ queryReturnTypeList );
 	}
 
-	private void finishInitialization(/*ArrayList sqlResultTypeList,*/ ArrayList queryReturnTypeList) {
-		//sqlResultTypes = ( Type[] ) sqlResultTypeList.toArray( new Type[sqlResultTypeList.size()] );
+	private void finishInitialization(ArrayList queryReturnTypeList) {
 		queryReturnTypes = ( Type[] ) queryReturnTypeList.toArray( new Type[queryReturnTypeList.size()] );
 		initializeColumnNames();
 		prepared = true;
@@ -287,7 +273,6 @@
 
 		ASTAppender appender = new ASTAppender( getASTFactory(), this );	// Get ready to start adding nodes.
 		int size = fromElements.size();
-		ArrayList sqlResultTypeList = new ArrayList( size );
 		ArrayList queryReturnTypeList = new ArrayList( size );
 
 		Iterator iterator = fromElements.iterator();
@@ -305,7 +290,6 @@
 						queryReturnTypeList.add( type );
 					}
 					fromElementsForLoad.add( fromElement );
-					sqlResultTypeList.add( type );
 					// Generate the select expression.
 					String text = fromElement.renderIdentifierSelect( size, k );
 					SelectExpressionImpl generatedExpr = ( SelectExpressionImpl ) appender.append( SqlTokenTypes.SELECT_EXPR, text, false );
@@ -325,7 +309,7 @@
 		else {
 			renderNonScalarSelects( selectExpressions, fromClause );
 		}
-		finishInitialization( /*sqlResultTypeList,*/ queryReturnTypeList );
+		finishInitialization( queryReturnTypeList );
 	}
 	
 	public static boolean VERSION2_SQL = false;
@@ -359,7 +343,7 @@
 	private boolean isReturnableEntity(SelectExpression selectExpression) throws SemanticException {
 		FromElement fromElement = selectExpression.getFromElement();
 		boolean isFetchOrValueCollection = fromElement != null && 
-				( fromElement.isFetch() || fromElement.isCollectionOfValuesOrComponents() ); 
+				( fromElement.isFetch() || fromElement.isCollectionOfValuesOrComponents() );
 		if ( isFetchOrValueCollection ) {
 			return false;
 		}
@@ -378,7 +362,7 @@
 	}
 	
 	private void initAliases(SelectExpression[] selectExpressions) {
-		if (constructorNode==null) {
+		if ( aggregatedSelectExpression == null ) {
 			aliases = new String[selectExpressions.length];
 			for ( int i=0; i<selectExpressions.length; i++ ) {
 				String alias = selectExpressions[i].getAlias();
@@ -386,7 +370,7 @@
 			}
 		}
 		else {
-			aliases = constructorNode.getAliases();
+			aliases = aggregatedSelectExpression.getAggregatedAliases();
 		}
 	}
 

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/util/ASTPrinter.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/util/ASTPrinter.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/util/ASTPrinter.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -27,10 +27,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.io.PrintWriter;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Map;
 
 import org.hibernate.hql.ast.tree.DisplayableNode;
@@ -39,71 +36,62 @@
 import antlr.collections.AST;
 
 /**
- * An 'ASCII art' AST printer for debugging ANTLR grammars.
+ * Utility for generating pretty "ASCII art" representations of syntax trees.
  *
  * @author Joshua Davis
+ * @author Steve Ebersole
  */
 public class ASTPrinter {
-	private Map tokenTypeNamesByTokenType;
-	private Class tokenTypeConstants;
-	private boolean showClassNames = true;
+	private final Map tokenTypeNameCache;
+	private final boolean showClassNames;
 
 	/**
-	 * Constructs an org.hibernate.hql.antlr.ASTPrinter, given the class that contains the token type
-	 * constants (typically the '{grammar}TokenTypes' interface generated by
-	 * ANTLR).
+	 * Constructs a printer.
+	 * <p/>
+	 * Delegates to {@link #ASTPrinter(Class, boolean)} with {@link #isShowClassNames showClassNames} as <tt>true</tt>
 	 *
-	 * @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 );
 	}
 
-	/**
-	 * Returns true if the node class names will be displayed.
-	 *
-	 * @return true if the node class names will be displayed.
-	 */
-	public boolean isShowClassNames() {
-		return showClassNames;
+	public ASTPrinter(boolean showClassNames) {
+		this( ( Map ) null, showClassNames );
 	}
 
 	/**
-	 * Enables or disables AST node class name display.
+	 * Constructs a printer.
 	 *
-	 * @param showClassNames true to enable class name display, false to disable
+	 * @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 void setShowClassNames(boolean showClassNames) {
-		this.showClassNames = showClassNames;
+	public ASTPrinter(Class tokenTypeConstants, boolean showClassNames) {
+		this( ASTUtil.generateTokenNameCache( tokenTypeConstants ), 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 ) );
+	private ASTPrinter(Map tokenTypeNameCache, boolean showClassNames) {
+		this.tokenTypeNameCache = tokenTypeNameCache;
+		this.showClassNames = showClassNames;
 	}
 
 	/**
-	 * Prints the AST in 'ASCII art' tree form to the specified print writer.
+	 * Getter for property 'showClassNames'.
 	 *
-	 * @param ast The AST to print.
-	 * @param pw  The print writer.
+	 * @return Value for property 'showClassNames'.
 	 */
-	public void showAst(AST ast, PrintWriter pw) {
-		ArrayList parents = new ArrayList();
-		showAst( parents, pw, ast );
-		pw.flush();
+	public boolean isShowClassNames() {
+		return showClassNames;
 	}
 
 	/**
-	 * Prints the AST in 'ASCII art' tree form into a string.
+	 * Renders the AST into 'ASCII art' form and returns that string representation.
 	 *
-	 * @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 @@
 	}
 
 	/**
-	 * 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 @@
 	 * @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 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 @@
 
         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 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

Modified: core/trunk/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -26,6 +26,10 @@
 
 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 @@
  * 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.
+	 * <p/>
+	 * 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 @@
 	 * 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 @@
 	 * 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) {
@@ -127,10 +149,33 @@
 	}
 
 	/**
+	 * Determine if a given node (test) is contained anywhere in the subtree
+	 * 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 subtree child of the parent.
+	 * @return True if child is contained in the parent's collection of children.
+	 */
+	public static boolean isSubtreeChild(AST fixture, AST test) {
+		AST n = fixture.getFirstChild();
+		while ( n != null ) {
+			if ( n == test ) {
+				return true;
+			}
+			if ( n.getFirstChild() != null && isSubtreeChild( n, test ) ) {
+				return true;
+			}
+			n = n.getNextSibling();
+		}
+		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.
+	 * @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) {
@@ -145,6 +190,7 @@
 	 * Returns the last direct child of 'n'.
 	 *
 	 * @param n The parent
+	 *
 	 * @return The last direct child of 'n'.
 	 */
 	public static AST getLastChild(AST n) {
@@ -155,6 +201,7 @@
 	 * Returns the last sibling of 'a'.
 	 *
 	 * @param a The sibling.
+	 *
 	 * @return The last sibling of 'a'.
 	 */
 	private static AST getLastSibling(AST a) {
@@ -170,6 +217,7 @@
 	 * 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) {
@@ -184,7 +232,8 @@
 	 * 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
+	 * @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) {
@@ -201,52 +250,10 @@
 	}
 
 	/**
-	 * 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).
-	 *
-	 * @param fixture The node against which to testto be checked for children.
-	 * @param test The node to be tested as being a subtree child of the parent.
-	 * @return True if child is contained in the parent's collection of children.
-	 */
-	public static boolean isSubtreeChild(AST fixture, AST test) {
-		AST n = fixture.getFirstChild();
-		while ( n != null ) {
-			if ( n == test ) {
-				return true;
-			}
-			if ( n.getFirstChild() != null && isSubtreeChild( n, test ) ) {
-				return true;
-			}
-			n = n.getNextSibling();
-		}
-		return false;
-	}
-
-	/**
 	 * 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 @@
 	 * 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 @@
 		}
 	}
 
+	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 @@
 		 * 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 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 @@
 		}
 	}
 
-	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.
+	 * <p/>
+	 * 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;
+	}
 }

Modified: core/trunk/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -47,6 +47,7 @@
 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 @@
 
 	private int selectLength;
 
-	private ResultTransformer selectNewTransformer;
+	private ResultTransformer implicitResultTransformer;
 	private String[] queryReturnAliases;
 
 	private LockMode[] defaultLockModes;
@@ -128,10 +129,10 @@
 		//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 @@
 	}
 
 	private boolean hasSelectNew() {
-		return selectNewTransformer!=null;
+		return implicitResultTransformer != null;
 	}
 
 	protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
@@ -374,7 +375,7 @@
 
 	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 @@
 				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 @@
 		}
 
 		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 @@
 			        session,
 			        queryReturnTypes,
 			        queryTranslator.getColumnNames(),
-			        HolderInstantiator.getHolderInstantiator(selectNewTransformer, queryParameters.getResultTransformer(), queryReturnAliases)
-				);
+			        buildHolderInstantiator( queryParameters.getResultTransformer() )
+			);
 
 			if ( stats ) {
 				session.getFactory().getStatisticsImplementor().queryExecuted(
@@ -461,7 +469,12 @@
 			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 --

Modified: core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -99,6 +99,7 @@
 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 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 @@
 				!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 @@
 			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)

Modified: core/trunk/core/src/main/java/org/hibernate/persister/entity/Queryable.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/entity/Queryable.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/persister/entity/Queryable.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -24,6 +24,8 @@
  */
 package org.hibernate.persister.entity;
 
+import org.hibernate.sql.SelectFragment;
+
 /**
  * Extends the generic <tt>EntityPersister</tt> contract to add
  * operations required by the Hibernate Query Language
@@ -60,6 +62,7 @@
 	 */
 	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
 	 */

Added: core/trunk/core/src/main/java/org/hibernate/sql/AliasGenerator.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/sql/AliasGenerator.java	                        (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/sql/AliasGenerator.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -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);
+}

Added: core/trunk/core/src/main/java/org/hibernate/sql/SelectExpression.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/sql/SelectExpression.java	                        (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/sql/SelectExpression.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -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();
+}

Modified: core/trunk/core/src/main/java/org/hibernate/sql/SelectFragment.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/sql/SelectFragment.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/sql/SelectFragment.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -46,7 +46,15 @@
 	private String[] usedAliases;
 	
 	public SelectFragment() {}
-	
+
+	public List getColumns() {
+		return columns;
+	}
+
+	public String getExtraSelectList() {
+		return extraSelectList;
+	}
+
 	public SelectFragment setUsedAliases(String[] aliases) {
 		usedAliases = aliases;
 		return this;

Modified: core/trunk/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -32,6 +32,7 @@
 
 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 @@
 		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}
 	 */

Modified: core/trunk/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentRenderer.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentRenderer.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentRenderer.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -26,13 +26,50 @@
 
 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 );
+	}
 }

Modified: core/trunk/core/src/main/java/org/hibernate/util/ArrayHelper.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/util/ArrayHelper.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/util/ArrayHelper.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -84,7 +84,7 @@
 	}
 
 	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 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 static String[] slice(String[] strings, int begin, int length) {
 		String[] result = new String[length];
-		for ( int i=0; i<length; i++ ) {
-			result[i] = strings[begin+i];
-		}
+		System.arraycopy( strings, begin, result, 0, length );
 		return result;
 	}
 
 	public static Object[] slice(Object[] objects, int begin, int length) {
 		Object[] result = new Object[length];
-		for ( int i=0; i<length; i++ ) {
-			result[i] = objects[begin+i];
-		}
+		System.arraycopy( objects, begin, result, 0, length );
 		return result;
 	}
 
@@ -160,25 +156,27 @@
 
 	public static String[] join(String[] x, String[] y) {
 		String[] result = new String[ x.length + y.length ];
-		for ( int i=0; i<x.length; i++ ) result[i] = x[i];
-		for ( int i=0; i<y.length; i++ ) result[i+x.length] = y[i];
+		System.arraycopy( x, 0, result, 0, x.length );
+		System.arraycopy( y, 0, result, x.length, y.length );
 		return result;
 	}
 
 	public static String[] join(String[] x, String[] y, boolean[] use) {
 		String[] result = new String[ x.length + countTrue(use) ];
-		for ( int i=0; i<x.length; i++ ) result[i] = x[i];
+		System.arraycopy( x, 0, result, 0, x.length );
 		int k = x.length;
 		for ( int i=0; i<y.length; i++ ) {
-			if ( use[i] ) result[k++] = y[i];
+			if ( use[i] ) {
+				result[k++] = y[i];
+			}
 		}
 		return result;
 	}
 
 	public static int[] join(int[] x, int[] y) {
 		int[] result = new int[ x.length + y.length ];
-		for ( int i=0; i<x.length; i++ ) result[i] = x[i];
-		for ( int i=0; i<y.length; i++ ) result[i+x.length] = y[i];
+		System.arraycopy( x, 0, result, 0, x.length );
+		System.arraycopy( y, 0, result, x.length, y.length );
 		return result;
 	}
 
@@ -236,9 +234,7 @@
 	}
 
 	public static void addAll(Collection collection, Object[] array) {
-		for ( int i=0; i<array.length; i++ ) {
-			collection.add( array[i] );
-		}
+		collection.addAll( Arrays.asList( array ) );
 	}
 
 	public static final String[] EMPTY_STRING_ARRAY = {};

Modified: core/trunk/core/src/main/java/org/hibernate/util/StringHelper.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/util/StringHelper.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/core/src/main/java/org/hibernate/util/StringHelper.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -27,6 +27,7 @@
 import java.util.Iterator;
 import java.util.StringTokenizer;
 import java.util.ArrayList;
+import java.util.Arrays;
 
 public final class StringHelper {
 
@@ -85,7 +86,13 @@
 		return buf.toString();
 	}
 
+	public static String repeat(char character, int times) {
+		char[] buffer = new char[times];
+		Arrays.fill( buffer, character );
+		return new String( buffer );
+	}
 
+
 	public static String replace(String template, String placeholder, String replacement) {
 		return replace( template, placeholder, replacement, false );
 	}

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java	2009-08-13 19:30:57 UTC (rev 17300)
@@ -104,6 +104,49 @@
 		return new FunctionalTestClassTestSuite( ASTParserLoadingTest.class );
 	}
 
+	public void testJPAQLQualifiedIdentificationVariables() {
+		Session s = openSession();
+		s.beginTransaction();
+		Human me = new Human();
+		me.setName( new Name( "Steve", null, "Ebersole" ) );
+		Human joe = new Human();
+		me.setName( new Name( "Joe", null, "Ebersole" ) );
+		me.setFamily( new HashMap() );
+		me.getFamily().put( "son", joe );
+		s.save( me );
+		s.save( joe );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		List results = s.createQuery( "select entry(h.family) from Human h" ).list();
+		assertEquals( 1, results.size() );
+		Object result = results.get(0);
+		assertTrue( Map.Entry.class.isAssignableFrom( result.getClass() ) );
+		Map.Entry entry = (Map.Entry) result;
+		assertTrue( String.class.isAssignableFrom( entry.getKey().getClass() ) );
+		assertTrue( Human.class.isAssignableFrom( entry.getValue().getClass() ) );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		results = s.createQuery( "select distinct key(h.family) from Human h" ).list();
+		assertEquals( 1, results.size() );
+		Object key = results.get(0);
+		assertTrue( String.class.isAssignableFrom( key.getClass() ) );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		s.delete( me );
+		s.delete( joe );
+		s.getTransaction().commit();
+		s.close();
+	}
+
 	public void testPaginationWithPolymorphicQuery() {
 		Session s = openSession();
 		s.beginTransaction();

Modified: core/trunk/testsuite/src/test/resources/log4j.properties
===================================================================
--- core/trunk/testsuite/src/test/resources/log4j.properties	2009-08-13 19:05:34 UTC (rev 17299)
+++ core/trunk/testsuite/src/test/resources/log4j.properties	2009-08-13 19:30:57 UTC (rev 17300)
@@ -8,3 +8,7 @@
 
 log4j.logger.org.hibernate.test=info
 log4j.logger.org.hibernate.tool.hbm2ddl=debug
+log4j.logger.org.hibernate.hql.ast.QueryTranslatorImpl=trace
+log4j.logger.org.hibernate.hql.ast.HqlSqlWalker=trace
+log4j.logger.org.hibernate.hql.ast.SqlGenerator=trace
+log4j.logger.org.hibernate.hql.ast.AST=trace
\ No newline at end of file



More information about the hibernate-commits mailing list