package com.qianmi.tda.util; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; /** * EvalUtil * Created by aqlu on 2016/10/31. */ @SuppressWarnings("WeakerAccess") @Slf4j public class EvalUtil { public static final String CMP_OP_EQUALS = "="; public static final String CMP_OP_NOT_EQUALS = "!="; public static final String CMP_OP_LESS_THAN = "<"; public static final String CMP_OP_GREATER_THAN = ">"; public static final String CMP_OP_LESS_THAN_EQUALS = "<="; public static final String CMP_OP_GREATER_THAN_EQUALS = ">="; public static final String CMP_OP_CONTAINS = "contains"; public static final String CMP_OP_NOT_CONTAINS = "!contains"; public static final String CMP_OP_MATCH = "match"; public static final String CMP_OP_NOT_MATCH = "!match"; public static final String[] OPERATORS = new String[]{CMP_OP_EQUALS, CMP_OP_NOT_EQUALS, CMP_OP_LESS_THAN, CMP_OP_GREATER_THAN, CMP_OP_LESS_THAN_EQUALS, CMP_OP_GREATER_THAN_EQUALS, CMP_OP_CONTAINS, CMP_OP_NOT_CONTAINS, CMP_OP_MATCH, CMP_OP_NOT_MATCH}; public static final List<String> OPERATOR_LIST = Arrays.asList(OPERATORS); /** * 比较两个对象 * * @param expect 期待值 * @param actual 实际值 * @param operator 比较操作符 * @return true:符合,false:不符合 */ public static boolean eval(Object expect, Object actual, String operator) { //校验运算符 if (!StringUtils.hasText(operator)) { throw new IllegalArgumentException("operator参数不能为空"); } else if (!OPERATOR_LIST.contains(operator)) { throw new IllegalArgumentException("不支持【" + operator + "】运算符, 仅支持:" + OPERATOR_LIST); } if (expect == null || actual == null) { // 期待值与实际值只要有一个为null时,只做"="与"!="比较 switch (operator) { case CMP_OP_EQUALS: return expect == actual; case CMP_OP_NOT_EQUALS: return expect != actual; default: return false; } } else if (expect instanceof Number) { //数字比较 if (actual instanceof Number) { return evalNumber((Number) expect, (Number) actual, operator); } else //noinspection SimplifiableIfStatement if (actual instanceof List) { return evalObjectAndList(expect, (List) actual, operator); } else { return false; } } else if (expect instanceof String) { // 字符串比较 if (actual instanceof String) { return evalString((String) expect, (String) actual, operator); } else //noinspection SimplifiableIfStatement if (actual instanceof List) { return evalObjectAndList(expect, (List) actual, operator); } else { return false; } } else if (expect instanceof List) { // List比较 return actual instanceof List && evalList((List) expect, (List) actual, operator, false); } else if (expect instanceof Map) { // Map比较 return actual instanceof Map && evalMap((Map) expect, (Map) actual, operator); } else if (expect instanceof Comparable) { //Comparable比较 return actual instanceof Comparable && evalComparable((Comparable) expect, (Comparable) actual, operator); } else { // 其它比较 switch (operator) { case CMP_OP_EQUALS: return expect.equals(actual); case CMP_OP_NOT_EQUALS: return !expect.equals(actual); case CMP_OP_LESS_THAN: case CMP_OP_GREATER_THAN: case CMP_OP_LESS_THAN_EQUALS: case CMP_OP_GREATER_THAN_EQUALS: case CMP_OP_CONTAINS: case CMP_OP_NOT_CONTAINS: case CMP_OP_MATCH: case CMP_OP_NOT_MATCH: default: throw new IllegalArgumentException(String.format("【%s】与【%s】不支持进行【%s】比较", expect, actual, operator)); } } } /** * 字符串比较 */ private static boolean evalString(String expect, String actual, String operator) { switch (operator) { case CMP_OP_EQUALS: return expect.equals(actual); case CMP_OP_NOT_EQUALS: return !expect.equals(actual); case CMP_OP_LESS_THAN: return actual.compareTo(expect) < 0; case CMP_OP_GREATER_THAN: return actual.compareTo(expect) > 0; case CMP_OP_LESS_THAN_EQUALS: return actual.compareTo(expect) <= 0; case CMP_OP_GREATER_THAN_EQUALS: return actual.compareTo(expect) >= 0; case CMP_OP_CONTAINS: return actual.contains(expect); case CMP_OP_NOT_CONTAINS: return !actual.contains(expect); case CMP_OP_MATCH: return actual.matches(expect); case CMP_OP_NOT_MATCH: return !actual.matches(expect); default: throw new IllegalArgumentException(String.format("【%s】与【%s】不支持进行【%s】比较", expect, actual, operator)); } } /** * 对象与列表比较 */ private static boolean evalObjectAndList(Object expect, List actual, String operator) { switch (operator) { case CMP_OP_CONTAINS: return actual.contains(expect); case CMP_OP_NOT_CONTAINS: return !actual.contains(expect); case CMP_OP_EQUALS: case CMP_OP_NOT_EQUALS: case CMP_OP_LESS_THAN: case CMP_OP_GREATER_THAN: case CMP_OP_LESS_THAN_EQUALS: case CMP_OP_GREATER_THAN_EQUALS: case CMP_OP_MATCH: case CMP_OP_NOT_MATCH: default: throw new IllegalArgumentException(String.format("【%s】与【%s】不支持进行【%s】比较", expect, actual, operator)); } } /** * 数字比较 */ private static boolean evalNumber(Number expect, Number actual, String operator) { switch (operator) { case CMP_OP_EQUALS: return compareNumbers(expect, actual) == 0; case CMP_OP_NOT_EQUALS: return compareNumbers(expect, actual) != 0; case CMP_OP_LESS_THAN: return compareNumbers(actual, expect) < 0; case CMP_OP_GREATER_THAN: return compareNumbers(actual, expect) > 0; case CMP_OP_LESS_THAN_EQUALS: return compareNumbers(actual, expect) <= 0; case CMP_OP_GREATER_THAN_EQUALS: return compareNumbers(actual, expect) >= 0; case CMP_OP_CONTAINS: case CMP_OP_NOT_CONTAINS: case CMP_OP_MATCH: case CMP_OP_NOT_MATCH: default: throw new IllegalArgumentException(String.format("【%s】与【%s】不支持进行【%s】比较", expect, actual, operator)); } } /** * 列表比较 */ private static boolean evalList(List expect, List actual, String operator, boolean ignoreOrder) { // 尝试对List进行排序 if(ignoreOrder) { try { Collections.sort(expect); Collections.sort(actual); } catch (Exception e) { //忽略排序失败 log.debug("排序失败, expect:{}, actual:{}", expect, actual, e); } } switch (operator) { case CMP_OP_EQUALS: return expect.equals(actual); case CMP_OP_NOT_EQUALS: return !expect.equals(actual); case CMP_OP_CONTAINS: for (Object expectElement : expect) { if (!actual.contains(expectElement)) { return false; } } return true; case CMP_OP_NOT_CONTAINS: for (Object expectElement : expect) { if (actual.contains(expectElement)) { return false; } } return true; case CMP_OP_LESS_THAN: case CMP_OP_GREATER_THAN: case CMP_OP_LESS_THAN_EQUALS: case CMP_OP_GREATER_THAN_EQUALS: case CMP_OP_MATCH: case CMP_OP_NOT_MATCH: default: throw new IllegalArgumentException(String.format("【%s】与【%s】不支持进行【%s】比较", expect, actual, operator)); } } /** * Map比较 */ private static boolean evalMap(Map expect, Map actual, String operator) { switch (operator) { case CMP_OP_EQUALS: return expect.equals(actual); case CMP_OP_NOT_EQUALS: return !expect.equals(actual); case CMP_OP_CONTAINS: for (Object expectKey : expect.keySet()) { Object actualValue = actual.get(expectKey); Object expectValue = expect.get(expectKey); if (actualValue == null && expectValue == null) { return true; } else if (actualValue != null && expectValue != null && actualValue.equals(expectValue)) { return true; } } return false; case CMP_OP_NOT_CONTAINS: case CMP_OP_LESS_THAN: case CMP_OP_GREATER_THAN: case CMP_OP_LESS_THAN_EQUALS: case CMP_OP_GREATER_THAN_EQUALS: case CMP_OP_MATCH: case CMP_OP_NOT_MATCH: default: throw new IllegalArgumentException(String.format("【%s】与【%s】不支持进行【%s】比较", expect, actual, operator)); } } /** * Comparable比较 */ @SuppressWarnings("unchecked") private static boolean evalComparable(Comparable expect, Comparable actual, String operator) { switch (operator) { case CMP_OP_EQUALS: return expect.equals(actual); case CMP_OP_NOT_EQUALS: return !expect.equals(actual); case CMP_OP_LESS_THAN: return actual.compareTo(expect) < 0; case CMP_OP_GREATER_THAN: return actual.compareTo(expect) > 0; case CMP_OP_LESS_THAN_EQUALS: return actual.compareTo(expect) <= 0; case CMP_OP_GREATER_THAN_EQUALS: return actual.compareTo(expect) >= 0; case CMP_OP_CONTAINS: case CMP_OP_NOT_CONTAINS: case CMP_OP_MATCH: case CMP_OP_NOT_MATCH: default: throw new IllegalArgumentException(String.format("【%s】与【%s】不支持进行【%s】比较", expect, actual, operator)); } } /** * @return -1 for negative, 0 for zero, 1 for positive. * @throws ArithmeticException if the number is NaN */ @SuppressWarnings("Duplicates") public static int getSignum(Number num) throws ArithmeticException { if (num instanceof Integer) { int n = num.intValue(); return n > 0 ? 1 : (n == 0 ? 0 : -1); } else if (num instanceof BigDecimal) { BigDecimal n = (BigDecimal) num; return n.signum(); } else if (num instanceof Double) { double n = num.doubleValue(); if (n > 0) return 1; else if (n == 0) return 0; else if (n < 0) return -1; else throw new ArithmeticException("The signum of " + n + " is not defined."); // NaN } else if (num instanceof Float) { float n = num.floatValue(); if (n > 0) return 1; else if (n == 0) return 0; else if (n < 0) return -1; else throw new ArithmeticException("The signum of " + n + " is not defined."); // NaN } else if (num instanceof Long) { long n = num.longValue(); return n > 0 ? 1 : (n == 0 ? 0 : -1); } else if (num instanceof Short) { short n = num.shortValue(); return n > 0 ? 1 : (n == 0 ? 0 : -1); } else if (num instanceof Byte) { byte n = num.byteValue(); return n > 0 ? 1 : (n == 0 ? 0 : -1); } else if (num instanceof BigInteger) { BigInteger n = (BigInteger) num; return n.signum(); } else { throw new IllegalArgumentException("不支持此数字,number:" + num); } } private static BigDecimal toBigDecimal(Number num) { try { return num instanceof BigDecimal ? (BigDecimal) num : new BigDecimal(num.toString()); } catch (NumberFormatException e) { // The exception message is useless, so we add a new one: throw new NumberFormatException("Can't parse this as BigDecimal number: " + num); } } /** * 比较两个数字,first>second返回1, first=second返回0, first<second返回-1 */ public static int compareNumbers(Number first, Number second) { // We try to find the result based on the sign (+/-/0) first, because: // - It's much faster than converting to BigDecial, and comparing to 0 is the most common comparison. // - It doesn't require any type conversions, and thus things like "Infinity > 0" won't fail. int firstSignum = EvalUtil.getSignum(first); int secondSignum = EvalUtil.getSignum(second); if (firstSignum != secondSignum) { return firstSignum < secondSignum ? -1 : (firstSignum > secondSignum ? 1 : 0); } else if (firstSignum == 0) { return 0; } else { BigDecimal left = toBigDecimal(first); BigDecimal right = toBigDecimal(second); return left.compareTo(right); } } }