001/*
002 *  Copyright (c) 2022-2025, 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
018import com.mybatisflex.core.constant.SqlConsts;
019import com.mybatisflex.core.dialect.IDialect;
020import com.mybatisflex.core.exception.FlexExceptions;
021import com.mybatisflex.core.util.CollectionUtil;
022import com.mybatisflex.core.util.StringUtil;
023
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.List;
027
028import static com.mybatisflex.core.constant.SqlConsts.DIVISION_SIGN;
029import static com.mybatisflex.core.constant.SqlConsts.MINUS_SIGN;
030import static com.mybatisflex.core.constant.SqlConsts.MULTIPLICATION_SIGN;
031import static com.mybatisflex.core.constant.SqlConsts.PLUS_SIGN;
032
033public class ArithmeticQueryColumn extends QueryColumn implements HasParamsColumn {
034
035    private List<ArithmeticInfo> arithmeticInfos;
036
037    public ArithmeticQueryColumn(Object value) {
038        arithmeticInfos = new ArrayList<>();
039        arithmeticInfos.add(new ArithmeticInfo(value));
040    }
041
042    @Override
043    public QueryColumn add(QueryColumn queryColumn) {
044        arithmeticInfos.add(new ArithmeticInfo(PLUS_SIGN, queryColumn));
045        return this;
046    }
047
048    @Override
049    public QueryColumn add(Number number) {
050        arithmeticInfos.add(new ArithmeticInfo(PLUS_SIGN, number));
051        return this;
052    }
053
054    @Override
055    public QueryColumn subtract(QueryColumn queryColumn) {
056        arithmeticInfos.add(new ArithmeticInfo(MINUS_SIGN, queryColumn));
057        return this;
058    }
059
060    @Override
061    public QueryColumn subtract(Number number) {
062        arithmeticInfos.add(new ArithmeticInfo(MINUS_SIGN, number));
063        return this;
064    }
065
066    @Override
067    public QueryColumn multiply(QueryColumn queryColumn) {
068        arithmeticInfos.add(new ArithmeticInfo(MULTIPLICATION_SIGN, queryColumn));
069        return this;
070    }
071
072    @Override
073    public QueryColumn multiply(Number number) {
074        arithmeticInfos.add(new ArithmeticInfo(MULTIPLICATION_SIGN, number));
075        return this;
076    }
077
078    @Override
079    public QueryColumn divide(QueryColumn queryColumn) {
080        arithmeticInfos.add(new ArithmeticInfo(DIVISION_SIGN, queryColumn));
081        return this;
082    }
083
084    @Override
085    public QueryColumn divide(Number number) {
086        arithmeticInfos.add(new ArithmeticInfo(DIVISION_SIGN, number));
087        return this;
088    }
089
090
091    @Override
092    String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
093        StringBuilder sql = new StringBuilder();
094        for (int i = 0; i < arithmeticInfos.size(); i++) {
095            sql.append(arithmeticInfos.get(i).toSql(queryTables, dialect, i));
096        }
097        if (StringUtil.isNotBlank(alias)) {
098            return WrapperUtil.withAlias(sql.toString(), alias, dialect);
099        }
100        return sql.toString();
101    }
102
103    @Override
104    public ArithmeticQueryColumn clone() {
105        ArithmeticQueryColumn clone = (ArithmeticQueryColumn) super.clone();
106        // deep clone ...
107        clone.arithmeticInfos = CollectionUtil.cloneArrayList(this.arithmeticInfos);
108        return clone;
109    }
110
111
112    @Override
113    String toConditionSql(List<QueryTable> queryTables, IDialect dialect) {
114        StringBuilder sql = new StringBuilder();
115        for (int i = 0; i < arithmeticInfos.size(); i++) {
116            sql.append(arithmeticInfos.get(i).toSql(queryTables, dialect, i));
117        }
118        return SqlConsts.BRACKET_LEFT + sql + SqlConsts.BRACKET_RIGHT;
119    }
120
121    @Override
122    public Object[] getParamValues() {
123        return arithmeticInfos.stream()
124            .map(arithmeticInfo -> arithmeticInfo.value)
125            .filter(HasParamsColumn.class::isInstance)
126            .map(value -> ((HasParamsColumn) value).getParamValues())
127            .flatMap(Arrays::stream)
128            .toArray();
129    }
130
131
132    static class ArithmeticInfo implements CloneSupport<ArithmeticInfo> {
133
134        private final String symbol;
135        private final Object value;
136
137        public ArithmeticInfo(Object value) {
138            this(null, value);
139        }
140
141        public ArithmeticInfo(String symbol, Object value) {
142            this.symbol = symbol;
143            this.value = value;
144        }
145
146        private String toSql(List<QueryTable> queryTables, IDialect dialect, int index) {
147            String valueSql;
148            if (value instanceof QueryColumn) {
149                valueSql = ((QueryColumn) value).toConditionSql(queryTables, dialect);
150            } else {
151                valueSql = String.valueOf(value);
152            }
153            return index == 0 ? valueSql : symbol + valueSql;
154        }
155
156        @Override
157        public ArithmeticInfo clone() {
158            try {
159                return (ArithmeticInfo) super.clone();
160            } catch (CloneNotSupportedException e) {
161                throw FlexExceptions.wrap(e);
162            }
163        }
164
165    }
166
167}