/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat Inc. 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 Inc. * * 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.dialect.function; import java.util.ArrayList; import java.util.List; import org.hibernate.QueryException; import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.Type; /** * A {@link org.hibernate.dialect.function.SQLFunction} providing support for implementing TRIM functionality * (as defined by both the ANSI SQL and JPA specs) in cases where the dialect may not support the full <tt>trim</tt> * function itself. * <p/> * Follows the <a href="http://en.wikipedia.org/wiki/Template_method_pattern">template</a> pattern in order to implement * the {@link #render} method. * * @author Steve Ebersole */ public abstract class AbstractAnsiTrimEmulationFunction implements SQLFunction { @Override public final boolean hasArguments() { return true; } @Override public final boolean hasParenthesesIfNoArguments() { return false; } @Override public final Type getReturnType(Type argumentType, Mapping mapping) throws QueryException { return StandardBasicTypes.STRING; } @Override public final String render(Type argumentType, List args, SessionFactoryImplementor factory) throws QueryException { // According to both the ANSI-SQL and JPA specs, trim takes a variable number of parameters between 1 and 4. // at least one paramer (trimSource) is required. From the SQL spec: // // <trim function> ::= // TRIM <left paren> <trim operands> <right paren> // // <trim operands> ::= // [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source> // // <trim specification> ::= // LEADING // | TRAILING // | BOTH // // If <trim specification> is omitted, BOTH is assumed. // If <trim character> is omitted, space is assumed if ( args.size() == 1 ) { // we have the form: trim(trimSource) // so we trim leading and trailing spaces return resolveBothSpaceTrimFunction().render( argumentType, args, factory ); // EARLY EXIT!!!! } else if ( "from".equalsIgnoreCase( ( String ) args.get( 0 ) ) ) { // we have the form: trim(from trimSource). // This is functionally equivalent to trim(trimSource) return resolveBothSpaceTrimFromFunction().render( argumentType, args, factory ); // EARLY EXIT!!!! } else { // otherwise, a trim-specification and/or a trim-character // have been specified; we need to decide which options // are present and "do the right thing" boolean leading = true; // should leading trim-characters be trimmed? boolean trailing = true; // should trailing trim-characters be trimmed? String trimCharacter; // the trim-character (what is to be trimmed off?) String trimSource; // the trim-source (from where should it be trimmed?) // potentialTrimCharacterArgIndex = 1 assumes that a // trim-specification has been specified. we handle the // exception to that explicitly int potentialTrimCharacterArgIndex = 1; String firstArg = ( String ) args.get( 0 ); if ( "leading".equalsIgnoreCase( firstArg ) ) { trailing = false; } else if ( "trailing".equalsIgnoreCase( firstArg ) ) { leading = false; } else if ( "both".equalsIgnoreCase( firstArg ) ) { } else { potentialTrimCharacterArgIndex = 0; } String potentialTrimCharacter = ( String ) args.get( potentialTrimCharacterArgIndex ); if ( "from".equalsIgnoreCase( potentialTrimCharacter ) ) { trimCharacter = "' '"; trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 1 ); } else if ( potentialTrimCharacterArgIndex + 1 >= args.size() ) { trimCharacter = "' '"; trimSource = potentialTrimCharacter; } else { trimCharacter = potentialTrimCharacter; if ( "from".equalsIgnoreCase( ( String ) args.get( potentialTrimCharacterArgIndex + 1 ) ) ) { trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 2 ); } else { trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 1 ); } } List<String> argsToUse = new ArrayList<String>(); argsToUse.add( trimSource ); argsToUse.add( trimCharacter ); if ( trimCharacter.equals( "' '" ) ) { if ( leading && trailing ) { return resolveBothSpaceTrimFunction().render( argumentType, argsToUse, factory ); } else if ( leading ) { return resolveLeadingSpaceTrimFunction().render( argumentType, argsToUse, factory ); } else { return resolveTrailingSpaceTrimFunction().render( argumentType, argsToUse, factory ); } } else { if ( leading && trailing ) { return resolveBothTrimFunction().render( argumentType, argsToUse, factory ); } else if ( leading ) { return resolveLeadingTrimFunction().render( argumentType, argsToUse, factory ); } else { return resolveTrailingTrimFunction().render( argumentType, argsToUse, factory ); } } } } /** * Resolve the function definition which should be used to trim both leading and trailing spaces. * <p/> * In this form, the imput arguments is missing the <tt>FROM</tt> keyword. * * @return The sql function */ protected abstract SQLFunction resolveBothSpaceTrimFunction(); /** * Resolve the function definition which should be used to trim both leading and trailing spaces. * <p/> * The same as {#link resolveBothSpaceTrimFunction} except that here the<tt>FROM</tt> is included and * will need to be accounted for during {@link SQLFunction#render} processing. * * @return The sql function */ protected abstract SQLFunction resolveBothSpaceTrimFromFunction(); /** * Resolve the function definition which should be used to trim leading spaces. * * @return The sql function */ protected abstract SQLFunction resolveLeadingSpaceTrimFunction(); /** * Resolve the function definition which should be used to trim trailing spaces. * * @return The sql function */ protected abstract SQLFunction resolveTrailingSpaceTrimFunction(); /** * Resolve the function definition which should be used to trim the specified character from both the * beginning (leading) and end (trailing) of the trim source. * * @return The sql function */ protected abstract SQLFunction resolveBothTrimFunction(); /** * Resolve the function definition which should be used to trim the specified character from the * beginning (leading) of the trim source. * * @return The sql function */ protected abstract SQLFunction resolveLeadingTrimFunction(); /** * Resolve the function definition which should be used to trim the specified character from the * end (trailing) of the trim source. * * @return The sql function */ protected abstract SQLFunction resolveTrailingTrimFunction(); }