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

org.apache.openjpa.persistence.criteria.Expressions Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * 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.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 javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaBuilder.Trimspec;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.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  List returnCopy(List 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  Set returnCopy(Set 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  the type of the resultant expression
     */
    public abstract static class UnaryFunctionalExpression extends ExpressionImpl {
        protected final ExpressionImpl e;
        /**
         * Supply the resultant type and input operand expression.
         */
        public UnaryFunctionalExpression(Class t, Expression e) {
            super(t);
            this.e  = (ExpressionImpl)e;
        }

        public UnaryFunctionalExpression(Expression e) {
            this((Class)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  the type of the resultant expression
     */
    public abstract static class BinarayFunctionalExpression extends ExpressionImpl{
        protected final ExpressionImpl e1;
        protected final ExpressionImpl e2;

        /**
         * Supply the resultant type and pair of input operand expressions.
         */
        public BinarayFunctionalExpression(Class 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  the type of the resultant expression
     */
    public abstract static class FunctionalExpression extends ExpressionImpl {
        protected final ExpressionImpl[] args;

        /**
         * Supply the resultant type and list of input operand expressions.
         */
        public FunctionalExpression(Class 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 extends UnaryFunctionalExpression {
        public  Abs(Expression 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 Count extends UnaryFunctionalExpression {
        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 {
        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 {
        public  Sqrt(Expression 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 extends UnaryFunctionalExpression {
        public  Max(Expression 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 extends UnaryFunctionalExpression {
        public  Min(Expression 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 {
        public  Size(Expression> x) {
            super(Integer.class, x);
        }

        public  Size(Collection x) {
            this(new Constant>(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 extends FunctionalExpression {
        private final String functionName;
        private final Class resultType;

        public  DatabaseFunction(String name, Class 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 extends UnaryFunctionalExpression {
        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 extends UnaryFunctionalExpression {
        public Cast(Expression x, Class 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 {
        public Concat(Expression x, Expression y) {
            super(String.class, x, y);
        }

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

        public Concat(String x, Expression 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 {
        private ExpressionImpl from;
        private ExpressionImpl len;

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

        public Substring(Expression s, Expression from) {
            this(s, (ExpressionImpl)from, null);
        }

        public Substring(Expression s) {
            this(s, (Expression)null, (Expression)null);
        }

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

        public Substring(Expression 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 {
        private ExpressionImpl pattern;
        private ExpressionImpl from;
        private ExpressionImpl path;

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

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

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

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

        public Locate(Expression 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 {
        static Expression defaultTrim = new Constant<>(Character.class, Character.valueOf(' '));
        static Trimspec defaultSpec = Trimspec.BOTH;
        private Trimspec ts;

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

        public Trim(Expression x, Expression y) {
            this(x, y, defaultSpec);
        }

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

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

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

        public Trim(Expression 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)e2).arg;
            Constant 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 extends BinarayFunctionalExpression {
        public Sum(Expression x, Expression y) {
            super((Class)x.getJavaType(), x, y);
        }

        public Sum(Expression x) {
            this(x, (Expression)null);
        }

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

        public Sum(Number x, Expression 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 extends BinarayFunctionalExpression {
        public Product(Expression x, Expression y) {
            super((Class)x.getJavaType(), x, y);
        }

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

        public Product(Number x, Expression 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 extends BinarayFunctionalExpression {
        public Diff(Expression x, Expression y) {
            super((Class)x.getJavaType(), x, y);
        }

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

        public Diff(Number x, Expression 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 extends BinarayFunctionalExpression {
        public Quotient(Expression x, Expression y) {
            super((Class)x.getJavaType(), x, y);
        }

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

        public Quotient(Number x, Expression 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 {
        public  Mod(Expression x, Expression y) {
            super(Integer.class, x,y);
        }
        public  Mod(Expression x, Integer y) {
            this(x,new Constant<>(Integer.class, y));
        }
        public  Mod(Integer x, Expression 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) {
            return Expressions.asValue(q, "MOD", OPEN_BRACE, e1, COMMA, e2, CLOSE_BRACE);
        }
    }

    public static class CurrentDate extends ExpressionImpl {
        public  CurrentDate() {
            super(java.sql.Date.class);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.getCurrentDate(getJavaType());
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return new StringBuilder("CURRENT_DATE");
        }
    }

    public static class CurrentTime extends ExpressionImpl {
        public  CurrentTime() {
            super(java.sql.Time.class);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.getCurrentTime(getJavaType());
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return new StringBuilder("CURRENT_TIME");
        }
    }

    public static class CurrentTimestamp extends ExpressionImpl {
        public  CurrentTimestamp() {
            super(java.sql.Timestamp.class);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.getCurrentTimestamp(getJavaType());
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return new StringBuilder("CURRENT_TIMESTAMP");
        }
    }

    public static class Equal extends BinaryLogicalExpression {
        public  Equal(Expression x, Expression y) {
            super(x,y);
        }

        public  Equal(Expression x, Object y) {
            this(x, new Constant(y));
        }

        @Override
        public PredicateImpl not() {
            return new NotEqual(e1, e2).markNegated();
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, CriteriaQueryImpl q){
            Value val1 = Expressions.toValue(e1, factory, q);
            Value val2 = Expressions.toValue(e2, factory, q);
            Expressions.setImplicitTypes(val1, val2, e1.getJavaType(), q);
            return factory.equal(val1, val2);
        }

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

    public static class NotEqual extends BinaryLogicalExpression {
        public  NotEqual(Expression x, Expression y) {
            super(x,y);
        }

        public  NotEqual(Expression x, Object y) {
            this(x, new Constant(y));
        }

        @Override
        public PredicateImpl not() {
            return new Equal(e1, e2).markNegated();
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, CriteriaQueryImpl q){
            Value val1 = Expressions.toValue(e1, factory, q);
            Value val2 = Expressions.toValue(e2, factory, q);
            Expressions.setImplicitTypes(val1, val2, e1.getJavaType(), q);
            return factory.notEqual(val1, val2);
        }

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

    public static class GreaterThan extends BinaryLogicalExpression {
        public  GreaterThan(Expression x, Expression y) {
            super(x,y);
        }

        public  GreaterThan(Expression x, Object y) {
            this(x, new Constant(y));
        }

        @Override
        public PredicateImpl not() {
            return new LessThanEqual(e1, e2).markNegated();
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, CriteriaQueryImpl q){
            Value val1 = Expressions.toValue(e1, factory, q);
            Value val2 = Expressions.toValue(e2, factory, q);
            Expressions.setImplicitTypes(val1, val2, e1.getJavaType(), q);
            return factory.greaterThan(val1, val2);
        }

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

    public static class GreaterThanEqual extends BinaryLogicalExpression {
        public  GreaterThanEqual(Expression x, Expression y) {
            super(x,y);
        }

        public  GreaterThanEqual(Expression x, Object y) {
            this(x, new Constant(y));
        }

        @Override
        public PredicateImpl not() {
            return new LessThan(e1, e2).markNegated();
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, CriteriaQueryImpl q){
            Value val1 = Expressions.toValue(e1, factory, q);
            Value val2 = Expressions.toValue(e2, factory, q);
            Expressions.setImplicitTypes(val1, val2, e1.getJavaType(), q);
            return factory.greaterThanEqual(val1, val2);
        }

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

    public static class LessThan extends BinaryLogicalExpression {
        public  LessThan(Expression x, Expression y) {
            super(x,y);
        }

        public  LessThan(Expression x, Object y) {
            this(x, new Constant(y));
        }

        @Override
        public PredicateImpl not() {
            return new GreaterThanEqual(e1, e2).markNegated();
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, CriteriaQueryImpl q){
            Value val1 = Expressions.toValue(e1, factory, q);
            Value val2 = Expressions.toValue(e2, factory, q);
            Expressions.setImplicitTypes(val1, val2, e1.getJavaType(), q);
            return factory.lessThan(val1, val2);
        }

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

    public static class LessThanEqual extends BinaryLogicalExpression {
        public  LessThanEqual(Expression x, Expression y) {
            super(x,y);
        }

        public  LessThanEqual(Expression x, Object y) {
            this(x, new Constant(y));
        }

        @Override
        public PredicateImpl not() {
            return new GreaterThan(e1, e2).markNegated();
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, CriteriaQueryImpl q){
            Value val1 = Expressions.toValue(e1, factory, q);
            Value val2 = Expressions.toValue(e2, factory, q);
            Expressions.setImplicitTypes(val1, val2, e1.getJavaType(), q);
            return factory.lessThanEqual(val1, val2);
        }


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

    public static class Between> extends PredicateImpl.And {
        private final ExpressionImpl e;
        private final ExpressionImpl v1;
        private final ExpressionImpl v2;

        public Between(Expression v, Expression x, Expression y) {
            super(new GreaterThanEqual(v,x), new LessThanEqual(v,y));
            e = (ExpressionImpl)v;
            v1 = (ExpressionImpl)x;
            v2 = (ExpressionImpl)y;
        }

        public Between(Expression v, Y x, Y y) {
            this(v, new Constant<>(x), new Constant<>(y));
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, e, " BETWEEN ", v1, " AND ", v2);
        }
    }

    public static class Constant extends ExpressionImpl {
        public final Object arg;
        public Constant(Class t, X x) {
            super(t);
            this.arg = x;
        }

        public Constant(X x) {
            this(x == null ? null : (Class)x.getClass(), x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            Object value = arg;
            Class literalClass = getJavaType();
            if (arg instanceof ParameterExpressionImpl) {
                return ((ParameterExpressionImpl)arg).toValue(factory, q);
            }
            int literalType = Literal.TYPE_UNKNOWN;
            if (Number.class.isAssignableFrom(literalClass)) {
                literalType = Literal.TYPE_NUMBER;
            } else if (Boolean.class.isAssignableFrom(literalClass)) {
                literalType = Literal.TYPE_BOOLEAN;
            } else if (String.class.isAssignableFrom(literalClass)) {
                literalType = Literal.TYPE_STRING;
            } else if (Enum.class.isAssignableFrom(literalClass)) {
                literalType = Literal.TYPE_ENUM;
            } else if (Class.class.isAssignableFrom(literalClass)) {
                literalType = Literal.TYPE_CLASS;
                Literal lit = factory.newTypeLiteral(value, Literal.TYPE_CLASS);
                ClassMetaData can = ((Types.Entity)q.getRoot().getModel()).meta;
                Class candidate = can.getDescribedType();
                if (candidate.isAssignableFrom((Class)value)) {
                   lit.setMetaData(q.getMetamodel().getRepository().getMetaData((Class)value, null, true));
                } else {
                    lit.setMetaData(can);
                }
                return lit;
            } else if (Collection.class.isAssignableFrom(literalClass)) {
                literalType = Literal.TYPE_COLLECTION;
            }
            return factory.newLiteral(value, literalType);
        }

        @Override
        public void acceptVisit(CriteriaExpressionVisitor visitor) {
            Expressions.acceptVisit(visitor, this, arg instanceof Expression ? ((Expression)arg) : null);
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            if (arg == null)
                return new StringBuilder("NULL");
            Class literalClass = getJavaType();
            if (arg instanceof ParameterExpressionImpl) {
                return ((ParameterExpressionImpl)arg).asValue(q);
            } else if (Number.class.isAssignableFrom(literalClass)) {
                return new StringBuilder(arg.toString());
            } else if (Boolean.class.isAssignableFrom(literalClass)) {
                return new StringBuilder(arg.toString());
            } else if (String.class.isAssignableFrom(literalClass)) {
                return new StringBuilder("'").append(arg.toString()).append("'");
            } else if (Enum.class.isAssignableFrom(literalClass)) {
                return new StringBuilder(arg.toString());
            } else if (Class.class.isAssignableFrom(literalClass)) {
                return new StringBuilder(((Class)arg).getSimpleName());
            } else if (Collection.class.isAssignableFrom(literalClass)) {
                return new StringBuilder(((Collection)arg).toString());
            }
            return new StringBuilder(arg.toString());
        }
    }

    public static class IsEmpty extends PredicateImpl {
        final ExpressionImpl collection;
        public IsEmpty(Expression collection) {
            super();
            this.collection = (ExpressionImpl)collection;
        }

        @Override
        public PredicateImpl not() {
            return new IsNotEmpty(collection).markNegated();
        }

        @Override
        Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            return Expressions.toValue(collection, factory, q);
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, CriteriaQueryImpl q){
            Value val = Expressions.toValue(collection, factory, q);
            return factory.isEmpty(val);
        }

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

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, collection, " IS EMPTY");
        }
    }

    public static class IsNotEmpty extends PredicateImpl {
        final ExpressionImpl collection;
        public IsNotEmpty(Expression collection) {
            super();
            this.collection = (ExpressionImpl)collection;
        }

        @Override
        public PredicateImpl not() {
            return new IsEmpty(collection).markNegated();
        }

        @Override
        Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            return Expressions.toValue(collection, factory, q);
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, CriteriaQueryImpl q){
            Value val = Expressions.toValue(collection, factory, q);
            // factory.isNotEmpty() not used to match JPQL
            return factory.not(factory.isEmpty(val));
        }

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

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, collection, " IS NOT EMPTY");
        }
    }


    public static class Index extends UnaryFunctionalExpression {
        public Index(Joins.List e) {
            super(Integer.class, e);
        }

        @Override
        public org.apache.openjpa.kernel.exps.Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            Value v = Expressions.toValue(e, factory, q);
            ClassMetaData meta = ((PathImpl)e)._member.fmd.getElement().getTypeMetaData();
            v.setMetaData(meta);
            return factory.index(v);
        }

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

    public static class IsMember extends PredicateImpl {
        final ExpressionImpl element;
        final ExpressionImpl collection;

        public IsMember(Expression element, Expression collection) {
            super();
            this.element = (ExpressionImpl)element;
            this.collection = (ExpressionImpl)collection;
        }

        public IsMember(E element, Expression collection) {
            this(new Constant<>(element), collection);
        }

        @Override
        public org.apache.openjpa.kernel.exps.Expression toKernelExpression(
            ExpressionFactory factory, CriteriaQueryImpl q) {
            org.apache.openjpa.kernel.exps.Expression contains = factory.contains(
                Expressions.toValue(collection, factory, q),
                Expressions.toValue(element, factory, q));
            return contains;
        }

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

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, element, "MEMBER OF ", collection);
        }
    }

    public static class Like extends PredicateImpl {
        public static final String MATCH_MULTICHAR  = "%";
        public static final String MATCH_SINGLECHAR = "_";

        final ExpressionImpl str;
        final ExpressionImpl pattern;
        final ExpressionImpl escapeChar;

        public Like(Expression x, Expression pattern, Expression escapeChar) {
            super();
            this.str = (ExpressionImpl)x;
            this.pattern = (ExpressionImpl)pattern;
            this.escapeChar = (ExpressionImpl)escapeChar;
        }

        public Like(Expression x, Expression pat, char esc) {
            this(x, pat, new Constant<>(Character.class, esc));
        }

        public Like(Expression x, Expression pattern) {
            this(x, pattern, null);
        }

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

        public Like(Expression x, String pat,
            Expression esc) {
            this(x, new Constant<>(pat), esc);
        }

        public Like(Expression x, String pat,  Character esc) {
            this(x, new Constant<>(pat), new Constant<>(esc));
        }

        @Override
        public org.apache.openjpa.kernel.exps.Expression toKernelExpression(
            ExpressionFactory factory, CriteriaQueryImpl q) {
            String escapeStr = escapeChar == null ? null :
                ((Character)((Literal)Expressions.toValue(
                    escapeChar, factory, q)).getValue()).toString();

            return factory.matches(
                Expressions.toValue(str, factory, q),
                Expressions.toValue(pattern, factory, q),
                MATCH_SINGLECHAR, MATCH_MULTICHAR, escapeStr);
        }

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

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, str, " LIKE ", pattern);
        }
    }

    public static class Coalesce extends ExpressionImpl implements CriteriaBuilder.Coalesce {
        private final List> values = new ArrayList<>();

        public Coalesce(Class cls) {
            super(cls);
        }

        @Override
        public Coalesce value(T value) {
            values.add(new Constant<>(value));
            return this;
        }

        @Override
        public Coalesce value(Expression value) {
            values.add(value);
            return this;
        }

        @Override
        public org.apache.openjpa.kernel.exps.Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            Value[] vs = new Value[values.size()];
            int i = 0;
            for (Expression e : values)
                vs[i++] = Expressions.toValue((ExpressionImpl)e, factory, q);
            return factory.coalesceExpression(vs);
        }

        @Override
        public void acceptVisit(CriteriaExpressionVisitor visitor) {
            Expressions.acceptVisit(visitor, this, values.toArray(new ExpressionImpl[values.size()]));
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "COALESCE", OPEN_BRACE, Expressions.asValue(q, values == null
                    ? null : values.toArray(new Expression[values.size()]), COMMA), CLOSE_BRACE);
        }
    }

    public static class Nullif extends ExpressionImpl {
        private Expression val1;
        private Expression val2;

        public Nullif(Expression x, Expression y) {
            super((Class)x.getJavaType());
            val1 = x;
            val2 = y;
        }

        public Nullif(Expression x, T y) {
            this(x, new Constant<>(y));
        }

        @Override
        public org.apache.openjpa.kernel.exps.Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            Value value1 = Expressions.toValue((ExpressionImpl)val1, factory, q);
            Value value2 = Expressions.toValue((ExpressionImpl)val2, factory, q);
            return factory.nullIfExpression(value1, value2);
        }

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

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, "NULLIF", OPEN_BRACE, val1, COMMA, val2, CLOSE_BRACE);
        }
    }

    public static class IsNull extends PredicateImpl {
        final ExpressionImpl e;
        public IsNull(ExpressionImpl e) {
            super();
            this.e = e;
        }

        @Override
        public PredicateImpl not() {
            return new IsNotNull(e).markNegated();
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(
            ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.equal(
                Expressions.toValue(e, factory, q),
                factory.getNull());
        }

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

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, e, " IS NULL");
        }
    }

    public static class IsNotNull extends PredicateImpl {
        final ExpressionImpl e;
        public IsNotNull(ExpressionImpl e) {
            super();
            this.e = e;
        }

        @Override
        public PredicateImpl not() {
            return new IsNull(e).markNegated();
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(
            ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.notEqual(
                Expressions.toValue(e, factory, q),
                factory.getNull());
        }

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

        @Override
        public StringBuilder asValue(AliasContext q) {
            return Expressions.asValue(q, e, " IS NOT NULL");
        }
    }


    public static class In extends PredicateImpl.Or implements CriteriaBuilder.In {
        final ExpressionImpl e;
        public In(Expression e) {
            super();
            this.e = (ExpressionImpl)e;
        }

        @Override
        public Expression getExpression() {
            return e;
        }

        @Override
        public In value(T value) {
            add(new Expressions.Equal(e,value));
            return this;
        }

        @Override
        public In value(Expression value) {
            add(new Expressions.Equal(e,value));
            return this;
        }

        @Override
        public PredicateImpl not() {
            In notIn = new In<>(e);
            notIn.markNegated();
            for (Predicate e : _exps) {
                notIn.add(e);
            }
            return notIn;
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(
            ExpressionFactory factory, CriteriaQueryImpl q) {
            org.apache.openjpa.kernel.exps.Expression inExpr = null;
            if (_exps.size() == 1) {
                Expressions.Equal e = (Expressions.Equal)_exps.get(0);
                ExpressionImpl e2 = e.e2;
                ExpressionImpl e1 = e.e1;

                Class e1JavaType = e1.getJavaType();
                Class e2JavaType = e2.getJavaType();

                // array and Collection
                if (BindableParameter.class.isInstance(e2) && BindableParameter.class.cast(e2).value() != null &&
                    ((e2JavaType.isArray() && e2JavaType.getComponentType().equals(e1JavaType))
                    || (Class.class.isInstance(e2JavaType) ||
                        (ParameterizedType.class.isInstance(e2JavaType)
                            && ParameterizedType.class.cast(e2JavaType).getActualTypeArguments().length > 0
                            && e1JavaType.equals(ParameterizedType.class.cast(e2JavaType).getActualTypeArguments()[0]))
                       ))) {
                    final BindableParameter bp = BindableParameter.class.cast(e2);
                    final Object value = bp.value();

                    _exps.clear();
                    if (value == null) {
                        add(new Expressions.Equal(e1, null));
                    } else if (value.getClass().isArray()) {
                        final int len = Array.getLength(value);
                        for (int i = 0; i < len; i++) {
                            add(new Expressions.Equal(e1, Array.get(value, i)));
                        }
                    } else if (Collection.class.isInstance(value)) {
                        for (final Object item : Collection.class.cast(value)) {
                            add(new Expressions.Equal(e1, item));
                        }
                    }
                } else {
                    // normal case
                    Value val2 = Expressions.toValue(e2, factory, q);
                    if (!(val2 instanceof Literal)) {
                        Value val1 = Expressions.toValue(e1, factory, q);
                        Expressions.setImplicitTypes(val1, val2, e1.getJavaType(), q);
                        inExpr = factory.contains(val2, val1);
                        return isNegated() ? factory.not(inExpr) : inExpr;
                    } else if (((Literal)val2).getParseType() == Literal.TYPE_COLLECTION) {
                        Collection coll = (Collection)((Literal)val2).getValue();
                        _exps.clear();
                        for (Object v : coll) {
                            add(new Expressions.Equal(e1,v));
                        }
                    }
                }
            }
            inExpr = super.toKernelExpression(factory, q);
            IsNotNull notNull = new Expressions.IsNotNull(e);

            return factory.and(inExpr, notNull.toKernelExpression(factory, q));
        }

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

        @Override
        public StringBuilder asValue(AliasContext q) {
            StringBuilder buffer = Expressions.asValue(q, e, " IN ", OPEN_BRACE);
            for (int i = 0; i < _exps.size(); i++) {
                buffer.append(((Equal)_exps.get(i)).e2.asValue(q)).append(i+1 == _exps.size() ? CLOSE_BRACE : COMMA);
            }
            return buffer;
        }
    }

    public static class Case extends ExpressionImpl implements CriteriaBuilder.Case {
        private final List> thens = new ArrayList<>();
        private final List> whens = new ArrayList<>();
        private Expression otherwise;

        public Case(Class cls) {
            super(cls);
        }

        @Override
        public Case when(Expression when, Expression then) {
            whens.add(when);
            thens.add(then);
            return this;
        }

        @Override
        public Case when(Expression when, T then) {
            return when(when, new Expressions.Constant<>(then));
        }

        @Override
        public Case otherwise(Expression otherwise) {
            this.otherwise = otherwise;
            return this;
        }

        @Override
        public Case otherwise(T otherwise) {
            return otherwise(new Expressions.Constant<>(otherwise));
        }

        @Override
        public org.apache.openjpa.kernel.exps.Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            int size = whens.size();
            org.apache.openjpa.kernel.exps.Expression[] exps = new org.apache.openjpa.kernel.exps.Expression[size];
            for (int i = 0; i < size; i++) {
                org.apache.openjpa.kernel.exps.Expression expr =
                    ((ExpressionImpl)whens.get(i)).toKernelExpression(factory, q);
                Value action = Expressions.toValue((ExpressionImpl)thens.get(i), factory, q);
                exps[i] = factory.whenCondition(expr, action);
            }

            Value other = Expressions.toValue((ExpressionImpl)otherwise, factory, q);
            return factory.generalCaseExpression(exps, other);
        }

        @Override
        public void acceptVisit(CriteriaExpressionVisitor visitor) {
            visitor.enter(this);
            for (int i = 0; i < whens.size(); i++) {
                Expressions.visitChildren(visitor, whens.get(i));
                Expressions.visitChildren(visitor, thens.get(i));
            }
            Expressions.visitChildren(visitor, otherwise);
            visitor.exit(this);
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            StringBuilder buffer = new StringBuilder("CASE ");
            int size = whens.size();
            for (int i = 0; i < size; i++) {
                buffer.append(Expressions.asValue(q, " WHEN ", whens.get(i), " THEN ", thens.get(i)));
            }
            buffer.append(Expressions.asValue(q, " ELSE ", otherwise, " END"));
            return buffer;
        }
    }

    public static class SimpleCase extends ExpressionImpl implements CriteriaBuilder.SimpleCase {
        private final List> thens = new ArrayList<>();
        private final List> whens = new ArrayList<>();
        private Expression otherwise;
        private Expression caseOperand;

        public SimpleCase(Class cls) {
            super(cls);
        }

        public SimpleCase(Expression expr) {
            super(null);
            this.caseOperand = expr;
        }

        @Override
        public Expression getExpression() {
            return caseOperand;
        }

        public SimpleCase when(Expression when, Expression then) {
            whens.add(when);
            thens.add(then);
            return this;
        }

        @Override
        public SimpleCase when(C when, Expression then) {
            return when(new Constant<>(when), then);
        }

        @Override
        public SimpleCase when(C when, R then) {
            return when(when, new Expressions.Constant<>(then));
        }

        @Override
        public SimpleCase otherwise(Expression otherwise) {
            this.otherwise = otherwise;
            return this;
        }

        @Override
        public SimpleCase otherwise(R otherwise) {
            return otherwise(new Expressions.Constant<>(otherwise));
        }

        @Override
        public org.apache.openjpa.kernel.exps.Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            Value caseOperandExpr = Expressions.toValue((ExpressionImpl)caseOperand, factory, q);
            int size = whens.size();
            org.apache.openjpa.kernel.exps.Expression[] exps = new org.apache.openjpa.kernel.exps.Expression[size];
            for (int i = 0; i < size; i++) {
                Value when = Expressions.toValue((ExpressionImpl)whens.get(i), factory, q);
                Value action = Expressions.toValue((ExpressionImpl)thens.get(i), factory, q);
                exps[i] = factory.whenScalar(when, action);
            }

            Value other = Expressions.toValue((ExpressionImpl)otherwise, factory, q);
            return factory.simpleCaseExpression(caseOperandExpr, exps, other);
        }

        @Override
        public void acceptVisit(CriteriaExpressionVisitor visitor) {
            visitor.enter(this);
            Expressions.visitChildren(visitor, caseOperand);
            for (int i = 0; i < whens.size(); i++) {
                Expressions.visitChildren(visitor, whens.get(i));
                Expressions.visitChildren(visitor, thens.get(i));
            }
            Expressions.visitChildren(visitor, otherwise);
            visitor.exit(this);
        }

        @Override
        public StringBuilder asValue(AliasContext q) {
            StringBuilder buffer = new StringBuilder("CASE ");
            int size = whens.size();
            for (int i = 0; i < size; i++) {
                buffer.append(Expressions.asValue(q, " WHEN ", whens.get(i), " THEN ", thens.get(i)));
            }
            buffer.append(Expressions.asValue(q, " ELSE ", otherwise, " END"));
            return buffer;
        }
    }

    public static class Lower extends UnaryFunctionalExpression {
        public Lower(Expression x) {
            super(String.class, x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.toLowerCase(Expressions.toValue(e, factory, q));
        }

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

    public static class Upper extends UnaryFunctionalExpression {
        public Upper(Expression x) {
            super(String.class, x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.toUpperCase(Expressions.toValue(e, factory, q));
        }

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

    public static class Length extends UnaryFunctionalExpression {
        public Length(Expression x) {
            super(Integer.class, x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.stringLength(Expressions.toValue(e, factory, q));
        }

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

    public static abstract class SubqueryPredicate extends PredicateImpl {
        final SubqueryImpl e;

        public SubqueryPredicate(Subquery x) {
            super();
            e = (SubqueryImpl)x;
        }

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

    public static abstract class SubqueryExpression extends ExpressionImpl {
        final SubqueryImpl e;

        public SubqueryExpression(Subquery x) {
            super((Class)x.getJavaType());
            e = (SubqueryImpl)x;
        }

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

    public static class Exists extends SubqueryPredicate {
        public Exists(Subquery x) {
            super(x);
        }

        @Override
        org.apache.openjpa.kernel.exps.Expression toKernelExpression(
            ExpressionFactory factory, CriteriaQueryImpl q) {
            org.apache.openjpa.kernel.exps.Expression exists =
                factory.isNotEmpty(Expressions.toValue(e, factory, q));
            return exists;
        }

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

    public static class All extends SubqueryExpression {
        public All(Subquery x) {
            super(x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.all(Expressions.toValue(e, factory, q));
        }

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

    public static class Any extends SubqueryExpression {
        public Any(Subquery x) {
            super(x);
        }

        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.any(Expressions.toValue(e, factory, q));
        }

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

    public static class Not extends PredicateImpl {
        protected final ExpressionImpl e;
        public Not(Expression ne) {
            super();
            e = (ExpressionImpl)ne;
        }

        @Override
        public org.apache.openjpa.kernel.exps.Expression toKernelExpression(
          ExpressionFactory factory, CriteriaQueryImpl q) {
            return factory.not(e.toKernelExpression(factory, q));
        }

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

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

    public static class CastAs extends ExpressionImpl {
        protected final ExpressionImpl actual;
        public CastAs(Class cast, ExpressionImpl actual) {
            super(cast);
            this.actual = actual;
        }

        @Override
        public org.apache.openjpa.kernel.exps.Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            org.apache.openjpa.kernel.exps.Value e = actual.toValue(factory, q);
            e.setImplicitType(getJavaType());
            return e;
        }

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

        @Override
        public StringBuilder asValue(AliasContext q) {
            return actual.asValue(q);
        }
    }

    /**
     * An expression that is composed of one or more expressions.
     *
     * @param 
     */
    public static class ListArgument extends ExpressionImpl {
        private final ExpressionImpl[] _args;
        public ListArgument(Class cls, ExpressionImpl... args) {
            super(cls);
            _args = args;
        }

        @Override
        public org.apache.openjpa.kernel.exps.Arguments toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
            org.apache.openjpa.kernel.exps.Value[] kvs = new org.apache.openjpa.kernel.exps.Value[_args.length];
            int i = 0;
            for (ExpressionImpl arg : _args) {
                kvs[i++] = arg.toValue(factory, q);
            }
            org.apache.openjpa.kernel.exps.Arguments e = factory.newArgumentList(kvs);
            e.setImplicitType(getJavaType());
            return e;
        }

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