Skip to content

Commit 4e79663

Browse files
chore: merge main into release [skip ci]
2 parents 9e7a152 + 346b939 commit 4e79663

25 files changed

Lines changed: 2023 additions & 3 deletions

microsphere-java-core/src/main/java/io/microsphere/collection/ListUtils.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,22 @@ public static <T> void forEach(List<T> values, Consumer<T> consumer) {
668668
forEach(values, (i, e) -> consumer.accept(e));
669669
}
670670

671+
/**
672+
* Adds the specified element to the list if it is not already present.
673+
*
674+
* <h3>Example Usage</h3>
675+
* <pre>{@code
676+
* List<String> list = new ArrayList<>(Arrays.asList("a", "b"));
677+
* ListUtils.addIfAbsent(list, "c"); // returns true, list = ["a", "b", "c"]
678+
* ListUtils.addIfAbsent(list, "a"); // returns false, list unchanged
679+
* }</pre>
680+
*
681+
* @param values the list to add the element to
682+
* @param newValue the element to add
683+
* @param <T> the type of elements in the list
684+
* @return {@code true} if the element was added, {@code false} if already present
685+
* @since 1.0.0
686+
*/
671687
public static <T> boolean addIfAbsent(List<T> values, T newValue) {
672688
return values.contains(newValue) ? false : values.add(newValue);
673689
}

microsphere-java-core/src/main/java/io/microsphere/util/AnnotationUtils.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -739,12 +739,20 @@ public static List<Annotation> getDeclaredAnnotations(AnnotatedElement annotated
739739
}
740740

741741
/**
742-
* Find all directly declared annotations of the annotated element with filters, not including
743-
* meta annotations.
742+
* Retrieves all declared annotations from the specified {@link AnnotatedElement}, including those
743+
* from its hierarchy, but excluding meta-annotations, applying the given filters.
744+
*
745+
* <h3>Example Usage</h3>
746+
* <pre>{@code
747+
* List<Annotation> all = AnnotationUtils.findAllDeclaredAnnotations(
748+
* MyClass.class, annotation -> true);
749+
* System.out.println(all); // all declared annotations on MyClass and its superclasses
750+
* }</pre>
744751
*
745752
* @param annotatedElement the annotated element
746753
* @param annotationsToFilter the annotations to filter
747754
* @return non-null read-only {@link List}
755+
* @since 1.0.0
748756
*/
749757
/**
750758
* Retrieves all declared annotations from the specified {@link AnnotatedElement}, including those from its hierarchy,
@@ -1220,10 +1228,20 @@ public static Map<String, Object> findAttributesMap(Annotation annotation, Strin
12201228

12211229
/**
12221230
* Find the attributes map from the specified annotation by the {@link Method attribute method}
1231+
* filters.
1232+
*
1233+
* <h3>Example Usage</h3>
1234+
* <pre>{@code
1235+
* Annotation annotation = ExampleClass.class.getAnnotation(CustomAnnotation.class);
1236+
* Map<String, Object> attrs = AnnotationUtils.findAttributesMap(
1237+
* annotation, method -> !"toString".equals(method.getName()));
1238+
* System.out.println(attrs); // {value=example, count=5}
1239+
* }</pre>
12231240
*
12241241
* @param annotation the specified annotation
12251242
* @param attributesToFilter the attribute methods to filter
12261243
* @return non-null read-only {@link Map}
1244+
* @since 1.0.0
12271245
*/
12281246
@Nonnull
12291247
@Immutable

microsphere-java-core/src/main/java/io/microsphere/util/ArrayUtils.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1873,6 +1873,21 @@ public static boolean contains(boolean[] values, boolean value) {
18731873
return false;
18741874
}
18751875

