/* * Copyright 2015, The Querydsl Team (http://www.querydsl.com/team) * * Licensed 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 com.querydsl.core.types.dsl; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; import com.querydsl.core.types.*; /** * {@code CaseBuilder} enables the construction of typesafe case-when-then-else * constructs : * e.g. * * <pre>{@code * Expression<String> cases = new CaseBuilder() * .when(c.annualSpending.gt(10000)).then("Premier") * .when(c.annualSpending.gt(5000)).then("Gold") * .when(c.annualSpending.gt(2000)).then("Silver") * .otherwise("Bronze"); * }</pre> * * @author tiwe * */ public final class CaseBuilder { private static class CaseElement<A> { @Nullable private final Predicate condition; private final Expression<A> target; public CaseElement(@Nullable Predicate condition, Expression<A> target) { this.condition = condition; this.target = target; } public Predicate getCondition() { return condition; } public Expression<A> getTarget() { return target; } } /** * Cascading typesafe Case builder * * @author tiwe * * @param <A> */ public abstract static class Cases<A, Q extends Expression<A>> { private final List<CaseElement<A>> cases = new ArrayList<CaseElement<A>>(); private final Class<? extends A> type; public Cases(Class<? extends A> type) { this.type = type; } Cases<A,Q> addCase(Predicate condition, Expression<A> expr) { cases.add(0, new CaseElement<A>(condition, expr)); return this; } protected abstract Q createResult(Class<? extends A> type, Expression<A> last); @SuppressWarnings("unchecked") public Q otherwise(A constant) { if (constant != null) { return otherwise(ConstantImpl.create(constant)); } else { return otherwise((Q) NullExpression.DEFAULT); } } @SuppressWarnings("unchecked") public Q otherwise(Expression<A> expr) { if (expr == null) { expr = (Expression) NullExpression.DEFAULT; } cases.add(0, new CaseElement<A>(null, expr)); Expression<A> last = null; for (CaseElement<A> element : cases) { if (last == null) { last = Expressions.operation(type, Ops.CASE_ELSE, element.getTarget()); } else { last = Expressions.operation(type, Ops.CASE_WHEN, element.getCondition(), element.getTarget(), last); } } return createResult(type, last); } public CaseWhen<A,Q> when(Predicate b) { return new CaseWhen<A,Q>(this, b); } } /** * Intermediate When state * * @author tiwe * * @param <A> */ public static class CaseWhen<A,Q extends Expression<A>> { private final Predicate b; private final Cases<A,Q> cases; public CaseWhen(Cases<A,Q> cases, Predicate b) { this.cases = cases; this.b = b; } public Cases<A,Q> then(A constant) { return then(ConstantImpl.create(constant)); } public Cases<A,Q> then(Expression<A> expr) { return cases.addCase(b, expr); } } /** * Initial state of Case construction * * @author tiwe * */ public static class Initial { private final Predicate when; public Initial(Predicate b) { this.when = b; } @SuppressWarnings("unchecked") public <A> Cases<A, SimpleExpression<A>> then(Expression<A> expr) { if (expr instanceof Predicate) { return (Cases) then((Predicate) expr); } else if (expr instanceof StringExpression) { return (Cases) then((StringExpression) expr); } else if (expr instanceof NumberExpression) { return then((NumberExpression) expr); } else if (expr instanceof DateExpression) { return then((DateExpression) expr); } else if (expr instanceof DateTimeExpression) { return then((DateTimeExpression) expr); } else if (expr instanceof TimeExpression) { return then((TimeExpression) expr); } else if (expr instanceof ComparableExpression) { return then((ComparableExpression) expr); } else { return thenSimple(expr); } } private <A> Cases<A, SimpleExpression<A>> thenSimple(Expression<A> expr) { return new Cases<A, SimpleExpression<A>>(expr.getType()) { @Override protected SimpleExpression<A> createResult(Class<? extends A> type, Expression<A> last) { return Expressions.operation(type, Ops.CASE, last); } }.addCase(when, expr); } public <A> Cases<A, SimpleExpression<A>> then(A constant) { return thenSimple(ConstantImpl.create(constant)); } // Boolean public Cases<Boolean, BooleanExpression> then(Predicate expr) { return thenBoolean(expr); } private Cases<Boolean, BooleanExpression> thenBoolean(Expression<Boolean> expr) { return new Cases<Boolean,BooleanExpression>(Boolean.class) { @Override protected BooleanExpression createResult(Class<? extends Boolean> type, Expression<Boolean> last) { return Expressions.booleanOperation(Ops.CASE, last); } }.addCase(when, expr); } public Cases<Boolean, BooleanExpression> then(boolean b) { return thenBoolean(ConstantImpl.create(b)); } // Comparable public <T extends Comparable> Cases<T, ComparableExpression<T>> then(ComparableExpression<T> expr) { return thenComparable(expr); } private <T extends Comparable> Cases<T, ComparableExpression<T>> thenComparable(Expression<T> expr) { return new Cases<T, ComparableExpression<T>>(expr.getType()) { @Override protected ComparableExpression<T> createResult(Class<? extends T> type, Expression<T> last) { return Expressions.comparableOperation(type, Ops.CASE, last); } }.addCase(when, expr); } public <A extends Comparable> Cases<A, ComparableExpression<A>> then(A arg) { return thenComparable(ConstantImpl.create(arg)); } // Date public <T extends Comparable> Cases<T, DateExpression<T>> then(DateExpression<T> expr) { return thenDate(expr); } private <T extends Comparable> Cases<T, DateExpression<T>> thenDate(Expression<T> expr) { return new Cases<T, DateExpression<T>>(expr.getType()) { @Override protected DateExpression<T> createResult(Class<? extends T> type, Expression<T> last) { return Expressions.dateOperation(type, Ops.CASE, last); } }.addCase(when, expr); } public Cases<java.sql.Date, DateExpression<java.sql.Date>> then(java.sql.Date date) { return thenDate(ConstantImpl.create(date)); } // DateTime public <T extends Comparable> Cases<T, DateTimeExpression<T>> then(DateTimeExpression<T> expr) { return thenDateTime(expr); } private <T extends Comparable> Cases<T, DateTimeExpression<T>> thenDateTime(Expression<T> expr) { return new Cases<T, DateTimeExpression<T>>(expr.getType()) { @Override protected DateTimeExpression<T> createResult(Class<? extends T> type, Expression<T> last) { return Expressions.dateTimeOperation(type, Ops.CASE, last); } }.addCase(when, expr); } public Cases<Timestamp, DateTimeExpression<Timestamp>> then(Timestamp ts) { return thenDateTime(ConstantImpl.create(ts)); } public Cases<java.util.Date, DateTimeExpression<java.util.Date>> then(java.util.Date date) { return thenDateTime(ConstantImpl.create(date)); } // Enum public <T extends Enum<T>> Cases<T,EnumExpression<T>> then(EnumExpression<T> expr) { return thenEnum(expr); } @SuppressWarnings("unchecked") private <T extends Enum<T>> Cases<T,EnumExpression<T>> thenEnum(Expression<T> expr) { return new Cases<T,EnumExpression<T>>(expr.getType()) { @Override protected EnumExpression<T> createResult(Class<? extends T> type, Expression<T> last) { return Expressions.enumOperation(type, Ops.CASE, last); } }.addCase(when, expr); } public <T extends Enum<T>> Cases<T, EnumExpression<T>> then(T arg) { return thenEnum(ConstantImpl.create(arg)); } // Number public <A extends Number & Comparable<?>> Cases<A, NumberExpression<A>> then(NumberExpression<A> expr) { return thenNumber(expr); } @SuppressWarnings("unchecked") private <A extends Number & Comparable<?>> Cases<A, NumberExpression<A>> thenNumber(Expression<A> expr) { return new Cases<A, NumberExpression<A>>(expr.getType()) { @Override protected NumberExpression<A> createResult(Class<? extends A> type, Expression<A> last) { return Expressions.numberOperation(type, Ops.CASE, last); } }.addCase(when, expr); } public <A extends Number & Comparable<?>> Cases<A, NumberExpression<A>> then(A num) { return thenNumber(ConstantImpl.create(num)); } // String public Cases<String,StringExpression> then(StringExpression expr) { return thenString(expr); } private Cases<String,StringExpression> thenString(Expression<String> expr) { return new Cases<String,StringExpression>(String.class) { @SuppressWarnings("unchecked") @Override protected StringExpression createResult(Class<? extends String> type, Expression<String> last) { return Expressions.stringOperation(Ops.CASE, last); } }.addCase(when, expr); } public Cases<String, StringExpression> then(String str) { return thenString(ConstantImpl.create(str)); } // Time public <T extends Comparable> Cases<T, TimeExpression<T>> then(TimeExpression<T> expr) { return thenTime(expr); } private <T extends Comparable> Cases<T, TimeExpression<T>> thenTime(Expression<T> expr) { return new Cases<T, TimeExpression<T>>(expr.getType()) { @Override protected TimeExpression<T> createResult(Class<? extends T> type, Expression<T> last) { return Expressions.timeOperation(type, Ops.CASE, last); } }.addCase(when, expr); } public Cases<Time, TimeExpression<Time>> then(Time time) { return thenTime(ConstantImpl.create(time)); } } public Initial when(Predicate b) { return new Initial(b); } }