You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2026/04/02 14:15:32 UTC

(juneau) branch master updated: Clean up ConverterUtils

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 1cda0d8c59 Clean up ConverterUtils
1cda0d8c59 is described below

commit 1cda0d8c595c9b5360394d0e37ab4dc82802fce6
Author: James Bognar <ja...@jamesbog-ltmjqf2.internal.salesforce.com>
AuthorDate: Thu Apr 2 07:15:27 2026 -0700

    Clean up ConverterUtils
---
 RELEASE-NOTES.txt                                  |  55 ++++++++
 docs/src/javadoc/overview.html                     |   1 +
 .../org/apache/juneau/bean/html5/HtmlElement.java  |   7 +-
 .../juneau/bean/html5/HtmlElementContainer.java    |   7 +-
 .../apache/juneau/bean/html5/HtmlElementMixed.java |   7 +-
 .../org/apache/juneau/internal/ConverterUtils.java | 147 ++-------------------
 .../org/apache/juneau/swap/AutoNumberSwap.java     |   4 +-
 7 files changed, 77 insertions(+), 151 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 9bb958b36b..25af15dcd0 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -168,6 +168,61 @@ Release Notes - Juneau - Version 9.2.1 - YYYY-MM-DD
       use ISO 8601 formatting for date/time/Duration types.
     * All parser sessions updated with date/time/Duration dispatch in parseAnything().
 
+** New Features - Converter Framework (juneau-commons)
+
+    * New org.apache.juneau.commons.conversion package providing a lightweight, BeanContext-free
+      type conversion framework:
+
+    * Converter interface:
+        - canConvert(Class<?> inType, Class<?> outType) - default returns true; override to restrict.
+        - to(Object o, Class<T> type) - converts o to the target class; throws InvalidConversionException
+          if no conversion path exists; returns null only when input is null.
+        - to(Object o, Type mainType, Type... args) - parameterized-type variant for generic targets
+          such as List<String> or Map<String,Integer>.
+
+    * CachingConverter abstract class:
+        - Abstract base class implementing Converter with two-level ConcurrentHashMap cache.
+        - Conversion functions are discovered once via findConversion(Class, Class) and cached for
+          all subsequent calls on the same type pair.
+        - Unconvertable type pairs are also cached (via a NO_CONVERSION sentinel) to avoid repeated
+          reflection overhead.
+        - Subclasses implement findConversion(Class<I>, Class<O>) and return a Conversion<I,O> lambda,
+          or null if no conversion is possible for that type pair.
+
+    * BasicConverter class:
+        - Concrete CachingConverter with a thread-safe BasicConverter.INSTANCE singleton.
+        - Supports a wide range of common conversions without requiring BeanContext/BeanSession:
+            - Identity / widening cast (any type to same or supertype).
+            - Numeric narrowing/widening: Number to Integer, Long, Short, Float, Double, Byte,
+              AtomicInteger, AtomicLong, and primitive equivalents.
+            - Boolean to Number (true=1, false=0) and Number to Boolean (intValue() != 0).
+            - CharSequence to Number (parsed via StringUtils.parseNumber()).
+            - CharSequence (length 1) or Number to Character.
+            - Any type to String (toString() with array support via Arrays.toString()).
+            - CharSequence to Enum (Enum.valueOf()).
+            - Collection/array to Collection subtype (copies elements; element type from args[0]).
+            - Map to Map subtype (copies entries; key/value types from args[0]/args[1]).
+            - Collection/array to array (element type from outType.getComponentType()).
+            - Special cases: String ↔ TimeZone, String ↔ Locale.
+            - Reflection-based static factory lookup:
+                fromString/valueOf/of/from/parse/create/forName/fromValue/builder(X)
+                or dynamic fromX/forX/parseX where X is the input class name.
+            - Reflection-based constructor lookup: public T(X) constructor.
+            - Instance toX() method lookup (e.g. toInteger()).
+
+    * Conversion functional interface:
+        - O to(I in, Class<?>... args) - used as the cached conversion lambda in CachingConverter.
+        - args carries type arguments for parameterized output types at call time.
+
+    * InvalidConversionException runtime exception:
+        - Thrown by Converter.to() when no conversion path exists between two types.
+        - Message format: "Cannot convert <inType> to <outType>".
+        - Callers can pre-check with canConvert() to avoid the exception.
+
+    * BeanContextConverter (renamed from GenericConverter) in org.apache.juneau.utils:
+        - Implements Converter and delegates to the default BeanContext session for type conversion.
+        - Provides access to the full Juneau framework conversion logic including bean mapping.
+
 Release Notes - Juneau - Version 9.2.0 - 2025-12-30
 
 ** Changes
diff --git a/docs/src/javadoc/overview.html b/docs/src/javadoc/overview.html
index d2b158d457..2fa7179f41 100644
--- a/docs/src/javadoc/overview.html
+++ b/docs/src/javadoc/overview.html
@@ -25,6 +25,7 @@
 		<li><strong>Microservices</strong> - Build lightweight, standalone microservices with embedded Jetty</li>
 		<li><strong>Configuration Management</strong> - Sophisticated configuration file API with variable resolution</li>
 		<li><strong>Fluent Assertions</strong> - Powerful testing framework with fluent-style assertions</li>
+		<li><strong>Type Conversion</strong> - Lightweight, BeanContext-free converter framework with caching and broad type support</li>
 		<li><strong>Zero Dependencies</strong> - Core marshalling requires no external dependencies</li>
 	</ul>
 
diff --git a/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElement.java b/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElement.java
index 303eb3de87..acf62d369d 100644
--- a/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElement.java
+++ b/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElement.java
@@ -26,8 +26,8 @@ import java.net.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.commons.conversion.*;
 import org.apache.juneau.html.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.xml.annotation.*;
 
 /**
@@ -177,12 +177,13 @@ public abstract class HtmlElement {
 	 * @param <T> The class type to convert this class to.
 	 * @param type
 	 * 	The class type to convert this class to.
-	 * 	See {@link ConverterUtils} for a list of supported conversion types.
+	 * 	See {@link BasicConverter} for a list of supported conversion types.
 	 * @param key The attribute name.
 	 * @return The attribute value, or <jk>null</jk> if the named attribute does not exist.
+	 * @throws InvalidConversionException If the attribute value cannot be converted to the specified type.
 	 */
 	public <T> T getAttr(Class<T> type, String key) {
-		return attrs == null ? null : ConverterUtils.toType(attrs.get(key), type);
+		return attrs == null ? null : BasicConverter.INSTANCE.to(attrs.get(key), type);
 	}
 
 	/**
diff --git a/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElementContainer.java b/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElementContainer.java
index 6457f78e7e..fb857cdb60 100644
--- a/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElementContainer.java
+++ b/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElementContainer.java
@@ -20,9 +20,8 @@ import static org.apache.juneau.xml.annotation.XmlFormat.*;
 
 import java.util.*;
 
-import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
-import org.apache.juneau.internal.*;
+import org.apache.juneau.commons.conversion.*;
 import org.apache.juneau.xml.annotation.*;
 
 /**
@@ -108,10 +107,10 @@ public class HtmlElementContainer extends HtmlElement {
 	 * @param type The class type of the node.
 	 * @param index The index of the node in the list of children.
 	 * @return The child node, or <jk>null</jk> if it doesn't exist.
-	 * @throws InvalidDataConversionException If node is not the expected type.
+	 * @throws InvalidConversionException If node is not the expected type.
 	 */
 	public <T> T getChild(Class<T> type, int index) {
-		return (children == null || children.size() <= index || index < 0 ? null : ConverterUtils.toType(children.get(index), type));
+		return (children == null || children.size() <= index || index < 0 ? null : BasicConverter.INSTANCE.to(children.get(index), type));
 	}
 
 	/**
diff --git a/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElementMixed.java b/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElementMixed.java
index ba372e5ff7..ae6ca34707 100644
--- a/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElementMixed.java
+++ b/juneau-bean/juneau-bean-html5/src/main/java/org/apache/juneau/bean/html5/HtmlElementMixed.java
@@ -20,9 +20,8 @@ import static org.apache.juneau.xml.annotation.XmlFormat.*;
 
 import java.util.*;
 
-import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
-import org.apache.juneau.internal.*;
+import org.apache.juneau.commons.conversion.*;
 import org.apache.juneau.xml.annotation.*;
 
 /**
@@ -114,10 +113,10 @@ public class HtmlElementMixed extends HtmlElement {
 	 * @param type The class type of the node.
 	 * @param index The index of the node in the list of children.
 	 * @return The child node, or <jk>null</jk> if it doesn't exist.
-	 * @throws InvalidDataConversionException If node is not the expected type.
+	 * @throws InvalidConversionException If node is not the expected type.
 	 */
 	public <T> T getChild(Class<T> type, int index) {
-		return (children == null || children.size() <= index || index < 0 ? null : ConverterUtils.toType(children.get(index), type));
+		return (children == null || children.size() <= index || index < 0 ? null : BasicConverter.INSTANCE.to(children.get(index), type));
 	}
 
 	/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ConverterUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ConverterUtils.java
index 72f4189cb9..5077cde3a8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ConverterUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ConverterUtils.java
@@ -22,141 +22,13 @@ import static org.apache.juneau.commons.utils.ThrowableUtils.*;
 
 import java.util.*;
 
-import org.apache.juneau.*;
 import org.apache.juneau.commons.collections.*;
+import org.apache.juneau.commons.conversion.BasicConverter;
 import org.apache.juneau.parser.*;
-import org.apache.juneau.swap.*;
 import org.apache.juneau.utils.*;
 
 /**
- * Utility class for efficiently converting objects between types.
- *
- * <p>
- * If the value isn't an instance of the specified type, then converts the value if possible.
- *
- * <p>
- * The following conversions are valid:
- * <table class='styled'>
- * 	<tr><th>Convert to type</th><th>Valid input value types</th><th>Notes</th></tr>
- * 	<tr>
- * 		<td>
- * 			A class that is the normal type of a registered {@link ObjectSwap}.
- * 		</td>
- * 		<td>
- * 			A value whose class matches the transformed type of that registered {@link ObjectSwap}.
- * 		</td>
- * 		<td>&nbsp;</td>
- * 	</tr>
- * 	<tr>
- * 		<td>
- * 			A class that is the transformed type of a registered {@link ObjectSwap}.
- * 		</td>
- * 		<td>
- * 			A value whose class matches the normal type of that registered {@link ObjectSwap}.
- * 		</td>
- * 		<td>&nbsp;</td>
- * 	</tr>
- * 	<tr>
- * 		<td>
- * 			{@code Number} (e.g. {@code Integer}, {@code Short}, {@code Float},...)
- * 			<br><code>Number.<jsf>TYPE</jsf></code> (e.g. <code>Integer.<jsf>TYPE</jsf></code>,
- * 			<code>Short.<jsf>TYPE</jsf></code>, <code>Float.<jsf>TYPE</jsf></code>,...)
- * 		</td>
- * 		<td>
- * 			{@code Number}, {@code String}, <jk>null</jk>
- * 		</td>
- * 		<td>
- * 			For primitive {@code TYPES}, <jk>null</jk> returns the JVM default value for that type.
- * 		</td>
- * 	</tr>
- * 	<tr>
- * 		<td>
- * 			{@code Map} (e.g. {@code Map}, {@code HashMap}, {@code TreeMap}, {@code JsonMap})
- * 		</td>
- * 		<td>
- * 			{@code Map}
- * 		</td>
- * 		<td>
- * 			If {@code Map} is not constructible, an {@code JsonMap} is created.
- * 		</td>
- * 	</tr>
- * 	<tr>
- * 		<td>
- * 			<c>Collection</c> (e.g. <c>List</c>, <c>LinkedList</c>, <c>HashSet</c>, <c>JsonList</c>)
- * 		</td>
- * 		<td>
- * 			<c>Collection&lt;Object&gt;</c>
- * 			<br><c>Object[]</c>
- * 		</td>
- * 		<td>
- * 			If <c>Collection</c> is not constructible, a <c>JsonList</c> is created.
- * 		</td>
- * 	</tr>
- * 	<tr>
- * 		<td>
- * 			<c>X[]</c> (array of any type X)
- * 		</td>
- * 		<td>
- * 			<c>List&lt;X&gt;</c>
- * 		</td>
- * 		<td>&nbsp;</td>
- * 	</tr>
- * 	<tr>
- * 		<td>
- * 			<c>X[][]</c> (multi-dimensional arrays)
- * 		</td>
- * 		<td>
- * 			<c>List&lt;List&lt;X&gt;&gt;</c>
- * 			<br><c>List&lt;X[]&gt;</c>
- * 			<br><c> List[]&lt;X&gt;</c>
- * 		</td>
- * 		<td>&nbsp;</td>
- * 	</tr>
- * 	<tr>
- * 		<td>
- * 			<c>Enum</c>
- * 		</td>
- * 		<td>
- * 			<c>String</c>
- * 		</td>
- * 		<td>&nbsp;</td>
- * 	</tr>
- * 	<tr>
- * 		<td>
- * 			Bean
- * 		</td>
- * 		<td>
- * 			<c>Map</c>
- * 		</td>
- * 		<td>&nbsp;</td>
- * 	</tr>
- * 	<tr>
- * 		<td>
- * 			<c>String</c>
- * 		</td>
- * 		<td>
- * 			Anything
- * 		</td>
- * 		<td>
- * 			Arrays are converted to JSON arrays
- * 		</td>
- * 	</tr>
- * 	<tr>
- * 		<td>
- * 			Anything with one of the following methods:
- * 			<br><code><jk>public static</jk> T fromString(String)</code>
- * 			<br><code><jk>public static</jk> T valueOf(String)</code>
- * 			<br><code><jk>public</jk> T(String)</code>
- * 		</td>
- * 		<td>
- * 			<c>String</c>
- * 		</td>
- * 		<td>
- * 			<br>
- * 		</td>
- * 	</tr>
- * </table>
- *
+ * Utility class for common type conversions and building typed collections and maps.
  */
 public class ConverterUtils {
 
@@ -165,10 +37,6 @@ public class ConverterUtils {
 	 */
 	private ConverterUtils() {}
 
-	// Session objects are usually not thread safe, but we're not using any feature
-	// of bean sessions that would cause thread safety issues.
-	private static final BeanSession session = BeanContext.DEFAULT_SESSION;
-
 	/**
 	 * Converts an object to a Boolean.
 	 *
@@ -176,7 +44,7 @@ public class ConverterUtils {
 	 * @return The converted object.
 	 */
 	public static Boolean toBoolean(Object o) {
-		return toType(o, Boolean.class);
+		return BasicConverter.INSTANCE.to(o, Boolean.class);
 	}
 
 	/**
@@ -186,7 +54,7 @@ public class ConverterUtils {
 	 * @return The converted object.
 	 */
 	public static Integer toInteger(Object o) {
-		return toType(o, Integer.class);
+		return BasicConverter.INSTANCE.to(o, Integer.class);
 	}
 
 	/**
@@ -210,14 +78,17 @@ public class ConverterUtils {
 	/**
 	 * Converts the specified object to the specified type.
 	 *
+	 * <p>
+	 * Uses the full {@link BeanContextConverter} to support bean-aware conversions such as
+	 * {@link java.util.Map} to bean, bean to bean, and {@link org.apache.juneau.swap.ObjectSwap} transforms.
+	 *
 	 * @param <T> The class type to convert the value to.
 	 * @param value The value to convert.
 	 * @param type The class type to convert the value to.
-	 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
 	 * @return The converted value.
 	 */
 	public static <T> T toType(Object value, Class<T> type) {
-		return session.convertToType(value, type);
+		return BeanContextConverter.INSTANCE.to(value, type);
 	}
 
 	/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/swap/AutoNumberSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/swap/AutoNumberSwap.java
index 7eaa40118a..4d52dc0ddb 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/swap/AutoNumberSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/swap/AutoNumberSwap.java
@@ -25,8 +25,8 @@ import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.commons.conversion.*;
 import org.apache.juneau.commons.reflect.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.serializer.*;
 
@@ -247,7 +247,7 @@ public class AutoNumberSwap<T> extends ObjectSwap<T,Number> {
 		if (unswapType == null)
 			throw new ParseException("No unparse methodology found for object.");
 		try {
-			Object o2 = ConverterUtils.toType(o, unswapType);
+			Object o2 = BasicConverter.INSTANCE.to(o, unswapType);
 			if (nn(unswapMethod))
 				return (T)unswapMethod.invoke(null, getMatchingArgs(unswapMethod.getParameterTypes(), session, o2));
 			if (nn(unswapConstructor))