1876+
/**
1877+
* Checks if the specified byte array contains the given value using binary search.
1878+
*
1879+
* <h3>Example Usage</h3>
1880+
* <pre>{@code
1881+
* byte[] sorted = {1, 2, 3, 4, 5};
1882+
* boolean found = ArrayUtils.contains(sorted, (byte) 3); // returns true
1883+
* boolean notFound = ArrayUtils.contains(sorted, (byte) 6); // returns false
1884+
* }</pre>
1885+
*
1886+
* @param values the sorted byte array to search
1887+
* @param value the value to find
1888+
* @return {@code true} if the array contains the value, {@code false} otherwise
1889+
* @since 1.0.0
1890+
*/
18761891
public static boolean contains(byte[] values, byte value) {
18771892
return binarySearch(values, value) > -1;
18781893
}

microsphere-java-core/src/main/java/io/microsphere/util/BaseUtils.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@
2525
@Deprecated
2626
public abstract class BaseUtils {
2727

28+
/**
29+
* The base class for utility classes. Instantiation is not supported.
30+
*
31+
* <h3>Example Usage</h3>
32+
* <pre>{@code
33+
* // Subclass BaseUtils to create a utility class:
34+
* public class MyUtils extends BaseUtils {
35+
* // utility methods
36+
* }
37+
* }</pre>
38+
*
39+
* @throws IllegalStateException always, as utility classes should not be instantiated
40+
* @since 1.0.0
41+
*/
2842
protected BaseUtils() throws IllegalStateException {
2943
throw new IllegalStateException("Not Supported!");
3044
}

microsphere-java-core/src/main/java/io/microsphere/util/CharSequenceComparator.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,23 @@ public class CharSequenceComparator implements Comparator<CharSequence> {
4646
private CharSequenceComparator() {
4747
}
4848

49+
/**
50+
* Compares two {@link CharSequence} instances lexicographically.
51+
* Null values are considered less than non-null values. Two null values are considered equal.
52+
*
53+
* <h3>Example Usage</h3>
54+
* <pre>{@code
55+
* int result = CharSequenceComparator.INSTANCE.compare("apple", "banana"); // negative
56+
* int equal = CharSequenceComparator.INSTANCE.compare("same", "same"); // 0
57+
* int nullFirst = CharSequenceComparator.INSTANCE.compare(null, "text"); // negative
58+
* }</pre>
59+
*
60+
* @param c1 the first {@link CharSequence} to compare; may be {@code null}
61+
* @param c2 the second {@link CharSequence} to compare; may be {@code null}
62+
* @return a negative integer, zero, or a positive integer as {@code c1} is less than,
63+
* equal to, or greater than {@code c2}
64+
* @since 1.0.0
65+
*/
4966
@Override
5067
public int compare(CharSequence c1, CharSequence c2) {
5168
String string1 = toString(c1);

microsphere-java-core/src/main/java/io/microsphere/util/ClassLoaderUtils.java

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,22 @@ public static ClassLoader getDefaultClassLoader() {
216216
return getDefaultClassLoader(classLoader);
217217
}
218218

219+
/**
220+
* Returns the given {@link ClassLoader} if it is non-null, otherwise falls back to
221+
* the {@linkplain ClassLoader#getSystemClassLoader() system ClassLoader}. A {@code null}
222+
* classLoader typically indicates the bootstrap ClassLoader.
223+
*
224+
* <h3>Example Usage</h3>
225+
* <pre>{@code
226+
* ClassLoader cl = Thread.currentThread().getContextClassLoader();
227+
* ClassLoader effective = ClassLoaderUtils.getDefaultClassLoader(cl);
228+
* // If cl is null (bootstrap), effective will be the system ClassLoader
229+
* }</pre>
230+
*
231+
* @param classLoader the {@link ClassLoader} to use, may be {@code null}
232+
* @return the provided classLoader if non-null, or the system ClassLoader otherwise
233+
* @since 1.0.0
234+
*/
219235
static ClassLoader getDefaultClassLoader(ClassLoader classLoader) {
220236
// classLoader is null indicates the bootstrap ClassLoader
221237
return classLoader == null ? getSystemClassLoader() : classLoader;
@@ -546,6 +562,29 @@ public static Class<?> loadClass(@Nullable ClassLoader classLoader, @Nullable St
546562
return doLoadClass(actualClassLoader, className);
547563
}
548564

565+
/**
566+
* Loads a {@link Class} with the specified name using the given {@link ClassLoader}.
567+
* Returns {@code null} for blank class names. If the class cannot be loaded, this method
568+
* attempts to resolve it as a primitive type before returning {@code null}.
569+
*
570+
* <h3>Example Usage</h3>
571+
* <pre>{@code
572+
* ClassLoader cl = Thread.currentThread().getContextClassLoader();
573+
* Class<?> klass = ClassLoaderUtils.doLoadClass(cl, "java.lang.String");
574+
* System.out.println(klass); // class java.lang.String
575+
*
576+
* Class<?> primitive = ClassLoaderUtils.doLoadClass(cl, "int");
577+
* System.out.println(primitive); // int
578+
*
579+
* Class<?> missing = ClassLoaderUtils.doLoadClass(cl, " ");
580+
* System.out.println(missing); // null
581+
* }</pre>
582+
*
583+
* @param classLoader the {@link ClassLoader} used to load the class
584+
* @param className the fully qualified name of the class to load, may be blank
585+
* @return the loaded {@link Class}, or {@code null} if the name is blank or the class cannot be found
586+
* @since 1.0.0
587+
*/
549588
protected static Class<?> doLoadClass(ClassLoader classLoader, String className) {
550589
if (isBlank(className)) {
551590
return null;
@@ -1000,6 +1039,24 @@ public static Set<ClassLoader> getInheritableClassLoaders(@Nullable ClassLoader
10001039
return unmodifiableSet(doGetInheritableClassLoaders(actualClassLoader));
10011040
}
10021041

1042+
/**
1043+
* Traverses the parent chain of the given {@link ClassLoader} and collects every
1044+
* ClassLoader in the hierarchy into a {@link Set}, starting from the provided
1045+
* classLoader up to (but not including) the bootstrap ClassLoader ({@code null} parent).
1046+
*
1047+
* <h3>Example Usage</h3>
1048+
* <pre>{@code
1049+
* ClassLoader appCl = ClassLoaderUtils.class.getClassLoader();
1050+
* Set<ClassLoader> hierarchy = ClassLoaderUtils.doGetInheritableClassLoaders(appCl);
1051+
* // hierarchy contains appCl and its parent (e.g. PlatformClassLoader)
1052+
* hierarchy.forEach(cl -> System.out.println(cl));
1053+
* }</pre>
1054+
*
1055+
* @param classLoader the starting {@link ClassLoader}; must not be {@code null}
1056+
* @return a non-null {@link Set} of ClassLoaders in the inheritance chain
1057+
* @throws NullPointerException if {@code classLoader} is {@code null}
1058+
* @since 1.0.0
1059+
*/
10031060
@Nonnull
10041061
static Set<ClassLoader> doGetInheritableClassLoaders(ClassLoader classLoader) throws NullPointerException {
10051062
Set<ClassLoader> classLoadersSet = newLinkedHashSet();
@@ -1752,6 +1809,25 @@ static ClassLoader getCallerClassLoader(int invocationFrame) {
17521809
return classLoader;
17531810
}
17541811

1812+
/**
1813+
* Resolves the effective {@link ClassLoader} to use. Returns the provided classLoader
1814+
* if it is non-null; otherwise delegates to {@link #getDefaultClassLoader()} to obtain
1815+
* a suitable default.
1816+
*
1817+
* <h3>Example Usage</h3>
1818+
* <pre>{@code
1819+
* ClassLoader provided = new URLClassLoader(new URL[0]);
1820+
* ClassLoader result = ClassLoaderUtils.findClassLoader(provided);
1821+
* // result == provided
1822+
*
1823+
* ClassLoader fallback = ClassLoaderUtils.findClassLoader(null);
1824+
* // fallback is the thread-context or system ClassLoader
1825+
* }</pre>
1826+
*
1827+
* @param classLoader the {@link ClassLoader} to use, may be {@code null}
1828+
* @return the provided classLoader if non-null, or the default ClassLoader otherwise
1829+
* @since 1.0.0
1830+
*/
17551831
@Nullable
17561832
static ClassLoader findClassLoader(@Nullable ClassLoader classLoader) {
17571833
return classLoader == null ? getDefaultClassLoader() : classLoader;
@@ -1775,11 +1851,50 @@ static Class<?> invokeFindLoadedClassMethod(ClassLoader classLoader, String clas
17751851
return loadedClass;
17761852
}
17771853

1854+
/**
1855+
* Logs an error message when the reflective invocation of
1856+
* {@link ClassLoader#findLoadedClass(String)} fails. The log includes the class name,
1857+
* the ClassLoader instance, and the current JVM vendor and version for diagnostics.
1858+
*
1859+
* <h3>Example Usage</h3>
1860+
* <pre>{@code
1861+
* try {
1862+
* // attempt reflective findLoadedClass invocation
1863+
* } catch (Throwable e) {
1864+
* ClassLoaderUtils.logOnFindLoadedClassInvocationFailed(
1865+
* Thread.currentThread().getContextClassLoader(),
1866+
* "com.example.MyClass", e);
1867+
* }
1868+
* }</pre>
1869+
*
1870+
* @param classLoader the {@link ClassLoader} on which the invocation was attempted
1871+
* @param className the name of the class that was being looked up
1872+
* @param e the {@link Throwable} that caused the failure
1873+
* @since 1.0.0
1874+
*/
17781875
static void logOnFindLoadedClassInvocationFailed(ClassLoader classLoader, String className, Throwable e) {
17791876
logger.error("The findLoadedClass(className : '{}' : String) method of java.lang.ClassLoader[{}] can't be invoked in the current JVM[vendor : {} , version : {}]",
17801877
className, classLoader, JAVA_VENDOR, JAVA_VERSION, e);
17811878
}
17821879

1880+
/**
1881+
* Builds a cache key by concatenating the class name with the hash code of the given
1882+
* {@link ClassLoader}. This key uniquely identifies a class within a specific ClassLoader
1883+
* for caching purposes.
1884+
*
1885+
* <h3>Example Usage</h3>
1886+
* <pre>{@code
1887+
* ClassLoader cl = Thread.currentThread().getContextClassLoader();
1888+
* String key = ClassLoaderUtils.buildCacheKey(cl, "com.example.MyClass");
1889+
* // key is something like "com.example.MyClass1234567890"
1890+
* System.out.println(key);
1891+
* }</pre>
1892+
*
1893+
* @param classLoader the {@link ClassLoader} whose hash code is appended to the key
1894+
* @param className the fully qualified class name
1895+
* @return a cache key string composed of the class name and ClassLoader hash code
1896+
* @since 1.0.0
1897+
*/
17831898
static String buildCacheKey(ClassLoader classLoader, String className) {
17841899
String cacheKey = className + classLoader.hashCode();
17851900
return cacheKey;
@@ -1829,6 +1944,21 @@ boolean supports(String name) {
18291944
return true;
18301945
}
18311946

1947+
/**
1948+
* Returns the resource name as-is without any transformation, since
1949+
* the {@code DEFAULT} resource type applies no normalization.
1950+
*
1951+
* <h3>Example Usage</h3>
1952+
* <pre>{@code
1953+
* String name = "META-INF/services/com.example.MyService";
1954+
* String normalized = ResourceType.DEFAULT.normalize(name);
1955+
* // normalized is "META-INF/services/com.example.MyService"
1956+
* }</pre>
1957+
*
1958+
* @param name the resource name to normalize
1959+
* @return the same resource name, unchanged
1960+
* @since 1.0.0
1961+
*/
18321962
@Override
18331963
public String normalize(String name) {
18341964
return name;
@@ -1853,6 +1983,24 @@ boolean supports(String name) {
18531983
return endsWith(name, CLASS_EXTENSION);
18541984
}
18551985

1986+
/**
1987+
* Normalizes a class resource name by replacing package-separator dots with
1988+
* slashes and ensuring the name ends with the {@code .class} extension.
1989+
* Returns {@code null} if the input is {@code null}.
1990+
*
1991+
* <h3>Example Usage</h3>
1992+
* <pre>{@code
1993+
* String normalized = ResourceType.CLASS.normalize("com.example.MyClass.class");
1994+
* // normalized is "com/example/MyClass.class"
1995+
*
1996+
* String withoutExt = ResourceType.CLASS.normalize("com.example.MyClass");
1997+
* // withoutExt is "com/example/MyClass.class"
1998+
* }</pre>
1999+
*
2000+
* @param name the class resource name to normalize, may be {@code null}
2001+
* @return the normalized path-style class resource name, or {@code null} if {@code name} is {@code null}
2002+
* @since 1.0.0
2003+
*/
18562004
@Override
18572005
public String normalize(String name) {
18582006
if (name == null) {
@@ -1887,6 +2035,24 @@ boolean supports(String name) {
18872035
return !CLASS.supports(name) && !contains(name, SLASH) && !contains(name, BACK_SLASH);
18882036
}
18892037

2038+
/**
2039+
* Normalizes a package resource name by replacing dots with slashes and
2040+
* ensuring the result ends with a trailing slash. Returns {@code null}
2041+
* if the input is {@code null}.
2042+
*
2043+
* <h3>Example Usage</h3>
2044+
* <pre>{@code
2045+
* String normalized = ResourceType.PACKAGE.normalize("com.example.mypackage");
2046+
* // normalized is "com/example/mypackage/"
2047+
*
2048+
* String alreadySlashed = ResourceType.PACKAGE.normalize("com/example/mypackage/");
2049+
* // alreadySlashed is "com/example/mypackage/"
2050+
* }</pre>
2051+
*
2052+
* @param name the dot-separated package name to normalize, may be {@code null}
2053+
* @return the slash-separated package path ending with a slash, or {@code null} if {@code name} is {@code null}
2054+
* @since 1.0.0
2055+
*/
18902056
@Override
18912057
String normalize(String name) {
18922058
if (name == null) {

microsphere-java-core/src/main/java/io/microsphere/util/ClassPathUtils.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ private static Set<String> initClassPaths() {
7676
return resolveClassPaths(true, runtimeMXBean::getClassPath);
7777
}
7878

79+
/**
80+
* Resolves class paths from the given supplier if class path is supported.
81+
*
82+
* <h3>Example Usage</h3>
83+
* <pre>{@code
84+
* Set<String> paths = ClassPathUtils.resolveClassPaths(true, () -> "/lib/a.jar:/lib/b.jar");
85+
* // paths contains ["/lib/a.jar", "/lib/b.jar"]
86+
*
87+
* Set<String> empty = ClassPathUtils.resolveClassPaths(false, () -> "");
88+
* // empty set returned when not supported
89+
* }</pre>
90+
*
91+
* @param classPathSupported whether the class path is supported
92+
* @param classPathSupplier the supplier providing the class path string
93+
* @return a set of resolved class paths, or an empty set if not supported
94+
* @since 1.0.0
95+
*/
7996
static Set<String> resolveClassPaths(boolean classPathSupported, Supplier<String> classPathSupplier) {
8097
if (classPathSupported) {
8198
return resolveClassPaths(classPathSupplier.get());

0 commit comments

Comments
 (0)