[hibernate-commits] Hibernate SVN: r15693 - in core/branches/SQL_GEN_REDESIGN: src/main/antlr and 9 other directories.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Sat Dec 13 11:25:05 EST 2008
Author: steve.ebersole at jboss.com
Date: 2008-12-13 11:25:05 -0500 (Sat, 13 Dec 2008)
New Revision: 15693
Added:
core/branches/SQL_GEN_REDESIGN/src/main/antlr/hql/
core/branches/SQL_GEN_REDESIGN/src/main/antlr/hql/parse.g
core/branches/SQL_GEN_REDESIGN/src/main/antlr/hql/resolve.g
core/branches/SQL_GEN_REDESIGN/src/main/antlr/sql/
core/branches/SQL_GEN_REDESIGN/src/main/antlr/sql/common.g
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/ASTFactoryImpl.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/HqlParser.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/PathCollector.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/DetailedSemanticException.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/QuerySyntaxException.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/AbstractToken.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/CommonHibernateLexer.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/JoinType.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/Node.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/NumericLiteralToken.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/PanicException.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/TokenImpl.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTAppender.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTIterator.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTParentsFirstIterator.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTPrinter.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTUtil.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/DisplayableNode.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ErrorCounter.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ErrorReporter.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/NodeTraverser.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ParseErrorHandler.java
core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/PathHelper.java
Modified:
core/branches/SQL_GEN_REDESIGN/pom.xml
Log:
initial working parse.g
Modified: core/branches/SQL_GEN_REDESIGN/pom.xml
===================================================================
--- core/branches/SQL_GEN_REDESIGN/pom.xml 2008-12-12 20:48:49 UTC (rev 15692)
+++ core/branches/SQL_GEN_REDESIGN/pom.xml 2008-12-13 16:25:05 UTC (rev 15693)
@@ -2,19 +2,13 @@
<modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.hibernate</groupId>
- <artifactId>hibernate-parent</artifactId>
- <version>3.4.0-SNAPSHOT</version>
- <relativePath>../parent/pom.xml</relativePath>
- </parent>
-
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
+ <version>3.4.0.SQL_GEN_REDESIGN</version>
<packaging>jar</packaging>
- <name>Hibernate Core</name>
- <description>The core functionality of Hibernate</description>
+ <name>Hibernate Core (SQL Generation Redesign)</name>
+ <description>The core functionality of Hibernate (with redesign of SQL generation)</description>
<dependencies>
<dependency>
@@ -29,6 +23,10 @@
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
<dependency>
<!-- YUCK, YUCK, YUCK!!!! -->
@@ -68,71 +66,148 @@
<version>2.1_3</version>
<optional>true</optional>
</dependency>
+ <!-- test-scoped dependencies for common testing dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <version>1.5.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.5.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.14</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>99.0-does-not-exist</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging-api</artifactId>
+ <version>99.0-does-not-exist</version>
+ <scope>test</scope>
+ </dependency>
+ <!-- / test-scoped dependencies for common testing dependencies -->
</dependencies>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.5.2</version>
+ </dependency>
+ <dependency>
+ <groupId>antlr</groupId>
+ <artifactId>antlr</artifactId>
+ <version>2.7.6</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>dom4j</groupId>
+ <artifactId>dom4j</artifactId>
+ <version>1.6.1</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <distributionManagement>
+ <repository>
+ <!-- Copy the dist to the local checkout of the JBoss maven2 repo ${maven.repository.root} -->
+ <!-- It is anticipated that ${maven.repository.root} be set in user's settings.xml -->
+ <!-- todo : replace this with direct svn access once the svnkit providers are available -->
+ <id>repository.jboss.org</id>
+ <url>file://${maven.repository.root}</url>
+ </repository>
+ <snapshotRepository>
+ <id>snapshots.jboss.org</id>
+ <name>JBoss Snapshot Repository</name>
+ <url>dav:https://snapshots.jboss.org/maven2</url>
+ </snapshotRepository>
+ </distributionManagement>
+
<build>
<plugins>
<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>antlr-maven-plugin</artifactId>
- <version>${antlrPluginVersion}</version>
- <configuration>
- <grammars>hql.g,hql-sql.g,sql-gen.g,order-by.g,order-by-render.g</grammars>
- </configuration>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
+ <id>enforce-java</id>
<goals>
- <goal>generate</goal>
+ <goal>enforce</goal>
</goals>
+ <configuration>
+ <rules>
+ <requireJavaVersion>
+ <version>[1.5,)</version>
+ </requireJavaVersion>
+ <requireMavenVersion>
+ <version>(2.0.7,)</version>
+ </requireMavenVersion>
+ </rules>
+ </configuration>
</execution>
</executions>
</plugin>
- </plugins>
- </build>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.4</source>
+ <target>1.4</target>
+ </configuration>
+ </plugin>
- <reporting>
- <plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>antlr-maven-plugin</artifactId>
<version>${antlrPluginVersion}</version>
<configuration>
- <!-- eventually should be based on the second phase grammar -->
- <grammars>hql.g</grammars>
+ <traceParser>true</traceParser>
+ <grammarDefs>
+ <grammar>
+ <name>sql/common.g</name>
+ </grammar>
+ <grammar>
+ <name>hql/parse.g</name>
+ <glib>sql/common.g</glib>
+ </grammar>
+ </grammarDefs>
</configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
</plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
- <configuration>
- <!--
- for the time being, gonna ignore the custom stylesheet (what did it do anyway???)
- <stylesheetfile>xyz</stylesheetfile>
- -->
- <groups>
- <group>
- <title>Core API</title>
- <packages>org.hibernate:org.hibernate.classic:org.hibernate.criterion:org.hibernate.metadata:org.hibernate.cfg:org.hibernate.usertype</packages>
- </group>
- <group>
- <title>Extension API</title>
- <packages>org.hibernate.id:org.hibernate.connection:org.hibernate.transaction:org.hibernate.type:org.hibernate.dialect*:org.hibernate.cache*:org.hibernate.event*:org.hibernate.action:org.hibernate.property:org.hibernate.loader*:org.hibernate.persister*:org.hibernate.proxy:org.hibernate.tuple:org.hibernate.transform:org.hibernate.collection:org.hibernate.jdbc</packages>
- </group>
- <group>
- <title>Miscellaneous API</title>
- <packages>org.hibernate.stat:org.hibernate.tool.hbm2ddl:org.hibernate.jmx:org.hibernate.mapping:org.hibernate.tool.instrument</packages>
- </group>
- <group>
- <title>Internal Implementation</title>
- <packages>org.hibernate.engine:org.hibernate.impl:org.hibernate.sql:org.hibernate.lob:org.hibernate.util:org.hibernate.exception:org.hibernate.hql:org.hibernate.hql.ast:org.hibernate.hql.antlr:org.hibernate.hql.classic:org.hibernate.intercept:org.hibernate.secure:org.hibernate.pretty</packages>
- </group>
- </groups>
- </configuration>
- </plugin>
+
</plugins>
- </reporting>
+ </build>
<properties>
<antlrPluginVersion>2.1</antlrPluginVersion>
</properties>
-</project>
\ No newline at end of file
+</project>
Added: core/branches/SQL_GEN_REDESIGN/src/main/antlr/hql/parse.g
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/antlr/hql/parse.g (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/antlr/hql/parse.g 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,1138 @@
+header {
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ *
+ * Portions of SQL grammar parsing copyright (C) 2003 by Lubos Vnuk. All rights
+ * reserved. These portions are distributed under license by Red Hat Middleware
+ * LLC and are covered by the above LGPL notice. If you redistribute this material,
+ * with or without modification, you must preserve this copyright notice in its
+ * entirety.
+ */
+package org.hibernate.hql.ast.phase.parse;
+
+import antlr.collections.AST;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.hibernate.sql.ast.common.CommonHibernateParserSupport;
+}
+
+/**
+ * An Antlr stream parser for building a syntax AST representing an input Hibernate Query Language (HQL) query.
+ *
+ * @author Lubos Vnuk
+ * @author Joshua Davis
+ * @author Steve Ebersole
+ */
+class GeneratedHqlParser extends CommonHibernateParserSupport;
+
+options {
+ importVocab = Sql92;
+ exportVocab = HqlParse;
+ buildAST = true;
+ k = 3;
+}
+
+tokens {
+ // Various synthetic tokens used to simplify or disambiguate the tree
+ ALIAS;
+ ALIAS_REF;
+ ARG_LIST;
+ ASSIGNMENT_OP;
+ CLASS_NAME;
+ CONSTRUCTOR;
+ COLLECTION_ROLE;
+ ENTITY_NAME;
+ FILTER_ENTITY;
+ GENERIC_FUNCTION;
+ INDEX_OP;
+ INSERTABILITY_SPEC;
+ IN_LIST;
+ IS_NOT_NULL;
+ IS_NULL;
+ JAVA_CONSTANT;
+ JPA_PARAM;
+ NAMED_PARAM;
+ NOT_BETWEEN;
+ NOT_IN;
+ NOT_LIKE;
+ ORDER_SPEC;
+ PROP_FETCH;
+ QUERY;
+ REGISTERED_FUNCTION;
+ SELECT_FROM;
+ SORT_KEY;
+ SORT_SPEC;
+ SQL_TOKEN;
+ UNARY_MINUS;
+ UNARY_PLUS;
+ PERSISTER_SPACE;
+// WEIRD_IDENT;
+
+// AGGREGATE;
+ EXPR_LIST;
+// ROW_STAR;
+ VECTOR_EXPR;
+}
+
+{
+ private static Logger log = LoggerFactory.getLogger( GeneratedHqlParser99.class );
+
+ protected String extractText(AST node) {
+ return node.getText();
+ }
+
+ protected String extractPath(AST node) {
+ return extractText( node );
+ }
+
+ protected boolean isJavaConstant(String test) {
+ return false;
+ }
+
+ protected AST processEqualityExpression(AST node) throws RecognitionException {
+ return node;
+ }
+
+ protected AST processMemberOf(AST path, AST notNode) {
+ return path;
+ }
+
+ protected AST processIsEmpty(AST collection, AST notNode) {
+ return null;
+ }
+
+ /**
+ * This method is overriden in the sub class in order to provide the
+ * 'keyword as identifier' hack.
+ * @param token The token to retry as an identifier.
+ * @param ex The exception to throw if it cannot be retried as an identifier.
+ */
+ protected AST handleIdentifierError(Token token,RecognitionException ex) throws RecognitionException, TokenStreamException {
+ // Base implementation: Just re-throw the exception.
+ throw ex;
+ }
+
+ /**
+ * This method looks ahead and converts . <token> into . IDENT when
+ * appropriate.
+ */
+ protected void handleDotIdent() throws TokenStreamException {
+ }
+
+ /**
+ * Returns the negated equivalent of the expression.
+ * @param node The expression to negate.
+ */
+ protected AST negateNode(AST node) {
+ // Just create a 'not' parent for the default behavior.
+ return #( [NOT,"not"], node );
+ }
+
+ protected void weakKeywords() throws TokenStreamException {
+ }
+
+ protected void potentialUpdatePersisterAlias() throws TokenStreamException {
+ }
+
+ protected void potentialDeletePersisterAlias() throws TokenStreamException {
+ }
+
+ protected void prepareForPersisterReferenceRoot() throws TokenStreamException {
+ }
+
+ protected void prepareForCrossJoinElements() throws TokenStreamException {
+ }
+
+ protected void prepareForQualifiedJoinElements() throws TokenStreamException {
+ }
+
+ protected void unequivocalKeywordAsIdentifier() throws TokenStreamException {
+ }
+
+ protected void transferTrackingInfo(AST source, AST target) {
+ int type = target.getType();
+ String text = target.getText();
+ target.initialize( source );
+ target.setType( type );
+ target.setText( text );
+ }
+}
+
+// Rules dealing with various IDENT structures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+identifier :
+ IDENT
+ exception
+ catch [RecognitionException ex] {
+ identifier_AST = handleIdentifierError(LT(1),ex);
+ }
+;
+
+path :
+ identifier { handleDotIdent(); } ( DOT^ identifier )*
+;
+
+/**
+ * Specialied IDENT ( DOT IDENT ) recognition rule for cases where we are fully expecting an entity name.
+ * <p/>
+ * Use the {@link #path} rule here because it is less complex to resolve (and so i assume faster).
+ */
+protected
+entityName :
+ p:path {
+ String name = extractPath( #p );
+ #entityName = #( [ENTITY_NAME,name] );
+ transferTrackingInfo( #p, #entityName );
+ }
+;
+
+protected
+className :
+ p:path {
+ String name = extractPath( #p );
+ #className = #( [CLASS_NAME,name] );
+ transferTrackingInfo( #p, #className );
+ }
+;
+
+
+// alias/correlation recognition rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * Creation of an alias or correlation-name.
+ */
+alias :
+ // if the AS is present, it signals that whatever follows is unequivocally an identifier
+ ( AS! { unequivocalKeywordAsIdentifier(); } )? {weakKeywords();} i:identifier {
+ #i.setType( ALIAS );
+ }
+;
+
+/**
+ * Reference to an alias or correlation-name
+ */
+aliasReference :
+ {weakKeywords();} i:identifier {
+ #i.setType( ALIAS_REF );
+ }
+;
+
+
+// Statement rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * The main grammar rule
+ */
+statement :
+ updateStatement
+ | deleteStatement
+ | insertStatement
+ | selectStatement
+;
+
+
+
+// filter rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+filterStatement :
+ filteredSelectFrom (whereClause)? ( groupByClause ( havingClause )? )? (orderByClause)? {
+ #filterStatement = #( [QUERY,"query"], #filterStatement );
+ }
+;
+
+filteredSelectFrom :
+ ( s:selectClause )? ( f:fromClause )? {
+ if ( #f != null ) {
+ throw new SemanticException( "collection filters cannot specify explicit FROM clause" );
+ }
+ // Create an artificial token so the 'FROM' can be placed
+ // before the SELECT in the tree to make tree processing
+ // simpler.
+ #filteredSelectFrom = #( [SELECT_FROM,"SELECT_FROM"], #f, #s );
+ }
+;
+
+
+// <tt>UPDATE</tt> statement ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * Recognize an <tt>UPDATE</tt> statement
+ */
+updateStatement :
+ UPDATE^ (VERSIONED)?
+ (FROM!)? {unequivocalKeywordAsIdentifier();} entityName {potentialUpdatePersisterAlias();} (alias)?
+ setClause
+ (whereClause)?
+;
+
+setClause
+ : (SET^ assignment (COMMA! assignment)*)
+ ;
+
+assignment :
+ assignmentField eo:EQUALS_OP^ newValue {
+ #eo.setType( ASSIGNMENT_OP );
+ }
+;
+
+assignmentField :
+ path
+;
+
+newValue :
+ concatenation
+;
+
+
+// <tt>DELETE</tt> statement ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * Recognize an HQL <tt>DELETE</tt> statement
+ */
+deleteStatement :
+ DELETE^
+ ( FROM! )? {unequivocalKeywordAsIdentifier();} entityName {potentialDeletePersisterAlias();} (alias)?
+ ( whereClause )?
+;
+
+
+// <tt>INSERT</tt> statement ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * Recognize an HQL <tt>INSERT</tt> statement
+ */
+insertStatement :
+ INSERT^ intoClause selectStatement
+;
+
+intoClause :
+ INTO^ {unequivocalKeywordAsIdentifier();} entityName insertabilitySpecification
+;
+
+insertabilitySpecification :
+ LEFT_PAREN! insertablePropertySpecification ( COMMA! insertablePropertySpecification )* RIGHT_PAREN! {
+ #insertabilitySpecification = #( [INSERTABILITY_SPEC, "insertability-spec"], #insertabilitySpecification );
+ }
+;
+
+/**
+ * The property being inserted into.
+ */
+insertablePropertySpecification :
+ // todo : ok for this to just be IDENT?
+ // do we want to allow users to target specific component sub-properties?
+ identifier
+;
+
+
+// <tt>SELECT</tt> statement rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * Recognize an HQL <tt>SELECT</tt> statement.
+ * <p/>
+ * This corresponds most closely to the <cursor specification> rule in ISO/ANSI SQL...
+ */
+selectStatement :
+ queryExpression ( orderByClause )? {
+ #selectStatement = #( [QUERY,"query"], #selectStatement );
+ }
+;
+
+orderByClause :
+ ORDER^ BY! sortSpecification ( COMMA! sortSpecification )*
+;
+
+/**
+ * Reconition rule for what ANSI SQL terms the <tt>sort specification</tt>, which is essentially each thing upon which
+ * the results should be sorted.
+ */
+sortSpecification :
+ sortKey (collationSpecification)? (orderingSpecification)? {
+ #sortSpecification = #( [SORT_SPEC, "{sort specification}"], #sortSpecification );
+ }
+;
+
+/**
+ * Reconition rule for what ANSI SQL terms the <tt>sort key</tt> which is the expression (column, function, etc) upon
+ * which to base the sorting.
+ */
+sortKey :
+ expression
+;
+
+/**
+ * Reconition rule for what ANSI SQL terms the <tt>collation specification</tt> used to allow specifying that sorting for
+ * the given {@link #sortSpecification} be treated within a specific character-set.
+ */
+collationSpecification! :
+ c:COLLATE cn:collationName {
+ #collationSpecification = #( [COLLATE, extractText( #cn )] );
+ }
+;
+
+/**
+ * The collation name wrt {@link #collationSpecification}. Namely, the character-set.
+ */
+collationName :
+ identifier
+;
+
+/**
+ * Reconition rule for what ANSI SQL terms the <tt>ordering specification</tt>; <tt>ASCENDING</tt> or
+ * <tt>DESCENDING</tt>.
+ */
+orderingSpecification :
+ // todo : what about "ascending" and "descending" literals?
+ ( ASCENDING | DESCENDING) {
+ #orderingSpecification = #( [ORDER_SPEC, extractText( #orderingSpecification )] );
+ }
+;
+
+queryExpression :
+ querySpec ( ( UNION | INTERSECT | EXCEPT ) (ALL)? querySpec )*
+;
+
+querySpec :
+ selectFrom
+ ( whereClause )?
+ ( groupByClause ( havingClause )? )?
+;
+
+selectFrom! :
+ ( s:selectClause )? f:fromClause {
+ // Create an artificial token so the 'FROM' can be placed
+ // before the SELECT in the tree to make tree processing
+ // simpler.
+ #selectFrom = #( [SELECT_FROM,"SELECT_FROM"], f, s );
+ }
+;
+
+subQuery :
+ queryExpression {
+ #subQuery = #( [QUERY,"query"], #subQuery );
+ }
+;
+
+
+// table/persister related rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+fromClause :
+ FROM^ {unequivocalKeywordAsIdentifier();} persisterSpaces
+;
+
+persisterSpaces :
+ // todo : better name?
+ persisterSpace ( COMMA! persisterSpace )*
+;
+
+persisterSpace :
+ {prepareForPersisterReferenceRoot();} persisterSpaceRoot ( qualifiedJoin | crossJoin )* {
+ #persisterSpace = #( [PERSISTER_SPACE, "persister-space"], #persisterSpace );
+ }
+;
+
+/**
+ * Recognition rule for root persister references. Forms include:<ol>
+ * <li>[entityName [AS alias]? [FETCH ALL PROPERTIES]?] - which is the standard entity persister reference</li>
+ * <li>[IN (collection-reference) [AS alias]?] - which is the JPA variant of a collection join</li>
+ * <li>[alias IN CLASS entityName] - which is the legacy HQL syntax</li>
+ * <li>[alias IN [ELEMENTS|INDICES]? (collection-reference)] - legacy HQL syntax for a collection join</li>
+ *</ol>
+ * <p/>
+ * NOTE that only options #1 and #3 are valid as the initial root space for a top-level query since
+ * all the other forms require an already existing persister reference in order to reference a collection.
+ */
+persisterSpaceRoot :
+ hibernateLegacySyntax
+ | jpaCollectionReference
+ | mainEntityPersisterReference
+;
+
+hibernateLegacySyntax :
+ a:identifier! IN! (
+ // allow these legacy Hibernate syntaxes, but normalize them and emit a warning...
+ CLASS! {unequivocalKeywordAsIdentifier();} en:entityName! {
+ log.warn( "encountered deprecated, legacy from-clause syntax : [<alias> in class <entity-name>]" );
+ #a.setType( ALIAS );
+ #hibernateLegacySyntax = #( #en, #a );
+ }
+ | ( ELEMENTS! | INDICES! )? LEFT_PAREN! {unequivocalKeywordAsIdentifier();} p:path! RIGHT_PAREN! {
+ log.warn( "encountered deprecated, legacy from-clause syntax : [<alias> in <elements|indices> (<collection-ref>)]" );
+ // todo : properly treat ELEMENTS | INDICES...
+ #a.setType( ALIAS );
+ #hibernateLegacySyntax = #( [JOIN,"join"], [INNER, "inner"], #p, #a );
+ }
+ )
+;
+
+mainEntityPersisterReference :
+ entityName (alias)? (propertyFetch)?
+;
+
+jpaCollectionReference :
+ IN! LEFT_PAREN! {unequivocalKeywordAsIdentifier();} p2:path RIGHT_PAREN! ( a2:alias )? {
+ #jpaCollectionReference = #( [JOIN,"join"], [INNER, "inner"], #p2, #a2 );
+ }
+;
+
+crossJoin :
+ CROSS JOIN^ {prepareForCrossJoinElements();} entityName (alias)? (propertyFetch)?
+;
+
+qualifiedJoin! :
+ (jt:nonCrossJoinType)? j:JOIN {if (#jt==null) #jt=#([INNER]);} (f:FETCH)? {prepareForQualifiedJoinElements();} p:path (a:alias)? (
+ // try to use the ON keyword to disambiguate
+ js:joinSpecification {
+ if ( #f != null ) {
+ throw new org.hibernate.QueryException( "Cannot use fetch keyword in conjunction with an ad hoc join" );
+ }
+ String entityName = extractPath( #p );
+ #qualifiedJoin = #( #j, #jt, [ENTITY_NAME,entityName], #a, #js );
+ }
+ | (pf:propertyFetch)? (w:withClause)? {
+ #qualifiedJoin = #( #j, #jt, #f, #a, #pf, #p, #w );
+ }
+ )
+;
+
+nonCrossJoinType :
+ INNER
+ | outerJoinType ( OUTER )?
+;
+
+outerJoinType :
+ LEFT
+ | RIGHT
+;
+
+joinSpecification :
+ ON^ logicalExpression
+;
+
+withClause :
+ WITH^ logicalExpression
+;
+
+propertyFetch! :
+ FETCH! ALL! PROPERTIES! {
+ #propertyFetch = #( [PROP_FETCH, "property-fetch"] );
+ }
+;
+
+
+// select clause related rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+selectClause
+ : SELECT^ // NOTE: The '^' after a token causes the corresponding AST node to be the root of the sub-tree.
+ { weakKeywords(); } // Weak keywords can appear immediately after a SELECT token.
+ (DISTINCT)? ( selectedPropertiesList | newExpression | selectObject )
+ ;
+
+newExpression
+ : (NEW! path) op:LEFT_PAREN^ {#op.setType(CONSTRUCTOR);} selectedPropertiesList RIGHT_PAREN!
+ ;
+
+selectObject
+ : OBJECT^ LEFT_PAREN! identifier RIGHT_PAREN!
+ ;
+
+selectedPropertiesList
+ : aliasedExpression ( COMMA! aliasedExpression )*
+ ;
+
+aliasedExpression
+ : expression ( AS^ identifier )?
+ ;
+
+
+
+// where clause rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+whereClause :
+ WHERE^ logicalExpression
+;
+
+
+
+// group by clause rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+groupByClause :
+ GROUP^ BY! groupingSpecification
+;
+
+groupingSpecification :
+ groupingValue ( COMMA groupingValue )*
+;
+
+groupingValue :
+ path ( collationSpecification )?
+;
+
+
+
+// having clause related rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+havingClause :
+ HAVING^ logicalExpression
+;
+
+// expressions
+// Note that most of these expressions follow the pattern
+// thisLevelExpression :
+// nextHigherPrecedenceExpression
+// (OPERATOR nextHigherPrecedenceExpression)*
+// which is a standard recursive definition for a parsing an expression.
+//
+// Operator precedence in HQL
+// lowest --> ( 7) OR
+// ( 6) AND, NOT
+// ( 5) equality: ==, <>, !=, is
+// ( 4) relational: <, <=, >, >=,
+// LIKE, NOT LIKE, BETWEEN, NOT BETWEEN, IN, NOT IN
+// ( 3) addition and subtraction: +(binary) -(binary)
+// ( 2) multiplication: * / %, concatenate: ||
+// highest --> ( 1) +(unary) -(unary)
+// [] () (method call) . (dot -- identifier qualification)
+// aggregate function
+// () (explicit parenthesis)
+//
+// Note that the above precedence levels map to the rules below...
+// Once you have a precedence chart, writing the appropriate rules as below
+// is usually very straightfoward
+
+logicalExpression
+ : expression
+ ;
+
+// Main expression rule
+expression
+ : logicalOrExpression
+ ;
+
+// level 7 - OR
+logicalOrExpression
+ : logicalAndExpression ( OR^ logicalAndExpression )*
+ ;
+
+// level 6 - AND, NOT
+logicalAndExpression
+ : negatedExpression ( AND^ negatedExpression )*
+ ;
+
+// NOT nodes aren't generated. Instead, the operator in the sub-tree will be
+// negated, if possible. Expressions without a NOT parent are passed through.
+negatedExpression!
+{ weakKeywords(); } // Weak keywords can appear in an expression, so look ahead.
+ : NOT^ x:negatedExpression { #negatedExpression = negateNode(#x); }
+ | y:equalityExpression { #negatedExpression = #y; }
+ ;
+
+//## OP: EQUALS_OP | LESS_THAN_OP | GREATER_THAN_OP | LESS_THAN_OR_EQUALS_OP | GREATER_THAN_OR_EQUALS_OP | NOT_EQUALS_OP | LIKE;
+
+// level 5 - EQUALS_OP, NOT_EQUALS_OP
+equalityExpression :
+ x:relationalExpression (
+ IS! (not:NOT!)? ( nullness:NULL! | emptiness:EMPTY! ) {
+ if ( #nullness != null ) {
+ log.debug( "handling <is [not] null> : " + #x.toStringTree() );
+ #equalityExpression = #( [IS_NULL, "is null"], #x );
+ if ( #not != null ) {
+ negateNode( #equalityExpression );
+ }
+ }
+ else {
+ #equalityExpression = processIsEmpty( #x, #not );
+ }
+ }
+ | ( EQUALS_OP^ | NOT_EQUALS_OP^ ) relationalExpression
+ )* {
+ // Post process the equality expression to clean up 'is null', etc.
+ #equalityExpression = processEqualityExpression(#equalityExpression);
+ }
+;
+
+// level 4 - LESS_THAN_OP, GREATER_THAN_OP, LESS_THAN_OR_EQUALS_OP, GREATER_THAN_OR_EQUALS_OP, LIKE, NOT LIKE, BETWEEN, NOT BETWEEN
+// NOTE: The NOT prefix for LIKE and BETWEEN will be represented in the
+// token type. When traversing the AST, use the token type, and not the
+// token text to interpret the semantics of these nodes.
+relationalExpression
+ : concatenation (
+ ( ( ( LESS_THAN_OP^ | GREATER_THAN_OP^ | LESS_THAN_OR_EQUALS_OP^ | GREATER_THAN_OR_EQUALS_OP^ ) additiveExpression )* )
+ // Disable node production for the optional 'not'.
+ | (n:NOT!)? (
+ // Represent the optional NOT prefix using the token type by
+ // testing 'n' and setting the token type accordingly.
+ (i:IN^ {
+ #i.setType( (n == null) ? IN : NOT_IN);
+ #i.setText( (n == null) ? "in" : "not in");
+ }
+ inList)
+ | (b:BETWEEN^ {
+ #b.setType( (n == null) ? BETWEEN : NOT_BETWEEN);
+ #b.setText( (n == null) ? "between" : "not between");
+ }
+ betweenList )
+ | (l:LIKE^ {
+ #l.setType( (n == null) ? LIKE : NOT_LIKE);
+ #l.setText( (n == null) ? "like" : "not like");
+ }
+ concatenation likeEscape)
+ | (MEMBER! OF! p:path! {
+ processMemberOf( #p, #n );
+ } ) )
+ )
+ ;
+
+likeEscape
+ : (ESCAPE^ concatenation)?
+ ;
+
+inList
+ : x:compoundExpr
+ { #inList = #([IN_LIST,"inList"], #inList); }
+ ;
+
+betweenList
+ : concatenation AND! concatenation
+ ;
+
+//level 4 - string concatenation
+concatenation :
+ additiveExpression (
+ co:CONCATENATION_OP^ { #co.setType(ARG_LIST); #co.setText("concat-series"); } additiveExpression ( CONCATENATION_OP! additiveExpression )* {
+ #concatenation = #( [CONCATENATION_OP, "||"], #co );
+ }
+ )?
+;
+
+
+// level 3 - binary plus and minus
+additiveExpression
+ : multiplyExpression ( ( PLUS_SIGN^ | MINUS_SIGN^ ) multiplyExpression )*
+ ;
+
+// level 2 - binary multiply and divide
+multiplyExpression
+ : unaryExpression ( ( ASTERISK^ | SOLIDUS^ ) unaryExpression )*
+ ;
+
+// level 1 - unary minus, unary plus, not
+unaryExpression
+ : MINUS_SIGN^ {#MINUS_SIGN.setType(UNARY_MINUS);} unaryExpression
+ | PLUS_SIGN^ {#PLUS_SIGN.setType(UNARY_PLUS);} unaryExpression
+ | caseExpression
+ | quantifiedExpression
+ | standardFunction
+ | setFunction
+ | collectionFunction
+ | collectionExpression
+ | atom
+ ;
+
+caseExpression :
+ caseAbbreviation
+ | caseSpecification
+;
+
+caseAbbreviation :
+ ( NULLIF LEFT_PAREN unaryExpression COMMA unaryExpression RIGHT_PAREN )
+ | ( COALESCE LEFT_PAREN unaryExpression (COMMA unaryExpression)* RIGHT_PAREN )
+;
+
+caseSpecification :
+ simpleCase
+ | searchedCase
+;
+
+simpleCase :
+ CASE unaryExpression (simpleCaseWhenClause)+ (elseClause)? END
+;
+
+simpleCaseWhenClause :
+ WHEN unaryExpression THEN result
+;
+
+result :
+ unaryExpression
+;
+
+elseClause :
+ ELSE result
+;
+
+searchedCase :
+ CASE (searchedWhenClause)+ (elseClause)? END
+;
+
+searchedWhenClause :
+ WHEN logicalExpression THEN unaryExpression
+;
+
+quantifiedExpression // todo : this should be a function of the predicates (logicalExpression) not of concatenation/additiveExpression: these can come only in very certain circumstances.
+ : ( SOME^ | EXISTS^ | ALL^ | ANY^ )
+ ( identifier | collectionExpression | (LEFT_PAREN! ( subQuery ) RIGHT_PAREN!) )
+ ;
+
+standardFunction :
+ castFunction
+ | concatFunction
+ | substringFunction
+ | trimFunction
+ | upperFunction
+ | lowerFunction
+ | lengthFunction
+ | locateFunction
+ | absFunction
+ | sqrtFunction
+ | modFunction
+ | sizeFunction
+ | indexFunction
+ | currentDateFunction
+ | currentTimeFunction
+ | currentTimestampFunction
+ | extractFunction
+ | positionFunction
+ | charLengthFunction
+ | octetLengthFunction
+ | bitLengthFunction
+// | USER
+// | CURRENT_USER
+// | SESSION_USER
+// | SYSTEM_USER
+;
+
+castFunction :
+ CAST^ LEFT_PAREN! unaryExpression AS! dataType RIGHT_PAREN!
+;
+
+dataType :
+ // todo : temp...
+ identifier
+;
+
+concatFunction :
+ CONCAT^ LEFT_PAREN! unaryExpression ( COMMA! unaryExpression )+ RIGHT_PAREN!
+;
+
+substringFunction :
+ SUBSTRING^ LEFT_PAREN! unaryExpression COMMA! unaryExpression ( COMMA! unaryExpression)? RIGHT_PAREN!
+;
+
+trimFunction :
+ TRIM^ LEFT_PAREN! trimOperands RIGHT_PAREN!
+;
+
+trimOperands :
+ trimSpecification unaryExpression FROM! unaryExpression
+ | tsp:trimSpecification FROM ts:unaryExpression {
+ #trimOperands = #( #tsp, [CHAR_STRING," "], #ts );
+ }
+ | FROM! ts3:unaryExpression {
+ #trimOperands = #( [BOTH,"both"], [CHAR_STRING," "], #ts3 );
+ }
+ | sve1:unaryExpression ( FROM! sve2:unaryExpression ) {
+ if ( #sve2 != null ) {
+ #trimOperands = #( [BOTH,"both"], #sve1, #sve2 );
+ }
+ else {
+ #trimOperands = #( [BOTH,"both"], [CHAR_STRING," "], #sve1 );
+ }
+ }
+;
+
+trimSpecification :
+ LEADING
+ | TRAILING
+ | BOTH
+;
+
+upperFunction :
+ UPPER^ LEFT_PAREN! concatenation RIGHT_PAREN!
+;
+
+lowerFunction :
+ LOWER^ LEFT_PAREN! concatenation RIGHT_PAREN!
+;
+
+lengthFunction :
+ LENGTH^ LEFT_PAREN! concatenation RIGHT_PAREN!
+;
+
+locateFunction :
+ LOCATE^ LEFT_PAREN! unaryExpression COMMA! unaryExpression ( COMMA! unaryExpression )? RIGHT_PAREN!
+;
+
+absFunction :
+ ABS^ LEFT_PAREN! unaryExpression RIGHT_PAREN!
+;
+
+sqrtFunction :
+ SQRT^ LEFT_PAREN! unaryExpression RIGHT_PAREN!
+;
+
+modFunction :
+ MOD^ LEFT_PAREN! unaryExpression COMMA! unaryExpression RIGHT_PAREN!
+;
+
+sizeFunction :
+ SIZE^ LEFT_PAREN! path RIGHT_PAREN!
+;
+
+indexFunction :
+ INDEX^ LEFT_PAREN! aliasReference RIGHT_PAREN!
+;
+
+currentDateFunction :
+ CURRENT_DATE ( LEFT_PAREN! RIGHT_PAREN! )?
+;
+
+currentTimeFunction :
+ CURRENT_TIME ( LEFT_PAREN! RIGHT_PAREN! )?
+;
+
+currentTimestampFunction :
+ CURRENT_TIMESTAMP ( LEFT_PAREN! RIGHT_PAREN! )?
+;
+
+extractFunction :
+ EXTRACT^ LEFT_PAREN! extractField FROM extractSource RIGHT_PAREN!
+;
+
+extractField :
+ datetimeField
+ | timeZoneField
+;
+
+datetimeField :
+ nonSecondDatetimeField
+ | SECOND
+;
+
+timeZoneField :
+ TIMEZONE_HOUR
+ | TIMEZONE_MINUTE
+;
+
+extractSource :
+ concatenation
+;
+
+positionFunction :
+ POSITION^ LEFT_PAREN! unaryExpression IN! unaryExpression RIGHT_PAREN!
+;
+
+charLengthFunction :
+ ( CHAR_LENGTH^ | CHARACTER_LENGTH^ ) LEFT_PAREN! concatenation RIGHT_PAREN!
+;
+
+octetLengthFunction :
+ OCTET_LENGTH^ LEFT_PAREN! unaryExpression RIGHT_PAREN!
+;
+
+bitLengthFunction :
+ BIT_LENGTH^ LEFT_PAREN! unaryExpression RIGHT_PAREN!
+;
+
+setFunction :
+ ( SUM^ | AVG^ | MAX^ | MIN^ ) LEFT_PAREN! additiveExpression RIGHT_PAREN!
+ | COUNT^ LEFT_PAREN! ( ASTERISK | ( ( DISTINCT | ALL )? ( path | collectionExpression ) ) ) RIGHT_PAREN!
+;
+
+collectionFunction :
+ ( MAXELEMENT^ | MAXINDEX^ | MINELEMENT^ | MININDEX^ ) LEFT_PAREN! identPrimary RIGHT_PAREN!
+;
+
+collectionExpression :
+ ( ELEMENTS^ | INDICES^ ) LEFT_PAREN! path RIGHT_PAREN!
+;
+
+
+// level 0 - expression atom
+// ident qualifier ('.' ident ), array index ( [ expr ] ),
+// method call ( '.' ident '(' exprList ') )
+atom :
+ primaryExpression (
+ DOT^ identifier ( options { greedy=true; } :
+ ( op:LEFT_PAREN^ {#op.setType(GENERIC_FUNCTION);} exprList RIGHT_PAREN! )
+ )?
+ | lb:LEFT_BRACKET^ {#lb.setType(INDEX_OP);} expression RIGHT_BRACKET!
+ )*
+ ;
+
+// level 0 - the basic element of an expression
+primaryExpression :
+ identPrimary ( options {greedy=true;} : DOT^ CLASS )?
+ | constant
+ | parameterSpecification
+ | LEFT_PAREN! (expressionOrVector | subQuery) RIGHT_PAREN!
+ ;
+
+parameterSpecification! :
+ c:COLON! { weakKeywords(); } name:identifier! {
+ #c.setType( NAMED_PARAM );
+ #c.setText( #name.getText() );
+ #parameterSpecification = #c;
+ }
+ | p:PARAM! ( position:NUM_INT_LITERAL! )? {
+ if ( #position == null ) {
+ #parameterSpecification = #p;
+ }
+ else {
+ #position.setType( JPA_PARAM );
+ #parameterSpecification = #position;
+ }
+ }
+;
+
+// This parses normal expression and a list of expressions separated by commas. If a comma is encountered
+// a parent VECTOR_EXPR node will be created for the list.
+expressionOrVector!
+ : e:expression ( v:vectorExpr )? {
+ // If this is a vector expression, create a parent node for it.
+ if (#v != null)
+ #expressionOrVector = #([VECTOR_EXPR,"{vector}"], #e, #v);
+ else
+ #expressionOrVector = #e;
+ }
+ ;
+
+vectorExpr
+ : COMMA! expression (COMMA! expression)*
+ ;
+
+// identifier, followed by member refs (dot ident), or method calls.
+// 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(); }
+ ( options { greedy=true; } : DOT^ ( identifier | ELEMENTS | o:OBJECT { #o.setType(IDENT); } ) )*
+ ( options { greedy=true; } : ( op:LEFT_PAREN^ { #op.setType(GENERIC_FUNCTION);} exprList RIGHT_PAREN! ) )? {
+ if ( #op == null ) {
+ String path = extractPath( #identPrimary );
+ if ( isJavaConstant( path ) ) {
+ #identPrimary = #( [JAVA_CONSTANT,path] );
+ }
+ }
+ }
+;
+
+//aggregate
+// : ( SUM^ | AVG^ | MAX^ | MIN^ ) LEFT_PAREN! additiveExpression RIGHT_PAREN! { #aggregate.setType(AGGREGATE); }
+// // Special case for count - It's 'parameters' can be keywords.
+// | COUNT^ LEFT_PAREN! ( ASTERISK { #ASTERISK.setType(ROW_STAR); } | ( ( DISTINCT | ALL )? ( path | collectionExpr ) ) ) RIGHT_PAREN!
+// | collectionExpr
+// ;
+//
+//collectionExpr
+// : (ELEMENTS^ | INDICES^) LEFT_PAREN! path RIGHT_PAREN!
+// ;
+
+// NOTE: compoundExpr can be a 'path' where the last token in the path is '.elements' or '.indicies'
+compoundExpr
+ : collectionExpression
+ | path
+ | (LEFT_PAREN! ( (expression (COMMA! expression)*) | subQuery ) RIGHT_PAREN!)
+ ;
+
+exprList :
+ ( trimSpec:trimSpecification )? {
+ if(#trimSpec != null) #trimSpec.setType(IDENT);
+ }
+ (
+ expression ( (COMMA! expression)+ | FROM { #FROM.setType(IDENT); } expression | AS! identifier )?
+ | FROM { #FROM.setType(IDENT); } expression
+ )?
+ { #exprList = #([EXPR_LIST,"exprList"], #exprList); }
+ ;
+
+constant :
+ literal
+ | NULL
+ | TRUE
+ | FALSE
+;
+
+literal :
+ numericLiteral
+ | characterLiteral
+ | dateLiteral
+ | timeLiteral
+ | timestampLiteral
+ | intervalLiteral
+;
+
+numericLiteral :
+ UNSIGNED_INTEGER
+ | NUM_INT_LITERAL
+ | NUM_LONG_LITERAL
+ | NUM_DOUBLE_LITERAL
+ | NUM_FLOAT_LITERAL
+;
+
+characterLiteral :
+ CHAR_STRING
+ | NATIONAL_CHAR_STRING_LIT
+ | BIT_STRING_LIT
+ | HEX_STRING_LIT
+;
+
+dateLiteral :
+ DATE^ CHAR_STRING
+;
+
+timeLiteral :
+ TIME^ CHAR_STRING
+;
+
+timestampLiteral :
+ TIMESTAMP^ CHAR_STRING
+;
+
+intervalLiteral :
+ INTERVAL ( PLUS_SIGN | MINUS_SIGN )? CHAR_STRING intervalQualifier
+;
+
+intervalQualifier :
+ intervalStartField ( TO intervalEndField | )
+ | SECOND ( LEFT_PAREN UNSIGNED_INTEGER ( COMMA UNSIGNED_INTEGER )? RIGHT_PAREN )?
+;
+
+intervalStartField :
+ nonSecondDatetimeField ( LEFT_PAREN UNSIGNED_INTEGER RIGHT_PAREN )?
+;
+
+intervalEndField :
+ nonSecondDatetimeField
+ | SECOND ( LEFT_PAREN UNSIGNED_INTEGER RIGHT_PAREN )?
+;
+
+nonSecondDatetimeField :
+ YEAR
+ | MONTH
+ | DAY
+ | HOUR
+ | MINUTE
+;
Added: core/branches/SQL_GEN_REDESIGN/src/main/antlr/hql/resolve.g
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/antlr/hql/resolve.g (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/antlr/hql/resolve.g 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,844 @@
+header {
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ *
+ * Portions of SQL grammar parsing copyright (C) 2003 by Lubos Vnuk. All rights
+ * reserved. These portions are distributed under license by Red Hat Middleware
+ * LLC and are covered by the above LGPL notice. If you redistribute this material,
+ * with or without modification, you must preserve this copyright notice in its
+ * entirety.
+ */
+package org.hibernate.hql.ast.phase.parse;
+import antlr.collections.AST;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+}
+
+/**
+ * An Antlr tree parser for "resolving" or "normalizing" an HQL syntax AST. This
+ * parser provides the vast majority of the semantic analysis of the HQL AST.
+ * <p/>
+ * Both "resolving" and "normalizing" here seek a single goal of building a
+ * dis-ambiguated, generic query AST.
+ * <p/>
+ * The act of resolving is essentially the process of simplifying complex node
+ * structures into atomic components based on contextual information (aka, the
+ * current parser state). The main thrust of this process is breaking down
+ * dot-structures (a series of DOT INDET pairs) into <ul>
+ * <li>a series of "implicit" join structures injected into the from clause tree</li>
+ * <li>a simple structure representing the "meaning" of the "leaf" of said dot-structure</li>
+ * </ul>
+ * <p/>
+ * The act of normalizing essentially refers to the process of dis-ambiguating
+ * node structures based on their context and creating a unified AST
+ * representation for different ways to express the same "idea". An example of this
+ * is normalizing implicit join dot-structures into a structure akin to its explicit
+ * join corollary.
+ *
+ * @author Joshua Davis
+ * @author Steve Ebersole
+ */
+class GeneratedHqlResolver extends TreeParser;
+
+options {
+ importVocab=HqlParse;
+ exportVocab=HqlResolve;
+ buildAST=true;
+}
+
+tokens {
+ PROPERTY_REF;
+ ENTITY_PERSISTER_REF;
+ COLLECTION_PERSISTER_REF;
+ PERSISTER_ALIAS_REF;
+ BOGUS;
+
+// SELECT_CLAUSE;
+}
+
+
+// -- Declarations --
+{
+ private static Logger log = LoggerFactory.getLogger( GeneratedHqlResolver.class );
+
+ private int ordinalParamCount = 0;
+
+ protected boolean isPersisterReferenceAlias(AST alias) {
+ return false;
+ }
+
+ protected AST resolveAlias(AST alias) {
+ return alias;
+ }
+
+ protected boolean isUnqualifiedPropertyReference(AST property) {
+ return false;
+ }
+
+ protected AST buildUnqualifiedPropertyReference(AST propertyNameNode) {
+ return propertyNameNode;
+ }
+
+ protected AST resolvePropertyPathTerminus(AST source, AST propertyNameNode) {
+ return propertyNameNode;
+ }
+
+ protected AST resolvePropertyPathIntermediary(AST source, AST propertyNameNode) {
+ return propertyNameNode;
+ }
+
+ protected void validateIndexOperationOperands(AST collectionPropertyRef, AST selector) {
+ }
+
+ protected AST resolveQualifiedRoot(AST alias) {
+ return null;
+ }
+
+ protected AST resolveUnqualifiedRoot(AST alias) {
+ return null;
+ }
+
+ protected AST resolveIndexedRoot(AST alias) {
+ return null;
+ }
+
+
+ // Statement node BEGIN/END handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected void pushStatement(AST statementNode) {
+ }
+
+ protected void popStatement() {
+ }
+
+
+ // property-path context pushing/popping ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected void pushFromClausePropertyPathContext(AST joinType, AST fetch, AST alias, AST propertyFetch) {
+ }
+
+ protected void popFromClausePropertyPathContext() {
+ }
+
+ protected void pushOnFragmentPropertyPathContext(AST rhsPersisterReference) {
+ }
+
+ protected void popOnFragmentPropertyPathContext() {
+ }
+
+ protected void pushWithFragmentPropertyPathContext(AST rhsPersisterReference) {
+ }
+
+ protected void popWithFragmentPropertyPathContext() {
+ }
+
+ protected void pushSelectClausePropertyPathContext() {
+ }
+
+ protected void popSelectClausePropertyPathContext() {
+ }
+
+
+ // persister reference handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected AST buildEntityPersisterReference(AST entityName, AST alias, AST propertyFetch) {
+ return entityName;
+ }
+
+ protected AST buildAdHocJoinNode(AST persisterReference, AST joinType, AST withFragment) {
+ return persisterReference;
+ }
+
+ protected void applyWithFragment(AST withFragment) {
+ }
+
+
+ // parameter handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private AST generateNamedParameter(AST delim, AST name) throws SemanticException {
+ // use the source delim node to properly transfer line/column number information :(
+ AST param = #( delim );
+ param.setType( NAMED_PARAM );
+ param.setText( name.getText() );
+ return param;
+ }
+
+ private AST generateOrdinalParameter(AST source) throws SemanticException {
+ // use the source node to properly transfer line/column number information :(
+ AST param = #( source );
+ param.setType( ORDINAL_PARAM );
+ param.setText( Integer.toString( ordinalParamCount++ ) );
+ return param;
+ }
+
+ protected void injectSelectAlias( AST selectExpression, AST alias) {
+ }
+
+ protected void registerSelectExpression(AST selectExpression) {
+ }
+
+ protected AST handleSelectedPropertyRef(AST propertyRef) {
+ return propertyRef;
+ }
+}
+
+// Statement rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * The main grammar rule
+ */
+statement :
+ updateStatement
+ | deleteStatement
+ | insertStatement
+ | selectStatement
+;
+
+
+// <tt>UPDATE</tt> statement ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * Recognize an <tt>UPDATE</tt> statement
+ */
+updateStatement :
+ #(
+ UPDATE { pushStatement( #updateStatement ); }
+ (VERSIONED)?
+ ENTITY_NAME (ALIAS)?
+ setClause
+ (whereClause)? { popStatement(); }
+ )
+;
+
+setClause :
+ #( SET (assignment)+ )
+;
+
+assignment :
+ #( ASSIGNMENT_OP assignmentField newValue )
+;
+
+assignmentField :
+ propertyReference
+;
+
+newValue :
+ valueExpression
+;
+
+
+
+// <tt>DELETE</tt> statement ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * Recognize an HQL <tt>DELETE</tt> statement
+ */
+deleteStatement :
+ #(
+ DELETE { pushStatement( #deleteStatement ); }
+ ENTITY_NAME (ALIAS)?
+ (whereClause)? { popStatement(); }
+ )
+;
+
+
+// <tt>INSERT</tt> statement ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * Recognize an HQL <tt>INSERT</tt> statement
+ */
+insertStatement :
+ #(
+ INSERT { pushStatement( #insertStatement ); }
+ intoClause
+ queryExpression { popStatement(); }
+ )
+;
+
+intoClause :
+ #( INTO ENTITY_NAME insertabilitySpecification )
+;
+
+insertabilitySpecification :
+ #( INSERTABILITY_SPEC (insertablePropertySpecification)+ )
+;
+
+/**
+ * The property being inserted into.
+ */
+insertablePropertySpecification :
+ IDENT
+;
+
+
+// <tt>SELECT</tt> statement rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * Recognize an HQL <tt>SELECT</tt> statement.
+ * <p/>
+ * This corresponds most closely to the <cursor specification> rule in ISO/ANSI SQL...
+ */
+selectStatement :
+ #( QUERY queryExpression orderByClause )
+;
+
+orderByClause :
+ #( ORDER (sortSpecification)+ )
+;
+
+sortSpecification :
+ #( SORT_SPEC sortKey (collationSpecification)? (orderingSpecification)? )
+;
+
+sortKey :
+ // todo : do we want to explicitly limit these?
+ valueExpression
+;
+
+collationSpecification :
+ COLLATE
+;
+
+orderingSpecification :
+ ORDER_SPEC
+;
+
+queryExpression :
+ #(
+ QUERY { pushStatement( #queryExpression ); }
+ querySpec ( ( UNION | INTERSECT | EXCEPT ) (ALL)? querySpec )* { popStatement( #queryExpression ); }
+ )
+;
+
+subquery :
+ queryExpression
+;
+
+querySpec :
+ selectFrom ( whereClause )? ( groupByClause ( havingClause )? )?
+;
+
+
+selectFrom! :
+ #( SELECT_FROM fromClause (selectClause)? )
+;
+
+fromClause :
+ #( FROM (persisterSpace)+ )
+;
+
+persisterSpace :
+ #( PERSISTER_SPACE entityPersisterReference ( qualifiedJoin | crossJoin )* )
+;
+
+entityPersisterReference! :
+ en:ENTITY_NAME (a:ALIAS)? (pf:PROP_FETCH)? {
+ #entityPersisterReference = buildEntityPersisterReference( en, a, pf );
+ }
+;
+
+crossJoin :
+ #( JOIN CROSS entityPersisterReference )
+;
+
+qualifiedJoin :
+ #(
+ j:JOIN (jt:nonCrossJoinType)? (
+ e:entityPersisterReference (on:onFragment[#e])? {
+ buildAdHocJoinNode( #e, #jt, #on );
+ if ( on != null ) {
+ popOnFragmentPropertyPathContext();
+ }
+ }
+ | (f:FETCH)? (a:ALIAS)? (pf:PROP_FETCH)? { pushFromClausePropertyPathContext( jt, #f, #a, #pf ); } prop:propertyRef (with:withFragment[#prop])? {
+ popFromClausePropertyPathContext();
+ }
+ )
+ )
+;
+
+nonCrossJoinType :
+ ( (LEFT | RIGHT) (OUTER)? )
+ | INNER
+ ;
+
+onFragment[ AST rhsPersisterReference ]
+ : #( o:ON { pushOnFragmentPropertyPathContext( rhsPersisterReference ); } le:logicalExpr ) {
+ #onFragment = #( o, le );
+ }
+ ;
+
+withFragment[ AST rhsPropertyReference ]
+ : #( w:WITH { pushWithFragmentPropertyPathContext( rhsPropertyReference ); } le:logicalExpr ) {
+ #withFragment = #( w, le );
+ applyWithFragment( #withFragment );
+ popWithFragmentPropertyPathContext();
+ }
+ ;
+
+
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// The statement sub-rules
+
+selectStatement :
+ query ( ( m:MINUS | u:UNION ) (a:ALL)? query )*
+;
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+query
+ : #(QUERY { pushStatement( #query ); }
+ // The first phase places the FROM first to make processing the SELECT simpler.
+ #( SELECT_FROM fromClause (selectClause)? )
+ (whereClause)?
+ (groupClause)?
+ (orderClause)? {
+ popStatement();
+ }
+ )
+ ;
+
+selectClause
+ : #( SELECT { pushSelectClausePropertyPathContext(); } ( d:DISTINCT )? x:selectExprList ) {
+ #selectClause = #([SELECT_CLAUSE,"{select clause}"], #d, #x);
+ popSelectClausePropertyPathContext();
+ }
+ ;
+
+selectExprList
+ : ( selectExprValue )+
+ ;
+
+selectExprValue
+ : ( selectExpr | aliasedSelectExpr ) {
+ registerSelectExpression( #selectExprValue );
+ }
+ ;
+
+aliasedSelectExpr
+ : #( AS se:selectExpr i:identifier! ) {
+ injectSelectAlias( #se, #i );
+ #aliasedSelectExpr = #se;
+ }
+ ;
+
+// TODO : the old grammar had both literal and constant available as select expressions, but that is probably not valid; it is certainly not valid for non-subqueries
+selectExpr
+ : (persisterReferenceAliasCheck) => persisterRefRef
+ | pr:propertyRef { #selectExpr = handleSelectedPropertyRef( #pr ); }
+ | arithmeticExpr
+ | count
+ | literal
+ | constant
+ ;
+
+persisterRefRef!
+ : alias:IDENT {
+ #persisterRefRef = #( [ PERSISTER_REF_REF, alias.getText() ] );
+ }
+ ;
+
+count
+ : #(COUNT ( DISTINCT | ALL )? ( propertyRef | literal | ROW_STAR ) )
+ ;
+
+fromClause
+ : #( f:FROM rootFromElement ( explicitJoin | rootFromElement )* )
+ ;
+
+rootFromElement!
+ : #( RANGE e:entityPersisterReference ) {
+ #rootFromElement = #e;
+ }
+ ;
+
+entityPersisterReference!
+ : en:ENTITY_NAME (a:ALIAS)? (pf:PROP_FETCH)? {
+ #entityPersisterReference = buildEntityPersisterReference( en, a, pf );
+ }
+ ;
+
+explicitJoin!
+ : #(
+ j:JOIN (jt:joinType)?
+ (
+ e:entityPersisterReference (on:onFragment[#e])? {
+ buildAdHocJoinNode( #e, #jt, #on );
+ if ( on != null ) {
+ popOnFragmentPropertyPathContext();
+ }
+ }
+ | (f:FETCH)? (a:ALIAS)? (pf:PROP_FETCH)? { pushFromClausePropertyPathContext( jt, #f, #a, #pf ); } prop:propertyRef (with:withFragment[#prop])? {
+ popFromClausePropertyPathContext();
+ }
+ )
+ )
+ ;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// propertyRef related rules...
+//
+
+/**
+ * The top level property ref recognition rule.
+ */
+propertyRef
+ : (unqualifiedPropertyRefCheck) => unqualifiedPropertyRef
+ | implicitJoin
+ | indexOperation
+ ;
+
+/**
+ * A rule utilizing a validating semantic predicate to contextually
+ * ensure that we have unqualified property reference. Generally
+ * used from within syntactic predicates to disambiguate the match path
+ *
+ * see {@link #implicitJoinSource} for example
+ */
+unqualifiedPropertyRefCheck!
+ : prop:IDENT { isUnqualifiedPropertyReference( prop ) }?
+ ;
+
+/**
+ * AST construction rule for building AST relating to *known*
+ * unqualified property references. Do not call this rule unless
+ * you know for certain (ala, have verified via the unqualifiedPropertyRefCheck
+ * rule or similiar) that the next token is an IDENT representing an
+ * unqualified property reference.
+ */
+unqualifiedPropertyRef!
+ : prop:IDENT {
+ #unqualifiedPropertyRef = buildUnqualifiedPropertyReference( prop );
+ }
+ ;
+
+/**
+ * A rule utilizing a validating semantic predicate to contextually
+ * ensure that we have an alias for a persister reference previously
+ * encountered and processed. Generally used from within syntactic
+ * predicates to disambiguate the path.
+ */
+persisterReferenceAliasCheck!
+ : alias:IDENT { isPersisterReferenceAlias( alias ) }?
+ ;
+
+/**
+ * AST construction rule for building AST relating to *known*
+ * persister reference aliases. Do not call this rule unless
+ * you know for certain (ala, have verified via the persisterReferenceAliasCheck
+ * rule or similiar) that the next token is an IDENT representing an
+ * alias for a persister reference
+ */
+persisterReferenceAlias!
+ : alias:IDENT {
+ #persisterReferenceAlias = resolveAlias( alias );
+ }
+ ;
+
+/**
+ * Perhaps better named as 'complex property ref' or 'pathed property ref' i.e.
+ * Anyway, the basic idea is (DOT <source> IDENT)
+ */
+implicitJoin!
+ : #( d:DOT source:implicitJoinSource prop:IDENT) {
+ #implicitJoin = resolvePropertyPathTerminus( #source, #prop );
+ }
+ ;
+
+intermediateImplicitJoin!
+ : #( d:DOT source:implicitJoinSource prop:IDENT) {
+ #intermediateImplicitJoin = resolvePropertyPathIntermediary( #source, #prop );
+ }
+ ;
+
+/**
+ * The output of the implicitJoinSource rule is a {@link org.hibernate.sql.ast.phase.resolve.patg.PropertyPathPart}
+ * reference which is a tokenized and encoded representation of the current path expression
+ * resolution state.
+ */
+implicitJoinSource
+ : (persisterReferenceAliasCheck) => a:IDENT { #implicitJoinSource = resolveQualifiedRoot( #a ); }
+ | (unqualifiedPropertyRefCheck) => pr:IDENT { #implicitJoinSource = resolveUnqualifiedRoot( #pr ); }
+ | intermediateImplicitJoin
+ | i:indexOperation { #implicitJoinSource = resolveIndexedRoot( #i ); }
+ ;
+
+indexOperation
+ : #( i:INDEX_OP coll:propertyRef selector:indexSelector ) {
+ validateIndexOperationOperands( #coll, #selector );
+ }
+ ;
+
+indexSelector
+ : constant
+ | parameter
+ | arithmeticExpr
+ | collectionFunctionCall
+ ;
+
+//
+// <end> propertyRef related rules...
+///////////////////////////////////////////////////////////////////////////////
+
+collectionFunctionCall
+ : #( METHOD_CALL pathAsIdent ( #(EXPR_LIST alias ) )? )
+ ;
+
+alias!
+ : i:identifier {
+ #alias = resolveAlias( i );
+ }
+ ;
+
+joinType
+ : ( (LEFT | RIGHT) (OUTER)? )
+ | FULL
+ | INNER
+ ;
+
+onFragment[ AST rhsPersisterReference ]
+ : #( o:ON { pushOnFragmentPropertyPathContext( rhsPersisterReference ); } le:logicalExpr ) {
+ #onFragment = #( o, le );
+ }
+ ;
+
+withFragment[ AST rhsPropertyReference ]
+ : #( w:WITH { pushWithFragmentPropertyPathContext( rhsPropertyReference ); } le:logicalExpr ) {
+ #withFragment = #( w, le );
+ applyWithFragment( #withFragment );
+ popWithFragmentPropertyPathContext();
+ }
+ ;
+
+
+intoClause
+ : #(i:INTO (subtree)* )
+ ;
+
+whereClause
+ : #(WHERE logicalExpr )
+ ;
+
+groupClause
+ : #(GROUP (subtree)* )
+ ;
+
+orderClause
+ : #(ORDER (subtree)* )
+ ;
+
+
+logicalExpr
+ : #(AND logicalExpr logicalExpr)
+ | #(OR logicalExpr logicalExpr)
+ | #(NOT logicalExpr)
+ | comparisonExpr
+ ;
+
+comparisonExpr
+ :
+ ( #(EQ exprOrSubquery exprOrSubquery)
+ | #(NE exprOrSubquery exprOrSubquery)
+ | #(LT exprOrSubquery exprOrSubquery)
+ | #(GT exprOrSubquery exprOrSubquery)
+ | #(LE exprOrSubquery exprOrSubquery)
+ | #(GE exprOrSubquery exprOrSubquery)
+ | #(LIKE exprOrSubquery expr ( #(ESCAPE expr) )? )
+ | #(NOT_LIKE exprOrSubquery expr ( #(ESCAPE expr) )? )
+ | #(BETWEEN exprOrSubquery exprOrSubquery exprOrSubquery)
+ | #(NOT_BETWEEN exprOrSubquery exprOrSubquery exprOrSubquery)
+ | #(IN exprOrSubquery inRhs )
+ | #(NOT_IN exprOrSubquery inRhs )
+ | #(IS_NULL exprOrSubquery)
+ | #(IS_NOT_NULL exprOrSubquery)
+ | #(EXISTS ( expr | collectionFunctionOrSubselect ) )
+ )
+ ;
+
+inRhs
+ : #(IN_LIST ( collectionFunctionOrSubselect | ( (expr)* ) ) )
+ ;
+
+exprOrSubquery
+ : expr
+ | query
+ | #(ANY collectionFunctionOrSubselect)
+ | #(ALL collectionFunctionOrSubselect)
+ | #(SOME collectionFunctionOrSubselect)
+ ;
+
+collectionFunctionOrSubselect
+ : collectionFunction
+ | query
+ ;
+
+collectionFunction
+ : #( ELEMENTS propertyRef )
+ | #( INDICES propertyRef )
+ ;
+
+aggregateExpr
+ : expr
+ | collectionFunction
+ ;
+
+expr
+ : addrExpr
+ | #( VECTOR_EXPR (expr)* )
+ | constant
+ | arithmeticExpr
+ | functionCall // Function call, not in the SELECT clause.
+ | parameter
+ | count // Count, not in the SELECT clause.
+ ;
+
+arithmeticExpr
+ : #(PLUS expr expr)
+ | #(MINUS expr expr)
+ | #(DIV expr expr)
+ | #(STAR expr expr)
+ | #(UNARY_MINUS expr)
+ | caseExpr
+ ;
+
+caseExpr
+ : #(CASE (#(WHEN logicalExpr expr))+ (#(ELSE expr))?)
+ | #(CASE2 expr (#(WHEN expr expr))+ (#(ELSE expr))?)
+ ;
+
+//addrExpr
+// : propertyRef
+// | #(INDEX_OP addrExprLhs expr)
+// ;
+
+addrExpr
+ : propertyRef
+ ;
+
+addrExprLhs
+ : addrExpr
+ ;
+
+constant
+ : NULL
+ | TRUE
+ | FALSE
+ | javaConstant
+ ;
+
+javaConstant
+ : JAVA_CONSTANT
+ ;
+
+literal
+ : NUM_INT
+ | NUM_LONG
+ | NUM_FLOAT
+ | NUM_DOUBLE
+ | QUOTED_STRING
+ ;
+
+parameter!
+ : #( c:COLON (c2:COLON)? i:identifier ) {
+ if ( c2 != null ) {
+ // allows escaping the colon...
+ String text = ":" + i.getText();
+ #parameter = #( [IDENT, text] );
+ }
+ else {
+ #parameter = generateNamedParameter( c, i );
+ }
+ }
+ | #( p:PARAM (n:NUM_INT)? ) {
+ if ( n != null ) {
+ // An ejb3-style "positional parameter", which we handle internally as a named-parameter
+ #parameter = generateNamedParameter( p, n );
+ }
+ else {
+ #parameter = generateOrdinalParameter( p );
+ }
+ }
+ ;
+
+functionCall
+ : #(METHOD_CALL pathAsIdent ( #(EXPR_LIST (expr)* ) )? )
+ | #(AGGREGATE aggregateExpr )
+ ;
+
+//propertyRef
+// : propertyPath
+// ;
+
+propertyName
+ : identifier
+ | CLASS
+ | ELEMENTS
+ | INDICES
+ ;
+
+// Matches a path and returns the normalized string for the path (usually
+// fully qualified a class name).
+pathAsString returns [String p] {
+ p = "???";
+ String x = "?x?";
+ }
+ : a:identifier { p = a.getText(); }
+ | #(DOT x=pathAsString y:identifier) {
+ StringBuffer buf = new StringBuffer();
+ buf.append(x).append(".").append(y.getText());
+ p = buf.toString();
+ }
+ ;
+
+// Returns a path as a single identifier node.
+pathAsIdent {
+ String text = "?text?";
+ }
+ : text=pathAsString {
+ #pathAsIdent = #([IDENT,text]);
+ }
+ ;
+
+identifier
+ : (IDENT | WEIRD_IDENT)
+ ;
+
+// General subtree. Matches anything, copies the tree verbatim.
+subtree
+ : #(. (subtree)*)
+ ;
Added: core/branches/SQL_GEN_REDESIGN/src/main/antlr/sql/common.g
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/antlr/sql/common.g (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/antlr/sql/common.g 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,424 @@
+header
+{
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ *
+ * Portions of SQL grammar parsing copyright (C) 2003 by Lubos Vnuk. All rights
+ * reserved. These portions are distributed under license by Red Hat Middleware
+ * LLC and are covered by the above LGPL notice. If you redistribute this material,
+ * with or without modification, you must preserve this copyright notice in its
+ * entirety.
+ */
+package org.hibernate.sql.ast.common;
+
+import antlr.collections.AST;
+import antlr.Token;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+}
+
+/**
+ * A convenience base class for Hibernate parsers.
+ * <p/>
+ * Our parsers are generally dealing with some aspect of SQL, and so this parser provides commonly needed
+ * actions as well as exporting the basic common vocaulary of needed tokens (including all ISO/ANSI SQL-92
+ * key/reserved words).
+ *
+ * @author Lubos Vnuk
+ * @author Joshua Davis
+ * @author Steve Ebersole
+ */
+class CommonHibernateParserSupport extends Parser;
+
+options
+{
+ exportVocab=Sql92;
+ buildAST=true;
+ k=2;
+// codeGenMakeSwitchThreshold=4; // Code optimization
+// codeGenBitsetTestThreshold=8; // Code optimization
+}
+
+tokens
+{
+ // SQL key/reserved words
+ ALL = "all";
+ AND = "and";
+ ANY = "any";
+ AS = "as";
+ ASCENDING = "asc";
+ AT = "at";
+ AVG = "avg";
+ BETWEEN = "between";
+ BIT = "bit";
+ BIT_LENGTH = "bit_lenght";
+ BOTH = "both";
+ BY = "by";
+ CASE = "case";
+ CAST = "cast";
+ CHAR = "char";
+ CHARACTER = "character";
+ CHAR_LENGTH = "char_length";
+ CHARACTER_LENGTH = "character_length";
+ COALESCE = "coalesce";
+ COLLATE = "collate";
+ CONVERT = "convert";
+ CORRESPONDING = "corresponding";
+ COUNT = "count";
+ CROSS = "cross";
+ CURRENT = "current";
+ CURRENT_DATE = "current_date";
+ CURRENT_TIME = "current_time";
+ CURRENT_TIMESTAMP = "current_timestamp";
+ DATE = "date";
+ DAY = "day";
+ DEC = "dec";
+ DECIMAL = "decimal";
+ DEFAULT = "default";
+ DELETE = "delete";
+ DESCENDING = "desc";
+ DISTINCT = "distinct";
+ DOUBLE = "double";
+ ELSE = "else";
+ END = "end";
+ ESCAPE = "escape";
+ EXCEPT = "except";
+ EXISTS = "exists";
+ EXTRACT = "extract";
+ FALSE = "false";
+ FLOAT = "float";
+ FOR = "for";
+ FROM = "from";
+ FULL = "full";
+ GLOBAL = "global";
+ GROUP = "group";
+ HAVING = "having";
+ HOUR = "hour";
+ IN = "in";
+ INDICATOR = "indicator";
+ INNER = "inner";
+ INSERT = "insert";
+ INT = "int";
+ INTEGER = "integer";
+ INTERSECT = "intersect";
+ INTERVAL = "interval";
+ INTO = "into";
+ IS = "is";
+ JOIN = "join";
+ LEADING = "leading";
+ LEFT = "left";
+ LIKE = "like";
+ LOCAL = "local";
+ LOWER = "lower";
+ MATCH = "match";
+ MAX = "max";
+ MIN = "min";
+ MINUS = "minus";
+ MINUTE = "minute";
+ MODULE = "module";
+ MONTH = "month";
+ NATIONAL = "national";
+ NATURAL = "natural";
+ NCHAR = "nchar";
+ NOT = "not";
+ NULL = "null";
+ NULLIF = "nullif";
+ NUMERIC = "numeric";
+ OCTET_LENGTH = "octet_length";
+ OF = "of";
+ ON = "on";
+ ONLY = "only";
+ OR = "or";
+ ORDER = "order";
+ OUTER = "outer";
+ OVERLAPS = "overlaps";
+ PARTIAL = "partial";
+ POSITION = "position";
+ PRECISION = "precision";
+ READ = "read";
+ REAL = "real";
+ RIGHT = "right";
+ SECOND = "second";
+ SELECT = "select";
+ SET = "set";
+ SMALLINT = "smallint";
+ SOME = "some";
+ SUBSTRING = "substring";
+ SUM = "sum";
+ TABLE = "table";
+ THEN = "then";
+ TIME = "time";
+ TIMESTAMP = "timestamp";
+ TIMEZONE_HOUR = "timezone_hour";
+ TIMEZONE_MINUTE = "timezone_minute";
+ TO = "to";
+ TRAILING = "trailing";
+ TRANSLATE = "translate";
+ TRIM = "trim";
+ TRUE = "true";
+ UNION = "union";
+ UNIQUE = "unique";
+ UNKNOWN = "unknown";
+ UPDATE = "update";
+ UPPER = "upper";
+ USING = "using";
+ VALUE = "value";
+ VALUES = "values";
+ VARCHAR = "varchar";
+ VARYING = "varying";
+ WHEN = "when";
+ WHERE = "where";
+ WITH = "with";
+ YEAR = "year";
+ ZONE = "zone";
+
+ DOT;
+
+ // synthetic numeric literal types
+ NUM_INT_LITERAL;
+ NUM_LONG_LITERAL;
+ NUM_DOUBLE_LITERAL;
+ NUM_FLOAT_LITERAL;
+
+ NATIONAL_CHAR_STRING_LIT;
+ BIT_STRING_LIT;
+ HEX_STRING_LIT;
+
+ // HQL-specific keywords
+ ABS = "abs";
+ CLASS = "class";
+ CONCAT = "concat";
+ ELEMENTS = "elements";
+ EMPTY = "empty";
+ FETCH = "fetch";
+ INDEX = "index";
+ INDICES = "indices";
+ LENGTH = "length";
+ LOCATE = "locate";
+ MAXELEMENT = "maxelement";
+ MAXINDEX = "maxindex";
+ MEMBER = "member";
+ MINELEMENT = "minelement";
+ MININDEX = "minindex";
+ MOD = "mod";
+ NEW = "new";
+ OBJECT = "object";
+ PROPERTIES = "properties";
+ SIZE = "size";
+ SQRT = "sqrt";
+ VERSIONED = "versioned";
+}
+
+{
+ private static final Logger log = LoggerFactory.getLogger( CommonHibernateParserSupport.class );
+
+ // Grammar actions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void showAST(AST ast) {
+ showAST( ast, "AST" );
+ }
+
+ public void showAST(AST ast, String title) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( title + " [tree string] : " + ast.toStringTree() );
+ }
+ }
+}
+
+noRules :
+;
+
+
+// Lexer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * A Lexer for recognizing ISO/ANSI SQL vocabnulary tokens.
+ *
+ * @author Lubos Vnuk
+ * @author Joshua Davis
+ * @author Steve Ebersole
+ */
+class CommonHibernateLexerSupport extends Lexer;
+
+options {
+ exportVocab = Sql92;
+ testLiterals = false;
+ k = 2;
+ charVocabulary = '\u0000'..'\uFFFE';
+ caseSensitive = false;
+ caseSensitiveLiterals = false;
+}
+
+
+{
+ /**
+ * Lexer action used to hook callbacks from lexer rules whenever we encounter a token
+ * which could conceivable be used as an identifer. This is used to provide "keyword-as-identifier"
+ * handling (where a keyword is used as an identifier).
+ *
+ * @param possibleIdentifier Whether the token could be an idientifier.
+ */
+ protected void setPossibleIdentifier(boolean possibleIdentifier) {
+ }
+
+ protected void markAsApproximate() {
+ }
+}
+
+EQUALS_OP : '=' ;
+NOT_EQUALS_OP : "!=" | "^=";
+SQL_NOT_EQUALS_OP : "<>" { $setType(NOT_EQUALS_OP); };
+
+LESS_THAN_OP : '<';
+LESS_THAN_OR_EQUALS_OP : "<=";
+
+GREATER_THAN_OP : '>';
+GREATER_THAN_OR_EQUALS_OP : ">=";
+
+CONCATENATION_OP : "||";
+VERTICAL_BAR : '|';
+
+PARAM : '?' ;
+
+COLON : ':';
+SEMICOLON : ';' ;
+
+LEFT_BRACKET : '[' ;
+RIGHT_BRACKET : ']' ;
+
+LEFT_PAREN : '(' ;
+RIGHT_PAREN : ')' ;
+
+PLUS_SIGN : '+' ;
+MINUS_SIGN : '-';
+
+ASTERISK : '*' ;
+SOLIDUS : '/' ;
+
+COMMA : ',' ;
+
+PERCENT : '%' ;
+AMPERSAND : '&' ;
+
+
+CHAR_STRING :
+ ('\'' (options{greedy=true;}: ~('\'' | '\r' | '\n') | '\'' '\'' | NEWLINE)* '\'' )+
+// | '\'' {$setType(QUOTE);}
+;
+
+//QUOTED_STRING :
+// '\'' ( (ESCqs)=> ESCqs | ~'\'' )* '\''
+//;
+//
+//protected
+//ESCqs :
+// '\'' '\''
+//;
+
+/**
+ * Recognize either double-quote (") or back-tick (`) as delimiting a quoted identifier
+ */
+QUOTED_IDENT :
+ '"' (~('"' | '\r' | '\n') | '"' '"')+ '"'
+ | '`' (~('`' | '\r' | '\n') | '`' '`')+ '`'
+;
+
+IDENT options {testLiterals=true;} :
+ ( NATIONAL_CHAR_STRING_LIT {$setType(NATIONAL_CHAR_STRING_LIT);}
+ | BIT_STRING_LIT {$setType(BIT_STRING_LIT);}
+ | HEX_STRING_LIT {$setType(HEX_STRING_LIT);}
+ )
+ | (SIMPLE_LETTER | '_' | '$') (SIMPLE_LETTER | '_' | '$' | '0'..'9')* {
+ setPossibleIdentifier( true );
+// $setType( testLiteralsTable( IDENT ) );
+ }
+;
+
+protected
+NATIONAL_CHAR_STRING_LIT :
+ 'n' ('\'' (options{greedy=true;}: ~('\'' | '\r' | '\n' ) | '\'' '\'' | NEWLINE)* '\'' )+
+;
+
+protected
+BIT_STRING_LIT :
+ 'b' ('\'' ('0' | '1')* '\'' )+
+;
+
+protected
+HEX_STRING_LIT :
+ 'x' ("\'" ('a'..'f' | '0'..'9')* "\'" )+
+;
+
+protected
+SIMPLE_LETTER :
+ 'a'..'z'
+ | '\u0080'..'\ufffe'
+;
+
+/**
+ * This rule actually recognizes all numeric literals despite the name...
+ */
+NUM_INT_LITERAL :
+ // IMPL NOTE : 2 basic alt-branches:
+ // 1) starting with unsigned-int
+ // 2) starting with a decimal ('.')
+ UNSIGNED_INTEGER (
+ '.' UNSIGNED_INTEGER {$setType(NUM_DOUBLE_LITERAL);} ( 'e' SIGNED_INTEGER {markAsApproximate();} )? ( 'f' {$setType(NUM_FLOAT_LITERAL);} )?
+ | ( 'e' SIGNED_INTEGER {markAsApproximate();} )? ( ls:'l' {$setType(NUM_LONG_LITERAL);} )?
+ )
+ | '.' UNSIGNED_INTEGER {$setType(NUM_DOUBLE_LITERAL);} ( 'e' SIGNED_INTEGER {markAsApproximate();} )? ( 'f' {$setType(NUM_FLOAT_LITERAL);} )?
+ | '.' {$setType(DOT);}
+;
+
+protected
+SIGNED_INTEGER :
+ ( '+' | '-' )? UNSIGNED_INTEGER
+;
+
+protected
+UNSIGNED_INTEGER :
+ ('0'..'9')+
+;
+
+WHITESPACE :
+ ( SPACE | NEWLINE ) {
+ //ignore this token
+ $setType( Token.SKIP );
+ }
+;
+
+protected
+NEWLINE :
+ ( '\r' (options{greedy=true;}: '\n')? | '\n' ) {newline();}
+;
+
+protected
+SPACE :
+ ' ' | '\t'
+;
+
+protected
+ANY_CHAR :
+ .
+;
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/ASTFactoryImpl.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/ASTFactoryImpl.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/ASTFactoryImpl.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,42 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.hql.ast.phase.parse;
+
+import antlr.ASTFactory;
+
+import org.hibernate.sql.ast.common.Node;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public class ASTFactoryImpl extends ASTFactory {
+ /**
+ * {@inheritDoc}
+ */
+ public Class getASTNodeType(int tokenType) {
+ return Node.class;
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/HqlParser.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/HqlParser.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/HqlParser.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,623 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.hql.ast.phase.parse;
+
+import org.hibernate.sql.ast.util.ASTUtil;
+import org.hibernate.sql.ast.util.ASTPrinter;
+import org.hibernate.sql.ast.util.ErrorCounter;
+import org.hibernate.sql.ast.util.ParseErrorHandler;
+import org.hibernate.QueryException;
+import org.hibernate.MappingException;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.sql.ast.common.CommonHibernateLexer;
+import org.hibernate.sql.ast.common.TokenImpl;
+import org.hibernate.sql.ast.common.Node;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.StringReader;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+import antlr.collections.AST;
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+import antlr.Token;
+import antlr.ASTPair;
+import antlr.MismatchedTokenException;
+import antlr.SemanticException;
+import antlr.ASTFactory;
+
+/**
+ * The parser used by Hibernate to generate an AST given an input
+ * HQL string (a "stream parser"). The produced AST is then used
+ * (and mutated) by later phases/parsers to apply semantic resolution;
+ * this parser, however, is all about syntax resolution.
+ *
+ * @author Steve Ebersole
+ */
+public class HqlParser extends GeneratedHqlParser {
+ private static final Logger log = LoggerFactory.getLogger( HqlParser.class );
+
+ private final Context context;
+ private final ParseErrorHandler parseErrorHandler = new ErrorCounter();
+ private final ASTPrinter printer = new ASTPrinter( HqlParseTokenTypes.class );
+ private int traceDepth = 0;
+
+ public static interface Context {
+ /**
+ * Does this name represent an entity name?
+ *
+ * @param name The name to check
+ * @return True if the name represents an entioty name; false otherwise.
+ */
+ public boolean isEntityName(String name);
+
+ /**
+ * Get the import "replacement" name for the given name (e.g., 'User' -> 'com.acme.User'). <tt>null</tt>
+ * indicates an unrecognized name.
+ *
+ * @param name The name for which to locate the imported name.
+ * @return The corresponding imported name, or null.
+ */
+ public String getImportedName(String name);
+
+ /**
+ * Does the given name represent a registered function name?
+ *
+ * @param name The name to check
+ * @return True if the name matches a registered function name; false otherwise.
+ */
+ public boolean isRegisteredFunctionName(String name);
+ }
+
+ public HqlParser(String hql, Context context) {
+ super( new CommonHibernateLexer( new StringReader( hql ) ) );
+ this.context = context;
+ super.setASTFactory( new ASTFactoryImpl() );
+ }
+
+ public HqlParser(String hql, final SessionFactoryImplementor sessionFactoryImplementor) {
+ this(
+ hql,
+ new Context() {
+ public boolean isEntityName(String name) {
+ return findEntityPersisterByName( name ) != null;
+ }
+
+ public String getImportedName(String name) {
+ return sessionFactoryImplementor.getImportedClassName( name );
+ }
+
+ public boolean isRegisteredFunctionName(String name) {
+ return sessionFactoryImplementor.getDialect().getFunctions().get( name ) != null;
+ }
+
+ private EntityPersister findEntityPersisterByName(String name) throws MappingException {
+ try {
+ return sessionFactoryImplementor.getEntityPersister( name );
+ }
+ catch ( MappingException ignore ) {
+ // unable to locate it using this name
+ }
+
+ // If that didn't work, try using the 'import' name.
+ String importedClassName = sessionFactoryImplementor.getImportedClassName( name );
+ if ( importedClassName == null ) {
+ return null;
+ }
+ return sessionFactoryImplementor.getEntityPersister( importedClassName );
+ }
+ }
+ );
+ }
+
+
+ // overrides of Antlr infastructure methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void reportError(RecognitionException e) {
+ parseErrorHandler.reportError( e );
+ }
+
+ public void reportError(String s) {
+ parseErrorHandler.reportError( s );
+ }
+
+ public void reportWarning(String s) {
+ parseErrorHandler.reportWarning( s );
+ }
+
+ public ParseErrorHandler getParseErrorHandler() {
+ return parseErrorHandler;
+ }
+
+ static public void panic() {
+ //overriden to avoid System.exit
+ throw new QueryException( "Parser: panic" );
+ }
+
+ public void setASTFactory(ASTFactory astFactory) {
+ throw new UnsupportedOperationException( "not allowed!" );
+ }
+
+
+// various AST output methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void traceIn(String s) throws TokenStreamException {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = StringHelper.repeat( "-", (traceDepth++ * 2) ) + "->";
+ trace( prefix + s );
+ }
+
+ public void traceOut(String s) throws TokenStreamException {
+ if ( inputState.guessing > 0 ) {
+ return;
+ }
+ String prefix = "<-" + StringHelper.repeat( "-", (--traceDepth * 2) );
+ trace( prefix + s );
+ }
+
+ private void trace(String msg) {
+ System.out.println( msg );
+// log.trace( msg );
+ }
+
+ public void dumpAst(AST ast) {
+ dumpAst( ast, "DUMP" );
+ }
+
+ public void dumpAst(AST ast, String header) {
+ log.info( printer.showAsString( ast, header ) );
+ }
+
+ public void showAst(AST ast, PrintStream out) {
+ showAst( ast, new PrintWriter( out ) );
+ }
+
+ private void showAst(AST ast, PrintWriter pw) {
+ printer.showAst( ast, pw );
+ }
+
+
+ // overrides of grammar semantic actions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public String extractPath(AST tree) {
+ try {
+ return PathCollector.getPath( tree );
+ }
+ catch ( Throwable t ) {
+ return tree.getText();
+ }
+ }
+
+ public boolean isRegisteredFunction(AST tree) {
+ return context.isRegisteredFunctionName( extractPath( tree ) );
+ }
+
+ public boolean isJavaConstant(String path) {
+ try {
+ log.debug( "Testing path [" + path + "] as potential java constant" );
+ Object value = ReflectHelper.getConstantValueStrictly( path );
+ log.debug( "Resolved path to java constant [" + value + "]" );
+ return true;
+ }
+ catch( Throwable t ) {
+ log.debug( "Path did not resolve to java constant : " + t );
+ return false;
+ }
+ }
+
+ public String resolveEntityName(String name) {
+ return context.getImportedName( name );
+ }
+
+ public String resolveDynamicInstantiationPojoName(AST name) throws SemanticException {
+ String path = extractPath( name );
+ if ( "list".equals( path ) || "map".equals( path ) ) {
+ return path;
+ }
+ else {
+ String importedName = context.getImportedName( path );
+ try {
+ Class importedClass = ReflectHelper.classForName( importedName );
+ return importedClass.getName();
+ }
+ catch ( ClassNotFoundException e ) {
+ throw new SemanticException( "Unable to locate dynamic instantiation class [" + importedName + "]" );
+ }
+ }
+ }
+
+ protected AST processEqualityExpression(AST x) {
+ if ( x == null ) {
+ log.warn( "processEqualityExpression() : No expression to process!" );
+ return null;
+ }
+
+ int type = x.getType();
+ if ( log.isTraceEnabled() ) {
+ log.trace( "processEqualityExpression() -> type : {}, name : {}", Integer.toString( type ), ASTUtil.getTokenTypeName( HqlParseTokenTypes.class, type ) );
+ }
+ if ( type == EQUALS_OP || type == NOT_EQUALS_OP || type == SQL_NOT_EQUALS_OP ) {
+ boolean negated = ( type != EQUALS_OP );
+ if ( x.getNumberOfChildren() == 2 ) {
+ AST a = x.getFirstChild();
+ AST b = a.getNextSibling();
+ // (EQ NULL b) => (IS_NULL b)
+ if ( a.getType() == NULL && b.getType() != NULL ) {
+ return createIsNullParent( b, negated );
+ }
+ // (EQ a NULL) => (IS_NULL a)
+ else if ( b.getType() == NULL && a.getType() != NULL ) {
+ return createIsNullParent( a, negated );
+ }
+ else if ( b.getType() == EMPTY ) {
+ return processIsEmpty( a, negated );
+ }
+ else {
+ return x;
+ }
+ }
+ else {
+ return x;
+ }
+ }
+ else {
+ return x;
+ }
+ }
+
+ private AST createIsNullParent(AST node, boolean negated) {
+ node.setNextSibling( null );
+ int type = negated ? IS_NOT_NULL : IS_NULL;
+ String text = negated ? "is not null" : "is null";
+ return ASTUtil.createParent( astFactory, type, text, node );
+ }
+
+ protected AST processMemberOf(AST path, AST notNode) {
+ AST inNode = notNode == null ? astFactory.create( IN, "in" ) : astFactory.create( NOT_IN, "not in" );
+ AST subqueryNode = createSubquery( path );
+ AST inListNode = ASTUtil.createParent( astFactory, IN_LIST, "in-list", subqueryNode );
+ inNode.addChild( inListNode );
+ return inNode;
+ }
+
+ private AST createSubquery(AST subquerySource) {
+// AST ast = ASTUtil.createParent( astFactory, RANGE, "RANGE", node );
+// todo : what is the type of 'node'?
+ log.debug(
+ "Generating subquery; incoming node type = {}; incoming node = [{}]",
+ printer.getTokenTypeName( subquerySource.getType() ),
+ subquerySource
+ );
+ AST fromNode = ASTUtil.createParent( astFactory, FROM, "from", subquerySource );
+ AST selectFromNode = ASTUtil.createParent( astFactory, SELECT_FROM, "SELECT_FROM", fromNode );
+ return ASTUtil.createParent( astFactory, QUERY, "QUERY", selectFromNode );
+ }
+
+ protected AST processIsEmpty(AST collection, AST notToken) {
+ return processIsEmpty( collection, notToken != null );
+ }
+
+ private AST processIsEmpty(AST node, boolean negated) {
+ node.setNextSibling( null );
+ AST ast = createSubquery( node );
+ ast = ASTUtil.createParent( astFactory, EXISTS, "exists", ast );
+ // Add NOT if it's negated.
+ if ( negated ) {
+ ast = ASTUtil.createParent( astFactory, NOT, "not", ast );
+ }
+ return ast;
+ }
+ public AST negateNode(AST x) {
+ //TODO: switch statements are always evil! We already had bugs because
+ // of forgotten token types. Use polymorphism for this!
+ switch ( x.getType() ) {
+ case OR:
+ x.setType(AND);
+ x.setText("{and}");
+ negateNode( x.getFirstChild() );
+ negateNode( x.getFirstChild().getNextSibling() );
+ return x;
+ case AND:
+ x.setType(OR);
+ x.setText("{or}");
+ negateNode( x.getFirstChild() );
+ negateNode( x.getFirstChild().getNextSibling() );
+ return x;
+ case EQUALS_OP:
+ x.setType( NOT_EQUALS_OP );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (EQ a b) ) => (NE a b)
+ case NOT_EQUALS_OP:
+ x.setType( EQUALS_OP );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (NE a b) ) => (EQ a b)
+ case GREATER_THAN_OP:
+ x.setType( LESS_THAN_OR_EQUALS_OP );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (GT a b) ) => (LE a b)
+ case LESS_THAN_OP:
+ x.setType( GREATER_THAN_OR_EQUALS_OP );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (LT a b) ) => (GE a b)
+ case GREATER_THAN_OR_EQUALS_OP:
+ x.setType( LESS_THAN_OP );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (GE a b) ) => (LT a b)
+ case LESS_THAN_OR_EQUALS_OP:
+ x.setType( GREATER_THAN_OP );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (LE a b) ) => (GT a b)
+ case LIKE:
+ x.setType( NOT_LIKE );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (LIKE a b) ) => (NOT_LIKE a b)
+ case NOT_LIKE:
+ x.setType( LIKE );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (NOT_LIKE a b) ) => (LIKE a b)
+ case IN:
+ x.setType( NOT_IN );
+ x.setText( "{not}" + x.getText() );
+ return x;
+ case NOT_IN:
+ x.setType( IN );
+ x.setText( "{not}" + x.getText() );
+ return x;
+ case IS_NULL:
+ x.setType( IS_NOT_NULL );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (IS_NULL a b) ) => (IS_NOT_NULL a b)
+ case IS_NOT_NULL:
+ x.setType( IS_NULL );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (IS_NOT_NULL a b) ) => (IS_NULL a b)
+ case BETWEEN:
+ x.setType( NOT_BETWEEN );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (BETWEEN a b) ) => (NOT_BETWEEN a b)
+ case NOT_BETWEEN:
+ x.setType( BETWEEN );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (NOT_BETWEEN a b) ) => (BETWEEN a b)
+ case NOT:
+ return x.getFirstChild(); // (NOT (NOT x) ) => (x)
+ default:
+ return super.negateNode( x ); // Just add a 'not' parent.
+ }
+ }
+
+ protected void transferTrackingInfo(AST source, AST target) {
+ if ( target instanceof Node ) {
+ ( ( Node ) target ).transferTrackingInfo( source );
+ }
+ else {
+ super.transferTrackingInfo( source, target );
+ }
+ }
+
+ /**
+ * Overrides the base behavior to retry keywords as identifiers.
+ *
+ * @param token The token.
+ * @param ex The recognition exception.
+ * @return AST - The new AST.
+ * @throws antlr.RecognitionException if the substitution was not possible.
+ * @throws antlr.TokenStreamException if the substitution was not possible.
+ */
+ public AST handleIdentifierError(Token token, RecognitionException ex) throws RecognitionException, TokenStreamException {
+ // If the token can tell us if it could be an identifier...
+ if ( token instanceof TokenImpl ) {
+ TokenImpl hqlToken = ( TokenImpl ) token;
+ // ... and the token could be an identifer and the error is
+ // a mismatched token error ...
+ if ( hqlToken.isPossibleIdentifier() && ex instanceof MismatchedTokenException ) {
+ MismatchedTokenException mte = ( MismatchedTokenException ) ex;
+ // ... and the expected token type was an identifier, then:
+ if ( mte.expecting == IDENT ) {
+ // Use the token as an identifier.
+ reportWarning(
+ "Keyword '"+ token.getText()
+ + "' is being interpreted as an identifier due to: "
+ + mte.getMessage()
+ );
+ // Add the token to the AST.
+ ASTPair currentAST = new ASTPair();
+// token.setType( WEIRD_IDENT );
+ token.setType( IDENT );
+ astFactory.addASTChild( currentAST, astFactory.create( token ) );
+ consume();
+ return currentAST.root;
+ }
+ }
+ }
+ return super.handleIdentifierError( token, ex );
+ }
+
+ public void handleDotIdent() throws TokenStreamException {
+ // This handles HHH-354, where there is a strange property name in a where clause.
+ // If the lookahead contains a DOT then something that isn't an IDENT...
+ handleDotIdent( 1 );
+ }
+
+ protected int handleDotIdent(int offset) throws TokenStreamException {
+ while ( LA( offset ) == DOT ) {
+ switch ( LA( offset+1 ) ) {
+ case IDENT:
+ case CLASS:
+ break; // break the case statement, not the loop...
+ default:
+ convertPossibleIdentifier( ( TokenImpl ) LT( offset+1 ) );
+ }
+ offset += 2;
+ }
+ return offset;
+ }
+
+ protected void weakKeywords() throws TokenStreamException {
+ switch ( LA( 1 ) ) {
+ case ORDER:
+ case GROUP:
+ // Case 1: Multi token keywords GROUP BY and ORDER BY
+ // The next token ( LT(2) ) should be 'by'... otherwise, this is just an ident.
+ if ( LA( 2 ) != BY ) {
+ convertPossibleIdentifier( ( TokenImpl ) LT(1) );
+ }
+ break;
+ case IDENT:
+ break;
+ default :
+ if ( LA(0) == FROM && LA(2) == DOT ) {
+ convertPossibleIdentifier( ( TokenImpl ) LT(1) );
+ }
+ }
+ }
+
+ protected void unequivocalKeywordAsIdentifier() throws TokenStreamException {
+ if ( LA(1) == IDENT ) {
+ return;
+ }
+ convertPossibleIdentifier( ( TokenImpl ) LT(1) );
+ }
+
+ protected void potentialUpdatePersisterAlias() throws TokenStreamException {
+ switch( LA(1) ) {
+ case AS:
+ // alias rule will handle this...
+ case SET:
+ // SET marks the beginning of the UPDATE's SET-clause
+ break;
+ default:
+ convertPossibleIdentifier( ( TokenImpl ) LT(1) );
+ }
+ }
+
+ protected void potentialDeletePersisterAlias() throws TokenStreamException {
+ switch( LA(1) ) {
+ case AS:
+ // alias rule will handle this...
+ case WHERE:
+ break;
+ default:
+ convertPossibleIdentifier( ( TokenImpl ) LT(1) );
+ }
+ }
+
+ protected void prepareForPersisterReferenceRoot() throws TokenStreamException {
+ if ( LA(1) == IN ) {
+ return;
+ }
+
+ unequivocalKeywordAsIdentifier();
+
+ if ( LA(1) == IDENT && LA(2) == IN ) {
+ return;
+ }
+
+ prepareForBasicEntityPersisterReference();
+ }
+
+ protected void prepareForCrossJoinElements() throws TokenStreamException {
+ unequivocalKeywordAsIdentifier();
+ prepareForBasicEntityPersisterReference();
+ }
+
+ protected void prepareForBasicEntityPersisterReference() throws TokenStreamException {
+ int offset = handleDotIdent( 2 );
+ int next = LA(offset);
+ switch ( next ) {
+ case AS: {
+ // the next token would need to be the persister alias
+ convertPossibleIdentifier( (TokenImpl) LT(offset+1) );
+ break;
+ }
+ case IDENT : {
+ // nothing to do
+ break;
+ }
+ case WHERE:
+ case COMMA:
+ case ON:
+ case WITH:
+ case JOIN:
+ case RIGHT:
+ case LEFT:
+ case CROSS: {
+ // single token structural elements indicating that the next thing could not be an alias
+ break;
+ }
+ case UNION:
+ case INTERSECT:
+ case EXCEPT: {
+ int nextNext = LA(offset+1);
+ if ( nextNext == SELECT || nextNext == FROM ) {
+ break;
+ }
+ else if ( nextNext == ALL ) {
+ int nextNextNext = LA(offset+2);
+ if ( nextNextNext == SELECT || nextNextNext == FROM ) {
+ break;
+ }
+ }
+ convertPossibleIdentifier( ( TokenImpl ) LT(offset) );
+ break;
+ }
+ case GROUP:
+ case ORDER:
+ if ( LA(offset+1) != BY ) {
+ convertPossibleIdentifier( ( TokenImpl ) LT(offset) );
+ }
+ break;
+ case FETCH:
+ if ( ! ( LA(offset+1) == ALL && LA(offset+2) == PROPERTIES ) ) {
+ // not sure sure I like allowing 'fetch' as an indentifier
+ log.warn( "interpretting [fetch] keyword as alias; consider using differen alias" );
+ convertPossibleIdentifier( ( TokenImpl ) LT(offset) );
+ }
+ break;
+ default:
+ convertPossibleIdentifier( ( TokenImpl ) LT(offset) );
+ break;
+ }
+ }
+
+ protected void prepareForQualifiedJoinElements() throws TokenStreamException {
+ unequivocalKeywordAsIdentifier();
+ prepareForBasicEntityPersisterReference();
+ }
+
+ protected void convertPossibleIdentifier(TokenImpl token) {
+ if ( token.isPossibleIdentifier() ) {
+ token.setType( IDENT );
+ log.debug( "Converting keyword-to-identifier [{}]; [id={}]", token, new Integer(System.identityHashCode( token ) ) );
+ }
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/PathCollector.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/PathCollector.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/hql/ast/phase/parse/PathCollector.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,74 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.hql.ast.phase.parse;
+
+import org.hibernate.sql.ast.util.NodeTraverser;
+
+import antlr.collections.AST;
+
+/**
+ * Utilizes a NodeTraverser in order to collect a path from
+ * a (expecting dot-structure) an AST.
+ *
+ * @author Steve Ebersole
+ */
+public class PathCollector implements NodeTraverser.VisitationStrategy, HqlParseTokenTypes {
+ private String path = "";
+
+ private PathCollector() {
+ }
+
+ public void visit(AST node) {
+ if ( node.getType() == DOT ) {
+ path += '.';
+ }
+ else if ( "$".equals( node.getText() ) ) {
+ // allowable for treating java constants
+ path += '$';
+ }
+ else {
+ path += node.getText();
+ }
+ }
+
+ public static String getPath(AST dotStructure) {
+ if ( dotStructure.getType() == IDENT ) {
+ return dotStructure.getText();
+ }
+
+ return extractText( dotStructure );
+ }
+
+ private static String extractText(AST node) {
+ AST lhs = node.getFirstChild();
+ AST rhs = lhs.getNextSibling();
+
+ if ( lhs.getType() == IDENT ) {
+ return lhs.getText() + node.getText() + rhs.getText();
+ }
+ else {
+ return extractText( lhs ) + node.getText() + rhs.getText();
+ }
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/DetailedSemanticException.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/DetailedSemanticException.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/DetailedSemanticException.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,77 @@
+// $Id: DetailedSemanticException.java 5690 2005-02-12 20:27:50Z pgmjsd $
+package org.hibernate.sql.ast;
+
+import antlr.SemanticException;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Thrown when a call to the underlying Hibernate engine fails, indicating
+ * some form of semantic exception (e.g. a class name was not found in the
+ * current mappings, etc.).
+ */
+public class DetailedSemanticException extends SemanticException {
+ private Throwable cause;
+ private boolean showCauseMessage = true;
+
+ public DetailedSemanticException(String message) {
+ super( message );
+ }
+
+ public DetailedSemanticException(String s, Throwable e) {
+ super( s );
+ cause = e;
+ }
+
+ /**
+ * Converts everything to a string.
+ *
+ * @return a string.
+ */
+ public String toString() {
+ if ( cause == null || ( !showCauseMessage ) ) {
+ return super.toString();
+ }
+ else {
+ return super.toString() + "\n[cause=" + cause.toString() + "]";
+ }
+ }
+
+ /**
+ * Prints a stack trace.
+ */
+ public void printStackTrace() {
+ super.printStackTrace();
+ if ( cause != null ) {
+ cause.printStackTrace();
+ }
+ }
+
+ /**
+ * Prints a stack trace to the specified print stream.
+ *
+ * @param s the print stream.
+ */
+ public void printStackTrace(PrintStream s) {
+ super.printStackTrace( s );
+ if ( cause != null ) {
+ s.println( "Cause:" );
+ cause.printStackTrace( s );
+ }
+ }
+
+ /**
+ * Prints this throwable and its backtrace to the specified print writer.
+ *
+ * @param w the print writer.s
+ */
+ public void printStackTrace(PrintWriter w) {
+ super.printStackTrace( w );
+ if ( cause != null ) {
+ w.println( "Cause:" );
+ cause.printStackTrace( w );
+ }
+ }
+
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/QuerySyntaxException.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/QuerySyntaxException.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/QuerySyntaxException.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,55 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast;
+
+import antlr.RecognitionException;
+import org.hibernate.QueryException;
+
+/**
+ * Exception thrown when there is a syntax error in the HQL.
+ *
+ * @author josh
+ */
+public class QuerySyntaxException extends QueryException {
+ public QuerySyntaxException(String message) {
+ super( message );
+ }
+
+ public QuerySyntaxException(String message, String hql) {
+ this( message );
+ setQueryString( hql );
+ }
+
+ public static QuerySyntaxException convert(RecognitionException e) {
+ return convert( e, null );
+ }
+
+ public static QuerySyntaxException convert(RecognitionException e, String hql) {
+ String positionInfo = e.getLine() > 0 && e.getColumn() > 0
+ ? " near line " + e.getLine() + ", column " + e.getColumn()
+ : "";
+ return new QuerySyntaxException( e.getMessage() + positionInfo, hql );
+ }
+
+}
\ No newline at end of file
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/AbstractToken.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/AbstractToken.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/AbstractToken.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,77 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.common;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractToken extends antlr.CommonToken {
+ private int previousTokenType;
+
+ /**
+ * Getter for property 'previousTokenType'.
+ *
+ * @return Value for property 'previousTokenType'.
+ */
+ public int getPreviousTokenType() {
+ return previousTokenType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setType(int type) {
+ this.previousTokenType = getType();
+ super.setType( type );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toDisplayString() {
+ StringBuffer text = new StringBuffer( super.toString() );
+ text.append( "['" )
+ .append( getText() )
+ .append( "', <" )
+ .append( getType() )
+ .append( "> previously: <" )
+ .append( getPreviousTokenType() )
+ .append( ">, line=" )
+ .append( line )
+ .append( ", col=" )
+ .append( col );
+ appendTextualInfo( text );
+ text.append( "]" );
+ return text.toString();
+ }
+
+ protected void appendTextualInfo(StringBuffer text) {
+ }
+
+ public String getLocation() {
+ return getLine() + ":" + getColumn();
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/CommonHibernateLexer.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/CommonHibernateLexer.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/CommonHibernateLexer.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,70 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.common;
+
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * The common lexer for Hibernate stream parsers.
+ *
+ * @author Steve Ebersole
+ */
+public class CommonHibernateLexer extends CommonHibernateLexerSupport {
+ private boolean possibleIdentifier = false;
+
+ public CommonHibernateLexer(InputStream in) {
+ super( in );
+ super.setTokenObjectClass( TokenImpl.class.getName() );
+ }
+
+ public CommonHibernateLexer(Reader in) {
+ super( in );
+ super.setTokenObjectClass( TokenImpl.class.getName() );
+ }
+
+ public void setTokenObjectClass(String s) {
+ // no-op, we've already set ours in the constructor
+ }
+
+ protected void setPossibleIdentifier(boolean possibleIdentifier) {
+ this.possibleIdentifier = possibleIdentifier;
+ }
+
+ protected antlr.Token makeToken(int i) {
+ TokenImpl token = ( TokenImpl ) super.makeToken( i );
+ token.setPossibleIdentifier( possibleIdentifier );
+ possibleIdentifier = false;
+ return token;
+ }
+
+ public void panic() {
+ panic("CharScanner: panic");
+ }
+
+ public void panic(String s) {
+ // todo : better exception type?
+ throw new PanicException( s );
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/JoinType.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/JoinType.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/JoinType.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,62 @@
+package org.hibernate.sql.ast.common;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+/**
+ * Represents a canonical join type.
+ * <p/>
+ * Note that currently HQL really only supports inner and left outer joins
+ * (though cross joins can also be achieved). This is because joins in HQL
+ * are always defined in relation to a mapped association. However, when we
+ * start allowing users to specify ad-hoc joins this may need to change to
+ * allow the full spectrum of join types. Thus the others are provided here
+ * currently just for completeness and for future expansion.
+ *
+ * @author Steve Ebersole
+ */
+public class JoinType implements Serializable {
+ /**
+ * Represents an inner join.
+ */
+ public static final JoinType INNER = new JoinType( "inner" );
+ /**
+ * Represents a left outer join.
+ */
+ public static final JoinType LEFT = new JoinType( "left outer" );
+ /**
+ * Represents a right outer join.
+ */
+ public static final JoinType RIGHT = new JoinType( "right outer" );
+ /**
+ * Represents a cross join (aka a cartesian product).
+ */
+ public static final JoinType CROSS = new JoinType( "cross" );
+ /**
+ * Represents a full join.
+ */
+ public static final JoinType FULL = new JoinType( "full" );
+
+ private static final HashMap INSTANCES = new HashMap();
+ static {
+ INSTANCES.put( INNER.name, INNER );
+ INSTANCES.put( LEFT.name, LEFT );
+ INSTANCES.put( RIGHT.name, RIGHT );
+ INSTANCES.put( CROSS.name, CROSS );
+ INSTANCES.put( FULL.name, FULL );
+ }
+
+ private final String name;
+
+ private JoinType(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ private Object readResolve() {
+ return INSTANCES.get( name );
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/Node.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/Node.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/Node.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,92 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.common;
+
+import antlr.Token;
+import antlr.collections.AST;
+
+/**
+ * Basic AST node
+ *
+ * @author Joshua Davis
+ * @author Steve Ebersole
+ */
+public class Node extends antlr.CommonAST {
+ private String filename;
+ private int line;
+ private int column;
+
+ public Node() {
+ super();
+ }
+
+ public Node(Token tok) {
+ super( tok ); // NOTE: This will call initialize(tok)!
+ }
+
+ public void initialize(Token token) {
+ super.initialize( token );
+ // Propagate line/column information from the lexer during
+ // stream parsing.
+ filename = token.getFilename();
+ line = token.getLine();
+ column = token.getColumn();
+ }
+
+ public void initialize(AST ast) {
+ super.initialize( ast );
+ if ( ast instanceof Node ) {
+ // Propagate line/column information from the source AST during tree walking.
+ transferTrackingInfo( ( Node ) ast );
+ }
+ }
+
+ public void transferTrackingInfo(AST ast) {
+ if ( ast instanceof Node ) {
+ transferTrackingInfo( ( Node ) ast );
+ }
+ else {
+ line = ast.getLine();
+ column = ast.getColumn();
+ }
+ }
+
+ public void transferTrackingInfo(Node node) {
+ filename = node.filename;
+ line = node.line;
+ column = node.column;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public int getLine() {
+ return line;
+ }
+
+ public int getColumn() {
+ return column;
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/NumericLiteralToken.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/NumericLiteralToken.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/NumericLiteralToken.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,50 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.common;
+
+/**
+ * A custom token class for representing numeric literals. The reasoning is that there are 2 classifications
+ * by which we need to identify numeric literals:<ol>
+ * <li>The SQL notions of <tt>exact</tt> and <tt>approximate</tt></li>
+ * <li>The java language types (i.e. <tt>int</tt>, <tt>long</tt>, etc)</li>
+ * </ol>
+ * <p/>
+ * Explicitly handling the intersection of these two classification sets is unwieldy (EXACT_INTEGER_LITERAL, etc) so
+ * we instead track one classification by the token-types, and the other is tracked by state on the token. Since we
+ * generally need to treat with the java types, we use this custom token to track whether the literal is <tt>exact</tt>
+ * or <tt>approximate</tt>.
+ *
+ * @author Steve Ebersole
+ */
+public class NumericLiteralToken extends AbstractToken {
+ private boolean isApproximate = false; // assume exact
+
+ public boolean isApproximate() {
+ return isApproximate;
+ }
+
+ public void setApproximate(boolean approximate) {
+ isApproximate = approximate;
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/PanicException.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/PanicException.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/PanicException.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,41 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.common;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Thrown to indicate a panic situation ossuring in a lexer.
+ *
+ * @author Steve Ebersole
+ */
+public class PanicException extends HibernateException {
+ public PanicException(String s) {
+ super( s );
+ }
+
+ public PanicException(String string, Throwable root) {
+ super( string, root );
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/TokenImpl.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/TokenImpl.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/common/TokenImpl.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,58 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.common;
+
+/**
+ * A custom token class for various Hibernate lexers; specifically we are adding the
+ * {@link #isPossibleIdentifier() keyword-as-identifier} capability and tracking of any changes in token types
+ * via {@link #getPreviousTokenType()}.
+ *
+ * @author Steve Ebersole
+ */
+public class TokenImpl extends AbstractToken {
+ private boolean possibleIdentifier;
+
+ /**
+ * Getter for property 'possibleIdentifier'.
+ *
+ * @return Value for property 'possibleIdentifier'.
+ */
+ public boolean isPossibleIdentifier() {
+ return possibleIdentifier;
+ }
+
+ /**
+ * Setter for property 'possibleIdentifier'.
+ *
+ * @param possibleIdentifier Value to set for property 'possibleIdentifier'.
+ */
+ public void setPossibleIdentifier(boolean possibleIdentifier) {
+ this.possibleIdentifier = possibleIdentifier;
+ }
+
+ protected void appendTextualInfo(StringBuffer text) {
+ text.append( ",possibleIdentifier?=" ).append( possibleIdentifier );
+ }
+
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTAppender.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTAppender.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTAppender.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,68 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.util;
+
+import antlr.ASTFactory;
+import antlr.collections.AST;
+
+/**
+ * Appends child nodes to a parent efficiently.
+ *
+ * @author Joshua Davis
+ */
+public class ASTAppender {
+ private AST parent;
+ private AST last;
+ private ASTFactory factory;
+
+ public ASTAppender(ASTFactory factory, AST parent) {
+ this( parent );
+ this.factory = factory;
+ }
+
+ public ASTAppender(AST parent) {
+ this.parent = parent;
+ this.last = ASTUtil.getLastChild( parent );
+ }
+
+ public AST append(int type, String text, boolean appendIfEmpty) {
+ if ( text != null && ( appendIfEmpty || text.length() > 0 ) ) {
+ return append( factory.create( type, text ) );
+ }
+ else {
+ return null;
+ }
+ }
+
+ public AST append(AST child) {
+ if ( last == null ) {
+ parent.setFirstChild( child );
+ }
+ else {
+ last.setNextSibling( child );
+ }
+ last = child;
+ return last;
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTIterator.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTIterator.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTIterator.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,92 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.util;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import antlr.collections.AST;
+
+/**
+ * Depth first iteration of an ANTLR AST.
+ *
+ * @author Joshua Davis
+ */
+public class ASTIterator implements Iterator {
+ private AST next;
+ private LinkedList parents = new LinkedList();
+
+ public void remove() {
+ throw new UnsupportedOperationException( "remove() is not supported" );
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public Object next() {
+ return nextNode();
+ }
+
+ public ASTIterator(AST tree) {
+ next = tree;
+ down();
+ }
+
+ public AST nextNode() {
+ AST current = next;
+ if ( next != null ) {
+ AST nextSibling = next.getNextSibling();
+ if ( nextSibling == null ) {
+ next = pop();
+ }
+ else {
+ next = nextSibling;
+ down();
+ }
+ }
+ return current;
+ }
+
+ private void down() {
+ while ( next != null && next.getFirstChild() != null ) {
+ push( next );
+ next = next.getFirstChild();
+ }
+ }
+
+ private void push(AST parent) {
+ parents.addFirst( parent );
+ }
+
+ private AST pop() {
+ if ( parents.size() == 0 ) {
+ return null;
+ }
+ else {
+ return ( AST ) parents.removeFirst();
+ }
+ }
+
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTParentsFirstIterator.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTParentsFirstIterator.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTParentsFirstIterator.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,96 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.util;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import antlr.collections.AST;
+
+/**
+ * Depth first iteration of an ANTLR AST.
+ *
+ * @author Joshua Davis
+ */
+public class ASTParentsFirstIterator implements Iterator {
+ private AST next;
+ private AST tree;
+ private LinkedList parents = new LinkedList();
+
+ public void remove() {
+ throw new UnsupportedOperationException( "remove() is not supported" );
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public Object next() {
+ return nextNode();
+ }
+
+ public ASTParentsFirstIterator(AST tree) {
+ this.tree = next = tree;
+ }
+
+ public AST nextNode() {
+ AST current = next;
+ if ( next != null ) {
+ AST child = next.getFirstChild();
+ if ( child == null ) {
+ AST sibling = next.getNextSibling();
+ if ( sibling == null ) {
+ AST parent = pop();
+ while ( parent != null && parent.getNextSibling() == null )
+ parent = pop();
+ next = ( parent != null ) ? parent.getNextSibling() : null;
+ }
+ else {
+ next = sibling;
+ }
+ }
+ else {
+ if ( next != tree ) {
+ push( next );
+ }
+ next = child;
+ }
+ }
+ return current;
+ }
+
+ private void push(AST parent) {
+ parents.addFirst( parent );
+ }
+
+ private AST pop() {
+ if ( parents.size() == 0 ) {
+ return null;
+ }
+ else {
+ return ( AST ) parents.removeFirst();
+ }
+ }
+
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTPrinter.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTPrinter.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTPrinter.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,224 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Map;
+
+import org.hibernate.util.StringHelper;
+
+import antlr.collections.AST;
+
+/**
+ * Utility for generating pretty "ASCII art" representations of syntax trees.
+ *
+ * @author Joshua Davis
+ * @author Steve Ebersole
+ */
+public class ASTPrinter {
+ private final Map tokenTypeNameCache;
+ private final boolean showClassNames;
+
+ /**
+ * Constructs a printer.
+ * <p/>
+ * Delegates to {@link #ASTPrinter(Class, boolean)} with {@link #isShowClassNames showClassNames} as <tt>true</tt>
+ *
+ * @param tokenTypeConstants The token types to use during printing; typically the {vocabulary}TokenTypes.java
+ * interface generated by ANTLR.
+ */
+ public ASTPrinter(Class tokenTypeConstants) {
+ this( ASTUtil.generateTokenNameCache( tokenTypeConstants ), true );
+ }
+
+ public ASTPrinter(boolean showClassNames) {
+ this( ( Map ) null, showClassNames );
+ }
+
+ /**
+ * Constructs a printer.
+ *
+ * @param tokenTypeConstants The token types to use during printing; typically the {vocabulary}TokenTypes.java
+ * interface generated by ANTLR.
+ * @param showClassNames Should the class names of the {@link org.hibernate.sql.ast.common.Node} impls be displayed.
+ */
+ 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;
+ }
+
+ /**
+ * Renders the AST into 'ASCII art' form and returns that string representation.
+ *
+ * @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) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream( baos );
+ ps.println( header );
+ showAst( ast, ps );
+ ps.flush();
+ return new String( baos.toByteArray() );
+ }
+
+ /**
+ * Prints the AST in 'ASCII art' form to the specified print stream.
+ *
+ * @param ast The AST to print.
+ * @param out The print stream to which the AST should be printed.
+ */
+ public 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 to which the AST should be written.
+ */
+ public void showAst(AST ast, PrintWriter pw) {
+ ArrayList parents = new ArrayList();
+ showAst( parents, pw, ast );
+ pw.flush();
+ }
+
+ /**
+ * Returns the token type name for the given token type.
+ *
+ * @param type The token type.
+ * @return String - The token type name from the token type constant class,
+ * or just the integer as a string if none exists.
+ */
+ public String getTokenTypeName(int type) {
+ if ( tokenTypeNameCache == null ) {
+ return Integer.toString( type );
+ }
+ return ( String ) tokenTypeNameCache.get( new Integer( type ) );
+ }
+
+ private void showAst(ArrayList parents, PrintWriter pw, AST ast) {
+ if ( ast == null ) {
+ pw.println( "AST is null!" );
+ return;
+ }
+
+ for ( int i = 0; i < parents.size(); i++ ) {
+ AST parent = ( AST ) parents.get( i );
+ if ( parent.getNextSibling() == null ) {
+
+ pw.print( " " );
+ }
+ else {
+ pw.print( " | " );
+ }
+ }
+
+ if ( ast.getNextSibling() == null ) {
+ pw.print( " \\-" );
+ }
+ else {
+ pw.print( " +-" );
+ }
+
+ showNode( pw, ast );
+
+ ArrayList newParents = new ArrayList( parents );
+ newParents.add( ast );
+ for ( AST child = ast.getFirstChild(); child != null; child = child.getNextSibling() ) {
+ showAst( newParents, pw, child );
+ }
+ newParents.clear();
+ }
+
+ private void showNode(PrintWriter pw, AST ast) {
+ String s = nodeToString( ast, isShowClassNames() );
+ pw.println( s );
+ }
+
+ public String nodeToString(AST ast, boolean showClassName) {
+ if ( ast == null ) {
+ return "{node:null}";
+ }
+ StringBuffer buf = new StringBuffer();
+ buf.append( "[" ).append( getTokenTypeName( ast.getType() ) ).append( "] " );
+ if ( showClassName ) {
+ buf.append( StringHelper.unqualify( ast.getClass().getName() ) ).append( ": " );
+ }
+
+ buf.append( "'" );
+ String text = ast.getText();
+ 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() );
+ }
+ return buf.toString();
+ }
+
+ public static void appendEscapedMultibyteChars(String text, StringBuffer buf) {
+ char[] chars = text.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ char aChar = chars[i];
+ if (aChar > 256) {
+ buf.append("\\u");
+ buf.append(Integer.toHexString(aChar));
+ }
+ else
+ buf.append(aChar);
+ }
+ }
+
+ public static String escapeMultibyteChars(String text)
+ {
+ StringBuffer buf = new StringBuffer();
+ appendEscapedMultibyteChars(text,buf);
+ return buf.toString();
+ }
+}
\ No newline at end of file
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTUtil.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTUtil.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ASTUtil.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,435 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.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;
+import antlr.collections.impl.ASTArray;
+
+/**
+ * Provides utility methods for AST traversal and manipulation.
+ *
+ * @author Joshua Davis
+ * @author Steve Ebersole
+ */
+public final class ASTUtil {
+ /**
+ * Disallow instantiation.
+ *
+ * @deprecated (tellclovertoignorethis)
+ */
+ private ASTUtil() {
+ }
+
+ /**
+ * Creates a single node AST.
+ * <p/>
+ * TODO : remove this; this is silly...
+ *
+ * @param astFactory The factory.
+ * @param type The node type.
+ * @param text The node text.
+ *
+ * @return AST - A single node tree.
+ */
+ public static AST create(ASTFactory astFactory, int type, String text) {
+ return astFactory.create( type, text );
+ }
+
+ /**
+ * 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 prevSibling The previous sibling.
+ *
+ * @return The created AST node.
+ */
+ public static AST createSibling(ASTFactory astFactory, int type, String text, AST prevSibling) {
+ AST node = astFactory.create( type, text );
+ 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 );
+ return node;
+ }
+
+ /**
+ * Creates a 'binary operator' subtree, given the information about the
+ * parent and the two child nodex.
+ *
+ * @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.
+ *
+ * @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) {
+ ASTArray array = createAstArray( factory, 3, parentType, parentText, child1 );
+ array.add( child2 );
+ return factory.make( array );
+ }
+
+ /**
+ * Creates a single parent of the specified child (i.e. a 'unary operator'
+ * subtree).
+ *
+ * @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.
+ *
+ * @return AST - A new sub-tree of the form "(parent child)"
+ */
+ public static AST createParent(ASTFactory factory, int parentType, String parentText, AST child) {
+ ASTArray array = createAstArray( factory, 2, parentType, parentText, child );
+ return factory.make( array );
+ }
+
+ public static AST createTree(ASTFactory factory, AST[] nestedChildren) {
+ AST[] array = new AST[2];
+ int limit = nestedChildren.length - 1;
+ for ( int i = limit; i >= 0; i-- ) {
+ if ( i != limit ) {
+ array[1] = nestedChildren[i + 1];
+ array[0] = nestedChildren[i];
+ factory.make( array );
+ }
+ }
+ 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!" );
+ }
+
+ /**
+ * Makes the child node a sibling of the parent, reconnecting all siblings.
+ *
+ * @param parent the parent
+ * @param child the child
+ */
+ public static void makeSiblingOfParent(AST parent, AST child) {
+ AST prev = findPreviousSibling( parent, child );
+ if ( prev != null ) {
+ prev.setNextSibling( child.getNextSibling() );
+ }
+ else { // child == parent.getFirstChild()
+ parent.setFirstChild( child.getNextSibling() );
+ }
+ child.setNextSibling( parent.getNextSibling() );
+ parent.setNextSibling( child );
+ }
+
+ public static String getPathText(AST n) {
+ StringBuffer buf = new StringBuffer();
+ getPathText( buf, n );
+ return buf.toString();
+ }
+
+ private static void getPathText(StringBuffer buf, AST n) {
+ AST firstChild = n.getFirstChild();
+ // If the node has a first child, recurse into the first child.
+ if ( firstChild != null ) {
+ getPathText( buf, firstChild );
+ }
+ // Append the text of the current node.
+ buf.append( n.getText() );
+ // If there is a second child (RHS), recurse into that child.
+ if ( firstChild != null && firstChild.getNextSibling() != null ) {
+ getPathText( buf, firstChild.getNextSibling() );
+ }
+ }
+
+ public static boolean hasExactlyOneChild(AST n) {
+ return n != null && n.getFirstChild() != null && n.getFirstChild().getNextSibling() == null;
+ }
+
+ public static void appendSibling(AST n, AST s) {
+ while ( n.getNextSibling() != null ) {
+ n = n.getNextSibling();
+ }
+ n.setNextSibling( s );
+ }
+
+ /**
+ * 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
+ */
+ public static void insertChild(AST parent, AST child) {
+ if ( parent.getFirstChild() == null ) {
+ parent.setFirstChild( child );
+ }
+ else {
+ AST n = parent.getFirstChild();
+ parent.setFirstChild( child );
+ child.setNextSibling( n );
+ }
+ }
+
+ 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.
+ */
+ public static interface FilterPredicate {
+ /**
+ * 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);
+ }
+
+ /**
+ * A predicate that uses inclusion, rather than exclusion semantics.
+ */
+ public abstract static class IncludePredicate implements FilterPredicate {
+ public final boolean exclude(AST node) {
+ return !include( node );
+ }
+
+ public abstract boolean include(AST node);
+ }
+
+ public static List collectChildren(AST root, FilterPredicate predicate) {
+ return new CollectingNodeVisitor( predicate ).collect( root );
+ }
+
+ private static class CollectingNodeVisitor implements NodeTraverser.VisitationStrategy {
+ private final FilterPredicate predicate;
+ private final List collectedNodes = new ArrayList();
+
+ public CollectingNodeVisitor(FilterPredicate predicate) {
+ this.predicate = predicate;
+ }
+
+ public void visit(AST node) {
+ if ( predicate == null || !predicate.exclude( node ) ) {
+ collectedNodes.add( node );
+ }
+ }
+
+ public List getCollectedNodes() {
+ return collectedNodes;
+ }
+
+ public List collect(AST root) {
+ NodeTraverser traverser = new NodeTraverser( this );
+ traverser.traverseDepthFirst( root );
+ return collectedNodes;
+ }
+ }
+
+ /**
+ * 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 ) {
+ }
+ }
+ }
+ 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.
+ */
+ public static String getConstantName(Class owner, int value) {
+ String tokenTypeName = Integer.toString( value );
+ if ( owner != null ) {
+ Field[] fields = owner.getFields();
+ for ( int i = 0; i < fields.length; i++ ) {
+ final Integer fieldValue = extractIntegerValue( fields[i] );
+ if ( fieldValue != null && fieldValue.intValue() == value ) {
+ 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;
+ }
+
+ /**
+ * Just a friendly renaming of {@link #getConstantName}; in fact this method just simply delegates to that one.
+ *
+ * @param tokenTypeInterface The *TokenTypes interface (or one of its implementors).
+ * @param tokenType The token type value.
+ * @return The corresponding name.
+ *
+ * @see #getConstantName
+ */
+ public static String getTokenTypeName(Class tokenTypeInterface, int tokenType) {
+ return getConstantName( tokenTypeInterface, tokenType );
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/DisplayableNode.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/DisplayableNode.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/DisplayableNode.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,40 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.util;
+
+/**
+ * Contract for {@link org.hibernate.sql.ast.common.Node} implementations which wish to return customized display information
+ * about themselves.
+ *
+ * @author Steve Ebersole
+ * @author Joshua Davis
+ */
+public interface DisplayableNode {
+ /**
+ * Returns additional display text for the AST node.
+ *
+ * @return String - The additional display text.
+ */
+ public String getDisplayText();
+}
\ No newline at end of file
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ErrorCounter.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ErrorCounter.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ErrorCounter.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,95 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.util;
+
+import antlr.RecognitionException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.QueryException;
+import org.hibernate.sql.ast.QuerySyntaxException;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An error handler that counts parsing errors and warnings.
+ */
+public class ErrorCounter implements ParseErrorHandler {
+ private Log log = LogFactory.getLog( ErrorCounter.class );
+
+ private List errorList = new ArrayList();
+ private List warningList = new ArrayList();
+ private List recognitionExceptions = new ArrayList();
+
+ public void reportError(RecognitionException e) {
+ String msg = e.toString();
+ reportError( msg );
+ recognitionExceptions.add( e );
+ if ( log.isTraceEnabled() ) {
+ log.trace( msg, e );
+ }
+ }
+
+ public void reportError(String message) {
+ log.error( message );
+ errorList.add( message );
+ }
+
+ public void reportWarning(String message) {
+ log.debug( message );
+ warningList.add( message );
+ }
+
+ public int getErrorCount() {
+ return errorList.size();
+ }
+
+ private String getErrorString() {
+ StringBuffer buf = new StringBuffer();
+ for ( Iterator iterator = errorList.iterator(); iterator.hasNext(); ) {
+ buf.append( ( String ) iterator.next() );
+ if ( iterator.hasNext() ) buf.append( "\n" );
+
+ }
+ return buf.toString();
+ }
+
+ public void throwQueryException() throws QueryException {
+ if ( getErrorCount() > 0 ) {
+ if ( recognitionExceptions.size() > 0 ) {
+ throw QuerySyntaxException.convert( ( RecognitionException ) recognitionExceptions.get( 0 ) );
+ }
+ else {
+ throw new QueryException( getErrorString() );
+ }
+ }
+ else {
+ // all clear
+ if ( log.isDebugEnabled() ) {
+ log.debug( "throwQueryException() : no errors" );
+ }
+ }
+ }
+}
\ No newline at end of file
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ErrorReporter.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ErrorReporter.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ErrorReporter.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,39 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.util;
+
+import antlr.RecognitionException;
+
+/**
+ * Implementations will report or handle errors invoked by an ANTLR base parser.
+ *
+ * @author Joshua Davis
+ */
+public interface ErrorReporter {
+ void reportError(RecognitionException e);
+
+ void reportError(String s);
+
+ void reportWarning(String s);
+}
\ No newline at end of file
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/NodeTraverser.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/NodeTraverser.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/NodeTraverser.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,67 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.util;
+
+import antlr.collections.AST;
+
+/**
+ * A visitor for traversing an AST tree.
+ *
+ * @author Steve Ebersole
+ */
+public class NodeTraverser {
+ public static interface VisitationStrategy {
+ public void visit(AST node);
+ }
+
+ private final VisitationStrategy strategy;
+
+ public NodeTraverser(VisitationStrategy strategy) {
+ this.strategy = strategy;
+ }
+
+ /**
+ * Traverse the AST tree depth first.
+ * <p/>
+ * Note that the AST passed in is not visited itself. Visitation starts
+ * with its children.
+ *
+ * @param ast
+ */
+ public void traverseDepthFirst(AST ast) {
+ if ( ast == null ) {
+ throw new IllegalArgumentException( "node to traverse cannot be null!" );
+ }
+ visitDepthFirst( ast.getFirstChild() );
+ }
+
+ private void visitDepthFirst(AST ast) {
+ if ( ast == null ) {
+ return;
+ }
+ strategy.visit( ast );
+ visitDepthFirst( ast.getFirstChild() );
+ visitDepthFirst( ast.getNextSibling() );
+ }
+}
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ParseErrorHandler.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ParseErrorHandler.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/ParseErrorHandler.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,39 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.util;
+
+import org.hibernate.QueryException;
+
+
+/**
+ * Defines the behavior of an error handler for the Hibernate parsers.
+ *
+ * @author Joshua Davis
+ */
+public interface ParseErrorHandler extends ErrorReporter {
+
+ int getErrorCount();
+
+ void throwQueryException() throws QueryException;
+}
\ No newline at end of file
Added: core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/PathHelper.java
===================================================================
--- core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/PathHelper.java (rev 0)
+++ core/branches/SQL_GEN_REDESIGN/src/main/java/org/hibernate/sql/ast/util/PathHelper.java 2008-12-13 16:25:05 UTC (rev 15693)
@@ -0,0 +1,112 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.sql.ast.util;
+
+import org.hibernate.util.StringHelper;
+import org.hibernate.sql.ast.common.Sql92TokenTypes;
+
+import antlr.ASTFactory;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Provides utility methods for dealing with path expressions.
+ * <p/>
+ * Note that these utilities do not properly account for index operations.
+ *
+ * @author Joshua Davis
+ * @author Steve Ebersole
+ */
+public final class PathHelper implements Sql92TokenTypes {
+ private static final Log log = LogFactory.getLog( PathHelper.class );
+
+ /**
+ * Direct instantiation of PathHelper disallowed.
+ */
+ private PathHelper() {
+ }
+
+ /**
+ * Turns a path into an AST.
+ *
+ * @param path The path.
+ * @param factory The AST factory to use.
+ * @return An HQL AST representing the path.
+ */
+ public static AST parsePath(String path, ASTFactory factory) {
+ String[] identifiers = StringHelper.split( ".", path );
+ AST lhs = null;
+ for ( int i = 0; i < identifiers.length; i++ ) {
+ String identifier = identifiers[i];
+ AST child = ASTUtil.create( factory, IDENT, identifier );
+ if ( i == 0 ) {
+ lhs = child;
+ }
+ else {
+ lhs = ASTUtil.createBinarySubtree( factory, DOT, ".", lhs, child );
+ }
+ }
+ if ( log.isDebugEnabled() ) {
+ log.debug( "parsePath() : " + path + " -> " + ASTUtil.getDebugString( lhs ) );
+ }
+ return lhs;
+ }
+
+ /**
+ * Provides the inverse functionality of {@link #parsePath}. In other words, for any path
+ * 'p' not involving index operations, p == reconstitutePathString( parsePath( p, someASTFactory ) ).
+ *
+ * @param pathAST The path AST structure.
+ * @return The corresponding path string.
+ */
+ public static String rebuildPathExpression(AST pathAST) {
+ final StringBuffer buffer = new StringBuffer();
+ visitExpression( pathAST, buffer );
+ return buffer.toString();
+ }
+
+ private static void visitExpression(AST expression, StringBuffer buffer) {
+ if ( DOT == expression.getType() ) {
+ visitDot( expression.getFirstChild(), expression.getFirstChild().getNextSibling(), buffer );
+ }
+ else if ( IDENT == expression.getType() ) {
+ visitIdent( expression, buffer );
+ }
+ }
+
+ private static void visitDot(AST lhs, AST rhs, StringBuffer buffer) {
+ visitExpression( lhs, buffer );
+ buffer.append( '.' ).append( rhs.getText() );
+ }
+
+ private static void visitIdent(AST ident, StringBuffer buffer) {
+ buffer.append( ident.getText() );
+ }
+
+ public static String getAlias(String path) {
+ return StringHelper.root( path );
+ }
+}
More information about the hibernate-commits
mailing list