BooleanBuilder.java

package com.github.mygreen.sqlmapper.metamodel.support;

import com.github.mygreen.sqlmapper.metamodel.Predicate;
import com.github.mygreen.sqlmapper.metamodel.Visitor;
import com.github.mygreen.sqlmapper.metamodel.operation.BooleanOperation;
import com.github.mygreen.sqlmapper.metamodel.operation.PredicateOperation;
import com.github.mygreen.sqlmapper.metamodel.operator.BooleanOp;

import lombok.Getter;

/**
 * {@link Predicate} 式を組み立てるためのヘルパークラス。
 *
 *
 * <pre class="highlight"><code class="java">
 * QEmployee employee = QEmployee.employee;
 * BooleanBuilder builder = new BooleanBuilder();
 * for (String name : names) {
 *     builder.or(employee.name.eq(name));
 * }
 * </code></pre>
 *
 *
 * @author T.TSUCHIE
 *
 */
public class BooleanBuilder implements Predicate {

    /**
     * 現在の式
     */
    @Getter
    private Predicate predicate;

    /**
     * {@link BooleanBuilder} のインスタンスを作成します。
     */
    public BooleanBuilder() {

    }

    /**
     * 初期式を指定して、{@link BooleanBuilder} のインスタンスを作成します。
     * @param initial 諸域式
     */
    public BooleanBuilder(Predicate initial) {
        this.predicate = initial;
    }

    @Override
    public Class<? extends Boolean> getType() {
        return Boolean.class;
    }

    /**
     * {@inheritDoc}
     * <p>評価する式がない場合は何もしません。
     */
    @Override
    public <C> void accept(Visitor<C> visitor, C context) {
        if(predicate != null) {
            predicate.accept(visitor, context);
        }
    }

    /**
     * {@inheritDoc}
     *
     * @return 否定する式がない場合は {@literal null} を返す。
     */
    @Override
    public Predicate not() {

        if(predicate != null) {
            return predicate.not();
        }

        return null;

    }

    /**
     * 式を持つかどうか判定します。
     * @return 式を持つとき、{@literal true}を返します。
     */
    public boolean hasValue() {
        return predicate != null;
    }

    /**
     * 右辺を論理積( {@literal 左辺 AND 右辺})で評価します。
     * <p>左辺が存在しない場合は何もしません。
     *
     * @param right 右辺。nullの場合、何もしません。
     * @return 自身のインスタンス
     */
    public BooleanBuilder and(Predicate right) {

        if(right != null) {
            if(predicate == null) {
                this.predicate = right;
            } else {
                predicate = new PredicateOperation(BooleanOp.AND, predicate, right);
            }
        }

        return this;

    }

    /**
     * 右辺を論理和( {@literal 左辺 OR 右辺})で評価します。
     * <p>左辺が存在しない場合は何もしません。
     *
     * @param right 右辺。nullの場合、何もしません。
     * @return 自身のインスタンス
     */
    public BooleanBuilder or(Predicate right) {

        if(right != null) {
            if(predicate == null) {
                this.predicate = right;
            } else {
                predicate = new PredicateOperation(BooleanOp.OR, predicate, right);
            }
        }

        return this;

    }

    /**
     * 引数で指定した全ての和({@literal OR})に対して積({@literal AND})を取ります。
     * <p>例:{@literal 左辺 AND (A OR B OR C ...)}
     *
     * @param predicates 和(OR)を取る対象の式
     * @return {@literal 左辺 AND (右辺1 OR 右辺2 OR 右辺3 ...)}
     */
    public BooleanBuilder andAnyOf(Predicate... predicates) {

        if(predicates == null || predicates.length == 0) {
            // 右辺がnullの場合何もしない
            return this;

        }

        // 引数の述語をORで繋げる
        Predicate right = null;
        for(Predicate b : predicates) {
            if(b != null) {
                right = (right == null) ? b : new BooleanOperation(BooleanOp.OR, right, b);
            }
        }

        return and(right);

    }

    /**
     * 引数で指定した全ての積({@literal AND})に対して和({@literal OR})を取ります。
     * <p>例:{@literal 左辺 OR (A AND B AND C ...)}
     *
     * @param predicates 和(AND)を取る対象の式
     * @return {@literal 左辺 OR (右辺1 AND 右辺2 AND 右辺3 ...)}
     */
    public BooleanBuilder orAllOf(Predicate... predicates) {

        if(predicates == null || predicates.length == 0) {
            // 右辺がnullの場合何もしない
            return this;
        }

        // 引数の述語をANDで繋げる
        Predicate right = null;
        for(Predicate b : predicates) {
            if(b != null) {
                right = (right == null) ? b : new BooleanOperation(BooleanOp.AND, right, b);
            }
        }

        return or(right);

    }

}