All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.query.criteria.internal.CriteriaBuilderImpl Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * 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 .
 */
package org.hibernate.query.criteria.internal;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import javax.persistence.Tuple;
import javax.persistence.criteria.CollectionJoin;
import javax.persistence.criteria.CompoundSelection;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.MapJoin;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import javax.persistence.criteria.SetJoin;
import javax.persistence.criteria.Subquery;

import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.internal.expression.BinaryArithmeticOperation;
import org.hibernate.query.criteria.internal.expression.CoalesceExpression;
import org.hibernate.query.criteria.internal.expression.CompoundSelectionImpl;
import org.hibernate.query.criteria.internal.expression.ConcatExpression;
import org.hibernate.query.criteria.internal.expression.LiteralExpression;
import org.hibernate.query.criteria.internal.expression.NullLiteralExpression;
import org.hibernate.query.criteria.internal.expression.NullifExpression;
import org.hibernate.query.criteria.internal.expression.ParameterExpressionImpl;
import org.hibernate.query.criteria.internal.expression.SearchedCaseExpression;
import org.hibernate.query.criteria.internal.expression.SimpleCaseExpression;
import org.hibernate.query.criteria.internal.expression.SizeOfPluralAttributeExpression;
import org.hibernate.query.criteria.internal.expression.SubqueryComparisonModifierExpression;
import org.hibernate.query.criteria.internal.expression.UnaryArithmeticOperation;
import org.hibernate.query.criteria.internal.expression.function.AbsFunction;
import org.hibernate.query.criteria.internal.expression.function.AggregationFunction;
import org.hibernate.query.criteria.internal.expression.function.BasicFunctionExpression;
import org.hibernate.query.criteria.internal.expression.function.CurrentDateFunction;
import org.hibernate.query.criteria.internal.expression.function.CurrentTimeFunction;
import org.hibernate.query.criteria.internal.expression.function.CurrentTimestampFunction;
import org.hibernate.query.criteria.internal.expression.function.LengthFunction;
import org.hibernate.query.criteria.internal.expression.function.LocateFunction;
import org.hibernate.query.criteria.internal.expression.function.LowerFunction;
import org.hibernate.query.criteria.internal.expression.function.ParameterizedFunctionExpression;
import org.hibernate.query.criteria.internal.expression.function.SqrtFunction;
import org.hibernate.query.criteria.internal.expression.function.SubstringFunction;
import org.hibernate.query.criteria.internal.expression.function.TrimFunction;
import org.hibernate.query.criteria.internal.expression.function.UpperFunction;
import org.hibernate.query.criteria.internal.path.PluralAttributePath;
import org.hibernate.query.criteria.internal.path.RootImpl;
import org.hibernate.query.criteria.internal.predicate.BetweenPredicate;
import org.hibernate.query.criteria.internal.predicate.BooleanAssertionPredicate;
import org.hibernate.query.criteria.internal.predicate.BooleanExpressionPredicate;
import org.hibernate.query.criteria.internal.predicate.BooleanStaticAssertionPredicate;
import org.hibernate.query.criteria.internal.predicate.ComparisonPredicate;
import org.hibernate.query.criteria.internal.predicate.ComparisonPredicate.ComparisonOperator;
import org.hibernate.query.criteria.internal.predicate.CompoundPredicate;
import org.hibernate.query.criteria.internal.predicate.ExistsPredicate;
import org.hibernate.query.criteria.internal.predicate.InPredicate;
import org.hibernate.query.criteria.internal.predicate.IsEmptyPredicate;
import org.hibernate.query.criteria.internal.predicate.LikePredicate;
import org.hibernate.query.criteria.internal.predicate.MemberOfPredicate;
import org.hibernate.query.criteria.internal.predicate.NullnessPredicate;

/**
 * Hibernate implementation of the JPA {@link CriteriaBuilder} contract.
 *
 * @author Steve Ebersole
 */
public class CriteriaBuilderImpl implements HibernateCriteriaBuilder, Serializable {
	private final SessionFactoryImpl sessionFactory;

