This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 2aaba20a81 Utility class cleanup
2aaba20a81 is described below
commit 2aaba20a81fa498952edd34450166c5cea480639
Author: James Bognar <[email protected]>
AuthorDate: Fri Oct 31 10:48:47 2025 -0400
Utility class cleanup
---
.../juneau/assertions/FluentObjectAssertion.java | 2 +-
.../juneau/common/io}/StringBuilderWriter.java | 2 +-
.../org/apache/juneau/common/reflect}/Setter.java | 4 +-
.../org/apache/juneau/common/utils/ClassUtils.java | 106 ++++++++++++++++
.../java/org/apache/juneau/common/utils/Utils.java | 106 ++++++++++++++++
.../main/java/org/apache/juneau/cp/BeanStore.java | 4 +-
.../java/org/apache/juneau/cp/BeanStoreEntry.java | 2 +-
.../java/org/apache/juneau/internal/Utils2.java | 13 --
.../main/java/org/apache/juneau/xml/XmlUtils.java | 1 +
.../apache/juneau/rest/widget/MenuItemWidget.java | 1 +
.../juneau/assertions/BeanAssertion_Test.java | 6 +-
.../juneau/common/utils/ClassUtils_Test.java | 138 +++++++++++++++++++++
.../java/org/apache/juneau/cp/BeanStore_Test.java | 9 +-
.../juneau/utils/StringBuilderWriterTest.java | 1 +
14 files changed, 367 insertions(+), 28 deletions(-)
diff --git
a/juneau-core/juneau-assertions/src/main/java/org/apache/juneau/assertions/FluentObjectAssertion.java
b/juneau-core/juneau-assertions/src/main/java/org/apache/juneau/assertions/FluentObjectAssertion.java
index d0d39d5306..18e309b450 100644
---
a/juneau-core/juneau-assertions/src/main/java/org/apache/juneau/assertions/FluentObjectAssertion.java
+++
b/juneau-core/juneau-assertions/src/main/java/org/apache/juneau/assertions/FluentObjectAssertion.java
@@ -435,7 +435,7 @@ public class FluentObjectAssertion<T,R> extends
FluentAssertion<R> {
public R isSame(T value) throws AssertionError {
if (this.value == value)
return returns();
- throw error(MSG_notTheSameValue, value, Utils2.identity(value),
this.value, Utils2.identity(this.value));
+ throw error(MSG_notTheSameValue, value, identity(value),
this.value, identity(this.value));
}
/**
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringBuilderWriter.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/io/StringBuilderWriter.java
similarity index 98%
rename from
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringBuilderWriter.java
rename to
juneau-core/juneau-common/src/main/java/org/apache/juneau/common/io/StringBuilderWriter.java
index b54f72883d..f4705adcb1 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringBuilderWriter.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/io/StringBuilderWriter.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.juneau.internal;
+package org.apache.juneau.common.io;
import java.io.*;
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/Setter.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/Setter.java
similarity index 96%
rename from
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/Setter.java
rename to
juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/Setter.java
index c5164372f5..e1ff6fdc90 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/Setter.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/Setter.java
@@ -14,12 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.juneau.internal;
+package org.apache.juneau.common.reflect;
import java.lang.reflect.*;
-import org.apache.juneau.common.reflect.*;
-
/**
* Encapsulate a bean setter method that may be a method or field.
*
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/ClassUtils.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/ClassUtils.java
index cd86659e62..28b8a8fab7 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/ClassUtils.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/ClassUtils.java
@@ -217,6 +217,31 @@ public class ClassUtils {
/**
* Returns the fully-qualified class name for the specified object.
*
+ * <p>
+ * This method returns the canonical JVM class name including the full
package path.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Regular classes</jc>
+ * className(String.<jk>class</jk>); <jc>//
"java.lang.String"</jc>
+ * className(<jk>new</jk> HashMap<>());
<jc>// "java.util.HashMap"</jc>
+ *
+ * <jc>// Inner classes</jc>
+ * className(Map.Entry.<jk>class</jk>); <jc>//
"java.util.Map$Entry"</jc>
+ *
+ * <jc>// Primitives</jc>
+ * className(<jk>int</jk>.<jk>class</jk>);
<jc>// "int"</jc>
+ * className(<jk>boolean</jk>.<jk>class</jk>);
<jc>// "boolean"</jc>
+ *
+ * <jc>// Arrays</jc>
+ * className(String[].<jk>class</jk>); <jc>//
"[Ljava.lang.String;"</jc>
+ * className(<jk>int</jk>[].<jk>class</jk>);
<jc>// "[I"</jc>
+ * className(String[][].<jk>class</jk>); <jc>//
"[[Ljava.lang.String;"</jc>
+ *
+ * <jc>// Null</jc>
+ * className(<jk>null</jk>); <jc>//
null</jc>
+ * </p>
+ *
* @param value The object to get the class name for.
* @return The name of the class or <jk>null</jk> if the value was null.
*/
@@ -227,6 +252,32 @@ public class ClassUtils {
/**
* Returns the simple (non-qualified) class name for the specified
object.
*
+ * <p>
+ * This method returns only the simple class name without any package
or outer class information.
+ * For inner classes, only the innermost class name is returned.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Regular classes</jc>
+ * simpleClassName(String.<jk>class</jk>); <jc>//
"String"</jc>
+ * simpleClassName(<jk>new</jk> HashMap<>());
<jc>// "HashMap"</jc>
+ *
+ * <jc>// Inner classes</jc>
+ * simpleClassName(Map.Entry.<jk>class</jk>); <jc>//
"Entry"</jc>
+ *
+ * <jc>// Primitives</jc>
+ * simpleClassName(<jk>int</jk>.<jk>class</jk>);
<jc>// "int"</jc>
+ * simpleClassName(<jk>boolean</jk>.<jk>class</jk>);
<jc>// "boolean"</jc>
+ *
+ * <jc>// Arrays</jc>
+ * simpleClassName(String[].<jk>class</jk>); <jc>//
"String[]"</jc>
+ * simpleClassName(<jk>int</jk>[].<jk>class</jk>);
<jc>// "int[]"</jc>
+ * simpleClassName(String[][].<jk>class</jk>); <jc>//
"String[][]"</jc>
+ *
+ * <jc>// Null</jc>
+ * simpleClassName(<jk>null</jk>); <jc>//
null</jc>
+ * </p>
+ *
* @param value The object to get the simple class name for.
* @return The simple name of the class or <jk>null</jk> if the value
was null.
*/
@@ -234,6 +285,61 @@ public class ClassUtils {
return value == null ? null : value instanceof Class<?> ?
((Class<?>)value).getSimpleName() : value.getClass().getSimpleName();
}
+ /**
+ * Returns the simple qualified class name for the specified object.
+ *
+ * <p>
+ * This returns the simple class name including outer class names, but
without the package.
+ * Inner class separators ($) are replaced with dots (.).
+ * Array types are properly formatted with brackets.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Regular classes</jc>
+ * simpleQualifiedClassName(String.<jk>class</jk>);
<jc>// "String"</jc>
+ * simpleQualifiedClassName(<jk>new</jk> HashMap<>());
<jc>// "HashMap"</jc>
+ *
+ * <jc>// Inner classes</jc>
+ * simpleQualifiedClassName(Map.Entry.<jk>class</jk>);
<jc>// "Map.Entry"</jc>
+ * simpleQualifiedClassName(Outer.Inner.Deep.<jk>class</jk>);
<jc>// "Outer.Inner.Deep"</jc>
+ *
+ * <jc>// Primitives</jc>
+ * simpleQualifiedClassName(<jk>int</jk>.<jk>class</jk>);
<jc>// "int"</jc>
+ * simpleQualifiedClassName(<jk>boolean</jk>.<jk>class</jk>);
<jc>// "boolean"</jc>
+ *
+ * <jc>// Object arrays</jc>
+ * simpleQualifiedClassName(String[].<jk>class</jk>);
<jc>// "String[]"</jc>
+ * simpleQualifiedClassName(Map.Entry[].<jk>class</jk>);
<jc>// "Map.Entry[]"</jc>
+ * simpleQualifiedClassName(String[][].<jk>class</jk>);
<jc>// "String[][]"</jc>
+ *
+ * <jc>// Primitive arrays</jc>
+ * simpleQualifiedClassName(<jk>int</jk>[].<jk>class</jk>);
<jc>// "int[]"</jc>
+ * simpleQualifiedClassName(<jk>boolean</jk>[][].<jk>class</jk>);
<jc>// "boolean[][]"</jc>
+ *
+ * <jc>// Null</jc>
+ * simpleQualifiedClassName(<jk>null</jk>);
<jc>// null</jc>
+ * </p>
+ *
+ * @param value The object to get the simple qualified class name for.
+ * @return The simple qualified name of the class or <jk>null</jk> if
the value was null.
+ */
+ public static String simpleQualifiedClassName(Object value) {
+ if (value == null)
+ return null;
+ var clazz = value instanceof Class<?> ? ((Class<?>)value) :
value.getClass();
+
+ // Handle array types by recursively getting component type
+ if (clazz.isArray()) {
+ return
simpleQualifiedClassName(clazz.getComponentType()) + "[]";
+ }
+
+ // Handle non-array types
+ var className = clazz.getName();
+ var lastDot = className.lastIndexOf('.');
+ var simpleName = lastDot == -1 ? className :
className.substring(lastDot + 1);
+ return simpleName.replace('$', '.');
+ }
+
/**
* Returns the class types for the specified arguments.
*
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/Utils.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/Utils.java
index 5d61b6ecc1..c556ed88bf 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/Utils.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/Utils.java
@@ -923,6 +923,31 @@ public class Utils {
/**
* Shortcut for calling {@link ClassUtils#className(Object)}.
*
+ * <p>
+ * Returns the fully-qualified class name including the full package
path.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Regular classes</jc>
+ * cn(String.<jk>class</jk>); <jc>//
"java.lang.String"</jc>
+ * cn(<jk>new</jk> HashMap<>()); <jc>//
"java.util.HashMap"</jc>
+ *
+ * <jc>// Inner classes</jc>
+ * cn(Map.Entry.<jk>class</jk>); <jc>//
"java.util.Map$Entry"</jc>
+ *
+ * <jc>// Primitives</jc>
+ * cn(<jk>int</jk>.<jk>class</jk>); <jc>//
"int"</jc>
+ * cn(<jk>boolean</jk>.<jk>class</jk>); <jc>//
"boolean"</jc>
+ *
+ * <jc>// Arrays</jc>
+ * cn(String[].<jk>class</jk>); <jc>//
"[Ljava.lang.String;"</jc>
+ * cn(<jk>int</jk>[].<jk>class</jk>); <jc>//
"[I"</jc>
+ * cn(String[][].<jk>class</jk>); <jc>//
"[[Ljava.lang.String;"</jc>
+ *
+ * <jc>// Null</jc>
+ * cn(<jk>null</jk>); <jc>// null</jc>
+ * </p>
+ *
* @param value The object to get the class name for.
* @return The name of the class or <jk>null</jk> if the value was null.
*/
@@ -933,6 +958,32 @@ public class Utils {
/**
* Shortcut for calling {@link ClassUtils#simpleClassName(Object)}.
*
+ * <p>
+ * Returns only the simple class name without any package or outer
class information.
+ * For inner classes, only the innermost class name is returned.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Regular classes</jc>
+ * scn(String.<jk>class</jk>); <jc>// "String"</jc>
+ * scn(<jk>new</jk> HashMap<>()); <jc>//
"HashMap"</jc>
+ *
+ * <jc>// Inner classes</jc>
+ * scn(Map.Entry.<jk>class</jk>); <jc>// "Entry"</jc>
+ *
+ * <jc>// Primitives</jc>
+ * scn(<jk>int</jk>.<jk>class</jk>); <jc>//
"int"</jc>
+ * scn(<jk>boolean</jk>.<jk>class</jk>); <jc>//
"boolean"</jc>
+ *
+ * <jc>// Arrays</jc>
+ * scn(String[].<jk>class</jk>); <jc>// "String[]"</jc>
+ * scn(<jk>int</jk>[].<jk>class</jk>); <jc>//
"int[]"</jc>
+ * scn(String[][].<jk>class</jk>); <jc>// "String[][]"</jc>
+ *
+ * <jc>// Null</jc>
+ * scn(<jk>null</jk>); <jc>// null</jc>
+ * </p>
+ *
* @param value The object to get the simple class name for.
* @return The simple name of the class or <jk>null</jk> if the value
was null.
*/
@@ -940,6 +991,48 @@ public class Utils {
return ClassUtils.simpleClassName(value);
}
+ /**
+ * Shortcut for calling {@link
ClassUtils#simpleQualifiedClassName(Object)}.
+ *
+ * <p>
+ * Returns the simple class name including outer class names, but
without the package.
+ * Inner class separators ($) are replaced with dots (.).
+ * Array types are properly formatted with brackets.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Regular classes</jc>
+ * sqcn(String.<jk>class</jk>); <jc>//
"String"</jc>
+ * sqcn(<jk>new</jk> HashMap<>()); <jc>//
"HashMap"</jc>
+ *
+ * <jc>// Inner classes</jc>
+ * sqcn(Map.Entry.<jk>class</jk>); <jc>//
"Map.Entry"</jc>
+ * sqcn(Outer.Inner.Deep.<jk>class</jk>); <jc>//
"Outer.Inner.Deep"</jc>
+ *
+ * <jc>// Primitives</jc>
+ * sqcn(<jk>int</jk>.<jk>class</jk>);
<jc>// "int"</jc>
+ * sqcn(<jk>boolean</jk>.<jk>class</jk>);
<jc>// "boolean"</jc>
+ *
+ * <jc>// Object arrays</jc>
+ * sqcn(String[].<jk>class</jk>); <jc>//
"String[]"</jc>
+ * sqcn(Map.Entry[].<jk>class</jk>); <jc>//
"Map.Entry[]"</jc>
+ * sqcn(String[][].<jk>class</jk>); <jc>//
"String[][]"</jc>
+ *
+ * <jc>// Primitive arrays</jc>
+ * sqcn(<jk>int</jk>[].<jk>class</jk>);
<jc>// "int[]"</jc>
+ * sqcn(<jk>boolean</jk>[][].<jk>class</jk>);
<jc>// "boolean[][]"</jc>
+ *
+ * <jc>// Null</jc>
+ * sqcn(<jk>null</jk>); <jc>//
null</jc>
+ * </p>
+ *
+ * @param value The object to get the simple qualified class name for.
+ * @return The simple qualified name of the class or <jk>null</jk> if
the value was null.
+ */
+ public static String sqcn(Object value) {
+ return ClassUtils.simpleQualifiedClassName(value);
+ }
+
/**
* Converts an object to a boolean.
*
@@ -1019,4 +1112,17 @@ public class Utils {
public static Supplier<String> ss(Supplier<?> s) {
return stringSupplier(s);
}
+
+ /**
+ * Converts the specified object into an identifiable string of the
form "Class[identityHashCode]"
+ * @param o The object to convert to a string.
+ * @return An identity string.
+ */
+ public static String identity(Object o) {
+ if (o instanceof Optional)
+ o = ((Optional<?>)o).orElse(null);
+ if (o == null)
+ return null;
+ return sqcn(o) + "@" + System.identityHashCode(o);
+ }
}
\ No newline at end of file
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStore.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStore.java
index 83578c8e1e..92164efa27 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStore.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStore.java
@@ -659,9 +659,9 @@ public class BeanStore {
Predicate<Boolean> nf = Utils::isTrue;
// @formatter:off
return filteredMap()
- .append("identity", Utils2.identity(this))
+ .append("identity", identity(this))
.append("entries",
entries.stream().map(BeanStoreEntry::properties).collect(toList()))
- .append("outer", Utils2.identity(outer.orElse(null)))
+ .append("outer", identity(outer.orElse(null)))
.append("parent",
parent.map(BeanStore::properties).orElse(null))
.appendIf(nf, "readOnly", readOnly)
.appendIf(nf, "threadSafe", threadSafe)
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStoreEntry.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStoreEntry.java
index 2c236b3617..ba674e33b9 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStoreEntry.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BeanStoreEntry.java
@@ -125,7 +125,7 @@ public class BeanStoreEntry<T> {
// @formatter:off
return filteredMap()
.append("type", scn(getType()))
- .append("bean", Utils2.identity(get()))
+ .append("bean", identity(get()))
.append("name", getName());
// @formatter:on
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/Utils2.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/Utils2.java
index 710f6398f4..c8583bca38 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/Utils2.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/Utils2.java
@@ -33,19 +33,6 @@ public class Utils2 extends Utils {
private static final ConcurrentHashMap<Class<?>,Map<String,MethodInfo>>
PROPERTIES_METHODS = new ConcurrentHashMap<>();
- /**
- * Converts the specified object into an identifiable string of the
form "Class[identityHashCode]"
- * @param o The object to convert to a string.
- * @return An identity string.
- */
- public static String identity(Object o) {
- if (o instanceof Optional)
- o = ((Optional<?>)o).orElse(null);
- if (o == null)
- return null;
- return ClassInfo.of(o).getShortName() + "@" +
System.identityHashCode(o);
- }
-
/**
* Searches for all <c>properties()</c> methods on the specified object
and creates a combine map of them.
*
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlUtils.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlUtils.java
index 9ed2895971..acfc9f9a8b 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlUtils.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlUtils.java
@@ -26,6 +26,7 @@ import java.util.*;
import javax.xml.stream.*;
import org.apache.juneau.*;
+import org.apache.juneau.common.io.*;
import org.apache.juneau.common.utils.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.xml.annotation.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
index 58a1cfd9a7..d686ab0b50 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
@@ -23,6 +23,7 @@ import static org.apache.juneau.common.utils.Utils.*;
import java.io.*;
+import org.apache.juneau.common.io.*;
import org.apache.juneau.html.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.rest.*;
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/assertions/BeanAssertion_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/assertions/BeanAssertion_Test.java
index ce794cc245..e38e44e23a 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/assertions/BeanAssertion_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/assertions/BeanAssertion_Test.java
@@ -215,9 +215,9 @@ class BeanAssertion_Test extends TestBase {
var nil = (A)null;
test(x1).isSame(x1);
test(nil).isSame(nil);
-
assertThrown(()->test(x1).isSame(x1a)).asMessage().asOneLine().isMatches("Not
the same value. Expect='a=1,b=2(BeanAssertion_Test$A@*)'.
Actual='a=1,b=2(BeanAssertion_Test$A@*)'.");
-
assertThrown(()->test(nil).isSame(x1a)).asMessage().asOneLine().isMatches("Not
the same value. Expect='a=1,b=2(BeanAssertion_Test$A@*)'.
Actual='null(null)'.");
-
assertThrown(()->test(x1).isSame(nil)).asMessage().asOneLine().isMatches("Not
the same value. Expect='null(null)'.
Actual='a=1,b=2(BeanAssertion_Test$A@*)'.");
+
assertThrown(()->test(x1).isSame(x1a)).asMessage().asOneLine().isMatches("Not
the same value. Expect='a=1,b=2(BeanAssertion_Test.A@*)'.
Actual='a=1,b=2(BeanAssertion_Test.A@*)'.");
+
assertThrown(()->test(nil).isSame(x1a)).asMessage().asOneLine().isMatches("Not
the same value. Expect='a=1,b=2(BeanAssertion_Test.A@*)'.
Actual='null(null)'.");
+
assertThrown(()->test(x1).isSame(nil)).asMessage().asOneLine().isMatches("Not
the same value. Expect='null(null)'.
Actual='a=1,b=2(BeanAssertion_Test.A@*)'.");
}
@Test void ca09_isSameJsonAs() {
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/ClassUtils_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/ClassUtils_Test.java
new file mode 100644
index 0000000000..22b2e153db
--- /dev/null
+++
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/ClassUtils_Test.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.common.utils;
+
+import static org.apache.juneau.common.utils.ClassUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+import org.junit.jupiter.api.*;
+
+/**
+ * Tests for {@link ClassUtils}.
+ */
+class ClassUtils_Test {
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // simpleQualifiedClassName tests
+
//-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void a01_simpleQualifiedClassName_topLevelClass() {
+ assertEquals("String", simpleQualifiedClassName(String.class));
+ assertEquals("ArrayList",
simpleQualifiedClassName(ArrayList.class));
+ }
+
+ @Test
+ public void a02_simpleQualifiedClassName_innerClass() {
+ assertEquals("Map.Entry",
simpleQualifiedClassName(Map.Entry.class));
+ }
+
+ @Test
+ public void a03_simpleQualifiedClassName_nestedInnerClass() {
+ class Outer {
+ class Inner {
+ class Deep {}
+ }
+ }
+ var deepClass = Outer.Inner.Deep.class;
+ var result = simpleQualifiedClassName(deepClass);
+ // Result will be something like
"ClassUtils_Test.1Outer.Inner.Deep"
+ assertTrue(result.endsWith("Outer.Inner.Deep"), result);
+ assertFalse(result.contains("$"), result);
+ }
+
+ @Test
+ public void a04_simpleQualifiedClassName_withObject() {
+ var obj = new HashMap<>();
+ assertEquals("HashMap", simpleQualifiedClassName(obj));
+ }
+
+ @Test
+ public void a05_simpleQualifiedClassName_null() {
+ assertNull(simpleQualifiedClassName(null));
+ }
+
+ @Test
+ public void a06_simpleQualifiedClassName_noPackage() {
+ // Test with a class that has no package (unlikely in practice,
but good to test)
+ var name = simpleQualifiedClassName(String.class);
+ assertFalse(name.contains(".java.lang"), name);
+ }
+
+ @Test
+ public void a07_simpleQualifiedClassName_anonymousClass() {
+ var anon = new Object() {};
+ var result = simpleQualifiedClassName(anon);
+ // Anonymous classes have names like "ClassUtils_Test$1"
+ // After conversion should be like "ClassUtils_Test.1"
+ assertNotNull(result);
+ assertFalse(result.contains("$"), result);
+ }
+
+ @Test
+ public void a08_simpleQualifiedClassName_arrayTypes() {
+ assertEquals("String[]",
simpleQualifiedClassName(String[].class));
+ assertEquals("String[][]",
simpleQualifiedClassName(String[][].class));
+ assertEquals("int[]", simpleQualifiedClassName(int[].class));
+ assertEquals("Map.Entry[]",
simpleQualifiedClassName(Map.Entry[].class));
+ }
+
+ @Test
+ public void a09_simpleQualifiedClassName_arrayObjects() {
+ var stringArray = new String[]{"a", "b"};
+ assertEquals("String[]", simpleQualifiedClassName(stringArray));
+
+ var intArray = new int[]{1, 2, 3};
+ assertEquals("int[]", simpleQualifiedClassName(intArray));
+
+ var multiDimArray = new String[][]{{"a"}};
+ assertEquals("String[][]",
simpleQualifiedClassName(multiDimArray));
+ }
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // sqcn shortcut tests
+
//-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void b01_sqcn_shortcut() {
+ // Test that the shortcut method works the same as the full
method
+ assertEquals("String", sqcn(String.class));
+ assertEquals("Map.Entry", sqcn(Map.Entry.class));
+ assertNull(sqcn(null));
+ }
+
+ @Test
+ public void b02_sqcn_withObject() {
+ var obj = new HashMap<>();
+ assertEquals("HashMap", sqcn(obj));
+ assertEquals(simpleQualifiedClassName(obj), sqcn(obj));
+ }
+
+ @Test
+ public void b03_sqcn_withArrays() {
+ assertEquals("String[]", sqcn(String[].class));
+ assertEquals("int[][]", sqcn(int[][].class));
+ assertEquals("Map.Entry[]", sqcn(Map.Entry[].class));
+
+ var arr = new String[]{"test"};
+ assertEquals("String[]", sqcn(arr));
+ }
+}
+
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
index 92c647c08c..fc3d330f23 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
@@ -29,6 +29,7 @@ import java.util.function.*;
import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.common.reflect.*;
+import org.apache.juneau.common.utils.*;
import org.apache.juneau.reflect.*;
import org.junit.jupiter.api.*;
@@ -162,10 +163,10 @@ class BeanStore_Test extends TestBase {
assertList(b.stream(A2.class).map(BeanStoreEntry::get),
a2a);
}
-
assertMatchesGlob("{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}]}",
b1p);
-
assertMatchesGlob("{*,entries:[{type:'A2',bean:'"+identity(a2a)+"'}],parent:{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}]}}",
b1c);
-
assertMatchesGlob("{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}],threadSafe:true}",
b2p);
-
assertMatchesGlob("{*,entries:[{type:'A2',bean:'"+identity(a2a)+"'}],parent:{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}],threadSafe:true},threadSafe:true}",
b2c);
+
assertMatchesGlob("{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+Utils.identity(a1a)+"'}]}",
b1p);
+
assertMatchesGlob("{*,entries:[{type:'A2',bean:'"+identity(a2a)+"'}],parent:{*,entries:[{type:'A1',bean:'"+Utils.identity(a1b)+"'},{type:'A1',bean:'"+Utils.identity(a1a)+"'}]}}",
b1c);
+
assertMatchesGlob("{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+Utils.identity(a1a)+"'}],threadSafe:true}",
b2p);
+
assertMatchesGlob("{*,entries:[{type:'A2',bean:'"+identity(a2a)+"'}],parent:{*,entries:[{type:'A1',bean:'"+Utils.identity(a1b)+"'},{type:'A1',bean:'"+Utils.identity(a1a)+"'}],threadSafe:true},threadSafe:true}",
b2c);
b1p.removeBean(A1.class);
b1c.clear().addBean(A1.class, a1a);
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/utils/StringBuilderWriterTest.java
b/juneau-utest/src/test/java/org/apache/juneau/utils/StringBuilderWriterTest.java
index 913accd857..cb995a16d0 100755
---
a/juneau-utest/src/test/java/org/apache/juneau/utils/StringBuilderWriterTest.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/utils/StringBuilderWriterTest.java
@@ -19,6 +19,7 @@ package org.apache.juneau.utils;
import static org.junit.jupiter.api.Assertions.*;
import org.apache.juneau.*;
+import org.apache.juneau.common.io.*;
import org.apache.juneau.internal.*;
import org.junit.jupiter.api.*;