Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3ce2def
Update pom.xml
mercyblitz Aug 26, 2025
0e5e451
Update ClassPathUtils.java
mercyblitz Sep 3, 2025
188b794
Update TestAnnotation.java
mercyblitz Sep 16, 2025
8c28af9
Update maven-build.yml
mercyblitz Sep 28, 2025
1d3d639
Merge pull request #192 from mercyblitz/dev
mercyblitz Sep 28, 2025
66405ee
Remove spring-core test dependency and add test sources #193
mercyblitz Sep 29, 2025
413a9df
Revert "Remove spring-core test dependency and add test sources #193"
mercyblitz Sep 29, 2025
4e75e71
Update Spring version and add spring5 profile #194
mercyblitz Sep 29, 2025
da1dfa8
Update FileUtilsTest.java
mercyblitz Sep 29, 2025
c4ec584
Update FileUtilsTest.java
mercyblitz Sep 29, 2025
6cc00dd
Refactor StringUtils.split methods and improve tests #191
mercyblitz Sep 30, 2025
df7d7d7
Fix protocol extraction in URLUtils
mercyblitz Sep 30, 2025
fed4170
Remove unnecessary sleep in testDeleteDirectoryOnIOException
mercyblitz Sep 30, 2025
d73d621
Update pom.xml
mercyblitz Oct 3, 2025
72d1219
Update pom.xml
mercyblitz Oct 4, 2025
0c17b14
Create CODE_OF_CONDUCT.md
mercyblitz Oct 4, 2025
1607fc2
Update pom.xml
mercyblitz Oct 4, 2025
d6ed398
Update pom.xml
mercyblitz Oct 4, 2025
bd57ada
Merge branch 'microsphere-projects:dev' into dev
mercyblitz Oct 4, 2025
86ce818
Update ClassDataRepositoryTest.java
mercyblitz Oct 4, 2025
5c20964
Merge branch 'dev' of https://github.com/mercyblitz/microsphere-java …
mercyblitz Oct 4, 2025
49094a2
Update pom.xml
mercyblitz Oct 4, 2025
2818838
Update StringUtils.java
mercyblitz Oct 4, 2025
2b29afd
Update StringUtilsTest.java
mercyblitz Oct 4, 2025
70770cf
Merge pull request #195 from mercyblitz/dev
mercyblitz Oct 4, 2025
9882dc3
Update StringUtilsTest.java
mercyblitz Oct 4, 2025
ce40551
Merge branch 'dev' of https://github.com/mercyblitz/microsphere-java …
mercyblitz Oct 4, 2025
d42bbbb
Fix split assertion logic in StringUtilsTest
mercyblitz Oct 4, 2025
985ddc8
Improve executor termination in FileUtilsTest
mercyblitz Oct 4, 2025
ed3ea11
Update return type of testDeleteDirectoryOnIOException0
mercyblitz Oct 4, 2025
8b07ae3
Update FileUtilsTest.java
mercyblitz Oct 4, 2025
6c42c68
Merge pull request #199 from mercyblitz/dev
mercyblitz Oct 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/maven-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ '8', '11' , '17' , '21' ]
java: [ '8', '11' , '17' , '21' , '25' ]
steps:
- name: Checkout Source
uses: actions/checkout@v4
Expand Down
48 changes: 48 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Contributor Code of Conduct

As contributors and maintainers of this project, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating
documentation, submitting pull requests or patches, and other activities.

We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

By adopting this Code of Conduct, project maintainers commit themselves to
fairly and consistently applying these principles to every aspect of managing
this project. Project maintainers who do not follow or enforce the Code of
Conduct may be permanently removed from the project team.

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting a project maintainer at [mercyblitz@gmail.com](mailto:mercyblitz@gmail.com). All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. Maintainers are
obligated to maintain confidentiality with regard to the reporter of an
incident.


This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.3.0, available at https://www.contributor-covenant.org/version/1/3/0/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.concurrent.TimeUnit.DAYS;

