gt: 						builder.append( operandToken );
context: /*
   * Hibernate, Relational Persistence for Idiomatic Java
   *
   * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
   * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
   */
  package org.hibernate.sql;
  
  import java.util.ArrayList;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Locale;
  import java.util.Set;
  import java.util.StringTokenizer;
  
  import org.hibernate.HibernateException;
  import org.hibernate.dialect.Dialect;
  import org.hibernate.dialect.function.SQLFunction;
  import org.hibernate.dialect.function.SQLFunctionRegistry;
  import org.hibernate.engine.spi.SessionFactoryImplementor;
  import org.hibernate.internal.util.StringHelper;
  import org.hibernate.sql.ordering.antlr.ColumnMapper;
  import org.hibernate.sql.ordering.antlr.OrderByAliasResolver;
  import org.hibernate.sql.ordering.antlr.OrderByFragmentTranslator;
  import org.hibernate.sql.ordering.antlr.OrderByTranslation;
  import org.hibernate.sql.ordering.antlr.SqlValueReference;
  import org.hibernate.sql.ordering.antlr.TranslationContext;
  
  /**
   * Parses SQL fragments specified in mapping documents
   *
   * @author Gavin King
   */
  public final class Template {
  
  	private static final Set<String> KEYWORDS = new HashSet<String>();
  	private static final Set<String> BEFORE_TABLE_KEYWORDS = new HashSet<String>();
  	private static final Set<String> FUNCTION_KEYWORDS = new HashSet<String>();
  	static {
  		KEYWORDS.add("and");
  		KEYWORDS.add("or");
  		KEYWORDS.add("not");
  		KEYWORDS.add("like");
  		KEYWORDS.add("escape");
  		KEYWORDS.add("is");
  		KEYWORDS.add("in");
  		KEYWORDS.add("between");
  		KEYWORDS.add("null");
  		KEYWORDS.add("select");
  		KEYWORDS.add("distinct");
  		KEYWORDS.add("from");
  		KEYWORDS.add("join");
  		KEYWORDS.add("inner");
  		KEYWORDS.add("outer");
  		KEYWORDS.add("left");
  		KEYWORDS.add("right");
  		KEYWORDS.add("on");
  		KEYWORDS.add("where");
  		KEYWORDS.add("having");
  		KEYWORDS.add("group");
  		KEYWORDS.add("order");
  		KEYWORDS.add("by");
  		KEYWORDS.add("desc");
  		KEYWORDS.add("asc");
  		KEYWORDS.add("limit");
  		KEYWORDS.add("any");
  		KEYWORDS.add("some");
  		KEYWORDS.add("exists");
  		KEYWORDS.add("all");
  		KEYWORDS.add("union");
  		KEYWORDS.add("minus");
  
  		BEFORE_TABLE_KEYWORDS.add("from");
  		BEFORE_TABLE_KEYWORDS.add("join");
  
  		FUNCTION_KEYWORDS.add("as");
  		FUNCTION_KEYWORDS.add("leading");
  		FUNCTION_KEYWORDS.add("trailing");
  		FUNCTION_KEYWORDS.add("from");
  		FUNCTION_KEYWORDS.add("case");
  		FUNCTION_KEYWORDS.add("when");
  		FUNCTION_KEYWORDS.add("then");
  		FUNCTION_KEYWORDS.add("else");
  		FUNCTION_KEYWORDS.add("end");
  	}
  
  	public static final String TEMPLATE = "$PlaceHolder$";
  
  	private Template() {}
  
  	public static String renderTransformerReadFragment(
  			String fragment,
  			String... columnNames) {
  		// NOTE : would need access to SessionFactoryImplementor to make this configurable
  		for ( String columnName : columnNames ) {
  			fragment = fragment.replace( columnName, TEMPLATE + '.' + columnName );
  		}
  		return fragment;
  	}
  
  	public static String renderWhereStringTemplate(String sqlWhereString, Dialect dialect, SQLFunctionRegistry functionRegistry) {
  		return renderWhereStringTemplate(sqlWhereString, TEMPLATE, dialect, functionRegistry);
  	}
  
  	/**
  	 * Same functionality as {@link #renderWhereStringTemplate(String, String, Dialect, SQLFunctionRegistry)},
  	 * except that a SQLFunctionRegistry is not provided (i.e., only the dialect-defined functions are
  	 * considered).  This is only intended for use by the annotations project until the
  	 * many-to-many/map-key-from-target-table feature is pulled into core.
  	 *
  	 * @deprecated Only intended for annotations usage; use {@link #renderWhereStringTemplate(String, String, Dialect, SQLFunctionRegistry)} instead
  	 */
  	@Deprecated
  	@SuppressWarnings({ "JavaDoc" })
  	public static String renderWhereStringTemplate(String sqlWhereString, String placeholder, Dialect dialect) {
  		return renderWhereStringTemplate(
  				sqlWhereString,
  				placeholder,
  				dialect,
  				new SQLFunctionRegistry( dialect, java.util.Collections.<String, SQLFunction>emptyMap() )
  		);
  	}
  
  	/**
  	 * Takes the where condition provided in the mapping attribute and interpolates the alias.
  	 * Handles sub-selects, quoted identifiers, quoted strings, expressions, SQL functions,
  	 * named parameters.
  	 *
  	 * @param sqlWhereString The string into which to interpolate the placeholder value
  	 * @param placeholder The value to be interpolated into the the sqlWhereString
  	 * @param dialect The dialect to apply
  	 * @param functionRegistry The registry of all sql functions
  	 * @return The rendered sql fragment
  	 */
  	public static String renderWhereStringTemplate(String sqlWhereString, String placeholder, Dialect dialect, SQLFunctionRegistry functionRegistry ) {
  
  		// IMPL NOTE : The basic process here is to tokenize the incoming string and to iterate over each token
  		//		in turn.  As we process each token, we set a series of flags used to indicate the type of context in
  		// 		which the tokens occur.  Depending on the state of those flags we decide whether we need to qualify
  		//		identifier references.
  
  		String symbols = new StringBuilder()
  				.append( "=><!+-*/()',|&`" )
  				.append( StringHelper.WHITESPACE )
  				.append( dialect.openQuote() )
  				.append( dialect.closeQuote() )
  				.toString();
  		StringTokenizer tokens = new StringTokenizer( sqlWhereString, symbols, true );
  		StringBuilder result = new StringBuilder();
  
  		boolean quoted = false;
  		boolean quotedIdentifier = false;
  		boolean beforeTable = false;
  		boolean inFromClause = false;
  		boolean afterFromTable = false;
  
  		boolean hasMore = tokens.hasMoreTokens();
  		String nextToken = hasMore ? tokens.nextToken() : null;
  		while ( hasMore ) {
  			String token = nextToken;
  			String lcToken = token.toLowerCase(Locale.ROOT);
  			hasMore = tokens.hasMoreTokens();
  			nextToken = hasMore ? tokens.nextToken() : null;
  
  			boolean isQuoteCharacter = false;
  
  			if ( !quotedIdentifier && "'".equals(token) ) {
  				quoted = !quoted;
  				isQuoteCharacter = true;
  			}
  
  			if ( !quoted ) {
  				boolean isOpenQuote;
  				if ( "`".equals(token) ) {
  					isOpenQuote = !quotedIdentifier;
  					token = lcToken = isOpenQuote
  							? Character.toString( dialect.openQuote() )
  							: Character.toString( dialect.closeQuote() );
  					quotedIdentifier = isOpenQuote;
  					isQuoteCharacter = true;
  				}
  				else if ( !quotedIdentifier && ( dialect.openQuote()==token.charAt(0) ) ) {
  					isOpenQuote = true;
  					quotedIdentifier = true;
  					isQuoteCharacter = true;
  				}
  				else if ( quotedIdentifier && ( dialect.closeQuote()==token.charAt(0) ) ) {
  					quotedIdentifier = false;
  					isQuoteCharacter = true;
  					isOpenQuote = false;
  				}
  				else {
  					isOpenQuote = false;
  				}
  
  				if ( isOpenQuote ) {
  					result.append( placeholder ).append( '.' );
  				}
  			}
  
  			// Special processing for ANSI SQL EXTRACT function
  			if ( "extract".equals( lcToken ) && "(".equals( nextToken ) ) {
  				final String field = extractUntil( tokens, "from" );
  				final String source = renderWhereStringTemplate(
  						extractUntil( tokens, ")" ),
  						placeholder,
  						dialect,
  						functionRegistry
  				);
  				result.append( "extract(" ).append( field ).append( " from " ).append( source ).append( ')' );
  
  				hasMore = tokens.hasMoreTokens();
  				nextToken = hasMore ? tokens.nextToken() : null;
  
  				continue;
  			}
  
  			// Special processing for ANSI SQL TRIM function
  			if ( "trim".equals( lcToken ) && "(".equals( nextToken ) ) {
  				List<String> operands = new ArrayList<String>();
  				StringBuilder builder = new StringBuilder();
  
  				boolean hasMoreOperands = true;
  				String operandToken = tokens.nextToken();
  				boolean quotedOperand = false;
  				while ( hasMoreOperands ) {
  					final boolean isQuote = "'".equals( operandToken );
  					if ( isQuote ) {
  						quotedOperand = !quotedOperand;
  						if ( !quotedOperand ) {
  							operands.add( builder.append( '\'' ).toString() );
  							builder.setLength( 0 );
  						}
  						else {
  							builder.append( '\'' );
  						}
  					}
  					else if ( quotedOperand ) {
  