/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Middleware LLC. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA * */ package org.hibernate.sql.ordering.antlr; import java.util.ArrayList; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.dialect.function.SQLFunction; import org.hibernate.internal.util.StringHelper; import org.hibernate.sql.Template; import org.jboss.logging.Logger; import antlr.CommonAST; import antlr.TokenStream; import antlr.collections.AST; /** * Extension of the Antlr-generated parser for the purpose of adding our custom parsing behavior. * * @author Steve Ebersole */ public class OrderByFragmentParser extends GeneratedOrderByFragmentParser { private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, OrderByFragmentParser.class.getName()); private final TranslationContext context; public OrderByFragmentParser(TokenStream lexer, TranslationContext context) { super( lexer ); super.setASTFactory( new Factory() ); this.context = context; } // handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private int traceDepth = 0; @Override public void traceIn(String ruleName) { if ( inputState.guessing > 0 ) { return; } String prefix = StringHelper.repeat( '-', (traceDepth++ * 2) ) + "-> "; LOG.trace(prefix + ruleName); } @Override public void traceOut(String ruleName) { if ( inputState.guessing > 0 ) { return; } String prefix = "<-" + StringHelper.repeat( '-', (--traceDepth * 2) ) + " "; LOG.trace(prefix + ruleName); } /** * {@inheritDoc} */ @Override protected void trace(String msg) { LOG.trace(msg); } /** * {@inheritDoc} */ @Override protected AST quotedIdentifier(AST ident) { return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, Template.TEMPLATE + "." + context.getDialect().quote( '`' + ident.getText() + '`' ) ); } /** * {@inheritDoc} */ @Override protected AST quotedString(AST ident) { return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, context.getDialect().quote( ident.getText() ) ); } /** * {@inheritDoc} */ @Override protected boolean isFunctionName(AST ast) { AST child = ast.getFirstChild(); // assume it is a function if it has parameters if ( child != null && "{param list}".equals( child.getText() ) ) { return true; } final SQLFunction function = context.getSqlFunctionRegistry().findSQLFunction( ast.getText() ); if ( function == null ) { return false; } // if function.hasParenthesesIfNoArguments() is true, then assume // ast.getText() is not a function. return ! function.hasParenthesesIfNoArguments(); } /** * {@inheritDoc} */ @Override protected AST resolveFunction(AST ast) { AST child = ast.getFirstChild(); if ( child != null ) { assert "{param list}".equals( child.getText() ); child = child.getFirstChild(); } final String functionName = ast.getText(); final SQLFunction function = context.getSqlFunctionRegistry().findSQLFunction( functionName ); if ( function == null ) { String text = functionName; if ( child != null ) { text += '('; while ( child != null ) { text += child.getText(); child = child.getNextSibling(); if ( child != null ) { text += ", "; } } text += ')'; } return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, text ); } else { ArrayList expressions = new ArrayList(); while ( child != null ) { expressions.add( child.getText() ); child = child.getNextSibling(); } final String text = function.render( null, expressions, context.getSessionFactory() ); return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, text ); } } /** * {@inheritDoc} */ @Override protected AST resolveIdent(AST ident) { String text = ident.getText(); String[] replacements; try { replacements = context.getColumnMapper().map( text ); } catch( Throwable t ) { replacements = null; } if ( replacements == null || replacements.length == 0 ) { return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, Template.TEMPLATE + "." + text ); } else if ( replacements.length == 1 ) { return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, Template.TEMPLATE + "." + replacements[0] ); } else { final AST root = getASTFactory().create( OrderByTemplateTokenTypes.IDENT_LIST, "{ident list}" ); for ( int i = 0; i < replacements.length; i++ ) { final String identText = Template.TEMPLATE + '.' + replacements[i]; root.addChild( getASTFactory().create( OrderByTemplateTokenTypes.IDENT, identText ) ); } return root; } } /** * {@inheritDoc} */ @Override protected AST postProcessSortSpecification(AST sortSpec) { assert SORT_SPEC == sortSpec.getType(); SortSpecification sortSpecification = ( SortSpecification ) sortSpec; AST sortKey = sortSpecification.getSortKey(); if ( IDENT_LIST == sortKey.getFirstChild().getType() ) { AST identList = sortKey.getFirstChild(); AST ident = identList.getFirstChild(); AST holder = new CommonAST(); do { holder.addChild( createSortSpecification( ident, sortSpecification.getCollation(), sortSpecification.getOrdering() ) ); ident = ident.getNextSibling(); } while ( ident != null ); sortSpec = holder.getFirstChild(); } return sortSpec; } private SortSpecification createSortSpecification( AST ident, CollationSpecification collationSpecification, OrderingSpecification orderingSpecification) { AST sortSpecification = getASTFactory().create( SORT_SPEC, "{{sort specification}}" ); AST sortKey = getASTFactory().create( SORT_KEY, "{{sort key}}" ); AST newIdent = getASTFactory().create( ident.getType(), ident.getText() ); sortKey.setFirstChild( newIdent ); sortSpecification.setFirstChild( sortKey ); if ( collationSpecification != null ) { sortSpecification.addChild( collationSpecification ); } if ( orderingSpecification != null ) { sortSpecification.addChild( orderingSpecification ); } return ( SortSpecification ) sortSpecification; } }