/* * 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.Timestamp; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.annotation.Nullable; import com.querydsl.core.types.*; /** * {@code CaseForEqBuilder} enables the construction of typesafe case-when-then-else constructs * for equals-operations : * e.g. * * <pre>{@code * QCustomer c = QCustomer.customer; * Expression<Integer> cases = c.annualSpending * .when(1000l).then(1) * .when(2000l).then(2) * .when(5000l).then(3) * .otherwise(4); * }</pre> * * @author tiwe * * @param <D> */ public final class CaseForEqBuilder<D> { private static class CaseElement<D> { @Nullable private final Expression<? extends D> eq; private final Expression<?> target; public CaseElement(@Nullable Expression<? extends D> eq, Expression<?> target) { this.eq = eq; this.target = target; } public Expression<? extends D> getEq() { return eq; } public Expression<?> getTarget() { return target; } } private final Expression<D> base; private final Expression<? extends D> other; private final List<CaseElement<D>> caseElements = new ArrayList<CaseElement<D>>(); private Class<?> type; public CaseForEqBuilder(Expression<D> base, Expression<? extends D> other) { this.base = base; this.other = other; } public <T> Cases<T,Expression<T>> then(Expression<T> 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 <T> Cases<T, Expression<T>> thenSimple(Expression<T> expr) { type = expr.getType(); return new Cases<T,Expression<T>>() { @Override protected Expression<T> createResult(Class<T> type, Expression<T> last) { return Expressions.operation(type, Ops.CASE_EQ, base, last); } }.when(other).then(expr); } public <T> Cases<T,Expression<T>> then(T then) { return then(ConstantImpl.create(then)); } @SuppressWarnings("unchecked") public <T> Cases<T,Expression<T>> thenNull() { return then((Expression<T>) NullExpression.DEFAULT); } // Boolean public Cases<Boolean, BooleanExpression> then(Boolean then) { return thenBoolean(ConstantImpl.create(then)); } public Cases<Boolean, BooleanExpression> then(BooleanExpression then) { return thenBoolean(then); } private Cases<Boolean, BooleanExpression> thenBoolean(Expression<Boolean> then) { type = then.getType(); return new Cases<Boolean, BooleanExpression>() { @Override protected BooleanExpression createResult(Class<Boolean> type, Expression<Boolean> last) { return Expressions.booleanOperation(Ops.CASE_EQ, base, last); } }.when(other).then(then); } // Comparable public <T extends Comparable> Cases<T, ComparableExpression<T>> then(T then) { return thenComparable(ConstantImpl.create(then)); } public <T extends Comparable> Cases<T, ComparableExpression<T>> then(ComparableExpression<T> then) { return thenComparable(then); } private <T extends Comparable> Cases<T, ComparableExpression<T>> thenComparable(Expression<T> then) { type = then.getType(); return new Cases<T, ComparableExpression<T>>() { @Override protected ComparableExpression<T> createResult(Class<T> type, Expression<T> last) { return Expressions.comparableOperation(type, Ops.CASE_EQ, base, last); } }.when(other).then(then); } // Date public Cases<java.sql.Date, DateExpression<java.sql.Date>> then(java.sql.Date then) { return thenDate(ConstantImpl.create(then)); } public <T extends Comparable> Cases<T, DateExpression<T>> then(DateExpression<T> then) { return thenDate(then); } private <T extends Comparable> Cases<T, DateExpression<T>> thenDate(Expression<T> then) { type = then.getType(); return new Cases<T, DateExpression<T>>() { @Override protected DateExpression<T> createResult(Class<T> type, Expression<T> last) { return Expressions.dateOperation(type, Ops.CASE_EQ, base, last); } }.when(other).then(then); } // DateTime public Cases<Date, DateTimeExpression<Date>> then(Date then) { return thenDateTime(ConstantImpl.create(then)); } public Cases<Timestamp, DateTimeExpression<Timestamp>> then(Timestamp then) { return thenDateTime(ConstantImpl.create(then)); } public <T extends Comparable> Cases<T, DateTimeExpression<T>> then(DateTimeExpression<T> then) { return thenDateTime(then); } private <T extends Comparable> Cases<T, DateTimeExpression<T>> thenDateTime(Expression<T> then) { type = then.getType(); return new Cases<T, DateTimeExpression<T>>() { @Override protected DateTimeExpression<T> createResult(Class<T> type, Expression<T> last) { return Expressions.dateTimeOperation(type, Ops.CASE_EQ, base, last); } }.when(other).then(then); } // Enum public <T extends Enum<T>> Cases<T, EnumExpression<T>> then(T then) { return thenEnum(ConstantImpl.create(then)); } public <T extends Enum<T>> Cases<T, EnumExpression<T>> then(EnumExpression<T> then) { return thenEnum(then); } private <T extends Enum<T>> Cases<T, EnumExpression<T>> thenEnum(Expression<T> then) { type = then.getType(); return new Cases<T, EnumExpression<T>>() { @Override protected EnumExpression<T> createResult(Class<T> type, Expression<T> last) { return Expressions.enumOperation(type, Ops.CASE_EQ, base, last); } }.when(other).then(then); } // Number public <T extends Number & Comparable<?>> Cases<T, NumberExpression<T>> then(T then) { return thenNumber(ConstantImpl.create(then)); } public <T extends Number & Comparable<?>> Cases<T,NumberExpression<T>> then(NumberExpression<T> then) { return thenNumber(then); } public <T extends Number & Comparable<?>> Cases<T,NumberExpression<T>> thenNumber(Expression<T> then) { type = then.getType(); return new Cases<T,NumberExpression<T>>() { @SuppressWarnings("unchecked") @Override protected NumberExpression<T> createResult(Class<T> type, Expression<T> last) { return Expressions.numberOperation(type, Ops.CASE_EQ, base, last); } }.when(other).then(then); } // String public Cases<String,StringExpression> then(String then) { return thenString(ConstantImpl.create(then)); } public Cases<String,StringExpression> then(StringExpression then) { return thenString(then); } private Cases<String,StringExpression> thenString(Expression<String> then) { type = then.getType(); return new Cases<String,StringExpression>() { @SuppressWarnings("unchecked") @Override protected StringExpression createResult(Class<String> type, Expression<String> last) { return Expressions.stringOperation(Ops.CASE_EQ, base, last); } }.when(other).then(then); } // Time public Cases<java.sql.Time, TimeExpression<java.sql.Time>> then(java.sql.Time then) { return thenTime(ConstantImpl.create(then)); } public <T extends Comparable> Cases<T, TimeExpression<T>> then(TimeExpression<T> then) { return thenTime(then); } private <T extends Comparable> Cases<T, TimeExpression<T>> thenTime(Expression<T> then) { type = then.getType(); return new Cases<T, TimeExpression<T>>() { @Override protected TimeExpression<T> createResult(Class<T> type, Expression<T> last) { return Expressions.timeOperation(type, Ops.CASE_EQ, base, last); } }.when(other).then(then); } /** * Intermediate step * * @param <T> Result type * @param <Q> Parent expression type */ public abstract class Cases<T, Q extends Expression<T>> { public CaseWhen<T, Q> when(Expression<? extends D> when) { return new CaseWhen<T,Q>(this, when); } public CaseWhen<T, Q> when(D when) { return when(ConstantImpl.create(when)); } @SuppressWarnings("unchecked") public Q otherwise(Expression<T> otherwise) { caseElements.add(0, new CaseElement<D>(null, otherwise)); Expression<T> last = null; for (CaseElement<D> element : caseElements) { if (last == null) { last = Expressions.operation((Class<T>) type, Ops.CASE_EQ_ELSE, element.getTarget()); } else { last = Expressions.operation((Class<T>) type, Ops.CASE_EQ_WHEN, base, element.getEq(), element.getTarget(), last); } } return createResult((Class<T>) type, last); } protected abstract Q createResult(Class<T> type, Expression<T> last); public Q otherwise(T otherwise) { return otherwise(ConstantImpl.create(otherwise)); } } /** * Intermediate step * * @param <T> Result type * @param <Q> Parent expression type */ public class CaseWhen<T, Q extends Expression<T>> { private final Cases<T, Q> cases; private final Expression<? extends D> when; public CaseWhen(Cases<T, Q> cases, Expression<? extends D> when) { this.cases = cases; this.when = when; } public Cases<T, Q> then(Expression<T> then) { caseElements.add(0, new CaseElement<D>(when, then)); return cases; } public Cases<T, Q> then(T then) { return then(ConstantImpl.create(then)); } } }