[feature] VersionUtil版本比较工具类

This commit is contained in:
winlans 2025-02-27 18:07:07 +08:00
parent 732bea6d0b
commit 4c6c3b057a
2 changed files with 275 additions and 0 deletions

View File

@ -0,0 +1,196 @@
package cn.hutool.core.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.UtilException;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 版本对比工具
* {@link cn.hutool.core.comparator.VersionComparator} 的封装
* 最主要功能包括
*
*
* <pre>
* 1. 版本表达式匹配
* 2. 单个版本匹配
* </pre>
*
* @author winlans
* @see cn.hutool.core.comparator.VersionComparator
*/
public class VersionUtil {
private static final Pattern COMPARE_REG = Pattern.compile("^[<>≥≤]=?");
// 默认多版本分隔符
private static final String defaultVersionsDelimiter = ";";
/**
* 是否匹配任意一个版本
*
* @param currentVersion 当前版本
* @param compareVersions 待匹配的版本列表
* @return true 包含待匹配的版本
*/
public static boolean anyMatch(String currentVersion, Collection<String> compareVersions) {
return matchEl(currentVersion, CollUtil.join(compareVersions, defaultVersionsDelimiter));
}
public static boolean anyMatch(String currentVersion, String... compareVersions) {
return matchEl(currentVersion, ArrayUtil.join(compareVersions, defaultVersionsDelimiter));
}
/**
* 当前版本大于待比较版本
*
* @param currentVersion 当前本本
* @param compareVersion 待比较版本
* @return true 当前版本大于待比较版本
*/
public static boolean isGreaterThan(String currentVersion, String compareVersion) {
return matchEl(currentVersion, ">" + compareVersion);
}
/**
* 当前版本大于等于待比较版本
*
* @param currentVersion 当前本本
* @param compareVersion 待比较版本
* @return true 当前版本大于等于待比较版本
*/
public static boolean isGreaterThanOrEqual(String currentVersion, String compareVersion) {
return matchEl(currentVersion, ">=" + compareVersion);
}
/**
* 当前版本小于待比较版本
*
* @param currentVersion 当前本本
* @param compareVersion 待比较版本
* @return true 当前版本小于待比较版本
*/
public static boolean isLessThan(String currentVersion, String compareVersion) {
return matchEl(currentVersion, "<" + compareVersion);
}
/**
* 当前版本小于等于待比较版本
*
* @param currentVersion 当前本本
* @param compareVersion 待比较版本
* @return true 当前版本小于等于待比较版本
*/
public static boolean isLessThanOrEqual(String currentVersion, String compareVersion) {
return matchEl(currentVersion, "<=" + compareVersion);
}
/**
* 当前版本是否满足版本表达式
* <pre>
* matchEl("1.0.2", ">=1.0.2") == true
* matchEl("1.0.2", "<1.0.1;1.0.2") == true
* matchEl("1.0.2", "<1.0.2") == false
* matchEl("1.0.2", "1.0.0-1.1.1") == true
* matchEl("1.0.2", "1.0.0-1.1.1") == true
* </pre>
*
* @param currentVersion 当前本本
* @param versionEl 版本表达式
* @return true 当前版本小于等于待比较版本
*/
public static boolean matchEl(String currentVersion, String versionEl) {
return matchEl(currentVersion, versionEl, defaultVersionsDelimiter);
}
/**
* 当前版本是否满足版本表达式
* <pre>
* matchEl("1.0.2", ">=1.0.2", ";") == true
* matchEl("1.0.2", "<1.0.1,1.0.2", ",") == true
* matchEl("1.0.2", "<1.0.2", ";") == false
* matchEl("1.0.2", "1.0.0-1.1.1", ",") == true
* matchEl("1.0.2", "1.0.1,1.0.2-1.1.1", ",") == true
* </pre>
*
* @param currentVersion 当前本本
* @param versionEl 版本表达式可以匹配多个条件使用指定的分隔符默认;分隔,
* {@code '-'}表示范围包含左右版本,如果 {@code '-'}的左边没有表示小于等于某个版本号 右边表示大于等于某个版本号
* 支持比较符号{@code '>'},{@code '<'}, {@code '>='},{@code '<='}{@code '≤'}{@code '≥'}
*
* <ul>
* <li>{@code 1.0.1-1.2.4, 1.9.8} 表示版本号 大于等于{@code 1.0.1}且小于等于{@code 1.2.4} 版本{@code 1.9.8}</li>
* <li>{@code >=2.0.0, 1.9.8} 表示版本号 大于等于{@code 2.0.0} 版本{@code 1.9.8}</li>
* </ul>
* @param versionsDelimiter 多表达式分隔符
* @return true 当前版本小于等于待比较版本
*/
public static boolean matchEl(String currentVersion, String versionEl, String versionsDelimiter) {
if (StrUtil.isBlank(versionsDelimiter)
|| StrUtil.equals("-", versionsDelimiter)
|| ReUtil.isMatch(COMPARE_REG, versionsDelimiter)) {
throw new UtilException("非法的版本分隔符:" + versionsDelimiter);
}
if (StrUtil.isBlank(versionEl) || StrUtil.isBlank(currentVersion)) {
return false;
}
String trimmedVersion = StrUtil.trim(currentVersion);
List<String> els = StrUtil.split(versionEl, versionsDelimiter, true, true);
if (CollUtil.isEmpty(els)) {
return false;
}
for (String el : els) {
el = el.trim();
Matcher matcher = COMPARE_REG.matcher(el);
if (matcher.find()) {
String op = matcher.group();
String ver = StrUtil.removePrefix(el, op);
switch (op) {
case ">=":
case "":
if (StrUtil.compareVersion(trimmedVersion, ver) >= 0) {
return true;
}
break;
case "<=":
case "":
if (StrUtil.compareVersion(trimmedVersion, ver) <= 0) {
return true;
}
break;
case "<":
if (StrUtil.compareVersion(trimmedVersion, ver) < 0) {
return true;
}
break;
case ">":
if (StrUtil.compareVersion(trimmedVersion, ver) > 0) {
return true;
}
break;
default:
return false;
}
} else if (StrUtil.contains(el, "-")) {
String[] pair = el.split("-");
String left = StrUtil.blankToDefault(StrUtil.trim(pair[0]), "");
String right = StrUtil.blankToDefault(StrUtil.trim(pair[1]), "");
boolean leftMatch = StrUtil.isBlank(left) || StrUtil.compareVersion(left, trimmedVersion) <= 0;
boolean rightMatch = StrUtil.isBlank(right) || StrUtil.compareVersion(right, trimmedVersion) >= 0;
if (leftMatch && rightMatch) {
return true;
}
} else if (Objects.equals(trimmedVersion, el)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,79 @@
package cn.hutool.core.util;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.exceptions.UtilException;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class VersionUtilTest {
@Test
void isGreaterThan() {
String currentVersion = " 1.0.2";
assertTrue(VersionUtil.isGreaterThan(currentVersion, "1.0.1"));
assertTrue(VersionUtil.isGreaterThan(currentVersion, "1"));
assertFalse(VersionUtil.isGreaterThan(currentVersion, "1.1"));
}
@Test
void isGreaterThanOrEqual() {
String currentVersion = "1.0.2 ";
assertTrue(VersionUtil.isGreaterThanOrEqual(currentVersion, "1.0.1"));
assertTrue(VersionUtil.isGreaterThanOrEqual(currentVersion, "1.0.2"));
assertFalse(VersionUtil.isGreaterThanOrEqual(currentVersion, "1.1"));
}
@Test
void isLessThan() {
String currentVersion = "1.0.2";
assertTrue(VersionUtil.isLessThan(currentVersion, "1.0.3"));
assertFalse(VersionUtil.isLessThan(currentVersion, "1"));
assertTrue(VersionUtil.isLessThan(currentVersion, "1.1"));
assertFalse(VersionUtil.isLessThan(currentVersion, "1.0.2"));
}
@Test
void isLessThanOrEqual() {
String currentVersion = "1.0.2";
assertTrue(VersionUtil.isLessThanOrEqual(currentVersion, "1.0.2"));
assertFalse(VersionUtil.isLessThanOrEqual(currentVersion, "1.0.1"));
assertFalse(VersionUtil.isLessThanOrEqual(currentVersion, "1.1"));
}
@Test
void matchEl() {
String currentVersion = "1.0.2";
assertTrue(VersionUtil.matchEl(currentVersion, "1.0.1;1.0.2"));
assertFalse(VersionUtil.matchEl(currentVersion, "1.0.1;1.0.3"));
assertTrue(VersionUtil.matchEl(currentVersion, "1.0.9;1.0.1-1.0.2"));
assertTrue(VersionUtil.matchEl(currentVersion, "1.0.9;1.0.1-1.0.3"));
assertTrue(VersionUtil.matchEl(currentVersion, "1.0.9,1.0.1-1.0.3", ","));
}
@Test
void matchEl_Exception_whenVersionDelimiterIllegal() {
List<String> illegalDelimiters = ListUtil.of("-", ">", ">=", "<", "<=", "", "", null, "", " ");
for (String illegalDelimiter : illegalDelimiters) {
assertThrows(UtilException.class, () -> {
String currentVersion = "1.0.2";
VersionUtil.matchEl(currentVersion, "1.0.1;1.0.2", illegalDelimiter);
});
}
}
@Test
void anyMatch() {
String currentVersion = "1.0.2";
assertTrue(VersionUtil.anyMatch(currentVersion, ListUtil.of("1.0.1", "1.0.3", "1.0.2")));
assertTrue(VersionUtil.anyMatch(currentVersion, "1.0.1", "1.0.2"));
}
@Test
void testMatchEl() {
}
}