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

cn.handyplus.lib.core.FormulaUtil Maven / Gradle / Ivy

The newest version!
package cn.handyplus.lib.core;

import cn.handyplus.lib.constants.BaseConstants;
import cn.handyplus.lib.exception.HandyException;

import java.math.BigDecimal;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;

/**
 * 公式工具类
 *
 * @author handy
 * @since 3.7.0
 */
public class FormulaUtil {

    private FormulaUtil() {
    }

    /**
     * 进行计算
     *
     * @param formula 公式
     * @param map     替换数据
     * @return 计算结果
     */
    public static Long evaluateFormulaToLong(String formula, Map map) {
        return NumberUtil.isNumericToLong(evaluateFormula(formula, map), 0L);
    }

    /**
     * 进行计算
     *
     * @param formula 公式
     * @param map     替换数据
     * @return 计算结果
     */
    public static Double evaluateFormulaToDouble(String formula, Map map) {
        return NumberUtil.isNumericToDouble(evaluateFormula(formula, map), 0.0);
    }

    /**
     * 进行计算
     *
     * @param formula 公式
     * @param map     替换数据
     * @return 计算结果
     */
    public static Integer evaluateFormulaToInt(String formula, Map map) {
        return evaluateFormulaToDouble(formula, map).intValue();
    }

    /**
     * 进行计算
     *
     * @param formula 公式
     * @param map     替换数据
     * @return 计算结果
     */
    public static String evaluateFormula(String formula, Map map) {
        Matcher matcher = BaseConstants.TEMPLATE_EXPRESSION_REGEX.matcher(formula);
        String variable;
        while (matcher.find()) {
            variable = matcher.group(1);
            formula = StrUtil.replace(formula, variable, BaseConstants.TEMPLATE_EXPRESSION_VALUE);
            // 替换变量数据
            for (String key : map.keySet()) {
                variable = variable.replace(key, map.get(key));
            }
            // 计算公式结果
            String result = calculateExpression(variable);
            formula = formula.replace(BaseConstants.TEMPLATE_EXPRESSION_VALUE, result);
        }
        return formula;
    }

    /**
     * 使用Script 进行计算
     *
     * @param expression 表达式
     * @return 计算结果
     */
    private static String calculateExpression(String expression) {
        String rpn = toRPN(expression);
        return String.valueOf(calculateRPN(rpn));
    }

    /**
     * 将中缀表达式转换为逆波兰表达式
     *
     * @param expression 表达式
     * @return 逆波兰表达式
     */
    private static String toRPN(String expression) {
        StringBuilder rpn = new StringBuilder();
        Stack stack = new Stack<>();
        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);
            // 如果是空格,则忽略
            if (c == ' ') {
                continue;
            }
            // 如果是数字,则直接输出
            if (Character.isDigit(c)) {
                while (i < expression.length() && (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.')) {
                    rpn.append(expression.charAt(i));
                    i++;
                }
                rpn.append(' ');
                i--;
            }
            // 如果是左括号,则入栈
            else if (c == '(') {
                stack.push(c);
            }
            // 如果是右括号,则将栈顶的运算符弹出并输出,直到遇到左括号
            else if (c == ')') {
                while (!stack.isEmpty() && stack.peek() != '(') {
                    rpn.append(stack.pop()).append(' ');
                }
                // 弹出左括号
                stack.pop();
            }
            // 如果是运算符,则判断其与栈顶运算符的优先级
            else if (isOperator(c)) {
                while (!stack.isEmpty() && getPriority(stack.peek()) >= getPriority(c)) {
                    rpn.append(stack.pop()).append(' ');
                }
                stack.push(c);
            }
        }

        // 将栈中剩余的运算符弹出并输出
        while (!stack.isEmpty()) {
            rpn.append(stack.pop()).append(' ');
        }

        return rpn.toString();
    }

    /**
     * 判断是否是运算符
     *
     * @param c 字符
     * @return true
     */
    private static boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/';
    }

    /**
     * 获取运算符的优先级
     *
     * @param operator 运算符
     * @return 级别
     */
    private static int getPriority(char operator) {
        switch (operator) {
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
                return 2;
            default:
                return 0;
        }
    }

    /**
     * 计算逆波兰表达式的结果
     *
     * @param rpn 逆波兰表达式
     * @return 结果
     */
    private static double calculateRPN(String rpn) {
        Stack stack = new Stack<>();
        String[] tokens = rpn.split(" ");

        for (String token : tokens) {
            if (isNumber(token)) {
                stack.push(Double.parseDouble(token));
            } else if (isOperator(token.charAt(0))) {
                double operand2 = stack.pop();
                double operand1 = stack.pop();
                double result = performOperation(operand1, operand2, token.charAt(0));
                stack.push(result);
            }
        }

        return stack.pop();
    }

    /**
     * 判断是否是数字
     *
     * @param token 字符
     * @return true
     */
    private static boolean isNumber(String token) {
        try {
            Double.parseDouble(token);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    /**
     * 执行运算操作
     *
     * @param operand1 操作数1
     * @param operand2 操作数2
     * @param operator 操作数3
     * @return 结果
     */
    private static double performOperation(double operand1, double operand2, char operator) {
        BigDecimal bigDecimalOperand1 = BigDecimal.valueOf(operand1);
        BigDecimal bigDecimalOperand2 = BigDecimal.valueOf(operand2);
        switch (operator) {
            case '+':
                return bigDecimalOperand1.add(bigDecimalOperand2).doubleValue();
            case '-':
                return bigDecimalOperand1.subtract(bigDecimalOperand2).doubleValue();
            case '*':
                return bigDecimalOperand1.multiply(bigDecimalOperand2).doubleValue();
            case '/':
                if (operand2 == 0) {
                    throw new HandyException("除数不能为0");
                }
                return bigDecimalOperand1.divide(bigDecimalOperand2, 2, BigDecimal.ROUND_HALF_UP).doubleValue();
            default:
                throw new HandyException("不支持的运算符: " + operator);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy