From f99405d11169f02f311b85b7efa37aff0e6c5666 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Tue, 14 Oct 2025 02:33:16 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=20`SemVer`=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zhouxy/plusone/commons/model/SemVer.java | 129 ++++++++++++++---- 1 file changed, 101 insertions(+), 28 deletions(-) diff --git a/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/model/SemVer.java b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/model/SemVer.java index fd78dbc..2ab0d56 100644 --- a/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/model/SemVer.java +++ b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/model/SemVer.java @@ -30,9 +30,7 @@ import com.google.common.base.Splitter; import xyz.zhouxy.plusone.commons.util.StringTools; -// TODO [优化] 优化正则表达式 // TODO [补充] 完善单元测试 -// TODO [doc] javadoc、README.md /** * SemVer 语义版本号 @@ -48,23 +46,42 @@ public class SemVer implements Comparable, Serializable { private final String value; private final int[] versionNumbers; - private final String[] preReleaseVersion; + @Nullable + private final String preReleaseVersion; + @Nullable private final String buildMetadata; private static final String VERSION_NUMBERS = "(?(?0|[1-9]\\d*)\\.(?0|[1-9]\\d*)\\.(?0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){0,2})"; - private static final String PRE_RELEASE_VERSION = "(?:-(?(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"; - private static final String BUILD_METADATA = "(?:\\+(?[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?"; + private static final String PRE_RELEASE_VERSION = "(?:-(?(?:0|[1-9]\\d{0,41}|\\d{0,18}[a-zA-Z-][0-9a-zA-Z-]{0,18})(?:\\.(?:0|[1-9]\\d{0,41}|\\d{0,18}[a-zA-Z-][0-9a-zA-Z-]{0,18})){0,18}))?"; + private static final String BUILD_METADATA = "(?:\\+(?[0-9a-zA-Z-]{1,18}(?:\\.[0-9a-zA-Z-]{1,18}){0,18}))?"; private static final Pattern PATTERN = Pattern.compile( "^" + VERSION_NUMBERS + PRE_RELEASE_VERSION + BUILD_METADATA + "$"); - private SemVer(String value, int[] versionNumbers, @Nullable String[] preReleaseVersion, @Nullable String buildMetadata) { + /** + * 创建语义化版本号的值对象 + * + * @param value 字符串值 + * @param versionNumbers 主版本号、次版本号、修订号 + * @param preReleaseVersion 先行版本号 + * @param buildMetadata 版本编译信息 + */ + private SemVer(String value, + int[] versionNumbers, + @Nullable String preReleaseVersion, + @Nullable String buildMetadata) { this.value = value; this.versionNumbers = versionNumbers; this.preReleaseVersion = preReleaseVersion; this.buildMetadata = buildMetadata; } + /** + * 创建 SemVer 对象 + * + * @param value 语义化版本号 + * @return SemVer 对象 + */ public static SemVer of(final String value) { checkArgument(StringTools.isNotBlank(value), "版本号不能为空"); final Matcher matcher = PATTERN.matcher(value); @@ -81,30 +98,57 @@ public class SemVer implements Comparable, Serializable { // 必须都是数字 .mapToInt(Integer::parseInt) .toArray(); - - final String[] preReleaseVersion = preReleaseVersionPart != null - ? Splitter.on('.').splitToStream(preReleaseVersionPart).toArray(String[]::new) - : null; - - return new SemVer(value, versionNumbers, preReleaseVersion, buildMetadataPart); + return new SemVer(value, versionNumbers, preReleaseVersionPart, buildMetadataPart); } + /** + * 获取主版本号 + * + * @return 主版本号 + */ public int getMajor() { return this.versionNumbers[0]; } + /** + * 获取次版本号 + * + * @return 次版本号 + */ public int getMinor() { return this.versionNumbers[1]; } + /** + * 获取修订号 + * + * @return 修订号 + */ public int getPatch() { return this.versionNumbers[2]; } + /** + * 获取先行版本号 + * + * @return 先行版本号 + */ + @Nullable + public String getPreReleaseVersion() { + return this.preReleaseVersion; + } + + /** + * 获取版本编译信息 + * + * @return 版本编译信息 + */ + @Nullable public String getBuildMetadata() { return buildMetadata; } + /** {@inheritDoc} */ @Override public int compareTo(@Nullable SemVer that) { if (that == null) { @@ -117,22 +161,26 @@ public class SemVer implements Comparable, Serializable { return comparePreReleaseVersion(that); } + /** + * 获取字符串值 + * + * @return 版本字符串 + */ public String getValue() { return value; } - - + /** {@inheritDoc} */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(versionNumbers); - result = prime * result + Arrays.hashCode(preReleaseVersion); - result = prime * result + Objects.hash(value, buildMetadata); + result = prime * result + Objects.hash(value, preReleaseVersion, buildMetadata); return result; } + /** {@inheritDoc} */ @Override public boolean equals(@Nullable Object obj) { if (this == obj) @@ -141,15 +189,21 @@ public class SemVer implements Comparable, Serializable { return false; SemVer other = (SemVer) obj; return Objects.equals(value, other.value) && Arrays.equals(versionNumbers, other.versionNumbers) - && Arrays.equals(preReleaseVersion, other.preReleaseVersion) + && Objects.equals(preReleaseVersion, other.preReleaseVersion) && Objects.equals(buildMetadata, other.buildMetadata); } + /** + * 获取 SemVer 的字符串表示。如 {@code v1.2.3-alpha.1+build.1234} + */ @Override public String toString() { return 'v' + value; } + /** + * 比较主版本号、次版本号、修订号 + */ private int compareVersionNumbers(SemVer that) { final int minLength = Integer.min(this.versionNumbers.length, that.versionNumbers.length); @@ -163,15 +217,24 @@ public class SemVer implements Comparable, Serializable { return this.versionNumbers.length - that.versionNumbers.length; } + /** + * 比较先行版本号 + */ private int comparePreReleaseVersion(SemVer that) { - final String[] preReleaseVersionOfThis = this.preReleaseVersion; - final String[] preReleaseVersionOfThat = that.preReleaseVersion; - byte thisWithoutPreReleaseVersionFlag = preReleaseVersionOfThis == null ? (byte) 1 : (byte) 0; - byte thatWithoutPreReleaseVersionFlag = preReleaseVersionOfThat == null ? (byte) 1 : (byte) 0; - if ((thisWithoutPreReleaseVersionFlag | thatWithoutPreReleaseVersionFlag) == 1) { + int thisWithoutPreReleaseVersionFlag = bool2Int(this.preReleaseVersion == null); + int thatWithoutPreReleaseVersionFlag = bool2Int(that.preReleaseVersion == null); + if (isTrue(thisWithoutPreReleaseVersionFlag | thatWithoutPreReleaseVersionFlag)) { return thisWithoutPreReleaseVersionFlag - thatWithoutPreReleaseVersionFlag; } - @SuppressWarnings("null") + + Splitter splitter = Splitter.on('.'); + + final String[] preReleaseVersionOfThis = splitter + .splitToStream(this.preReleaseVersion) // NOSONAR + .toArray(String[]::new); + final String[] preReleaseVersionOfThat = splitter + .splitToStream(that.preReleaseVersion) // NOSONAR + .toArray(String[]::new); final int minLength = Integer.min(preReleaseVersionOfThis.length, preReleaseVersionOfThat.length); for (int i = 0; i < minLength; i++) { int r = comparePartOfPreReleaseVersion(preReleaseVersionOfThis[i], preReleaseVersionOfThat[i]); @@ -182,6 +245,9 @@ public class SemVer implements Comparable, Serializable { return preReleaseVersionOfThis.length - preReleaseVersionOfThat.length; } + /** + * 比较先行版本号的组成部分 + */ private static int comparePartOfPreReleaseVersion(String p1, String p2) { boolean p1IsNumber = isAllDigits(p1); boolean p2IsNumber = isAllDigits(p2); @@ -195,11 +261,10 @@ public class SemVer implements Comparable, Serializable { return p2IsNumber ? 1 : p1.compareTo(p2); } - private static boolean isAllDigits(@Nullable String str) { - if (str == null || str.isEmpty()) { - return false; - } - + /** + * 判断字符串是否全为数字 + */ + private static boolean isAllDigits(String str) { for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (c < '0' || c > '9') { @@ -208,4 +273,12 @@ public class SemVer implements Comparable, Serializable { } return true; } + + private static int bool2Int(boolean expression) { + return expression ? 1 : 0; + } + + private static boolean isTrue(int b) { + return b != 0; + } }