	public CriteriaBuilderImpl(SessionFactoryImpl sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	/**
	 * Provides protected access to the underlying {@link SessionFactoryImpl}.
	 *
	 * @return The underlying {@link SessionFactoryImpl}
	 */
	public  SessionFactoryImpl getEntityManagerFactory() {
		return sessionFactory;
	}


	// Query builders ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	@Override
	public CriteriaQuery createQuery() {
		return new CriteriaQueryImpl( this, Object.class );
	}

	@Override
	public  CriteriaQuery createQuery(Class resultClass) {
		return new CriteriaQueryImpl( this, resultClass );
	}

	@Override
	public CriteriaQuery createTupleQuery() {
		return new CriteriaQueryImpl( this, Tuple.class );
	}

	@Override
	public  CriteriaUpdate createCriteriaUpdate(Class targetEntity) {
		return new CriteriaUpdateImpl( this );
	}

	@Override
	public  CriteriaDelete createCriteriaDelete(Class targetEntity) {
		return new CriteriaDeleteImpl( this );
	}


	// selections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	/**
	 * Package-protected method to centralize checking of criteria query multi-selects as defined by the
	 * {@link CriteriaQuery#multiselect(List)}  method.
	 *
	 * @param selections The selection varargs to check
	 *
	 * @throws IllegalArgumentException If the selection items are not valid per {@link CriteriaQuery#multiselect}
	 * documentation.
	 * "An argument to the multiselect method must not be a tuple-
     * or array-valued compound selection item."
	 */
	void checkMultiselect(List> selections) {
		final HashSet aliases = new HashSet( CollectionHelper.determineProperSizing( selections.size() ) );

		for ( Selection selection : selections ) {
			if ( selection.isCompoundSelection() ) {
				if ( selection.getJavaType().isArray() ) {
					throw new IllegalArgumentException(
							"Selection items in a multi-select cannot contain compound array-valued elements"
					);
				}
				if ( Tuple.class.isAssignableFrom( selection.getJavaType() ) ) {
					throw new IllegalArgumentException(
							"Selection items in a multi-select cannot contain compound tuple-valued elements"
					);
				}
			}
			if ( StringHelper.isNotEmpty( selection.getAlias() ) ) {
				boolean added = aliases.add( selection.getAlias() );
				if ( ! added ) {
					throw new IllegalArgumentException( "Multi-select expressions defined duplicate alias : " + selection.getAlias() );
				}
			}
		}
	}

	@Override
	public CompoundSelection tuple(Selection... selections) {
		return tuple( Arrays.asList( selections ) );
	}

	/**
	 * Version of {@link #tuple(Selection[])} taking a list.
	 *
	 * @param selections List of selections.
	 *
	 * @return The tuple compound selection
	 */
	public CompoundSelection tuple(List> selections) {
		checkMultiselect( selections );
		return new CompoundSelectionImpl( this, Tuple.class, selections );
	}

	@Override
	public CompoundSelection array(Selection... selections) {
		return array( Arrays.asList( selections ) );
	}

	/**
	 * Version of {@link #array(Selection[])} taking a list of selections.
	 *
	 * @param selections List of selections.
	 *
	 * @return The array compound selection
	 */
	public CompoundSelection array(List> selections) {
		return array( Object[].class, selections );
	}

	/**
	 * Version of {@link #array(Selection[])} taking a list of selections,
	 * as well as the type of array.
	 *
	 * @param type The type of array
	 * @param selections List of selections.
	 *
	 * @return The array compound selection
	 */
	public  CompoundSelection array(Class type, List> selections) {
		checkMultiselect( selections );
		return new CompoundSelectionImpl( this, type, selections );
	}

	@Override
	public  CompoundSelection construct(Class result, Selection... selections) {
		return construct( result, Arrays.asList( selections ) );
	}

	/**
	 * Version of {@link #construct(Class,Selection[])} taking the
	 * to-be-constructed type as well as a list of selections.
	 *
	 * @param result The result class to be constructed.
	 * @param selections The selections to use in the constructor call.
	 *
	 * @return The view compound selection.
	 */
	public  CompoundSelection construct(Class result, List> selections) {
		checkMultiselect( selections );
		return new CompoundSelectionImpl( this, result, selections );
	}


	// ordering ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	@Override
	public Order asc(Expression x) {
		return new OrderImpl( x, true );
	}

	@Override
	public Order desc(Expression x) {
		return new OrderImpl( x, false );
	}


	// predicates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	public Predicate wrap(Expression expression) {
		if ( Predicate.class.isInstance( expression ) ) {
			return ( (Predicate) expression );
		}
		else if ( PathImplementor.class.isInstance( expression ) ) {
			return new BooleanAssertionPredicate( this, expression, Boolean.TRUE );
		}
		else {
			return new BooleanExpressionPredicate( this, expression );
		}
	}

	@Override
	public Predicate not(Expression expression) {
		return wrap( expression ).not();
	}

	@Override
	@SuppressWarnings("unchecked")
	public Predicate and(Expression x, Expression y) {
		return new CompoundPredicate( this, Predicate.BooleanOperator.AND, x, y );
	}

	@Override
	@SuppressWarnings("unchecked")
	public Predicate or(Expression x, Expression y) {
		return new CompoundPredicate( this, Predicate.BooleanOperator.OR, x, y );
	}

	@Override
	public Predicate and(Predicate... restrictions) {
		return new CompoundPredicate( this, Predicate.BooleanOperator.AND, restrictions );
	}

	@Override
	public Predicate or(Predicate... restrictions) {
		return new CompoundPredicate( this, Predicate.BooleanOperator.OR, restrictions );
	}

	@Override
	public Predicate conjunction() {
		return new CompoundPredicate( this, Predicate.BooleanOperator.AND );
	}

	@Override
	public Predicate disjunction() {
		return new CompoundPredicate( this, Predicate.BooleanOperator.OR );
	}

	@Override
	public Predicate isTrue(Expression expression) {
		if ( CompoundPredicate.class.isInstance( expression ) ) {
			final CompoundPredicate predicate = (CompoundPredicate) expression;
			if ( predicate.getExpressions().size() == 0 ) {
				return new BooleanStaticAssertionPredicate(
						this,
						predicate.getOperator() == Predicate.BooleanOperator.AND
				);
			}
			return predicate;
		}
		else if ( Predicate.class.isInstance( expression ) ) {
			return (Predicate) expression;
		}
		return new BooleanAssertionPredicate( this, expression, Boolean.TRUE );
	}

	@Override
	public Predicate isFalse(Expression expression) {
		if ( CompoundPredicate.class.isInstance( expression ) ) {
			final CompoundPredicate predicate = (CompoundPredicate) expression;
			if ( predicate.getExpressions().size() == 0 ) {
				return new BooleanStaticAssertionPredicate(
						this,
						predicate.getOperator() == Predicate.BooleanOperator.OR
				);
			}
			predicate.not();
			return predicate;
		}
		else if ( Predicate.class.isInstance( expression ) ) {
			final Predicate predicate = (Predicate) expression;
			predicate.not();
			return predicate;
		}
		return new BooleanAssertionPredicate( this, expression, Boolean.FALSE );
	}

	@Override
	public Predicate isNull(Expression x) {
		return new NullnessPredicate( this, x );
	}

	@Override
	public Predicate isNotNull(Expression x) {
		return isNull( x ).not();
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate equal(Expression x, Expression y) {
		return new ComparisonPredicate( this, ComparisonOperator.EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate notEqual(Expression x, Expression y) {
		return new ComparisonPredicate( this, ComparisonOperator.NOT_EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate equal(Expression x, Object y) {
		return new ComparisonPredicate( this, ComparisonOperator.EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate notEqual(Expression x, Object y) {
		return new ComparisonPredicate( this, ComparisonOperator.NOT_EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public > Predicate greaterThan(Expression x, Expression y) {
		return new ComparisonPredicate( this, ComparisonOperator.GREATER_THAN, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public > Predicate lessThan(
			Expression x,
			Expression y) {
		return new ComparisonPredicate( this, ComparisonOperator.LESS_THAN, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public > Predicate greaterThanOrEqualTo(
			Expression x,
			Expression y) {
		return new ComparisonPredicate( this, ComparisonOperator.GREATER_THAN_OR_EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public > Predicate lessThanOrEqualTo(
			Expression x,
			Expression y) {
		return new ComparisonPredicate( this, ComparisonOperator.LESS_THAN_OR_EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public > Predicate greaterThan(
			Expression x,
			Y y) {
		return new ComparisonPredicate( this, ComparisonOperator.GREATER_THAN, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public > Predicate lessThan(
			Expression x,
			Y y) {
		return new ComparisonPredicate( this, ComparisonOperator.LESS_THAN, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public > Predicate greaterThanOrEqualTo(
			Expression x,
			Y y) {
		return new ComparisonPredicate( this, ComparisonOperator.GREATER_THAN_OR_EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public> Predicate lessThanOrEqualTo(
			Expression x,
			Y y) {
		return new ComparisonPredicate( this, ComparisonOperator.LESS_THAN_OR_EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate gt(Expression x, Expression y) {
		return new ComparisonPredicate( this, ComparisonOperator.GREATER_THAN, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate lt(Expression x, Expression y) {
		return new ComparisonPredicate( this, ComparisonOperator.LESS_THAN, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate ge(Expression x, Expression y) {
		return new ComparisonPredicate( this, ComparisonOperator.GREATER_THAN_OR_EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate le(Expression x, Expression y) {
		return new ComparisonPredicate( this, ComparisonOperator.LESS_THAN_OR_EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate gt(Expression x, Number y) {
		return new ComparisonPredicate( this, ComparisonOperator.GREATER_THAN, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate lt(Expression x, Number y) {
		return new ComparisonPredicate( this, ComparisonOperator.LESS_THAN, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate ge(Expression x, Number y) {
		return new ComparisonPredicate( this, ComparisonOperator.GREATER_THAN_OR_EQUAL, x, y );
	}

	@Override
	@SuppressWarnings("SuspiciousNameCombination")
	public Predicate le(Expression x, Number y) {
		return new ComparisonPredicate( this, ComparisonOperator.LESS_THAN_OR_EQUAL, x, y );
	}

	@Override
	public > Predicate between(
			Expression expression,
			Y lowerBound,
			Y upperBound) {
		return new BetweenPredicate( this, expression, lowerBound, upperBound );
	}

	@Override
	public > Predicate between(
			Expression expression,
			Expression lowerBound,
			Expression upperBound) {
		return new BetweenPredicate( this, expression, lowerBound, upperBound );
	}

	@Override
	public  In in(Expression expression) {
		return new InPredicate( this, expression );
	}

	public  In in(Expression expression, Expression... values) {
		return new InPredicate( this, expression, values );
	}

	public  In in(Expression expression, T... values) {
		return new InPredicate( this, expression, values );
	}

	public  In in(Expression expression, Collection values) {
		return new InPredicate( this, expression, values );
	}

	@Override
	public Predicate like(Expression matchExpression, Expression pattern) {
		return new LikePredicate( this, matchExpression, pattern );
	}

	@Override
	public Predicate like(Expression matchExpression, Expression pattern, Expression escapeCharacter) {
		return new LikePredicate( this, matchExpression, pattern, escapeCharacter );
	}

	@Override
	public Predicate like(Expression matchExpression, Expression pattern, char escapeCharacter) {
		return new LikePredicate( this, matchExpression, pattern, escapeCharacter );
	}

	@Override
	public Predicate like(Expression matchExpression, String pattern) {
		return new LikePredicate( this, matchExpression, pattern );
	}

	@Override
	public Predicate like(Expression matchExpression, String pattern, Expression escapeCharacter) {
		return new LikePredicate( this, matchExpression, pattern, escapeCharacter );
	}

	@Override
	public Predicate like(Expression matchExpression, String pattern, char escapeCharacter) {
		return new LikePredicate( this, matchExpression, pattern, escapeCharacter );
	}

	@Override
	public Predicate notLike(Expression matchExpression, Expression pattern) {
		return like( matchExpression, pattern ).not();
	}

	@Override
	public Predicate notLike(Expression matchExpression, Expression pattern, Expression escapeCharacter) {
		return like( matchExpression, pattern, escapeCharacter ).not();
	}

	@Override
	public Predicate notLike(Expression matchExpression, Expression pattern, char escapeCharacter) {
		return like( matchExpression, pattern, escapeCharacter ).not();
	}

	@Override
	public Predicate notLike(Expression matchExpression, String pattern) {
		return like( matchExpression, pattern ).not();
	}

	@Override
	public Predicate notLike(Expression matchExpression, String pattern, Expression escapeCharacter) {
		return like( matchExpression, pattern, escapeCharacter ).not();
	}

	@Override
	public Predicate notLike(Expression matchExpression, String pattern, char escapeCharacter) {
		return like( matchExpression, pattern, escapeCharacter ).not();
	}


	// parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	@Override
	public  ParameterExpression parameter(Class paramClass) {
		return new ParameterExpressionImpl(
				this,
				paramClass
		);
	}

	@Override
	public  ParameterExpression parameter(Class paramClass, String name) {
		return new ParameterExpressionImpl(
				this,
				paramClass,
				name
		);
	}

	@Override
	public  Expression literal(T value) {
		if ( value == null ) {
			throw new IllegalArgumentException( "literal value cannot be null" );
		}
		return new LiteralExpression( this, value );
	}

	@Override
	public  Expression nullLiteral(Class resultClass) {
		return new NullLiteralExpression( this, resultClass );
	}


	// aggregate functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	@Override
	public  Expression avg(Expression x) {
		return new AggregationFunction.AVG( this, x );
	}

	@Override
	public  Expression sum(Expression x) {
		return new AggregationFunction.SUM( this, x );
	}

	@Override
	public Expression sumAsLong(Expression x) {
		return new AggregationFunction.SUM( this, x, Long.class );
	}

	@Override
	public Expression sumAsDouble(Expression x) {
		return new AggregationFunction.SUM( this, x, Double.class );
	}

	@Override
	public  Expression max(Expression x) {
		return new AggregationFunction.MAX( this, x );
	}

	@Override
	public  Expression min(Expression x) {
		return new AggregationFunction.MIN( this, x );
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public > Expression greatest(Expression x) {
		return new AggregationFunction.GREATEST( this, x );
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public > Expression least(Expression x) {
		return new AggregationFunction.LEAST( this, x );
	}

	@Override
	public Expression count(Expression x) {
		return new AggregationFunction.COUNT( this, x, false );
	}

	@Override
	public Expression countDistinct(Expression x) {
		return new AggregationFunction.COUNT( this, x, true );
	}


	// other functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	@Override
	public  Expression function(String name, Class returnType, Expression... arguments) {
		return new ParameterizedFunctionExpression( this, returnType, name, arguments );
	}

	/**
	 * Create a reference to a function taking no params.
	 *
	 * @param name The function name.
	 * @param returnType The return type.
	 *
	 * @return The function expression
	 */
	public  Expression function(String name, Class returnType) {
		return new BasicFunctionExpression( this, returnType, name );
	}

	@Override
	public  Expression abs(Expression expression) {
		return new AbsFunction( this, expression );
	}

	@Override
	public Expression sqrt(Expression expression) {
		return new SqrtFunction( this, expression );
	}

	@Override
	public Expression currentDate() {
		return new CurrentDateFunction( this );
	}

	@Override
	public Expression currentTimestamp() {
		return new CurrentTimestampFunction( this );
	}

	@Override
	public Expression currentTime() {
		return new CurrentTimeFunction( this );
	}

	@Override
	public Expression substring(Expression value, Expression start) {
		return new SubstringFunction( this, value, start );
	}

	@Override
	public Expression substring(Expression value, int start) {
		return new SubstringFunction( this, value, start );
	}

	@Override
	public Expression substring(Expression value, Expression start, Expression length) {
		return new SubstringFunction( this, value, start, length );
	}

	@Override
	public Expression substring(Expression value, int start, int length) {
		return new SubstringFunction( this, value, start, length );
	}

	@Override
	public Expression trim(Expression trimSource ) {
		return new TrimFunction( this, trimSource );
	}

	@Override
	public Expression trim(Trimspec trimspec, Expression trimSource) {
		return new TrimFunction( this, trimspec, trimSource );
	}

	@Override
	public Expression trim(Expression trimCharacter, Expression trimSource) {
		return new TrimFunction( this, trimCharacter, trimSource );
	}

	@Override
	public Expression trim(Trimspec trimspec, Expression trimCharacter, Expression trimSource) {
		return new TrimFunction( this, trimspec, trimCharacter, trimSource );
	}

	@Override
	public Expression trim(char trimCharacter, Expression trimSource) {
		return new TrimFunction( this, trimCharacter, trimSource );
	}

	@Override
	public Expression trim(Trimspec trimspec, char trimCharacter, Expression trimSource) {
		return new TrimFunction( this, trimspec, trimCharacter, trimSource );
	}

	@Override
	public Expression lower(Expression value) {
		return new LowerFunction( this, value );
	}

	@Override
	public Expression upper(Expression value) {
		return new UpperFunction( this, value );
	}

	@Override
	public Expression length(Expression value) {
		return new LengthFunction( this, value );
	}

	@Override
	public Expression locate(Expression string, Expression pattern) {
		return new LocateFunction( this, pattern, string );
	}

	@Override
	public Expression locate(Expression string, Expression pattern, Expression start) {
		return new LocateFunction( this, pattern, string, start );
	}

	@Override
	public Expression locate(Expression string, String pattern) {
		return new LocateFunction( this, pattern, string );
	}

	@Override
	public Expression locate(Expression string, String pattern, int start) {
		return new LocateFunction( this, pattern, string, start );
	}


	// arithmetic operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	@Override
	public  Expression neg(Expression expression) {
		return new UnaryArithmeticOperation(
				this,
				UnaryArithmeticOperation.Operation.UNARY_MINUS,
				expression
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression sum(Expression expression1, Expression expression2) {
		if ( expression1 == null || expression2 == null ) {
			throw new IllegalArgumentException( "arguments to sum() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( expression1.getJavaType(), expression2.getJavaType() );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.ADD,
				expression1,
				expression2
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression prod(Expression expression1, Expression expression2) {
		if ( expression1 == null || expression2 == null ) {
			throw new IllegalArgumentException( "arguments to prod() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( expression1.getJavaType(), expression2.getJavaType() );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.MULTIPLY,
				expression1,
				expression2
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression diff(Expression expression1, Expression expression2) {
		if ( expression1 == null || expression2 == null ) {
			throw new IllegalArgumentException( "arguments to diff() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( expression1.getJavaType(), expression2.getJavaType() );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.SUBTRACT,
				expression1,
				expression2
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression sum(Expression expression, N n) {
		if ( expression == null || n == null ) {
			throw new IllegalArgumentException( "arguments to sum() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( expression.getJavaType(), n.getClass() );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.ADD,
				expression,
				n
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression prod(Expression expression, N n) {
		if ( expression == null || n == null ) {
			throw new IllegalArgumentException( "arguments to prod() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( expression.getJavaType(), n.getClass() );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.MULTIPLY,
				expression,
				n
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression diff(Expression expression, N n) {
		if ( expression == null || n == null ) {
			throw new IllegalArgumentException( "arguments to diff() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( expression.getJavaType(), n.getClass() );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.SUBTRACT,
				expression,
				n
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression sum(N n, Expression expression) {
		if ( expression == null || n == null ) {
			throw new IllegalArgumentException( "arguments to sum() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( n.getClass(), expression.getJavaType() );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.ADD,
				n,
				expression
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression prod(N n, Expression expression) {
		if ( n == null || expression == null ) {
			throw new IllegalArgumentException( "arguments to prod() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( n.getClass(), expression.getJavaType() );

		return (BinaryArithmeticOperation) new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.MULTIPLY,
				n,
				expression
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression diff(N n, Expression expression) {
		if ( n == null || expression == null ) {
			throw new IllegalArgumentException( "arguments to diff() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( n.getClass(), expression.getJavaType() );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.SUBTRACT,
				n,
				expression
		);
	}

	@Override
	@SuppressWarnings( {"unchecked"})
	public Expression quot(Expression expression1, Expression expression2) {
		if ( expression1 == null || expression2 == null ) {
			throw new IllegalArgumentException( "arguments to quot() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( expression1.getJavaType(), expression2.getJavaType(), true );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.DIVIDE,
				expression1,
				expression2
		);
	}

	@Override
	@SuppressWarnings( {"unchecked"})
	public Expression quot(Expression expression, Number number) {
		if ( expression == null || number == null ) {
			throw new IllegalArgumentException( "arguments to quot() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( expression.getJavaType(), number.getClass(), true );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.DIVIDE,
				expression,
				number
		);
	}

	@Override
	@SuppressWarnings( {"unchecked"})
	public Expression quot(Number number, Expression expression) {
		if ( expression == null || number == null ) {
			throw new IllegalArgumentException( "arguments to quot() cannot be null" );
		}

		final Class resultType = BinaryArithmeticOperation.determineResultType( number.getClass(), expression.getJavaType(), true );

		return new BinaryArithmeticOperation(
				this,
				resultType,
				BinaryArithmeticOperation.Operation.DIVIDE,
				number,
				expression
		);
	}

	@Override
	public Expression mod(Expression expression1, Expression expression2) {
		if ( expression1 == null || expression2 == null ) {
			throw new IllegalArgumentException( "arguments to mod() cannot be null" );
		}

		return new BinaryArithmeticOperation(
				this,
				Integer.class,
				BinaryArithmeticOperation.Operation.MOD,
				expression1,
				expression2
		);
	}

	@Override
	public Expression mod(Expression expression, Integer integer) {
		if ( expression == null || integer == null ) {
			throw new IllegalArgumentException( "arguments to mod() cannot be null" );
		}

		return new BinaryArithmeticOperation(
				this,
				Integer.class,
				BinaryArithmeticOperation.Operation.MOD,
				expression,
				integer
		);
	}

	@Override
	public Expression mod(Integer integer, Expression expression) {
		if ( integer == null || expression == null ) {
			throw new IllegalArgumentException( "arguments to mod() cannot be null" );
		}

		return new BinaryArithmeticOperation(
				this,
				Integer.class,
				BinaryArithmeticOperation.Operation.MOD,
				integer,
				expression
		);
	}


	// casting ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	@Override
	public ExpressionImplementor toLong(Expression expression) {
		return ( (ExpressionImplementor) expression ).asLong();
	}

	@Override
	public ExpressionImplementor toInteger(Expression expression) {
		return ( (ExpressionImplementor) expression ).asInteger();
	}

	@Override
	public ExpressionImplementor toFloat(Expression expression) {
		return ( (ExpressionImplementor) expression ).asFloat();
	}

	@Override
	public ExpressionImplementor toDouble(Expression expression) {
		return ( (ExpressionImplementor) expression ).asDouble();
	}

	@Override
	public ExpressionImplementor toBigDecimal(Expression expression) {
		return ( (ExpressionImplementor) expression ).asBigDecimal();
	}

	@Override
	public ExpressionImplementor toBigInteger(Expression expression) {
		return ( (ExpressionImplementor) expression ).asBigInteger();
	}

	@Override
	public ExpressionImplementor toString(Expression characterExpression) {
		return ( (ExpressionImplementor) characterExpression ).asString();
	}

	@Override
	@SuppressWarnings("unchecked")
	public  Join treat(Join join, Class type) {
		return treat( join, type, (j, t) -> ((JoinImplementor) j).treatAs( t ) );
	}

	@Override
	@SuppressWarnings("unchecked")
	public  CollectionJoin treat(CollectionJoin join, Class type) {
		return treat( join, type, (j, t) -> ((CollectionJoinImplementor) j).treatAs( t ) );
	}

	@Override
	@SuppressWarnings("unchecked")
	public  SetJoin treat(SetJoin join, Class type) {
		return treat( join, type, (j, t) -> ((SetJoinImplementor) j).treatAs( t ) );
	}

	@Override
	@SuppressWarnings("unchecked")
	public  ListJoin treat(ListJoin join, Class type) {
		return treat( join, type, (j, t) -> ((ListJoinImplementor) join).treatAs( type ) );
	}

	@Override
	@SuppressWarnings("unchecked")
	public  MapJoin treat(MapJoin join, Class type) {
		return treat( join, type, (j, t) -> ((MapJoinImplementor) join).treatAs( type ) );
	}

	@Override
	@SuppressWarnings("unchecked")
	public  Path treat(Path path, Class type) {
		return ( (PathImplementor) path ).treatAs( type );
	}

	@Override
	@SuppressWarnings("unchecked")
	public  Root treat(Root root, Class type) {
		return ((RootImpl) root).treatAs( type );
	}

	// subqueries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	@Override
	public Predicate exists(Subquery subquery) {
		return new ExistsPredicate( this, subquery );
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression all(Subquery subquery) {
		return new SubqueryComparisonModifierExpression(
				this,
				(Class) subquery.getJavaType(),
				subquery,
				SubqueryComparisonModifierExpression.Modifier.ALL
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression some(Subquery subquery) {
		return new SubqueryComparisonModifierExpression(
				this,
				(Class) subquery.getJavaType(),
				subquery,
				SubqueryComparisonModifierExpression.Modifier.SOME
		);
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public  Expression any(Subquery subquery) {
		return new SubqueryComparisonModifierExpression(
				this,
				(Class) subquery.getJavaType(),
				subquery,
				SubqueryComparisonModifierExpression.Modifier.ANY
		);
	}


	// miscellaneous expressions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	@Override
	@SuppressWarnings({ "RedundantCast" })
	public  Expression coalesce(Expression exp1, Expression exp2) {
		return coalesce( (Class) null, exp1, exp2 );
	}

	public  Expression coalesce(Class type, Expression exp1, Expression exp2) {
		return new CoalesceExpression( this, type ).value( exp1 ).value( exp2 );
	}

	@Override
	@SuppressWarnings({ "RedundantCast" })
	public  Expression coalesce(Expression exp1, Y exp2) {
		return coalesce( (Class) null, exp1, exp2 );
	}

	public  Expression coalesce(Class type, Expression exp1, Y exp2) {
		return new CoalesceExpression( this, type ).value( exp1 ).value( exp2 );
	}

	@Override
	public  Coalesce coalesce() {
		return coalesce( (Class)null );
	}

	public  Coalesce coalesce(Class type) {
		return new CoalesceExpression( this, type );
	}

	@Override
	public Expression concat(Expression string1, Expression string2) {
		return new ConcatExpression( this, string1, string2 );
	}

	@Override
	public Expression concat(Expression string1, String string2) {
		return new ConcatExpression( this, string1, string2 );
	}

	@Override
	public Expression concat(String string1, Expression string2) {
		return new ConcatExpression( this, string1, string2 );
	}

	@Override
	public  Expression nullif(Expression exp1, Expression exp2) {
		return nullif( null, exp1, exp2 );
	}

	public  Expression nullif(Class type, Expression exp1, Expression exp2) {
		return new NullifExpression( this, type, exp1, exp2 );
	}

	@Override
	public  Expression nullif(Expression exp1, Y exp2) {
		return nullif( null, exp1, exp2 );
	}

	public  Expression nullif(Class type, Expression exp1, Y exp2) {
		return new NullifExpression( this, type, exp1, exp2 );
	}

	@Override
	public  SimpleCase selectCase(Expression expression) {
		return selectCase( (Class)null, expression );
	}

	public  SimpleCase selectCase(Class type, Expression expression) {
		return new SimpleCaseExpression( this, type, expression );
	}

	@Override
	public  Case selectCase() {
		return selectCase( (Class)null );
	}

	public  Case selectCase(Class type) {
		return new SearchedCaseExpression( this, type );
	}

	@Override
	public > Expression size(C c) {
		int size = c == null ? 0 : c.size();
		return new LiteralExpression<>(this, Integer.class, size);
	}

	@Override
	public > Expression size(Expression exp) {
		if ( LiteralExpression.class.isInstance(exp) ) {
			return size( ( (LiteralExpression) exp ).getLiteral() );
		}
		else if ( PluralAttributePath.class.isInstance(exp) ) {
			return new SizeOfPluralAttributeExpression( this, (PluralAttributePath) exp );
		}
		// TODO : what other specific types?  any?
		throw new IllegalArgumentException("unknown collection expression type [" + exp.getClass().getName() + "]" );
	}

	@Override
	public > Expression> values(M map) {
		return new LiteralExpression<>( this, map.values() );
	}

	@Override
	public > Expression> keys(M map) {
		return new LiteralExpression<>( this, map.keySet() );
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public > Predicate isEmpty(Expression collectionExpression) {
		if ( PluralAttributePath.class.isInstance(collectionExpression) ) {
			return new IsEmptyPredicate( this, (PluralAttributePath) collectionExpression );
		}
		// TODO : what other specific types?  any?
		throw new IllegalArgumentException(
				"unknown collection expression type [" + collectionExpression.getClass().getName() + "]"
		);
	}

	@Override
	public > Predicate isNotEmpty(Expression collectionExpression) {
		return isEmpty( collectionExpression ).not();
	}

	@Override
	public > Predicate isMember(E e, Expression collectionExpression) {
		if ( ! PluralAttributePath.class.isInstance( collectionExpression ) ) {
			throw new IllegalArgumentException(
					"unknown collection expression type [" + collectionExpression.getClass().getName() + "]"
			);
		}
		return new MemberOfPredicate<>(
				this,
				e,
				(PluralAttributePath) collectionExpression
		);
	}

	@Override
	public > Predicate isNotMember(E e, Expression cExpression) {
		return isMember(e, cExpression).not();
	}

	@Override
	public > Predicate isMember(Expression elementExpression, Expression collectionExpression) {
		if ( ! PluralAttributePath.class.isInstance( collectionExpression ) ) {
			throw new IllegalArgumentException(
					"unknown collection expression type [" + collectionExpression.getClass().getName() + "]"
			);
		}
		return new MemberOfPredicate<>(
				this,
				elementExpression,
				(PluralAttributePath) collectionExpression
		);
	}

	@Override
	public > Predicate isNotMember(Expression eExpression, Expression cExpression) {
		return isMember(eExpression, cExpression).not();
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public > Predicate isMapEmpty(Expression mapExpression) {
		if ( PluralAttributePath.class.isInstance( mapExpression ) ) {
			return new IsEmptyPredicate( this, (PluralAttributePath) mapExpression );
		}
		// TODO : what other specific types?  any?
		throw new IllegalArgumentException(
				"unknown collection expression type [" + mapExpression.getClass().getName() + "]"
		);
	}

	@Override
	public > Predicate isMapNotEmpty(Expression mapExpression) {
		return isMapEmpty( mapExpression ).not();
	}

	@Override
	public > Expression mapSize(Expression mapExpression) {
		if ( LiteralExpression.class.isInstance( mapExpression ) ) {
			return mapSize( ( (LiteralExpression) mapExpression ).getLiteral() );
		}
		else if ( PluralAttributePath.class.isInstance( mapExpression ) ) {
			return new SizeOfPluralAttributeExpression( this, (PluralAttributePath) mapExpression );
		}
		// TODO : what other specific types?  any?
		throw new IllegalArgumentException("unknown collection expression type [" + mapExpression.getClass().getName() + "]" );
	}

	@Override
	public > Expression mapSize(M map) {
		int size = map == null ? 0 : map.size();
		return new LiteralExpression<>( this, Integer.class, size );
	}

	@SuppressWarnings("unchecked")
	private  K treat(
			Join join,
			Class type,
			BiFunction, Class, K> f) {
		final Set> joins = join.getParent().getJoins();
		final K treatAs = f.apply( join, type );
		joins.add( treatAs );
		return treatAs;
	}
}