com.iwuyc.tools.commons.util.math.Range Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of iwuyc-common Show documentation
Show all versions of iwuyc-common Show documentation
Common tools.Include utility classes,and much much more.
The newest version!
package com.iwuyc.tools.commons.util.math;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.iwuyc.tools.commons.exception.ExpressionException;
import com.iwuyc.tools.commons.util.string.StringUtils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import static com.iwuyc.tools.commons.util.math.BoundaryNumber.MAX_NUM;
import static com.iwuyc.tools.commons.util.math.BoundaryNumber.MAX_TAG;
import static com.iwuyc.tools.commons.util.math.BoundaryNumber.MIN_NUM;
import static com.iwuyc.tools.commons.util.math.BoundaryNumber.MIN_TAG;
/**
* 数值范围
*
*
* Example:
* · [0,10)|[10,100):
* 0:include
* -1:exclude
* 10:include
* 100:exclude
* 相当于[0,100)
*
* · [0,10)|(10,100):
* 0:include
* -1:exclude
* 10:exclude
* 100:exclude
*
* · [0,10)|(9,100):
* 相当于[0,100)
*
*
* @author @Neil
* @since @2017年10月15日
*/
public class Range {
private static final Cache PATTERN_CACHE = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).maximumSize(1_000).build();
/**
* 01 右闭,表示含右侧的数字
*/
private static final byte CONTAIN_RIGHT = 1;
/**
* 10 左闭,表示含左侧的数字
*/
private static final byte CONTAIN_LEAF = 2;
private final Collection ranges = new ArrayList<>();
private Range() {
}
/**
* 编译表达式。表达式以区间表示,多个区间以"|"隔开,无限大以max表示,无限小以min表示。
*
* example:
* [0,10]:表示0到10的数字,包含0跟10;
* (0,10]:表示0到10的数字,不包含0,但包含10;
* [0,10)|(10,20):表示0到20,但不包含10;
* [min,10]:表示小于等于10的数值;
* [0,max):表示大于等于0的数值;
*
* 对于min和max,是开还是闭已无所谓了,也就是说(min,max)等效于[min,max]
*
* @param rangeStr 表达式字符串。
* @return range 实例
* @throws IllegalArgumentException 如果表达式有问题,则会抛出这个错误。
*/
public static Range compiler(String rangeStr) throws IllegalArgumentException {
return compiler(rangeStr, true);
}
/**
* 编译表达式。表达式以区间表示,多个区间以"|"隔开,最大值以max表示,最小值以min表示
*
* @param rangeStr 表达式字符串。
* @return range 实例
* @throws ExpressionException 如果表达式有问题,则会抛出这个错误。
*/
public static Range compiler(String rangeStr, boolean cached) throws ExpressionException {
try {
final Callable rangeLoader = () -> getRange(rangeStr);
if (cached) {
return PATTERN_CACHE.get(rangeStr, rangeLoader);
} else {
return getRange(rangeStr);
}
} catch (Exception e) {
final Throwable cause = e.getCause();
if (cause instanceof ExpressionException) {
throw (ExpressionException) cause;
}
throw new ExpressionException(e.getMessage(), e);
}
}
private static Range getRange(String rangeStr) {
Range rootRange = new Range();
String[] rangeStrArr = rangeStr.split("[|]+");
RangeItem rangeItem;
for (String rangeStrItem : rangeStrArr) {
rangeStrItem = rangeStrItem.trim();
if (StringUtils.isEmpty(rangeStrItem)) {
continue;
}
rangeItem = itemCompiler(rangeStrItem);
if (!rangeItem.verify()) {
throw new ExpressionException("The expression was wrong.Expression:" + rangeStrItem);
}
rootRange.ranges.add(rangeItem);
}
return rootRange;
}
private static RangeItem itemCompiler(String rangeStr) {
RangeItem range = new RangeItem();
StringBuilder sb = new StringBuilder(rangeStr);
int firstStartIndex = sb.indexOf("[");
if (firstStartIndex < 0) {
firstStartIndex = sb.indexOf("(");
} else {
range.flag |= Range.CONTAIN_LEAF;
}
int splitFlagIndex = sb.indexOf(",");
int secondEndIndex = sb.indexOf("]");
if (secondEndIndex < 0) {
secondEndIndex = sb.indexOf(")");
} else {
range.flag |= Range.CONTAIN_RIGHT;
}
String firstNumStr = sb.substring(firstStartIndex + 1, splitFlagIndex).trim();
String secondNumStr = sb.substring(splitFlagIndex + 1, secondEndIndex).trim();
range.min = builderBigDecimal(firstNumStr);
range.max = builderBigDecimal(secondNumStr);
return range;
}
private static BigDecimal builderBigDecimal(String numStr) {
if (MAX_TAG.equals(numStr)) {
return MAX_NUM;
} else if (MIN_TAG.equals(numStr)) {
return MIN_NUM;
}
return new BigDecimal(numStr);
}
/**
* 判断一个数字是否在范围内。
*
* @param num 待判断的数字
* @return 如果在范围内,则返回true,否则返回false。
*/
public boolean inRange(Number num) {
return inRange(String.valueOf(num));
}
/**
* 判断一个数字是否在范围内。
*
* @param numStr 待判断的数字
* @return 如果在范围内,则返回true,否则返回false。
*/
public boolean inRange(String numStr) {
BigDecimal number = new BigDecimal(numStr);
return this.inRange(number);
}
/**
* 判断一个数字是否在范围内。
*
* @param number 待判断的数字
* @return 如果在范围内,则返回true,否则返回false。
*/
public boolean inRange(BigDecimal number) {
for (RangeItem range : ranges) {
if (range.judge(number)) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "Range [ranges=" + ranges + "]";
}
private static class RangeItem {
private BigDecimal min;
private BigDecimal max;
private byte flag = 0;
@Override
public String toString() {
return "Range [min=" + min + ", max=" + max + ", flag=" + flag + "]";
}
private boolean judge(BigDecimal number) {
int compareMin = this.min.compareTo(number);
if (compareMin > 0) {
return false;
} else if (0 == compareMin) {
return 0 != (this.flag & CONTAIN_LEAF);
}
int compareMax = this.max.compareTo(number);
if (compareMax < 0) {
return false;
} else if (0 == compareMax) {
return 0 != (this.flag & CONTAIN_RIGHT);
}
return true;
}
private boolean verify() {
return this.max.compareTo(this.min) >= 0;
}
}
}