/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.openjpa.persistence.criteria;

import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaBuilder.Trimspec;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Subquery;

import org.apache.openjpa.kernel.exps.ExpressionFactory;
import org.apache.openjpa.kernel.exps.Literal;
import org.apache.openjpa.kernel.exps.Value;
import org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.persistence.criteria.CriteriaExpressionVisitor.TraversalStyle;
import org.apache.openjpa.persistence.meta.Types;

/**
 * Expressions according to JPA 2.0.
 *
 * A facade to OpenJPA kernel expressions to enforce stronger typing.
 *
 * @author Pinaki Poddar
 * @author Fay Wang
 *
 * @since 2.0.0
 *
 */
class Expressions {
    static final String OPEN_BRACE = "(";
    static final String CLOSE_BRACE = ")";
    static final String COMMA = ",";

    /**
     * Convert the given Criteria expression to a corresponding kernel value
     * using the given ExpressionFactory.
     * Handles null expression.
     */
     static Value toValue(ExpressionImpl<?> e, ExpressionFactory factory, CriteriaQueryImpl<?> q) {
        return (e == null) ? factory.getNull() : e.toValue(factory, q);
    }

     static void setImplicitTypes(Value v1, Value v2, Class<?> expected, CriteriaQueryImpl<?> q) {
         JPQLExpressionBuilder.setImplicitTypes(v1, v2, expected, q.getMetamodel(),
             q.getParameterTypes(), q.toString());
     }

     /**
      * Visits the given expression and the given children recursively.
      * The order of traversal depends on the parent and is determined by the visitor.
      */
     static void acceptVisit(CriteriaExpressionVisitor visitor, CriteriaExpression parent, Expression<?>...exprs) {
         if (parent == null)
             return;
         TraversalStyle traversal = visitor.getTraversalStyle(parent);
         switch (traversal) {
         case INFIX :
             if (exprs == null || exprs.length == 0) {
                 visitor.enter(parent);
                 visitor.exit(parent);
                 return;
             }
             for (int i = 0; i < exprs.length; i++) {
                 ExpressionImpl<?> e = (ExpressionImpl<?>)exprs[i];
                 if (e != null) e.acceptVisit(visitor);
                 if (i + 1 != exprs.length) {
                     visitor.enter(parent);
                     visitor.exit(parent);
                 }
             }
             break;
         case POSTFIX:
             visitChildren(visitor,exprs);
             visitor.enter(parent);
             visitor.exit(parent);
             break;
         case PREFIX :
             visitor.enter(parent);
             visitor.exit(parent);
             visitChildren(visitor,exprs);
             break;
         case FUNCTION:
             visitor.enter(parent);
             visitChildren(visitor, exprs);
             visitor.exit(parent);
             break;
         }
     }

     static void visitChildren(CriteriaExpressionVisitor visitor, Expression<?>...exprs) {
         for (int i = 0; exprs != null && i < exprs.length; i++) {
             ExpressionImpl<?> e = (ExpressionImpl<?>)exprs[i];
             if (e != null) e.acceptVisit(visitor);
         }
     }

     /**
      * Renders the given expressions as a list of values separated by the given connector.
      */
     static StringBuilder asValue(AliasContext q, Expression<?>[] exps, String connector) {
         StringBuilder buffer = new StringBuilder();
         if (exps == null) return buffer;
         for (int i = 0; i < exps.length; i++) {
             buffer.append(((ExpressionImpl<?>)exps[i]).asValue(q));
             if (i+1 != exps.length) {
                 buffer.append(connector);
             }
         }
         return buffer;
     }

     /**
      * Renders the given arguments as a list of values separated by the given connector.
      */
     static StringBuilder asValue(AliasContext q, Object...params) {
         StringBuilder buffer = new StringBuilder();
         if (params == null) return buffer;
         for (int i = 0; i < params.length; i++) {
             Object o = params[i];
             if (o == null) {
                 if (i+1 < params.length && params[i+1].equals(COMMA)) {
                     i++;
                 }
                 continue;
             }
             if (o instanceof CriteriaExpression) {
                 buffer.append(((CriteriaExpression)o).asValue(q));
             } else {
                 buffer.append(o);
             }
         }
         return buffer;
     }

     /**
      * Return a list that is either empty (if the given list is null) or a list
      * whose mutation do not impact the original list.
      */
     static <X> List<X> returnCopy(List<X> list) {
         return list == null ? new ArrayList<>() : new CopyOnWriteArrayList<>(list);
     }

     /**
      * Return a set that is either empty (if the given set is null) or a set
      * whose mutation do not impact the original list.
      */
     static <X> Set<X> returnCopy(Set<X> set) {
         return set == null ? new HashSet<>() : new CopyOnWriteArraySet<>(set);
     }

     static org.apache.openjpa.kernel.exps.Expression and(ExpressionFactory factory,
             org.apache.openjpa.kernel.exps.Expression e1, org.apache.openjpa.kernel.exps.Expression e2) {
             return e1 == null ? e2 : e2 == null ? e1 : factory.and(e1, e2);
     }



     /**
     * Unary Functional Expression applies a unary function on a input operand Expression.
     *
     * @param <X> the type of the resultant expression
     */
    public abstract static class UnaryFunctionalExpression<X> extends ExpressionImpl<X> {
        protected final ExpressionImpl<?> e;
        /**
         * Supply the resultant type and input operand expression.
         */
        public UnaryFunctionalExpression(Class<X> t, Expression<?> e) {
            super(t);
            this.e  = (ExpressionImpl<?>)e;
        }

        public UnaryFunctionalExpression(Expression<X> e) {
            this((Class<X>)e.getJavaType(), e);
        }

        @Override
        public void acceptVisit(CriteriaExpressionVisitor visitor) {
            Expressions.acceptVisit(visitor, this, e);
        }
    }

    /**
     * Binary Functional Expression applies a binary function on a pair of input Expression.
     *
     * @param <X> the type of the resultant expression
     */
    public abstract static class BinarayFunctionalExpression<X> extends ExpressionImpl<X>{
        protected final ExpressionImpl<?> e1;
        protected final ExpressionImpl<?> e2;

        /**
         * Supply the resultant type and pair of input operand expressions.
         */
        public BinarayFunctionalExpression(Class<X> t, Expression<?> x, Expression<?> y) {
            super(t);
            e1 = (ExpressionImpl<?>)x;
            e2 = (ExpressionImpl<?>)y;
        }

        @Override
        public void acceptVisit(CriteriaExpressionVisitor visitor) {
            Expressions.acceptVisit(visitor, this, e1, e2);
        }
    }

    /**
     * Functional Expression applies a function on a list of input Expressions.
     *
     * @param <X> the type of the resultant expression
     */
    public abstract static class FunctionalExpression<X> extends ExpressionImpl<X> {
        protected final ExpressionImpl<?>[] args;

        /**
         * Supply the resultant type and list of input operand expressions.
         */
        public FunctionalExpression(Class<X> t, Expression<?>... args) {
            super(t);
            int len = args == null ? 0 : args.length;
            this.args = new ExpressionImpl<?>[len];
            for (int i = 0; args != null && i < args.length; i++) {
                this.args[i] = (ExpressionImpl<?>)args[i];
            }
        }

        @Override
        public void acceptVisit(CriteriaExpressionVisitor visitor) {
            Expressions.acceptVisit(visitor, this, args);
        }
    }

    /**
     * Binary Logical Expression applies a function on a pair of input Expression to generate a Predicate
     * i.e. an expression whose resultant type is Boolean.
     *
     */
   public static abstract class BinaryLogicalExpression extends PredicateImpl {
        protected final ExpressionImpl<?> e1;
        protected final ExpressionImpl<?> e2;

        public BinaryLogicalExpression(Expression<?> x, Expression<?> y) {
            super();
            e1 = (ExpressionImpl<?>)x;
            e2 = (ExpressionImpl<?>)y;
        }

        @Override
        public void acceptVisit(CriteriaExpressionVisitor visitor) {
            Expressions.acceptVisit(visitor, this, e1, e2);
        }
    }


    public static class Abs<X> extends UnaryFunctionalExpression<X> {
        public  Abs(Expression<X> x) {
            super(x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.abs(Expressions.toValue(e, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "ABS", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class Ceiling<X> extends UnaryFunctionalExpression<X> {
        public  Ceiling(Expression<X> x) {
            super(x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.ceiling(Expressions.toValue(e, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "CEILING", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class Exponential extends UnaryFunctionalExpression<Double> {
        public  Exponential(Expression<? extends Number> x) {
            super(Double.class, x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.exp(Expressions.toValue(e, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "EXP", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class Floor<X> extends UnaryFunctionalExpression<X> {
        public  Floor(Expression<X> x) {
            super(x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.floor(Expressions.toValue(e, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "FLOOR", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class NaturalLogarithm extends UnaryFunctionalExpression<Double> {
        public  NaturalLogarithm(Expression<? extends Number> x) {
            super(Double.class, x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.ln(Expressions.toValue(e, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "LN", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class Sign extends UnaryFunctionalExpression<Integer> {
        public  Sign(Expression<? extends Number> x) {
            super(Integer.class, x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.sign(Expressions.toValue(e, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "SIGN", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class Power<X, Y extends Number> extends BinarayFunctionalExpression<Double> {
        public Power(Expression<X> x, Expression<Y> y) {
            super(double.class, x, y);
        }
        public Power(Expression<X> x, Y y) {
            this(x,new Constant<>(y));
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.power(
                Expressions.toValue(e1, factory, q),
                Expressions.toValue(e2, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "POWER", OPEN_BRACE, e1, COMMA, e2, CLOSE_BRACE);
        }
    }

    public static class Round<X> extends BinarayFunctionalExpression<X> {
        public Round(Expression<?> x, Expression<?> y) {
            super(((Class<X>) x.getJavaType()), x, y);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.round(
                Expressions.toValue(e1, factory, q), 
                Expressions.toValue(e2, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "ROUND", OPEN_BRACE, e1, COMMA, e2, CLOSE_BRACE);
        }
    }

    public static class Count extends UnaryFunctionalExpression<Long> {
        private boolean _distinct;
        public  Count(Expression<?> x) {
            this(x, false);
        }

        public  Count(Expression<?> x, boolean distinct) {
            super(Long.class, x);
            _distinct = distinct;

        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value v = Expressions.toValue(e, factory, q);
            return _distinct ? factory.count(factory.distinct(v)) : factory.count(v);
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "COUNT", OPEN_BRACE, _distinct ? "DISTINCT"+OPEN_BRACE : "",
                e, _distinct ? CLOSE_BRACE : "", CLOSE_BRACE);
        }
    }

    public static class Avg extends UnaryFunctionalExpression<Double> {
        public  Avg(Expression<?> x) {
            super(Double.class, x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.avg(Expressions.toValue(e, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "AVG", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class Sqrt extends UnaryFunctionalExpression<Double> {
        public  Sqrt(Expression<? extends Number> x) {
            super(Double.class, x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.sqrt(Expressions.toValue(e, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "SQRT", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class Max<X> extends UnaryFunctionalExpression<X> {
        public  Max(Expression<X> x) {
            super(x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.max(Expressions.toValue(e, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "MAX", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class Min<X> extends UnaryFunctionalExpression<X> {
        public  Min(Expression<X> x) {
            super(x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.min(Expressions.toValue(e, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "MIN", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class Size extends UnaryFunctionalExpression<Integer> {
        public  Size(Expression<? extends Collection<?>> x) {
            super(Integer.class, x);
        }

        public  Size(Collection<?> x) {
            this(new Constant<Collection<?>>(x));
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value val = Expressions.toValue(e, factory, q);
            Value result;
            if (val instanceof Literal && ((Literal)val).getParseType() == Literal.TYPE_COLLECTION)
                result = factory.newLiteral(((Collection)((Literal)val).getValue()).size(),
                    Literal.TYPE_NUMBER);
            else
                result = factory.size(val);
            result.setImplicitType(Integer.class);
            return result;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "SIZE", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class DatabaseFunction<T> extends FunctionalExpression<T> {
        private final String functionName;
        private final Class<T> resultType;

        public  DatabaseFunction(String name, Class<T> resultType, Expression<?>... exps) {
            super(resultType, exps);
            functionName = name;
            this.resultType = resultType;
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            return factory.newFunction(functionName, getJavaType(),
                new Expressions.ListArgument(resultType, args).toValue(factory, q));
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, functionName, OPEN_BRACE, Expressions.asValue(q, args, COMMA), CLOSE_BRACE);
        }
    }


    public static class Type<X extends Class> extends UnaryFunctionalExpression<X> {
        public Type(PathImpl<?, ?> path) {
            super((Class)Class.class, path);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            return factory.type(Expressions.toValue(e, factory, q));
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "TYPE", OPEN_BRACE, e, CLOSE_BRACE);
        }
    }

    public static class Cast<B> extends UnaryFunctionalExpression<B> {
        public Cast(Expression<?> x, Class<B> b) {
            super(b, x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            return factory.cast(Expressions.toValue(e, factory, q), getJavaType());
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, OPEN_BRACE, getJavaType().getSimpleName(), CLOSE_BRACE, e);
        }
    }

    public static class Concat extends BinarayFunctionalExpression<String> {
        public Concat(Expression<String> x, Expression<String> y) {
            super(String.class, x, y);
        }

        public Concat(Expression<String> x, String y) {
            this(x, new Constant<>(y));
        }

        public Concat(String x, Expression<String> y) {
            this(new Constant<>(x), y);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            return factory.concat(
                Expressions.toValue(e1, factory, q),
                Expressions.toValue(e2, factory, q));
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "CONCAT", OPEN_BRACE, e1, COMMA, e2, CLOSE_BRACE);
        }
    }

    public static class Substring extends UnaryFunctionalExpression<String> {
        private ExpressionImpl<Integer> from;
        private ExpressionImpl<Integer> len;

        public Substring(Expression<String> s, Expression<Integer> from, Expression<Integer> len) {
            super(String.class, s);
            this.from = (ExpressionImpl<Integer>)from;
            this.len  = (ExpressionImpl<Integer>)len;
        }

        public Substring(Expression<String> s, Expression<Integer> from) {
            this(s, (ExpressionImpl<Integer>)from, null);
        }

        public Substring(Expression<String> s) {
            this(s, (Expression<Integer>)null, (Expression<Integer>)null);
        }

        public Substring(Expression<String> s, Integer from) {
            this(s, new Constant<>(from), null);
        }

        public Substring(Expression<String> s, Integer from, Integer len) {
            this(s, new Constant<>(from), new Constant<>(len));
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            return JPQLExpressionBuilder.convertSubstringArguments(factory,
                Expressions.toValue(e, factory, q),
                from == null ? null : from.toValue(factory, q),
                len == null ? null : len.toValue(factory, q));
        }

        @Override
        public void acceptVisit(CriteriaExpressionVisitor visitor) {
            super.acceptVisit(visitor);
            Expressions.acceptVisit(visitor, from, len);
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "SUBSTRING", OPEN_BRACE, e, COMMA, from, COMMA, len, CLOSE_BRACE);
        }
    }

    public static class Locate extends ExpressionImpl<Integer> {
        private ExpressionImpl<String> pattern;
        private ExpressionImpl<Integer> from;
        private ExpressionImpl<String> path;

        public Locate(Expression<String> path, Expression<String> pattern, Expression<Integer> from) {
            super(Integer.class);
            this.path = (ExpressionImpl<String>)path;
            this.pattern = (ExpressionImpl<String>)pattern;
            this.from = (ExpressionImpl<Integer>)from;
        }

        public Locate(Expression<String> path, Expression<String> pattern) {
            this(path, pattern, null);
         }

        public Locate(Expression<String> path, String pattern) {
            this(path, new Constant<>(pattern), null);
        }

        public Locate(String path, Expression<String> pattern) {
            this(new Constant<>(path), pattern, null);
        }

        public Locate(Expression<String> path, String pattern, int from) {
            this(path, new Constant<>(pattern), new Constant<>(from));
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value locateSearch = path.toValue(factory, q);
            Value locateFromIndex = (from == null ? null : Expressions.toValue(from, factory, q));
            Value locatePath = Expressions.toValue(pattern, factory, q);

            return factory.indexOf(locateSearch,
                locateFromIndex == null ? locatePath
                    : factory.newArgumentList(locatePath, locateFromIndex));
        }

        @Override
        public void acceptVisit(CriteriaExpressionVisitor visitor) {
            Expressions.acceptVisit(visitor, this, pattern, from, path);
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "LOCATE", OPEN_BRACE, pattern, COMMA, path, CLOSE_BRACE);
        }
    }

    public static class Trim extends BinarayFunctionalExpression<String> {
        static Expression<Character> defaultTrim = new Constant<>(Character.class, ' ');
        static Trimspec defaultSpec = Trimspec.BOTH;
        private Trimspec ts;

        public Trim(Expression<String> x, Expression<Character> y, Trimspec ts) {
            super(String.class, x, y);
            this.ts = ts;
        }

        public Trim(Expression<String> x, Expression<Character> y) {
            this(x, y, defaultSpec);
        }

        public Trim(Expression<String> x) {
            this(x, defaultTrim, defaultSpec);
        }

        public Trim(Expression<String> x, Character t) {
            this(x, new Constant<>(Character.class, t), defaultSpec);
        }

        public Trim(Expression<String> x, Character t, Trimspec ts) {
            this(x, new Constant<>(Character.class, t), ts);
        }

        public Trim(Expression<String> x, Trimspec ts) {
            this(x, defaultTrim, ts);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Boolean spec = null;
            if (ts != null) {
                switch (ts) {
                case LEADING  : spec = true;  break;
                case TRAILING : spec = false; break;
                case BOTH     : spec = null;  break;
                }
            }
            Character t = (Character)((Constant<Character>)e2).arg;
            Constant<String> e2 = new Constant<>(String.class, t.toString());
            return factory.trim(
                Expressions.toValue(e1, factory, q),
                Expressions.toValue(e2, factory, q), spec);
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "TRIM", OPEN_BRACE, e1, COMMA, e2, CLOSE_BRACE);
        }
    }

    public static class Sum<N extends Number> extends BinarayFunctionalExpression<N> {
        public Sum(Expression<? extends Number> x, Expression<? extends Number> y) {
            super((Class<N>)x.getJavaType(), x, y);
        }

        public Sum(Expression<? extends Number> x) {
            this(x, (Expression<? extends Number>)null);
        }

        public Sum(Expression<? extends Number> x, Number y) {
            this(x, new Constant<>(Number.class, y));
        }

        public Sum(Number x, Expression<? extends Number> y) {
            this(new Constant<>(Number.class, x), y);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value= (e2 == null)
            ?   factory.sum(Expressions.toValue(e1, factory, q))
            :   factory.add(
                   Expressions.toValue(e1, factory, q),
                   Expressions.toValue(e2, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return e2 == null
               ? Expressions.asValue(q, "SUM", OPEN_BRACE, e1, CLOSE_BRACE)
               : Expressions.asValue(q, e1, " + ", e2);
        }
     }

    public static class Product<N extends Number> extends BinarayFunctionalExpression<N> {
        public Product(Expression<? extends Number> x, Expression<? extends Number> y) {
            super((Class<N>)x.getJavaType(), x, y);
        }

        public Product(Expression<? extends Number> x, Number y) {
            this(x, new Constant<>(Number.class, y));
        }

        public Product(Number x, Expression<? extends Number> y) {
            this(new Constant<>(Number.class, x), y);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            return factory.multiply(
                Expressions.toValue(e1, factory, q),
                Expressions.toValue(e2, factory, q));
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, e1, " * " ,e2);
        }
    }

    public static class Diff<N extends Number> extends BinarayFunctionalExpression<N> {
        public Diff(Expression<? extends Number> x, Expression<? extends Number> y) {
            super((Class<N>)x.getJavaType(), x, y);
        }

        public Diff(Expression<? extends Number> x, Number y) {
            this(x, new Constant<>(Number.class, y));
        }

        public Diff(Number x, Expression<? extends Number> y) {
            this(new Constant<>(Number.class, x), y);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.subtract(
                Expressions.toValue(e1, factory, q),
                Expressions.toValue(e2, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, e1, " - " ,e2);
        }
    }


    public static class Quotient<N extends Number> extends BinarayFunctionalExpression<N> {
        public Quotient(Expression<? extends Number> x, Expression<? extends Number> y) {
            super((Class<N>)x.getJavaType(), x, y);
        }

        public Quotient(Expression<? extends Number> x, Number y) {
            this(x, new Constant<>(y));
        }

        public Quotient(Number x, Expression<? extends Number> y) {
            this(new Constant<>(x), y);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.divide(
                Expressions.toValue(e1, factory, q),
                Expressions.toValue(e2, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, e1, "%" ,e2);
        }
    }

    public static class Mod extends BinarayFunctionalExpression<Integer> {
        public  Mod(Expression<Integer> x, Expression<Integer> y) {
            super(Integer.class, x,y);
        }
        public  Mod(Expression<Integer> x, Integer y) {
            this(x,new Constant<>(Integer.class, y));
        }
        public  Mod(Integer x, Expression<Integer> y) {
            this(new Constant<>(Integer.class, x),y);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) {
            Value value = factory.mod(
                Expressions.toValue(e1, factory, q),
                Expressions.toValue(e2, factory, q));
            value.setImplicitType(getJavaType());
            return value;
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
