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

shz.orm.WhereInfo Maven / Gradle / Ivy

The newest version!
package shz.orm;

import shz.core.*;
import shz.core.model.PageInfo;
import shz.core.model.Range;
import shz.core.structure.limiter.Limiter;
import shz.core.type.TypeHelp;
import shz.orm.annotation.Where;
import shz.orm.enums.Condition;
import shz.orm.enums.ValueStrategy;
import shz.orm.exception.OrmClassNoFieldException;
import shz.orm.exception.OrmClassNoFieldWhereException;
import shz.orm.param.OrmParam;
import shz.orm.sql.ValueType;
import shz.orm.sql.WhereSql;
import shz.orm.sql.builder.SqlBuilder;

import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.function.Supplier;

final class WhereInfo {
    private record WhereFieldInfo(Field field, String column, String fieldName, Condition condition,
                                  ValueStrategy strategy) {
    }

    private static final class WhereValueInfo {
        final String column;
        Object val;
        final Condition condition;
        final ValueStrategy strategy;

        WhereValueInfo(String column, Object val, Condition condition, ValueStrategy strategy) {
            this.column = column;
            this.val = val;
            this.condition = condition;
            this.strategy = strategy;
        }
    }

    private static Map whereFields(ClassInfo classInfo, Class cls) {
        Map map = ToMap.get().build();
        AccessibleHelp.fields(cls, f -> {
            Where where = f.getAnnotation(Where.class);
            if (where == null) return false;
            String fieldName = NullHelp.isBlank(where.value()) ? f.getName() : where.value();
            String columnName = classInfo.toColumnName(fieldName);
            if (columnName == null) throw new OrmClassNoFieldException(classInfo.cls, fieldName);
            map.put(columnName, new WhereFieldInfo(f, columnName, fieldName, where.condition(), where.strategy()));
            return false;
        });
        return map;
    }

    static WhereSql where(ClassInfo classInfo, String fieldName, Object fieldValue, Condition condition, Supplier builderSupplier, Boolean logic) {
        String column = classInfo.toColumnName(fieldName);
        if (column == null) throw new OrmClassNoFieldException(classInfo.cls, fieldName);
        SqlBuilder builder = builderSupplier.get().where().append("1=1");
        List values = new LinkedList<>();
        if (where(column, classInfo.getFieldByName(fieldName).getType(), fieldValue, condition, ValueStrategy.DEFAULT, builder, values))
            return WhereSql.EMPTY;
        return whereSql(classInfo, builder, values, logic, null);
    }

    private static boolean where(String column, Class fieldType, Object val, Condition condition, ValueStrategy strategy, SqlBuilder builder, List values) {
        condition = condition(condition, fieldType);
        switch (strategy) {
            case DEFAULT:
                if (condition == Condition.EQ) {
                    if (val == null) {
                        builder.and().wrap(column).isNull();
                        return false;
                    }
                } else if (condition == Condition.BETWEEN) {
                    Range range = Range.asRange(val);
                    if (range == null || range.isEmpty()) return true;
                    val = range;
                } else if (condition == Condition.IN || condition == Condition.NOT_IN) {
                    Set set = ToSet.asSet(val);
                    if (set.isEmpty()) return true;
                    val = set;
                }
                break;
            case NOT_NULL:
                if (val == null) return false;
                break;
            case NOT_EMPTY:
                if (NullHelp.isEmpty(val)) return false;
                break;
            case NOT_BLANK:
                if (NullHelp.isBlank(val)) return false;
                break;
            default:
                throw PRException.impossible();
        }
        switch (condition) {
            case IS_NULL -> builder.and().wrap(column).isNull();
            case IS_NOT_NULL -> builder.and().wrap(column).isNotNull();
            case EQ -> {
                builder.and().wrap(column).eq();
                values.add(val);
            }
            case NEQ -> {
                builder.and().wrap(column).neq();
                values.add(val);
            }
            case GT -> {
                builder.and().wrap(column).gt();
                values.add(val);
            }
            case GE -> {
                builder.and().wrap(column).ge();
                values.add(val);
            }
            case LT -> {
                builder.and().wrap(column).lt();
                values.add(val);
            }
            case LE -> {
                builder.and().wrap(column).le();
                values.add(val);
            }
            case BETWEEN -> {
                Range range = Range.asRange(val);
                if (range != null && !range.isEmpty()) {
                    builder.and().wrap(column).between(range, ValueType.PLACEHOLDER);
                    if (NullHelp.nonBlank(range.getMin())) values.add(range.getMin());
                    if (NullHelp.nonBlank(range.getMax())) values.add(range.getMax());
                }
            }
            case IN -> {
                Set inSet = ToSet.asSet(val);
                if (!inSet.isEmpty()) {
                    builder.and().wrap(column).in(inSet.size());
                    values.addAll(inSet);
                }
            }
            case NOT_IN -> {
                Set notInSet = ToSet.asSet(val);
                if (!notInSet.isEmpty()) {
                    builder.and().wrap(column).notIn(notInSet.size());
                    values.addAll(notInSet);
                }
            }
            case LIKE -> {
                builder.and().wrap(column).like();
                values.add(val);
            }
            case LIKE_LEFT -> {
                builder.and().wrap(column).likeLeft();
                values.add(val);
            }
            case LIKE_RIGHT -> {
                builder.and().wrap(column).likeRight();
                values.add(val);
            }
            case REGEXP -> {
                builder.and().wrap(column).regexp();
                values.add(val);
            }
            default -> throw PRException.impossible();
        }
        return false;
    }

