001/*
002 *  Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.mybatisflex.core.query;
017
018
019import com.mybatisflex.core.constant.SqlConnector;
020import com.mybatisflex.core.constant.SqlConsts;
021import com.mybatisflex.core.constant.SqlOperator;
022import com.mybatisflex.core.dialect.IDialect;
023import com.mybatisflex.core.exception.FlexExceptions;
024import com.mybatisflex.core.util.ClassUtil;
025import com.mybatisflex.core.util.ObjectUtil;
026import com.mybatisflex.core.util.StringUtil;
027
028import java.lang.reflect.Array;
029import java.util.List;
030import java.util.function.BooleanSupplier;
031
032public class QueryCondition implements CloneSupport<QueryCondition> {
033
034
035    protected QueryColumn column;
036    protected String logic;
037    protected Object value;
038    protected boolean effective = true;
039
040    //当前条件的上一个条件
041    protected QueryCondition prev;
042
043    //当前条件的下一个条件
044    protected QueryCondition next;
045
046    //两个条件直接的连接符
047    protected SqlConnector connector;
048
049
050    public static QueryCondition createEmpty() {
051        return new QueryCondition().when(false);
052    }
053
054
055    public static QueryCondition create(String schema, String table, String column, String logic, Object value) {
056        QueryCondition condition = new QueryCondition();
057        condition.setColumn(new QueryColumn(schema, table, column));
058        condition.setLogic(logic);
059        condition.setValue(value);
060        return condition;
061    }
062
063    public static QueryCondition create(QueryColumn queryColumn, Object value) {
064        return create(queryColumn, SqlConsts.EQUALS, value);
065    }
066
067    public static QueryCondition create(QueryColumn queryColumn, String logic, Object value) {
068        QueryCondition condition = new QueryCondition();
069        condition.setColumn(queryColumn);
070        condition.setLogic(logic);
071        condition.setValue(value);
072        return condition;
073    }
074
075    public static QueryCondition create(QueryColumn queryColumn, SqlOperator logic, Object value) {
076        QueryCondition condition = new QueryCondition();
077        condition.setColumn(queryColumn);
078        condition.setLogic(logic.getValue());
079        condition.setValue(value);
080        return condition;
081    }
082
083    public QueryColumn getColumn() {
084        return column;
085    }
086
087    public void setColumn(QueryColumn column) {
088        this.column = column;
089    }
090
091    public Object getValue() {
092        return checkEffective() ? value : null;
093    }
094
095    public void setValue(Object value) {
096        this.value = value;
097    }
098
099    public String getLogic() {
100        return logic;
101    }
102
103    public void setLogic(String logic) {
104        this.logic = logic;
105    }
106
107    /**
108     * 动态条件构造。
109     *
110     * @param effective 是否启用该条件
111     * @return {@link QueryCondition}
112     */
113    public QueryCondition when(boolean effective) {
114        this.effective = effective;
115        return this;
116    }
117
118
119    /**
120     * 动态条件构造。
121     *
122     * @param fn 是否启用该条件
123     * @return {@link QueryCondition}
124     */
125    public QueryCondition when(BooleanSupplier fn) {
126        this.effective = fn.getAsBoolean();
127        return this;
128    }
129
130    public boolean checkEffective() {
131        return effective;
132    }
133
134
135    public QueryCondition and(String sql) {
136        return and(new RawFragment(sql));
137    }
138
139    public QueryCondition and(String sql, Object... params) {
140        return and(new RawFragment(sql, params));
141    }
142
143    public QueryCondition and(QueryCondition nextCondition) {
144        return new Brackets(this).and(nextCondition);
145    }
146
147    public QueryCondition or(String sql) {
148        return or(new RawFragment(sql));
149    }
150
151    public QueryCondition or(String sql, Object... params) {
152        return or(new RawFragment(sql, params));
153    }
154
155    public QueryCondition or(QueryCondition nextCondition) {
156        return new Brackets(this).or(nextCondition);
157    }
158
159    protected void connect(QueryCondition nextCondition, SqlConnector connector) {
160        if (this.next != null) {
161            this.next.connect(nextCondition, connector);
162        } else {
163            this.next = nextCondition;
164            this.connector = connector;
165            nextCondition.prev = this;
166        }
167    }
168
169    public String toSql(List<QueryTable> queryTables, IDialect dialect) {
170        StringBuilder sql = new StringBuilder();
171        //检测是否生效
172        if (checkEffective()) {
173            QueryCondition prevEffectiveCondition = getPrevEffectiveCondition();
174            if (prevEffectiveCondition != null) {
175                sql.append(prevEffectiveCondition.connector);
176            }
177            //列
178            sql.append(getColumn().toConditionSql(queryTables, dialect));
179
180            //逻辑符号
181            sql.append(logic);
182
183            //值(或者问号)
184            if (value instanceof QueryColumn) {
185                sql.append(((QueryColumn) value).toConditionSql(queryTables, dialect));
186            }
187            //子查询
188            else if (value instanceof QueryWrapper) {
189                sql.append(SqlConsts.BRACKET_LEFT)
190                    .append(dialect.buildSelectSql((QueryWrapper) value))
191                    .append(SqlConsts.BRACKET_RIGHT);
192            }
193            //原生sql
194            else if (value instanceof RawFragment) {
195                sql.append(((RawFragment) value).getContent());
196            }
197            //正常查询,构建问号
198            else {
199                appendQuestionMark(sql);
200            }
201        }
202
203        if (this.next != null) {
204            return sql + next.toSql(queryTables, dialect);
205        }
206
207        return sql.toString();
208    }
209
210
211    protected QueryCondition getPrevEffectiveCondition() {
212        if (prev == null) {
213            return null;
214        }
215        return prev.checkEffective() ? prev : prev.getPrevEffectiveCondition();
216    }
217
218    protected QueryCondition getNextEffectiveCondition() {
219        if (next == null) {
220            return null;
221        }
222        return next.checkEffective() ? next : next.getNextEffectiveCondition();
223    }
224
225
226    protected void appendQuestionMark(StringBuilder sqlBuilder) {
227        //noinspection StatementWithEmptyBody
228        if (SqlConsts.IS_NULL.equals(logic)
229            || SqlConsts.IS_NOT_NULL.equals(logic)
230            || value instanceof QueryColumn
231            || value instanceof QueryWrapper
232            || value instanceof RawFragment) {
233            //do nothing
234        }
235
236        //between, not between
237        else if (SqlConsts.BETWEEN.equals(logic) || SqlConsts.NOT_BETWEEN.equals(logic)) {
238            sqlBuilder.append(SqlConsts.AND_PLACEHOLDER);
239        }
240        //in, not in
241        else if (SqlConsts.IN.equals(logic) || SqlConsts.NOT_IN.equals(logic)) {
242            int paramsCount = calculateValueArrayCount();
243            sqlBuilder.append(SqlConsts.BRACKET_LEFT);
244            for (int i = 0; i < paramsCount; i++) {
245                sqlBuilder.append(SqlConsts.PLACEHOLDER);
246                if (i != paramsCount - 1) {
247                    sqlBuilder.append(SqlConsts.DELIMITER);
248                }
249            }
250            sqlBuilder.append(SqlConsts.BRACKET_RIGHT);
251        } else {
252            sqlBuilder.append(SqlConsts.PLACEHOLDER);
253        }
254    }
255
256
257    private int calculateValueArrayCount() {
258        Object[] values = (Object[]) value;
259        int paramsCount = 0;
260        for (Object object : values) {
261            if (object != null && ClassUtil.isArray(object.getClass())) {
262                paramsCount += Array.getLength(object);
263            } else {
264                paramsCount++;
265            }
266        }
267        return paramsCount;
268    }
269
270
271    boolean containsTable(String... tables) {
272        if (column == null || !checkEffective()) {
273            return nextContainsTable(tables);
274        }
275        for (String table : tables) {
276            String tableName = StringUtil.getTableNameWithAlisa(table)[0];
277            if (column.table != null && tableName.equals(column.table.name)) {
278                return true;
279            }
280        }
281        return nextContainsTable(tables);
282    }
283
284    boolean nextContainsTable(String... tables) {
285        if (next == null) {
286            return false;
287        }
288        return next.containsTable(tables);
289    }
290
291    @Override
292    public String toString() {
293        return "QueryCondition{" +
294            "column=" + column +
295            ", logic='" + logic + '\'' +
296            ", value=" + value +
297            ", effective=" + effective +
298            '}';
299    }
300
301    @Override
302    public QueryCondition clone() {
303        try {
304            QueryCondition clone = (QueryCondition) super.clone();
305            // deep clone ...
306            clone.column = ObjectUtil.clone(this.column);
307            clone.value = ObjectUtil.cloneObject(this.value);
308            clone.prev = clone.next = null;
309            if (this.next != null) {
310                clone.next = this.next.clone();
311                clone.next.prev = clone;
312            }
313            return clone;
314        } catch (CloneNotSupportedException e) {
315            throw FlexExceptions.wrap(e);
316        }
317    }
318
319}