/**
Expand All @@ -37,8 +37,8 @@
* @see Annotation
* @since 1.0.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Retention(RUNTIME)
@Target(TYPE)
@Documented
public @interface TestAnnotation {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1315,7 +1315,7 @@ private static String findSubProtocolsString(String url) {
if (startIndex > -1) {
int endIndex = url.indexOf("://", startIndex);
if (endIndex > startIndex) {
return url.substring(startIndex, endIndex);
return url.substring(startIndex + 1, endIndex);
}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* <li>Locating the runtime URL of a class by name or type</li>
* </ul>
*
* <h3>Example Usage:</h3>
* <h3>Example Usage</h3>
* <pre>{@code
* // Get all application class paths
* Set<String> classPaths = ClassPathUtils.getClassPaths();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@
package io.microsphere.util;

import io.microsphere.annotation.Immutable;
import io.microsphere.annotation.Nonnull;
import io.microsphere.annotation.Nullable;

import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static io.microsphere.util.ArrayUtils.asArray;
import static io.microsphere.collection.CollectionUtils.isEmpty;
import static io.microsphere.util.ArrayUtils.ofArray;
import static io.microsphere.util.CharSequenceUtils.isEmpty;
import static io.microsphere.util.CharSequenceUtils.length;
import static java.lang.Character.isDigit;
Expand Down Expand Up @@ -127,7 +131,8 @@ public static boolean isNotBlank(String value) {
* @param delimiter the char used as a delimiter to split the String
* @return an array of Strings, split by the delimiter; never null
*/
public static String[] split(String value, char delimiter) {
@Nonnull
public static String[] split(@Nullable String value, char delimiter) {
return split(value, valueOf(delimiter));
}

Expand All @@ -140,7 +145,9 @@ public static String[] split(String value, char delimiter) {
* <h3>Example Usage</h3>
* <pre>{@code
* StringUtils.split(null, ",") = []
* StringUtils.split("", null) = []
* StringUtils.split("", ";") = []
* StringUtils.split("abc", "") = ["a", "b", "c"]
* StringUtils.split("a,b,c", ",") = ["a", "b", "c"]
Comment on lines 145 to 151
Copy link

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document the behavior for a non-empty String with a null delimiter (returns the whole String as a single element) to clarify the difference from empty and blank inputs.

Copilot uses AI. Check for mistakes.
* StringUtils.split("a;b;c", ",") = ["a;b;c"]
* StringUtils.split("a,,b,c", ",") = ["a", "", "b", "c"]
Comment on lines 145 to 153
Copy link

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc omits the behavior for a non-empty input with a null delimiter (returns the entire String as a single-element array). Add an example (e.g., StringUtils.split("abc", null) = ["abc"]) to fully document the null-delimiter case.

Copilot uses AI. Check for mistakes.
Expand All @@ -150,12 +157,41 @@ public static String[] split(String value, char delimiter) {
* @param delimiter the String used as a delimiter to split the String, may be null or empty
* @return an array of Strings, split by the delimiter; never null
*/
public static String[] split(String value, String delimiter) {
if (isEmpty(value) || isEmpty(delimiter)) {
@Nonnull
public static String[] split(@Nullable String value, @Nullable String delimiter) {
int length = length(value);
if (length < 1) {
return EMPTY_STRING_ARRAY;
}
StringTokenizer stringTokenizer = new StringTokenizer(value, delimiter);
return (String[]) asArray(stringTokenizer, String.class);

if (delimiter == null) {
return ofArray(value);
}

int delimiterLength = delimiter.length();

List<String> result = new ArrayList<>();

if (delimiterLength == 0) {
for (int i = 0; i < value.length(); i++) {
result.add(value.substring(i, i + 1));
}
} else {
int startIndex = 0;
int endIndex;

while ((endIndex = value.indexOf(delimiter, startIndex)) > -1) {
String part = value.substring(startIndex, endIndex);
result.add(part);
startIndex = endIndex + delimiterLength;
}
if (startIndex <= length) {
// Add rest of String, but not in case of empty input.
result.add(value.substring(startIndex));
}
Comment on lines +188 to +191
Copy link

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition 'if (startIndex <= length)' is always true here (startIndex cannot exceed length) given earlier flow and the empty-input guard at line 162, making the check redundant. Remove the conditional and add the tail element unconditionally to simplify.

Suggested change
if (startIndex <= length) {
// Add rest of String, but not in case of empty input.
result.add(value.substring(startIndex));
}
// Add rest of String, but not in case of empty input.
result.add(value.substring(startIndex));

Copilot uses AI. Check for mistakes.
}

return toStringArray(result);
}

/**
Expand Down Expand Up @@ -780,6 +816,26 @@ public static String uncapitalize(String str) {
return changeFirstCharacter(str, false);
}

/**
* Convert the given {@link Collection} into a {@code String} array.
* <p>The {@code Collection} must contain {@code String} elements only.
*
* <h3>Example Usage</h3>
* <pre>{@code
* StringUtils.toStringArray(null) = []
* StringUtils.toStringArray(new ArrayList<>()) = []
* StringUtils.toStringArray(Arrays.asList("a", "b", "c")) = ["a", "b", "c"]
* }</pre>
*
* @param collection the {@code Collection} to convert
* (potentially {@code null} or empty)
* @return the resulting {@code String} array
*/
@Nonnull
public static String[] toStringArray(@Nullable Collection<String> collection) {
return isEmpty(collection) ? EMPTY_STRING_ARRAY : collection.toArray(EMPTY_STRING_ARRAY);
}

static String changeFirstCharacter(String str, boolean capitalize) {
int len = length(str);
if (len < 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import static java.util.concurrent.Executors.newFixedThreadPool;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand Down Expand Up @@ -113,6 +114,13 @@ void testDeleteDirectoryOnNotExists() throws IOException {

@Test
void testDeleteDirectoryOnIOException() throws Exception {
ExecutorService executor = newSingleThreadExecutor();
executor.submit(this::testDeleteDirectoryOnIOException0);
shutdown(executor);
executor.awaitTermination(5, SECONDS);
}

File testDeleteDirectoryOnIOException0() throws Exception {
File testDir = createRandomTempDirectory();

ExecutorService fileCreationExecutor = newSingleThreadExecutor();
Expand Down Expand Up @@ -161,6 +169,8 @@ void testDeleteDirectoryOnIOException() throws Exception {
assertNotNull(ioExceptionReference.get());
assertFalse(creatingFile.get());
assertFalse(deletingDirectory.get());

return testDir;
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ void testGetClassNamesInClassPath() {
for (String classPath : classPaths) {
Set<String> classNames = repository.getClassNamesInClassPath(classPath, true);
assertNotNull(classNames);
assertThrows(UnsupportedOperationException.class, classNames::clear);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import org.junit.jupiter.api.Test;

import static io.microsphere.collection.ListUtils.newLinkedList;
import static io.microsphere.collection.Lists.ofList;
import static io.microsphere.constants.SymbolConstants.COMMA;
import static io.microsphere.constants.SymbolConstants.COMMA_CHAR;
import static io.microsphere.constants.SymbolConstants.DOT;
import static io.microsphere.constants.SymbolConstants.SHARP;
import static io.microsphere.constants.SymbolConstants.SPACE;
import static io.microsphere.constants.SymbolConstants.SPACE_CHAR;
import static io.microsphere.constants.SymbolConstants.VERTICAL_BAR;
import static io.microsphere.util.ArrayUtils.ofArray;
import static io.microsphere.util.CharSequenceUtils.length;
import static io.microsphere.util.CharSequenceUtilsTest.TEST_BLANK_STRING;
import static io.microsphere.util.CharSequenceUtilsTest.TEST_CSV_STRING;
import static io.microsphere.util.CharSequenceUtilsTest.TEST_EMPTY_STRING;
Expand All @@ -31,17 +33,20 @@
import static io.microsphere.util.StringUtils.substringBefore;
import static io.microsphere.util.StringUtils.substringBeforeLast;
import static io.microsphere.util.StringUtils.substringBetween;
import static io.microsphere.util.StringUtils.toStringArray;
import static io.microsphere.util.StringUtils.trimAllWhitespace;
import static io.microsphere.util.StringUtils.trimLeadingWhitespace;
import static io.microsphere.util.StringUtils.trimTrailingWhitespace;
import static io.microsphere.util.StringUtils.trimWhitespace;
import static io.microsphere.util.StringUtils.uncapitalize;
import static java.util.Collections.emptyList;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.util.StringUtils.delimitedListToStringArray;

/**
* {@link StringUtils} Test
Expand Down Expand Up @@ -81,26 +86,39 @@ void testIsNotBlank() {

@Test
void testSplit() {
String[] values = split(null, SPACE_CHAR);
assertSame(EMPTY_STRING_ARRAY, values);
assertSame(EMPTY_STRING_ARRAY, assertSplit(null, SPACE));

values = split(TEST_EMPTY_STRING, SPACE);
assertSame(EMPTY_STRING_ARRAY, values);
assertSame(EMPTY_STRING_ARRAY, assertSplit(TEST_EMPTY_STRING, SPACE));

values = split(TEST_BLANK_STRING, null);
assertSame(EMPTY_STRING_ARRAY, values);
assertSame(EMPTY_STRING_ARRAY, assertSplit(TEST_EMPTY_STRING, EMPTY_STRING));

values = split(TEST_BLANK_STRING, SPACE);
assertArrayEquals(EMPTY_STRING_ARRAY, values);
assertArrayEquals(ofArray(TEST_BLANK_STRING), assertSplit(TEST_BLANK_STRING, null));

values = split(SPACE + SPACE, SPACE);
assertArrayEquals(EMPTY_STRING_ARRAY, values);
assertArrayEquals(ofArray(TEST_BLANK_STRING), assertSplit(TEST_BLANK_STRING, EMPTY_STRING));

values = split(SPACE + SPACE + SPACE, SPACE);
assertArrayEquals(EMPTY_STRING_ARRAY, values);
assertArrayEquals(ofArray(EMPTY_STRING, EMPTY_STRING), assertSplit(TEST_BLANK_STRING, SPACE));

values = split(TEST_CSV_STRING, COMMA_CHAR);
assertArrayEquals(ofArray("a", "b", "c"), values);
assertArrayEquals(ofArray(EMPTY_STRING, EMPTY_STRING, EMPTY_STRING), assertSplit(SPACE + SPACE, SPACE));

assertArrayEquals(ofArray(EMPTY_STRING, EMPTY_STRING, EMPTY_STRING, EMPTY_STRING), assertSplit(SPACE + SPACE + SPACE, SPACE));

assertArrayEquals(ofArray("a", "b", "c"), assertSplit(TEST_CSV_STRING, COMMA));

assertArrayEquals(ofArray(TEST_CSV_STRING), assertSplit(TEST_CSV_STRING, SPACE));

assertArrayEquals(ofArray("a", "", "b", "c"), assertSplit("a, , b, c", ", "));
}

String[] assertSplit(String str, String delimiter) {
String[] values = split(str, delimiter);
int length = length(delimiter);
if (length == 1) {
assertArrayEquals(split(str, delimiter.charAt(0)), values);
} else if (length > 1) {
String[] valuesFromString = delimitedListToStringArray(str, delimiter);
assertArrayEquals(valuesFromString, values);
}
return values;
}

@Test
Expand Down Expand Up @@ -161,7 +179,10 @@ void testEndsWith() {
@Test
void testReplace() {
assertNull(replace(null, null, null));

assertEquals(TEST_EMPTY_STRING, replace(TEST_EMPTY_STRING, null, null));
Copy link

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate assertion (line 184 repeats line 180) adds no additional coverage; remove the duplicate to reduce noise.

Copilot uses AI. Check for mistakes.
assertEquals(TEST_EMPTY_STRING, replace(TEST_EMPTY_STRING, "null", null));
assertEquals(TEST_CSV_STRING, replace(TEST_CSV_STRING, "null", "null"));
assertEquals(TEST_EMPTY_STRING, replace(TEST_EMPTY_STRING, TEST_EMPTY_STRING, null));
assertEquals(TEST_EMPTY_STRING, replace(TEST_EMPTY_STRING, TEST_EMPTY_STRING, TEST_EMPTY_STRING, 0));

Expand All @@ -173,6 +194,9 @@ void testReplace() {
assertEquals("a|b|c", replace(TEST_CSV_STRING, COMMA, VERTICAL_BAR));
assertEquals("a|b|c", replace(TEST_CSV_STRING, COMMA, VERTICAL_BAR, 100));
assertEquals("a|b,c", replace(TEST_CSV_STRING, COMMA, VERTICAL_BAR, 1));

assertEquals("abc", replace(TEST_CSV_STRING, COMMA, EMPTY_STRING));

}

@Test
Expand Down Expand Up @@ -227,6 +251,7 @@ void testSubstringBeforeLast() {
assertSame(TEST_EMPTY_STRING, substringBeforeLast(TEST_EMPTY_STRING, null));
assertSame(TEST_CSV_STRING, substringBeforeLast(TEST_CSV_STRING, null));
assertSame(TEST_CSV_STRING, substringBeforeLast(TEST_CSV_STRING, TEST_EMPTY_STRING));
assertSame(TEST_CSV_STRING, substringBeforeLast(TEST_CSV_STRING, SHARP));

assertEquals("a,b", substringBeforeLast(TEST_CSV_STRING, COMMA));
assertEquals("a,", substringBeforeLast(TEST_CSV_STRING, "b"));
Expand All @@ -246,6 +271,7 @@ void testSubstringAfterLast() {
assertEquals(",c", substringAfterLast(TEST_CSV_STRING, "b"));
assertEquals("c", substringAfterLast(TEST_CSV_STRING, COMMA));
assertEquals(TEST_EMPTY_STRING, substringAfterLast(TEST_CSV_STRING, "c"));
assertEquals(TEST_EMPTY_STRING, substringAfterLast(TEST_CSV_STRING, SHARP));
}

@Test
Expand Down Expand Up @@ -326,4 +352,12 @@ void testUncapitalize() {
assertSame("hello world", uncapitalize("hello world"));
}

@Test
void testToStringArray() {
assertSame(EMPTY_STRING_ARRAY, toStringArray(null));
assertSame(EMPTY_STRING_ARRAY, toStringArray(emptyList()));
assertSame(EMPTY_STRING_ARRAY, toStringArray(newLinkedList()));
assertArrayEquals(ofArray("a", "b", "c"), toStringArray(ofList("a", "b", "c")));
}

}
Loading