    private static Condition condition(Condition condition, Class fieldType) {
        if (condition != Condition.DEFAULT) return condition;
        if (Collection.class.isAssignableFrom(fieldType) || fieldType.isArray()) return Condition.IN;
        if (Range.class.isAssignableFrom(fieldType)) return Condition.BETWEEN;
        return Condition.EQ;
    }

    private static WhereSql whereSql(ClassInfo classInfo, SqlBuilder builder, List values, Boolean logic, Consumer orderBys) {
        if (classInfo.logicField != null && (logic == null || logic)) {
            builder.and().wrap(classInfo.logicName).eq();
            values.add(classInfo.logicVal);
        }
        if (orderBys != null) orderBys.accept(builder);
        return WhereSql.of(builder.build(), values);
    }

    static WhereSql where(ClassInfo classInfo, List fieldNames, List fieldValues, List conditions, Supplier builderSupplier, Boolean logic) {
        int size = NullHelp.size(fieldNames);
        NullHelp.requireNon(size == 0 || NullHelp.size(fieldValues) != size || NullHelp.size(conditions) != size);
        return where(classInfo, fieldNames, fieldValues, conditions::get, builderSupplier, logic);
    }

    private static WhereSql where(ClassInfo classInfo, List fieldNames, List fieldValues, IntFunction conditionFunc, Supplier builderSupplier, Boolean logic) {
        int size = NullHelp.size(fieldNames);
        SqlBuilder builder = builderSupplier.get().where().append("1=1");
        List values = new LinkedList<>();
        for (int i = 0; i < size; ++i) {
            String column = classInfo.toColumnName(fieldNames.get(i));
            if (column == null) throw new OrmClassNoFieldException(classInfo.cls, fieldNames.get(i));
            if (where(column, classInfo.getFieldByName(fieldNames.get(i)).getType(), fieldValues.get(i), conditionFunc.apply(i), ValueStrategy.DEFAULT, builder, values))
                return WhereSql.EMPTY;
        }
        return whereSql(classInfo, builder, values, logic, null);
    }

    static WhereSql where(ClassInfo classInfo, List fieldNames, List fieldValues, Condition condition, Supplier builderSupplier, Boolean logic) {
        int size = NullHelp.size(fieldNames);
        NullHelp.requireNon(size == 0 || NullHelp.size(fieldValues) != size);
        return where(classInfo, fieldNames, fieldValues, i -> condition, builderSupplier, logic);
    }

    static WhereSql where(ClassInfo classInfo, Object obj, List conditions, Supplier builderSupplier, Boolean logic, boolean orderBy, String... fieldNames) {
        Objects.requireNonNull(obj);
        int len = NullHelp.length(fieldNames);
        NullHelp.requireNon(len == 0 || NullHelp.size(conditions) != len);
        return where(classInfo, obj, conditions::get, builderSupplier, logic, orderBy, fieldNames);
    }

    private static WhereSql where(ClassInfo classInfo, Object obj, IntFunction conditionFunc, Supplier builderSupplier, Boolean logic, boolean orderBy, String... fieldNames) {
        int len = NullHelp.length(fieldNames);
        if (obj instanceof Map map) {
            List infoList = new ArrayList<>(len);
            for (int i = 0; i < len; ++i) {
                String columnName = classInfo.toColumnName(fieldNames[i]);
                if (columnName == null) throw new OrmClassNoFieldException(classInfo.cls, fieldNames[i]);
                infoList.add(new WhereValueInfo(columnName, map.get(fieldNames[i]), conditionFunc.apply(i), ValueStrategy.NOT_BLANK));
            }
            return where(classInfo, infoList, builderSupplier, logic, orderBy ? builder -> OrderByInfo.orderBy(classInfo, obj, builder) : null);
        }
        Class cls;
        NullHelp.requireNon(!TypeHelp.likeModel(cls = obj.getClass()));
        Map whereFields = whereFields(classInfo, cls);
        NullHelp.requireNonEmpty(whereFields);
        List infoList = new ArrayList<>(len);
        for (int i = 0; i < len; ++i) {
            String columnName = classInfo.toColumnName(fieldNames[i]);
            if (columnName == null) throw new OrmClassNoFieldException(classInfo.cls, fieldNames[i]);
            WhereFieldInfo info = whereFields.get(columnName);
            if (info == null) throw new OrmClassNoFieldWhereException(cls, fieldNames[i]);
            infoList.add(new WhereValueInfo(columnName, AccessibleHelp.getField(info.field, obj), conditionFunc.apply(i), info.strategy));
        }
        return where(classInfo, infoList, builderSupplier, logic, orderBy ? builder -> OrderByInfo.orderBy(classInfo, obj, builder) : null);
    }

    private static WhereSql where(ClassInfo classInfo, List infoList, Supplier builderSupplier, Boolean logic, Consumer orderBys) {
        SqlBuilder builder = builderSupplier.get().where().append("1=1");
        List values = new LinkedList<>();
        for (WhereValueInfo info : infoList)
            if (where(info.column, classInfo.getFieldByColumn(info.column).getType(), info.val, info.condition, info.strategy, builder, values))
                return WhereSql.EMPTY;
        return whereSql(classInfo, builder, values, logic, orderBys);
    }

    static WhereSql where(ClassInfo classInfo, Object obj, Condition condition, Supplier builderSupplier, Boolean logic, boolean orderBy, String... fieldNames) {
        Objects.requireNonNull(obj);
        NullHelp.requireNonEmpty(fieldNames);
        return where(classInfo, obj, i -> condition, builderSupplier, logic, orderBy, fieldNames);
    }

    static WhereSql where(ClassInfo classInfo, Object obj, Supplier builderSupplier, Boolean logic, boolean orderBy) {
        if (obj == null) return null;
        if (obj instanceof Map map) {
            List infoList = new ArrayList<>(map.size());
            map.forEach((k, v) -> {
                String key = ToString.normal(k);
                String columnName = classInfo.toColumnName(key);
                if (columnName == null) throw new OrmClassNoFieldException(classInfo.cls, key);
                infoList.add(new WhereValueInfo(columnName, map.get(k), Condition.DEFAULT, ValueStrategy.NOT_BLANK));
            });
            return where(classInfo, infoList, builderSupplier, logic, orderBy ? builder -> OrderByInfo.orderBy(classInfo, obj, builder) : null);
        }
        Class cls;
        NullHelp.requireNon(!TypeHelp.likeModel(cls = obj.getClass()));
        Map whereFields = whereFields(classInfo, cls);
        if (whereFields.isEmpty()) return WhereSql.EMPTY;
        List infoList = new ArrayList<>(whereFields.size());
        whereFields.forEach((k, v) -> infoList.add(new WhereValueInfo(k, AccessibleHelp.getField(v.field, obj), v.condition, v.strategy)));
        return where(classInfo, infoList, builderSupplier, logic, orderBy ? builder -> OrderByInfo.orderBy(classInfo, obj, builder) : null);
    }

    static WhereSql where(ClassInfo classInfo, Parameter[] parameters, Object[] args, Supplier builderSupplier) {
        if (parameters.length == 0) return null;
        List infoList = new LinkedList<>();
        for (int i = 0; i < parameters.length; ++i) {
            Class pt = parameters[i].getType();
            if (args[i] != null && Map.class.isAssignableFrom(pt)) {
                ((Map) args[i]).forEach((k, v) -> {
                    String columnName = classInfo.toColumnName(ToString.normal(k));
                    if (columnName == null) throw new OrmClassNoFieldException(classInfo.cls, ToString.normal(k));
                    infoList.add(new WhereValueInfo(columnName, v, Condition.DEFAULT, ValueStrategy.NOT_BLANK));
                });
                continue;
            }
            if (Collection.class.isAssignableFrom(pt) || Range.class.isAssignableFrom(pt) || pt.isArray() || TypeHelp.likeCommon(pt)) {
                Where where = parameters[i].getAnnotation(Where.class);
                if (where != null) {
                    String columnName = classInfo.toColumnName(where.value());
                    if (columnName == null) throw new OrmClassNoFieldException(classInfo.cls, where.value());
                    infoList.add(new WhereValueInfo(columnName, args[i], where.condition(), where.strategy()));
                }
                continue;
            }
            if (PageInfo.class.isAssignableFrom(pt) || Limiter.class.isAssignableFrom(pt) || OrmParam.class.isAssignableFrom(pt))
                continue;
            Map whereFields = whereFields(classInfo, pt);
            for (Map.Entry entry : whereFields.entrySet()) {
                WhereFieldInfo v = entry.getValue();
                infoList.add(new WhereValueInfo(entry.getKey(), AccessibleHelp.getField(v.field, args[i]), v.condition, v.strategy));
            }
        }
        return where(classInfo, infoList, builderSupplier, null, builder -> OrderByInfo.orderBy(classInfo, parameters, args, builder));
    }

    static WhereSql uniqueWhere(ClassInfo classInfo, Object entity, Supplier builderSupplier, String... fieldNames) {
        int len = NullHelp.length(fieldNames);
        List infoList = new ArrayList<>(len);
        for (int i = 0; i < len; ++i) {
            String columnName = classInfo.toColumnName(fieldNames[i]);
            if (columnName == null) throw new OrmClassNoFieldException(classInfo.cls, fieldNames[i]);
            Object val = classInfo.getByName(fieldNames[i], entity);
            if (NullHelp.nonBlank(val))
                infoList.add(new WhereValueInfo(columnName, val, Condition.EQ, ValueStrategy.NOT_BLANK));
        }
        if (infoList.isEmpty()) return WhereSql.EMPTY;
        return where(classInfo, infoList, builderSupplier, null, null);
    }
}