diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/StringTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/StringTools.java index 4000296..58d84de 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/StringTools.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/StringTools.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,16 @@ package xyz.zhouxy.plusone.commons.util; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Objects; import javax.annotation.Nullable; +import com.google.common.annotations.Beta; + +import xyz.zhouxy.plusone.commons.constant.PatternConsts; + /** * StringTools * @@ -45,6 +51,18 @@ public class StringTools { return false; } + public static boolean isBlank(@Nullable String cs) { + if (cs == null || cs.isEmpty()) { + return true; + } + for (int i = 0; i < cs.length(); i++) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + return true; + } + public static String repeat(String str, int times) { return repeat(str, times, Integer.MAX_VALUE); } @@ -54,6 +72,29 @@ public class StringTools { return String.valueOf(ArrayTools.repeat(str.toCharArray(), times, maxLength)); } + public static boolean isNotEmpty(@Nullable final String cs) { + return cs != null && !cs.isEmpty(); + } + + public static boolean isEmpty(@Nullable final String cs) { + return cs == null || cs.isEmpty(); + } + + @Beta + public static boolean isEmail(@Nullable final String cs) { + return RegexTools.matches(cs, PatternConsts.EMAIL); + } + + @Beta + public static boolean isURL(@Nullable final String cs) { + try { + new URL(cs); + } catch (MalformedURLException e) { + return false; + } + return true; + } + private StringTools() { throw new IllegalStateException("Utility class"); } diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/StringToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/StringToolsTests.java index 341b9b1..7bc2219 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/StringToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/StringToolsTests.java @@ -32,6 +32,10 @@ import org.junit.jupiter.api.Test; public class StringToolsTests { + // ================================ + // #region - isNotBlank + // ================================ + @Test void isNotBlank_NullString_ReturnsFalse() { assertFalse(StringTools.isNotBlank(null)); @@ -52,6 +56,206 @@ class StringToolsTests { assertTrue(StringTools.isNotBlank("Hello")); } + // ================================ + // #endregion - isNotBlank + // ================================ + + // ================================ + // #region - isBlank + // ================================ + + @Test + void isBlank_NullString_ReturnsTrue() { + assertTrue(StringTools.isBlank(null)); + } + + @Test + void isBlank_EmptyString_ReturnsTrue() { + assertTrue(StringTools.isBlank("")); + } + + @Test + void isBlank_WhitespaceString_ReturnsTrue() { + assertTrue(StringTools.isBlank(" ")); + } + + @Test + void isBlank_NonWhitespaceString_ReturnsFalse() { + assertFalse(StringTools.isBlank("Hello")); + } + + // ================================ + // #endregion - isBlank + // ================================ + + // ================================ + // #region - isNotEmpty + // ================================ + + @Test + void isNotEmpty_NullString_ReturnsFalse() { + assertFalse(StringTools.isNotEmpty(null)); + } + + @Test + void isNotEmpty_EmptyString_ReturnsFalse() { + assertFalse(StringTools.isNotEmpty("")); + } + + @Test + void isNotEmpty_WhitespaceString_ReturnsTrue() { + assertTrue(StringTools.isNotEmpty(" ")); + } + + @Test + void isNotEmpty_NonWhitespaceString_ReturnsTrue() { + assertTrue(StringTools.isNotEmpty("Hello")); + } + + // ================================ + // #endregion - isNotEmpty + // ================================ + + // ================================ + // #region - isEmpty + // ================================ + + @Test + void isEmpty_NullString_ReturnsTrue() { + assertTrue(StringTools.isEmpty(null)); + } + + @Test + void isEmpty_EmptyString_ReturnsTrue() { + assertTrue(StringTools.isEmpty("")); + } + + @Test + void isEmpty_WhitespaceString_ReturnsFalse() { + assertFalse(StringTools.isEmpty(" ")); + } + + @Test + void isEmpty_NonWhitespaceString_ReturnsFalse() { + assertFalse(StringTools.isEmpty("Hello")); + } + + // ================================ + // #endregion - isEmpty + // ================================ + + // ================================ + // #region - EMAIL + // ================================ + + @Test + public void testValidEmails() { + assertTrue(StringTools.isEmail("test@example.com")); + assertTrue(StringTools.isEmail("user.name+tag+sorting@example.com")); + assertTrue(StringTools.isEmail("user@sub.example.com")); + assertTrue(StringTools.isEmail("user@123.123.123.123")); + } + + @Test + public void testInvalidEmails() { + assertFalse(StringTools.isEmail(".username@example.com")); + assertFalse(StringTools.isEmail("@missingusername.com")); + assertFalse(StringTools.isEmail("plainaddress")); + assertFalse(StringTools.isEmail("username..username@example.com")); + assertFalse(StringTools.isEmail("username.@example.com")); + assertFalse(StringTools.isEmail("username@-example.com")); + assertFalse(StringTools.isEmail("username@-example.com")); + assertFalse(StringTools.isEmail("username@.com.com")); + assertFalse(StringTools.isEmail("username@.com.my")); + assertFalse(StringTools.isEmail("username@.com")); + assertFalse(StringTools.isEmail("username@com.")); + assertFalse(StringTools.isEmail("username@com")); + assertFalse(StringTools.isEmail("username@example..com")); + assertFalse(StringTools.isEmail("username@example.com-")); + assertFalse(StringTools.isEmail("username@example.com.")); + assertFalse(StringTools.isEmail("username@example")); + } + + // ================================ + // #endregion - EMAIL + // ================================ + + // ================================ + // #region - isURL + // ================================ + + /** + * TC1: 验证标准HTTP协议URL + */ + @Test + void isURL_ValidHttpURL_ReturnsTrue() { + assertTrue(StringTools.isURL("http://example.com")); + } + + /** + * TC2: 验证带路径参数的HTTPS复杂URL + */ + @Test + void isURL_ValidHttpsComplexURL_ReturnsTrue() { + assertTrue(StringTools.isURL("https://test.com:8080/api/v1?param=value#anchor")); + } + + /** + * TC3: 验证FTP协议URL + */ + @Test + void isURL_ValidFtpURL_ReturnsTrue() { + assertTrue(StringTools.isURL("ftp://files.example.com/directory/")); + } + + /** + * TC4: 验证非法协议处理 + */ + @Test + void isURL_InvalidProtocol_ReturnsFalse() { + assertFalse(StringTools.isURL("httpx://invalid.com")); + } + + /** + * TC5: 验证null输入处理 + */ + @Test + void isURL_NullInput_ReturnsFalse() { + assertFalse(StringTools.isURL(null)); + } + + /** + * TC6: 验证空字符串处理 + */ + @Test + void isURL_EmptyString_ReturnsFalse() { + assertFalse(StringTools.isURL(StringTools.EMPTY_STRING)); + } + + /** + * TC7: 验证缺失协议头处理 + */ + @Test + void isURL_MissingProtocol_ReturnsFalse() { + assertFalse(StringTools.isURL("www.example.com/path")); + } + + /** + * TC8: 验证未编码特殊字符处理 + */ + @Test + void isURL_UnencodedSpecialChars_ReturnsTrue() { + assertTrue(StringTools.isURL("http://example.com/测试")); + } + + // ================================ + // #endregion - isURL + // ================================ + + // ================================ + // #region - repeat + // ================================ + @Test void repeat_NullString_ThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { @@ -90,6 +294,10 @@ class StringToolsTests { assertEquals("", StringTools.repeat("Hello", 0)); } + // ================================ + // #endregion - repeat + // ================================ + @Test void test_constructor_isNotAccessible_ThrowsIllegalStateException() { Constructor[] constructors = StringTools.class.getDeclaredConstructors();