最近接到一个比较有趣的功能,在计算工资,社保以及其他需要计算的模块时,老大和我说这些计算项的公式每个月都会变很麻烦,问我能不能做成用户自己定义公式的那种,说自己忙没时间做就安排我做了,美名曰提升我的能力,老大既然这么说了我当然不能让他失望,O(∩_∩)O哈哈~。
当接到这个需求后我第一个就是想到了大学时学数据结构时候的课堂设计,那时候正式学习栈的时候,老师要求我们用栈写一个计算器,正好和这个需求类似,其中运用到了中缀表达式转后缀表达式。
中缀转后缀表达式参考:https://blog.csdn.net/weixin_44260779/article/details/90695746
搞懂中缀转后缀表达式后看看实现:
Parser
@Data
@NoArgsConstructor
public abstract class Parser {
protected Node node;
protected Map<String,Integer> countMap = new ConcurrentHashMap<>(0);
protected final GenericTokenParser functionParser = new GenericTokenParser("$", "$", new FunctionHandler());
protected Parser(Node node){
this.node = node;
}
protected void doParse(String expirence){
expirence = functionParser.parse(expirence);
expirence = replaceFunction(expirence);
resolution(expirence);
}
/**
* 中缀转后缀表达式
*
* @param expirence
*/
protected void resolution(String expirence) {
//定义临时存储运算符的栈
Stack<String> stack = new Stack();
StringBuilder tempStringBuilder = new StringBuilder();
for (int i = 0; i < expirence.length(); i++) {
tempStringBuilder = tempStringBuilder.append(expirence.substring(i, i + 1));
if (tempStringBuilder.toString().isEmpty() ||
( i + 2 <= expirence.length() && (!isTrue(expirence.substring(i + 1, i + 2))
&& !isTrue(tempStringBuilder.toString()) || isTrue(expirence.substring(i, i + 2)))) ) {
continue;
}
String temp = tempStringBuilder.toString();
//清空Stringbuilder
tempStringBuilder = new StringBuilder();
if ("(".equals(temp)) {
stack.push(temp);
//如果“)”直接入栈,运算符出栈进入队列,直到遇到“(”,并去除“(”
} else if (")".equals(temp)) {
while (true) {
if ("(".equals(stack.peek())) {
stack.pop();
break;
}
//","只用来判断,不加入到后缀表达式中
String operatorString = stack.pop();
if (!",".equals(operatorString)) {
node.getQueue().add(operatorString);
}
}
} else if (isTrue(temp)) {
//如果是普通运算符,将运算优先级大于等于他的从栈中取出加入到队列中,最后将当前运算符入栈
while (true) {
if (!stack.isEmpty() && getPriority(temp) <= getPriority(stack.peek())) {
//","只用来判断,不加入到后缀表达式中
String operatorString = stack.pop();
if (!",".equals(operatorString)) {
node.getQueue().add(operatorString);
}
} else {
break;
}
}
//兼容-1
if (node.getQueue().isEmpty() && "-".equals(temp)) {
node.getQueue().add("0");
}
if (!",".equals(temp)) {
stack.push(temp);
}
} else {
//如果是数字或者字段直接进入队列
node.getQueue().add(temp);
}
}
//将剩余的运算符加入到队列中
if (!stack.isEmpty()) {
while (!stack.isEmpty()) {
node.getQueue().add(stack.pop());
}
}
}
//运算符优先级
protected int getPriority(String temp) {
if ("+".equals(temp) || "-".equals(temp)) {
return 1;
} else if ("*".equals(temp) || "/".equals(temp)) {
return 2;
} else if (",".equals(temp) ) {
return 0;
} else if(">".equals(temp) || "<".equals(temp) || ">=".equals(temp) || "<=".equals(temp) ||
"==".equals(temp) || "!=".equals(temp) || "&&".equals(temp) || "||".equals(temp) ){
return -1;
}else {
return -2;
}
}
public Node createNode(String expirence,String temp){
List list = new ArrayList();
List param = new ArrayList();
list.add(String.class);
param.add(expirence);
Node child = (Node)new DefaultObjectFactory().
create(FunctionEumn.valueOf(temp.toUpperCase()).getClazz(),list,param);
return child;
}
/**
* 解析
* @param temp
* @return
*/
public abstract void parse(String temp);
/**
* 判断是否是特殊符号
* @param temp
* @return
*/
public abstract boolean isTrue(String temp);
/**
* 替换函数
* @param expirence
* @return
*/
protected abstract String replaceFunction(String expirence);
/**
* 判断是否是函数
*
* @param temp
*/
protected abstract boolean IsOperate(String temp) ;
class FunctionHandler implements TokenHandler {
final Map<String,String> map = new HashMap();
{
FunctionEumn.toList().forEach(
functionEumn->{
map.put(functionEumn.getCode(),((Class)functionEumn.getContent()).getSimpleName());
}
);
}
@Override
public String handleToken(String content) {
return map.get(content);
}
}
}
ExpirenceParser
@Data
public class ExpirenceParser extends Parser{
public ExpirenceParser(Node node) {
super(node);
}
@Override
protected String replaceFunction(String expirence){
//定义临时存储运算符的栈
Stack stack = new Stack();
StringBuilder result = new StringBuilder();
StringBuilder childExpirence = new StringBuilder();
StringBuilder tempStringBuilder = new StringBuilder();
for (int i = 0; i < expirence.length(); i++){
tempStringBuilder = tempStringBuilder.append(expirence.substring(i, i + 1));
if ( i + 2 <= expirence.length() && (!isTrue(expirence.substring(i + 1, i + 2))
&& !isTrue(tempStringBuilder.toString()) &&
!isTrue(expirence.substring(i, i + 2)))) {
continue;
}
String temp = tempStringBuilder.toString();
//清空Stringbuilder
tempStringBuilder = new StringBuilder();
if (IsOperate(temp)) {
if(!countMap.containsKey(temp)){
countMap.put(temp,0);
}
countMap.put(temp,countMap.get(temp)+1);
String nodeName = temp + "_" + countMap.get(temp);
result.append(nodeName);
stack.push(expirence.substring(++i, i + 1));
while (!stack.empty() && ++i < expirence.length()){
String substring = expirence.substring(i, i + 1);
if(StringUtils.isEmpty(substring) || substring.trim().isEmpty()){
continue;
}
if ("(".equals(substring)) {
stack.push(substring);
}else if (")".equals(substring)) {
stack.pop();
if(stack.isEmpty()){
break;
}
}
childExpirence.append(substring);
}
//创建子节点
Node child = createNode(childExpirence.toString(),temp);
node.getChildNode().put(nodeName,child);
childExpirence.setLength(0);
}else{
result.append(temp);
}
}
return result.toString();
}
protected boolean IsOperate(String temp) {
return FunctionEumn.isInclude(temp.toUpperCase());
}
@Override
public boolean isTrue(String temp) {
if ("+".equals(temp) || "-".equals(temp) || "*".equals(temp) || "/".equals(temp) || ",".equals(temp) || "!".equals(temp) || "=".equals(temp) ||
"|".equals(temp) || "&".equals(temp) || ">".equals(temp) || "<".equals(temp) || "&&".equals(temp) || "!=".equals(temp)
|| ">=".equals(temp) || "<=".equals(temp) || "==".equals(temp) || "||".equals(temp) || "(".equals(temp) || ")".equals(temp)) {
return true;
}
return false;
}
@Override
public void parse(String expirence) {
if(StringUtils.isEmpty(expirence)){
return;
}
doParse(expirence);
}
}
Parser是提供中缀转后缀表达式方法的基类,ExpirenceParser是用来解析普通表达式的(其中使用到的Node和ExpirenceNode代码贴在了下面)。我们dubug一下
可以看到正确转换成了后缀表达式,并且拿到正确结果。其中用到了MetaObject,这是mybatis下的一个反射工具类。
这个工具类提供了一些获取设置值等一系列方法,用这个我们就可以通过属性名去拿到对象的值进行计算。
计算的时候如果发现传过来的MetaObject不为空并且存在get方法的话就从MetaObject取值进行计算。
此时可以看到数字成为了a,b,因为result()方法指定了属性来源对象,所以a b会被解析为数字testObj1里面的3 和 4。公式就变成了 3 + 4 + 3 *(4+5) = 34
如果是程序中使用基本上面这些就能够完成了,但是这个需求是需要提供给客户使用的,有些用户可能习惯看到中文,所以上面这些还不够做成页面展示给用户使用。于是我便定义了一系列中文方法供用户使用。这里讲下思路:
表达式:1+$加法$(1,2,$减法$(6,7)+3)
1、例如这段表达式的$减法$(6,7)设计是后面生成一个减法的函数Node ===> 暂定SubNode(6,7); 函数node中用 , 分隔;我们在解析函数Node时除了,其他全部入栈,因为时减法Node所以在result方法中直接取出栈中数字进行减法计算就好了。
2、$加法$(1,2,$减法$(6,7)+3)这个加法Node就可以看成下面这样的表达式:
$加法$(1,2,SUBNODE_1+3)
这个SUBNODE_1其实就是上面创建的减法Node,因为减法Node的result方法可以得到一个结果,所以我们可以将SUBNODE_1看作是一个数字,并且记录到当前加法Node的childNode中,childNode是一个Map对象,用来记录子节点的,上面的减法node会以<SUBNODE_1,new SubNode()>的形式存入进去,而这个SubNode的每一项又可以看成是一个ExpirenceNode。我们在看看上面Node基类取值的方法getValue有段代码
else if(childNode.containsKey(template)){
return childNode.get(template).result(metaObject);
}
就是用来取子节点的计算结果的。
3、按照2的思路1+$加法$(1,2,$减法$(6,7)+3)就可以看成
1+AddNode_1
这个AddNode_1会以<AddNode_1,new AddNode()>的形式存在当前ExpirenceNode的childNode当中。
最终的设计是:
表达式:1+$加法$(1,2,$减法$(6,7)+3),首先使用GenericTokenParser(这个也是mybatis的一个工具类,替换字符串的)替换所有中文。得到如下表达式:
1+AddNode(1,2,SubNode(6,7)+3)
然后通过 2 3 转换 最终得到一个表达式为 1+AddNode_1 的ExpirenceNode。下面可以通过debug看下。
下面把测试类和剩下代码全部贴出来,有兴趣的小伙伴可以自行按最后图的结构copy直接就可以使用了。
测试类
public class TextAnalysis {
public static void main(String[] args) {
TestObj testObj = new TestObj(3D,4D,null);
TestObj testObj1 = new TestObj(3D,4D,testObj);
MetaObject metaObject = SystemMetaObject.forObject(testObj1);
ExpirenceNode mixNode = new ExpirenceNode("a+2+(3+$加法$(4,5,$加法$(b,((7+1)+1)*2)))");
System.out.println(mixNode.result(metaObject));
ExpirenceNode expirenceNode5 = new ExpirenceNode("1+$加法$(1,2,$减法$(6,7)+3)");
System.out.println(expirenceNode5.result(null));
ExpirenceNode mixNode1 = new ExpirenceNode("$减法$(6,7*0)");
System.out.println(mixNode1.result(null));
ExpirenceNode mixNode3 = new ExpirenceNode("$除法$(6,7*1)");
System.out.println(mixNode3.result(null));
ExpirenceNode mixNode2 = new ExpirenceNode("$如果$((5 < 2) && (5 > 2) || true ,$如果$($减法$(6,7*0)+13 *0 > 1+3,$加法$(4,5,$加法$(6,7)),5),4)");
System.out.println(mixNode2.result(null));
ExpirenceNode mixNode6 = new ExpirenceNode("$加法$(6,7)");
System.out.println(mixNode6.result(metaObject));
ExpirenceNode mixNode7 = new ExpirenceNode("$如果$(($加法$(a,b) - 5 >= $如果$(testObj.a-1 > 0,1,2)) && (true || false),$加法$(a,1),$加法$(a,b))");
System.out.println(mixNode7.result(metaObject));
ExpirenceNode mixNode13 = new ExpirenceNode("false && (true||false)");
System.out.println(mixNode13.result(null));
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class TestObj{
private Double a;
private Double b;
private TestObj testObj;
}
测试结果
引入依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
引入mybatis是因为使用到了mybati的工具类,GenericTokenParser 替换字符串的,mybatis的 #{}就是使用的这个进行替换的,DefaultObjectFactory调用构造方法创建对象,MetaObject一个反射的工具类对象,有对象的所有属性,并且提供一系列方法,如对象设置值,获取值,判断对象时候含有get set方法等等,就是一个提供方便使用反射的。
枚举
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EnumParam {
private Object content;
private String code;
}
@NoArgsConstructor
@AllArgsConstructor
public enum FunctionEumn{
EXPIRENCENODE("解析", ExpirenceNode.class),
ADDNODE("加法", AddNode.class),
SUBNODE("减法", SubNode.class),
MULNODE("乘法", MulNode.class),
DIVNODE("除法", DivNode.class),
MOLDNODE("取模", MoldNode.class),
IFNODE("如果", IfNode.class);
private String funName;
private Class clazz;
public String getFunName() {
return funName;
}
public Class getClazz() {
return clazz;
}
public static List<EnumParam> toList() {
List<EnumParam> list = new ArrayList<>();
for (FunctionEumn attachEnum : FunctionEumn.values()) {
EnumParam enumParam = new EnumParam(attachEnum.getClazz(),attachEnum.getFunName());
list.add(enumParam);
}
return list;
}
public static boolean isInclude(String key){
boolean include = false;
for (FunctionEumn e: FunctionEumn.values()){
if(e.clazz.getSimpleName().equalsIgnoreCase(key)){
include = true;
break;
}
}
return include;
}
}
FunctionParser
@Data
public class FunctionParser extends Parser{
public FunctionParser(Node node) {
super(node);
}
/**
* 中缀转后缀表达式
*
* @param expirence
*/
@Override
protected void resolution(String expirence) {
//定义临时存储运算符的栈
StringBuilder tempStringBuilder = new StringBuilder();
for (int i = 0; i < expirence.length(); i++) {
tempStringBuilder = tempStringBuilder.append(expirence.substring(i, i + 1));
if (tempStringBuilder.toString().isEmpty() || ( i + 2 <= expirence.length() && !isTrue(expirence.substring(i + 1, i + 2))
&& !isTrue(tempStringBuilder.toString())) ) {
continue;
}
String temp = tempStringBuilder.toString();
//清空Stringbuilder
tempStringBuilder = new StringBuilder();
if(!isTrue(temp)){
node.getQueue().add(temp);
}
}
}
@Override
protected String replaceFunction(String expirence){
//定义临时存储运算符的栈
Stack stack = new Stack();
StringBuilder result = new StringBuilder();
StringBuilder tempStringBuilder = new StringBuilder();
for (int i = 0; i < expirence.length(); i++){
tempStringBuilder = tempStringBuilder.append(expirence.substring(i, i + 1));
if(expirence.substring(i, i + 1).equals("(")){
stack.push("(");
}else if(expirence.substring(i, i + 1).equals(")")){
stack.pop();
}
if ( (i + 2 <= expirence.length() && !isTrue(expirence.substring(i + 1, i + 2))
&& !isTrue(tempStringBuilder.toString())) || !stack.isEmpty()) {
continue;
}
if(isTrue(tempStringBuilder.toString())){
result.append(",");
}else{
String temp = "EXPIRENCENODE";
if(!countMap.containsKey(temp)){
countMap.put(temp,0);
}
countMap.put(temp,countMap.get(temp)+1);
String nodeName = "EXPIRENCENODE_" + countMap.get(temp);
result.append(nodeName);
Node child = createNode(tempStringBuilder.toString(),temp);
node.getChildNode().put(nodeName,child);
}
tempStringBuilder.setLength(0);
}
return result.toString();
}
@Override
protected boolean IsOperate(String temp) {
return FunctionEumn.isInclude(temp.toUpperCase());
}
@Override
public boolean isTrue(String temp) {
if (",".equals(temp)) {
return true;
}
return false;
}
@Override
public void parse(String expirence) {
if(StringUtils.isEmpty(expirence)){
return;
}
doParse(expirence);
}
}
上面的Parser是用来解析表达式的解析类,ExpirenceParser用来解析普通表达式,FunctionParser用来解析函数表达式,FunctionEumn指定方法对应的FunctionNode。
Node
@Data
public abstract class Node {
//解析对象
protected Parser parser;
//表达式
protected String expirence;
//计算栈
protected Stack stack = new Stack();
//运算公式
protected List<String> queue = new ArrayList(0);
//子节点
protected Map<String,Node> childNode = new ConcurrentHashMap<>(0);
protected Node(){
}
protected Node(String expirence){
this.expirence = expirence;
}
public abstract Object result(MetaObject metaObject);
protected boolean isNum(String str){
return CalculateUtils.isNumeric(str);
}
public Object getValue(String template,MetaObject metaObject){
if(Objects.nonNull(metaObject) && metaObject.hasGetter(template)){
return metaObject.getValue(template);
}else if(childNode.containsKey(template)){
return childNode.get(template).result(metaObject);
}
return template;
}
}
ExpirenceNode
@Data
public class ExpirenceNode extends Node{
public ExpirenceNode(){
}
public ExpirenceNode(String expirence){
super(expirence);
this.parser = new ExpirenceParser(this);
parser.parse(expirence);
}
@Override
public Object result(MetaObject metaObject) {
return calculate(metaObject);
}
public Object calculate(MetaObject metaObject) {
//存放计算结果
List<String> queue = getQueue();
for (int i = 0; i < queue.size(); i++) {
String key = queue.get(i);
//如果是运算符进行运算
if (parser.isTrue(key)) {
String pop = stack.pop().toString();
String pop1 = stack.pop().toString();
stack.push(operation(pop, pop1, key));
//如果不是运算符直接放入到运算结果中等待运算
} else {
stack.push(getValue(key,metaObject));
}
}
//显示计算结果
return stack.pop().toString();
}
protected Object operation(Object obj1, Object obj2, String template) {
Object num = new Object();
if(isOperate(template)){
Boolean num1 = new Boolean(obj1.toString());
Boolean num2 = new Boolean(obj2.toString());
switch (template) {
case "&&":
num = num2 && num1;
break;
case "||":
num = num2 || num1;
break;
}
}else {
BigDecimal num1 = new BigDecimal(obj1.toString());
BigDecimal num2 = new BigDecimal(obj2.toString());
switch (template) {
case "+": num = num2.add(num1); break;
case "-": num = num2.subtract(num1); break;
case "*": num = num2.multiply(num1); break;
case "/": num = num2.divide(num1); break;
case ">":
num = num2.compareTo(num1) == 1;
break;
case "<":
num = num2.compareTo(num1) == -1;
break;
case ">=":
num = num2.compareTo(num1) > -1;
break;
case "<=":
num = num2.compareTo(num1) < 1;
break;
case "==":
num = num2.compareTo(num1) == 0;
break;
case "!=":
num = num2.compareTo(num1) != 0;
break;
}
}
return num;
}
protected boolean isOperate(String template) {
return "&&".equalsIgnoreCase(template) || "||".equalsIgnoreCase(template);
}
}
FunctionNode
public abstract class FunctionNode extends Node{
public FunctionNode(){
}
public FunctionNode(String expirence){
super(expirence);
this.parser = new FunctionParser(this);
parser.parse(expirence);
}
}
AddNode
@Data
public class AddNode extends FunctionNode{
public AddNode(){
}
public AddNode(String expirence){
super(expirence);
}
@Override
public Object result(MetaObject metaObject) {
for (String key : queue) {
stack.add(getValue(key,metaObject).toString());
if(stack.size() <= 1){
continue;
}
String pop = stack.pop().toString();
String pop1 = stack.pop().toString();
stack.push(CalculateUtils.add(pop1,pop));
}
return stack.pop();
}
}
DivNode
public class DivNode extends FunctionNode{
public DivNode(){
}
public DivNode(String expirence){
super(expirence);
}
@Override
public Object result(MetaObject metaObject) {
for (String key : queue) {
stack.push(getValue(key,metaObject).toString());
if(stack.size() <= 1){
continue;
}
String pop = stack.pop().toString();
String pop1 = stack.pop().toString();
stack.push(CalculateUtils.divide(pop1,pop));
}
return stack.pop();
}
}
MoldNode
public class MoldNode extends FunctionNode{
public MoldNode(){
}
public MoldNode(String expirence){
super(expirence);
}
@Override
public Object result(MetaObject metaObject) {
for (String key : queue) {
stack.push(getValue(key,metaObject).toString());
if(stack.size() <= 1){
continue;
}
Double pop = Double.valueOf(stack.pop().toString());
Double pop1 = Double.valueOf(stack.pop().toString());
stack.push(pop1 % pop);
}
return stack.pop();
}
}
MulNode
public class MulNode extends FunctionNode{
public MulNode(){
}
public MulNode(String expirence){
super(expirence);
}
@Override
public Object result(MetaObject metaObject) {
for (String key : queue) {
stack.push(getValue(key,metaObject).toString());
if(stack.size() <= 1){
continue;
}
String pop = stack.pop().toString();
String pop1 = stack.pop().toString();
stack.push(CalculateUtils.multiply(pop1,pop));
}
return stack.pop();
}
}
SubNode
public class SubNode extends FunctionNode{
public SubNode() {
}
public SubNode(String expirence){
super(expirence);
}
@Override
public Object result(MetaObject metaObject) {
for (String key : queue) {
stack.push(getValue(key,metaObject).toString());
if(stack.size() <= 1){
continue;
}
String pop = stack.pop().toString();
String pop1 = stack.pop().toString();
stack.push(CalculateUtils.subtract(pop1,pop));
}
return stack.pop();
}
}
IfNode
public class IfNode extends Node{
private ExpirenceNode condition;
private ExpirenceNode firstResult;
private ExpirenceNode secondResult;
public IfNode(){
}
public IfNode(String expirence){
super(expirence);
init(expirence);
this.parser = new ExpirenceParser(this);
}
@Override
public Object result(MetaObject metaObject) {
Boolean judge = Boolean.valueOf(condition.result(metaObject).toString());
return judge?firstResult.result(metaObject):secondResult.result(metaObject);
}
private void init(String expirence){
Stack<String> stack = new Stack();
Integer start = 0,end = 0,flag = 0;
for (int i = 0; i < expirence.length(); i++) {
String substring = expirence.substring(i, i + 1);
if("(".equals(substring)){
stack.push("(");
}else if(")".equals(substring)){
stack.pop();
}else if(",".equals(substring) && stack.empty()){
end = i;
if(flag.equals(0)){
condition = new ExpirenceNode(expirence.substring(start,end));
}else if(flag.equals(1)){
firstResult = new ExpirenceNode(expirence.substring(start+1,end));
}
start = end;
flag++;
}
}
secondResult = new ExpirenceNode(expirence.substring(end+1));
}
}
CalculateUtils
public class CalculateUtils {
//判断是否是整数
public static boolean isInteger(Object str) {
Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
return pattern.matcher(str.toString()).matches();
}
//判断是否是数字
public static boolean isNumeric(Object str) {
if(Objects.isNull(str)){
return false;
}
Pattern pattern = Pattern.compile("^(-?[0-9]+)(.[0-9]?)?$");
Matcher isNum = pattern.matcher(str.toString());
return isNum.matches();
}
//加
public static Double add(Object... a){
return add(add(3,a),0);
}
public static Double add(Integer scale,Object... a){
if(a.length == 1){
return add(a[0],0,scale);
}
if(a.length == 2){
if(!isNumeric(a[0])){
return add(a[1],0,scale);
}else if(!isNumeric(a[1])){
return add(a[0],0,scale);
}
return add(a[0],a[1],scale,BigDecimal.ROUND_UNNECESSARY);
}
return add(add(scale,Arrays.copyOf(a, a.length/2)),
add(scale,Arrays.copyOfRange(a, a.length/2,a.length)),
scale,BigDecimal.ROUND_UNNECESSARY);
}
public static Double add(Object a,Object b){
return add(a,b,2);
}
public static Double add(Object a,Object b,Integer scale){
return add(a,b,scale,BigDecimal.ROUND_HALF_UP);
}
public static Double add(Object a,Object b,Integer scale,int strategy){
a = Objects.isNull(a)?0:a;
b = Objects.isNull(b)?0:b;
if(isNumeric(a.toString()) && isNumeric(b.toString())){
BigDecimal aDecimal = new BigDecimal(a.toString());
BigDecimal bDecimal = new BigDecimal(b.toString());
return aDecimal.add(bDecimal).setScale(scale,strategy).doubleValue();
}
return 0d;
}
//减
public static Double subtract(Object... a){
return subtract(2,a);
}
public static Double subtract(Integer scale,Object... a){
if(a.length == 1){
return subtract(a[0],0,scale);
}
return subtract(a[0],add(scale+1,Arrays.copyOfRange(a, 1,a.length)),scale);
}
public static Double subtract(Object a,Object b){
return subtract(a,b,2);
}
public static Double subtract(Object a,Object b,Integer scale){
return subtract(a,b,scale,BigDecimal.ROUND_HALF_UP);
}
public static Double subtract(Object a,Object b,Integer scale,int strategy){
a = Objects.isNull(a)?0:a;
b = Objects.isNull(b)?0:b;
if(isNumeric(a.toString()) && isNumeric(b.toString())){
BigDecimal aDecimal = new BigDecimal(a.toString());
BigDecimal bDecimal = new BigDecimal(b.toString());
return aDecimal.subtract(bDecimal).setScale(scale,strategy).doubleValue();
}
return 0d;
}
//乘
public static Double multiply(Object... a){
return multiply(multiply(3,a),1);
}
public static Double multiply(Integer scale,Object... a){
if(a.length == 1){
return add(a[0],0,scale);
}
if(a.length == 2){
if(!isNumeric(a[0])){
return add(a[1],0,scale);
}else if(!isNumeric(a[1])){
return add(a[0],0,scale);
}
return multiply(a[0],a[1],scale,BigDecimal.ROUND_UNNECESSARY);
}
return multiply(multiply(scale,Arrays.copyOf(a, a.length/2)),
multiply(scale,Arrays.copyOfRange(a, a.length/2,a.length)),
scale,BigDecimal.ROUND_UNNECESSARY);
}
public static Double multiply(Object a,Object b){
return multiply(a,b,2);
}
public static Double multiply(Object a,Object b,Integer scale){
return multiply(a,b,scale,BigDecimal.ROUND_HALF_UP);
}
public static Double multiply(Object a,Object b,Integer scale,int strategy){
a = Objects.isNull(a)?0:a;
b = Objects.isNull(b)?0:b;
if(isNumeric(a.toString()) && isNumeric(b.toString())){
BigDecimal aDecimal = new BigDecimal(a.toString());
BigDecimal bDecimal = new BigDecimal(b.toString());
return aDecimal.multiply(bDecimal).setScale(scale,strategy).doubleValue();
}
return 0d;
}
//除
public static Double divide(Object... a){
return divide(2,a);
}
public static Double divide(Integer scale,Object... a){
if(a.length == 1){
return subtract(a[0],0,scale);
}
return divide(a[0],multiply(scale+1,Arrays.copyOfRange(a, 1,a.length)),scale);
}
public static Double divide(Object a,Object b){
return divide(a,b,2);
}
public static Double divide(Object a,Object b,Integer scale){
return divide(a,b,scale,BigDecimal.ROUND_HALF_UP);
}
public static Double divide(Object a,Object b,Integer scale,int strategy){
a = Objects.isNull(a)?0:a;
b = Objects.isNull(b)?0:b;
if(isNumeric(a.toString()) && isNumeric(b.toString())){
BigDecimal aDecimal = new BigDecimal(a.toString());
BigDecimal bDecimal = new BigDecimal(b.toString());
return aDecimal.divide(bDecimal,scale,strategy).doubleValue();
}
return 0d;
}
public static void main(String[] args) {
System.out.println(add("7.222",3D,"3",4L,"baba"));
System.out.println(subtract("7.222",3D,"3",3.995,"baba"));
System.out.println(multiply("7.222",3D,"3",4L,"baba"));
System.out.println(divide("7.222",3.1));
System.out.println(add("abc"));
System.out.println(subtract("abc"));
System.out.println(multiply("abc"));
System.out.println(divide("abc"));
}
}
上述代码按下图结构新建就可以了,然后运行测试类即可得到计算后的结果。
项目中的使用
上面公式:
$减法$(120,120/21.75*$除法$($病假(小时)$,8)0.3+120/21.75$加法$($除法$($事假(小时)$,8),$旷工$,$停工$))
得到的通勤补贴结果如下图等于114.48
现在我们改下公式:
$减法$(120,120/21.75*$除法$($病假(小时)$,8)0.3+120/21.75$加法$($除法$($事假(小时)$,8),$旷工$,$停工$))+10000
看下计算结果如下图成功解析公式计算得到10114.48
公式中如$病假(小时)$是事先定义好的计算对象中的属性,如下图: