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 51df205dd7 Replace Mutaters with BasicConverter
51df205dd7 is described below

commit 51df205dd7548dbb105bc66548df87a7c5ec5223
Author: James Bognar <[email protected]>
AuthorDate: Thu Apr 2 15:26:20 2026 -0700

    Replace Mutaters with BasicConverter
---
 .../juneau/commons/conversion/BasicConverter.java  | 90 +++++++++++-----------
 .../commons/conversion/CachingConverter.java       | 30 +++++---
 .../commons/conversion/ConfigurableConverter.java  | 15 ++++
 .../juneau/commons/conversion/Conversion.java      |  4 +-
 .../juneau/commons/conversion/Converter.java       | 50 ++++++------
 .../commons/conversion/ConverterSession.java       | 76 ++++++++++++++++++
 .../main/java/org/apache/juneau/BeanContext.java   | 46 +++++++++++
 .../main/java/org/apache/juneau/BeanSession.java   | 22 +++++-
 .../apache/juneau/utils/BeanContextConverter.java  |  6 +-
 .../commons/conversion/BasicConverter_Test.java    |  6 +-
 .../commons/conversion/CachingConverter_Test.java  | 24 +++---
 .../conversion/ConfigurableConverter_Test.java     | 22 +++---
 .../juneau/commons/conversion/Converter_Test.java  | 12 +--
 13 files changed, 285 insertions(+), 118 deletions(-)

diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/BasicConverter.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/BasicConverter.java
index 49308a6f0e..fd6d50284f 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/BasicConverter.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/BasicConverter.java
@@ -181,7 +181,7 @@ public class BasicConverter extends CachingConverter {
                if ((c = findSpecialConversion(inType, out)) != null) return c;
 
                if (out.isAssignableFrom(inType) && 
!Collection.class.isAssignableFrom(out) && !Map.class.isAssignableFrom(out))
-                       return (in, memberOf, args) -> (O) in;
+                       return (in, memberOf, session, args) -> (O) in;
 
                if (Number.class.isAssignableFrom(out) && (c = 
findNumberConversion(inType, out)) != null) return c;
 
@@ -224,31 +224,31 @@ public class BasicConverter extends CachingConverter {
        }
 
        private <I, O> Conversion<I, O> findNumberFromNumber(Class<O> outType) {
-               if (outType == Integer.class) return (in, memberOf, args) -> 
(O) Integer.valueOf(((Number) in).intValue());
-               if (outType == Long.class) return (in, memberOf, args) -> (O) 
Long.valueOf(((Number) in).longValue());
-               if (outType == Short.class) return (in, memberOf, args) -> (O) 
Short.valueOf(((Number) in).shortValue());
-               if (outType == Float.class) return (in, memberOf, args) -> (O) 
Float.valueOf(((Number) in).floatValue());
-               if (outType == Double.class) return (in, memberOf, args) -> (O) 
Double.valueOf(((Number) in).doubleValue());
-               if (outType == Byte.class) return (in, memberOf, args) -> (O) 
Byte.valueOf(((Number) in).byteValue());
-               if (outType == AtomicInteger.class) return (in, memberOf, args) 
-> (O) new AtomicInteger(((Number) in).intValue());
-               if (outType == AtomicLong.class) return (in, memberOf, args) -> 
(O) new AtomicLong(((Number) in).longValue());
+               if (outType == Integer.class) return (in, memberOf, session, 
args) -> (O) Integer.valueOf(((Number) in).intValue());
+               if (outType == Long.class) return (in, memberOf, session, args) 
-> (O) Long.valueOf(((Number) in).longValue());
+               if (outType == Short.class) return (in, memberOf, session, 
args) -> (O) Short.valueOf(((Number) in).shortValue());
+               if (outType == Float.class) return (in, memberOf, session, 
args) -> (O) Float.valueOf(((Number) in).floatValue());
+               if (outType == Double.class) return (in, memberOf, session, 
args) -> (O) Double.valueOf(((Number) in).doubleValue());
+               if (outType == Byte.class) return (in, memberOf, session, args) 
-> (O) Byte.valueOf(((Number) in).byteValue());
+               if (outType == AtomicInteger.class) return (in, memberOf, 
session, args) -> (O) new AtomicInteger(((Number) in).intValue());
+               if (outType == AtomicLong.class) return (in, memberOf, session, 
args) -> (O) new AtomicLong(((Number) in).longValue());
                return null;
        }
 
        private <O> Conversion<Boolean, O> findNumberFromBoolean(Class<O> 
outType) {
-               if (outType == Integer.class) return (in, memberOf, args) -> 
(O) Integer.valueOf(in.booleanValue() ? 1 : 0);
-               if (outType == Long.class) return (in, memberOf, args) -> (O) 
Long.valueOf(in.booleanValue() ? 1L : 0L);
-               if (outType == Short.class) return (in, memberOf, args) -> (O) 
Short.valueOf(in.booleanValue() ? (short) 1 : (short) 0);
-               if (outType == Float.class) return (in, memberOf, args) -> (O) 
Float.valueOf(in.booleanValue() ? 1f : 0f);
-               if (outType == Double.class) return (in, memberOf, args) -> (O) 
Double.valueOf(in.booleanValue() ? 1d : 0d);
-               if (outType == Byte.class) return (in, memberOf, args) -> (O) 
Byte.valueOf(in.booleanValue() ? (byte) 1 : (byte) 0);
-               if (outType == AtomicInteger.class) return (in, memberOf, args) 
-> (O) new AtomicInteger(in.booleanValue() ? 1 : 0);
-               if (outType == AtomicLong.class) return (in, memberOf, args) -> 
(O) new AtomicLong(in.booleanValue() ? 1L : 0L);
+               if (outType == Integer.class) return (in, memberOf, session, 
args) -> (O) Integer.valueOf(in.booleanValue() ? 1 : 0);
+               if (outType == Long.class) return (in, memberOf, session, args) 
-> (O) Long.valueOf(in.booleanValue() ? 1L : 0L);
+               if (outType == Short.class) return (in, memberOf, session, 
args) -> (O) Short.valueOf(in.booleanValue() ? (short) 1 : (short) 0);
+               if (outType == Float.class) return (in, memberOf, session, 
args) -> (O) Float.valueOf(in.booleanValue() ? 1f : 0f);
+               if (outType == Double.class) return (in, memberOf, session, 
args) -> (O) Double.valueOf(in.booleanValue() ? 1d : 0d);
+               if (outType == Byte.class) return (in, memberOf, session, args) 
-> (O) Byte.valueOf(in.booleanValue() ? (byte) 1 : (byte) 0);
+               if (outType == AtomicInteger.class) return (in, memberOf, 
session, args) -> (O) new AtomicInteger(in.booleanValue() ? 1 : 0);
+               if (outType == AtomicLong.class) return (in, memberOf, session, 
args) -> (O) new AtomicLong(in.booleanValue() ? 1L : 0L);
                return null;
        }
 
        private <O> Conversion<CharSequence, O> findNumberFromString(Class<O> 
outType) {
-               return (in, memberOf, args) -> (O) parseNumber(in.toString(), 
(Class<? extends Number>) outType);
+               return (in, memberOf, session, args) -> (O) 
parseNumber(in.toString(), (Class<? extends Number>) outType);
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
@@ -257,7 +257,7 @@ public class BasicConverter extends CachingConverter {
 
        private <I, O> Conversion<I, O> findBooleanConversion(Class<I> inType) {
                if (Number.class.isAssignableFrom(inType))
-                       return (in, memberOf, args) -> (O) 
Boolean.valueOf(((Number) in).intValue() != 0);
+                       return (in, memberOf, session, args) -> (O) 
Boolean.valueOf(((Number) in).intValue() != 0);
                return null;
        }
 
@@ -267,12 +267,12 @@ public class BasicConverter extends CachingConverter {
 
        private <I, O> Conversion<I, O> findCharacterConversion(Class<I> 
inType) {
                if (CharSequence.class.isAssignableFrom(inType))
-                       return (in, memberOf, args) -> {
+                       return (in, memberOf, session, args) -> {
                                var s = in.toString();
                                return s.length() == 1 ? (O) 
Character.valueOf(s.charAt(0)) : null;
                        };
                if (Number.class.isAssignableFrom(inType))
-                       return (in, memberOf, args) -> (O) 
Character.valueOf((char) ((Number) in).intValue());
+                       return (in, memberOf, session, args) -> (O) 
Character.valueOf((char) ((Number) in).intValue());
                return null;
        }
 
@@ -282,17 +282,17 @@ public class BasicConverter extends CachingConverter {
 
        private <I> Conversion<I, String> findToStringConversion(Class<I> 
inType) {
                if (inType.isArray()) {
-                       if (inType == int[].class) return (in, memberOf, args) 
-> Arrays.toString((int[]) in);
-                       if (inType == long[].class) return (in, memberOf, args) 
-> Arrays.toString((long[]) in);
-                       if (inType == double[].class) return (in, memberOf, 
args) -> Arrays.toString((double[]) in);
-                       if (inType == float[].class) return (in, memberOf, 
args) -> Arrays.toString((float[]) in);
-                       if (inType == boolean[].class) return (in, memberOf, 
args) -> Arrays.toString((boolean[]) in);
-                       if (inType == byte[].class) return (in, memberOf, args) 
-> Arrays.toString((byte[]) in);
-                       if (inType == short[].class) return (in, memberOf, 
args) -> Arrays.toString((short[]) in);
-                       if (inType == char[].class) return (in, memberOf, args) 
-> Arrays.toString((char[]) in);
-                       return (in, memberOf, args) -> 
Arrays.deepToString((Object[]) in);
+                       if (inType == int[].class) return (in, memberOf, 
session, args) -> Arrays.toString((int[]) in);
+                       if (inType == long[].class) return (in, memberOf, 
session, args) -> Arrays.toString((long[]) in);
+                       if (inType == double[].class) return (in, memberOf, 
session, args) -> Arrays.toString((double[]) in);
+                       if (inType == float[].class) return (in, memberOf, 
session, args) -> Arrays.toString((float[]) in);
+                       if (inType == boolean[].class) return (in, memberOf, 
session, args) -> Arrays.toString((boolean[]) in);
+                       if (inType == byte[].class) return (in, memberOf, 
session, args) -> Arrays.toString((byte[]) in);
+                       if (inType == short[].class) return (in, memberOf, 
session, args) -> Arrays.toString((short[]) in);
+                       if (inType == char[].class) return (in, memberOf, 
session, args) -> Arrays.toString((char[]) in);
+                       return (in, memberOf, session, args) -> 
Arrays.deepToString((Object[]) in);
                }
-               return (in, memberOf, args) -> in.toString();
+               return (in, memberOf, session, args) -> in.toString();
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
@@ -301,7 +301,7 @@ public class BasicConverter extends CachingConverter {
 
        private <I, O> Conversion<I, O> findEnumConversion(Class<I> inType, 
Class<O> outType) {
                if (CharSequence.class.isAssignableFrom(inType))
-                       return (in, memberOf, args) -> (O) 
Enum.valueOf((Class<Enum>) outType, in.toString());
+                       return (in, memberOf, session, args) -> (O) 
Enum.valueOf((Class<Enum>) outType, in.toString());
                return null;
        }
 
@@ -311,7 +311,7 @@ public class BasicConverter extends CachingConverter {
 
        private <I, O> Conversion<I, O> findCollectionConversion(Class<I> 
inType, Class<O> outType) {
                if (Collection.class.isAssignableFrom(inType) || 
inType.isArray()) {
-                       return (in, memberOf, args) -> {
+                       return (in, memberOf, session, args) -> {
                                var elemType = args.length > 0 ? args[0] : null;
                                if (elemType == null && !inType.isArray() && 
outType.isAssignableFrom(inType))
                                        return (O) in;
@@ -350,7 +350,7 @@ public class BasicConverter extends CachingConverter {
 
        private <I, O> Conversion<I, O> findMapConversion(Class<I> inType, 
Class<O> outType) {
                if (Map.class.isAssignableFrom(inType)) {
-                       return (in, memberOf, args) -> {
+                       return (in, memberOf, session, args) -> {
                                var keyType = args.length > 0 ? args[0] : null;
                                var valType = args.length > 1 ? args[1] : null;
                                if (keyType == null && 
outType.isAssignableFrom(inType))
@@ -393,7 +393,7 @@ public class BasicConverter extends CachingConverter {
                                                && !canConvert(inComponentType, 
componentType))
                                        return null;
                        }
-                       return (in, memberOf, args) -> {
+                       return (in, memberOf, session, args) -> {
                                if (Collection.class.isAssignableFrom(inType)) {
                                        var list = (Collection<?>) in;
                                        var arr = 
Array.newInstance(componentType, list.size());
@@ -418,13 +418,13 @@ public class BasicConverter extends CachingConverter {
 
        private <I, O> Conversion<I, O> findSpecialConversion(Class<I> inType, 
Class<O> outType) {
                if (inType == String.class && outType == TimeZone.class)
-                       return (Conversion<I, O>) (Conversion<String, 
TimeZone>) (in, memberOf, args) -> TimeZone.getTimeZone(in);
+                       return (Conversion<I, O>) (Conversion<String, 
TimeZone>) (in, memberOf, session, args) -> TimeZone.getTimeZone(in);
                if (TimeZone.class.isAssignableFrom(inType) && outType == 
String.class)
-                       return (Conversion<I, O>) (Conversion<TimeZone, 
String>) (in, memberOf, args) -> in.getID();
+                       return (Conversion<I, O>) (Conversion<TimeZone, 
String>) (in, memberOf, session, args) -> in.getID();
                if (inType == String.class && outType == Locale.class)
-                       return (Conversion<I, O>) (Conversion<String, Locale>) 
(in, memberOf, args) -> Locale.forLanguageTag(in.replace('_', '-'));
+                       return (Conversion<I, O>) (Conversion<String, Locale>) 
(in, memberOf, session, args) -> Locale.forLanguageTag(in.replace('_', '-'));
                if (CharSequence.class.isAssignableFrom(inType) && outType == 
Boolean.class)
-                       return (in, memberOf, args) -> {
+                       return (in, memberOf, session, args) -> {
                                var s = in.toString();
                                if (s.isEmpty() || "null".equals(s))
                                        return null;
@@ -443,7 +443,7 @@ public class BasicConverter extends CachingConverter {
                for (var name : FACTORY_METHOD_NAMES) {
                        var opt = findStaticMethod(ci, name, inType, outType);
                        if (opt.isPresent())
-                               return (in, memberOf, args) -> 
opt.get().invoke(null, in);
+                               return (in, memberOf, session, args) -> 
opt.get().invoke(null, in);
                }
 
                // Walk the type hierarchy (superclasses then interfaces) so 
that e.g. InputStreamReader
@@ -453,7 +453,7 @@ public class BasicConverter extends CachingConverter {
                        for (var prefix : new String[]{"from", "for", "parse"}) 
{
                                var opt = findStaticMethod(ci, prefix + inName, 
inType, outType);
                                if (opt.isPresent())
-                                       return (in, memberOf, args) -> 
opt.get().invoke(null, in);
+                                       return (in, memberOf, session, args) -> 
opt.get().invoke(null, in);
                        }
                }
                for (var iface : allInterfaces(inType)) {
@@ -461,7 +461,7 @@ public class BasicConverter extends CachingConverter {
                        for (var prefix : new String[]{"from", "for", "parse"}) 
{
                                var opt = findStaticMethod(ci, prefix + inName, 
inType, outType);
                                if (opt.isPresent())
-                                       return (in, memberOf, args) -> 
opt.get().invoke(null, in);
+                                       return (in, memberOf, session, args) -> 
opt.get().invoke(null, in);
                        }
                }
 
@@ -510,7 +510,7 @@ public class BasicConverter extends CachingConverter {
                        );
                        if (opt.isPresent()) {
                                var ctor = opt.get();
-                               return (in, memberOf, args) -> 
ctor.newInstance(memberOf, in);
+                               return (in, memberOf, session, args) -> 
ctor.newInstance(memberOf, in);
                        }
                }
 
@@ -521,7 +521,7 @@ public class BasicConverter extends CachingConverter {
                );
                if (opt.isPresent()) {
                        var ctor = opt.get();
-                       return (in, memberOf, args) -> ctor.newInstance(in);
+                       return (in, memberOf, session, args) -> 
ctor.newInstance(in);
                }
                return null;
        }
@@ -543,7 +543,7 @@ public class BasicConverter extends CachingConverter {
                );
                if (opt.isPresent()) {
                        var method = opt.get();
-                       return (in, memberOf, args) -> method.invoke(in);
+                       return (in, memberOf, session, args) -> 
method.invoke(in);
                }
                return null;
        }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/CachingConverter.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/CachingConverter.java
index cdd37efa1c..f524d775cf 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/CachingConverter.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/CachingConverter.java
@@ -55,7 +55,7 @@ public abstract class CachingConverter implements Converter {
        // Sentinel stored in the cache when findConversion() returns null.
        // ConcurrentHashMap does not permit null values, so we cannot store 
null directly.
        // Using this sentinel avoids re-invoking findConversion() for 
unconvertable type pairs.
-       private static final Conversion<?,?> NO_CONVERSION = (in, memberOf, 
args) -> null; // HTT: sentinel body is never invoked; ConcurrentHashMap 
prohibits null values so we use this placeholder
+       private static final Conversion<?,?> NO_CONVERSION = (in, memberOf, 
session, args) -> null; // HTT: sentinel body is never invoked; 
ConcurrentHashMap prohibits null values so we use this placeholder
 
        // Two-level cache: input type -> output type -> conversion function 
(or NO_CONVERSION sentinel).
        private final Map<Class<?>, Map<Class<?>, Conversion<?,?>>> conversions 
= new ConcurrentHashMap<>();
@@ -124,7 +124,7 @@ public abstract class CachingConverter implements Converter 
{
                var fn = (Conversion<Object, T>) lookupConversion(inType, type);
                if (fn == null)
                        throw new InvalidConversionException(inType, type);
-               return fn.to(o, null);
+               return fn.to(o, null, (ConverterSession)null);
        }
 
        /**
@@ -160,15 +160,19 @@ public abstract class CachingConverter implements 
Converter {
                var fn = (Conversion<Object, T>) lookupConversion(inType, 
rawType);
                if (fn == null)
                        throw new InvalidConversionException(inType, rawType);
-               return fn.to(o, null, argClasses);
+               return fn.to(o, null, (ConverterSession)null, argClasses);
        }
 
        /**
-        * Converts the specified object to the specified type, passing the 
outer instance for
-        * non-static inner class construction.
+        * Converts the specified object to the specified type, using the given 
outer instance and converter session.
+        *
+        * <p>
+        * The session is forwarded to the cached {@link Conversion} function 
so conversion lambdas can access
+        * contextual objects such as {@link java.util.TimeZone} or {@link 
java.util.Locale}.
         *
         * @param o The object to convert. Can be <jk>null</jk>.
         * @param memberOf The outer instance for non-static inner class 
construction, or <jk>null</jk>.
+        * @param session The converter session providing contextual objects, 
or <jk>null</jk>.
         * @param type The target type.
         * @param <T> The target type.
         * @return The converted object, or <jk>null</jk> if the input is 
<jk>null</jk>.
@@ -176,7 +180,7 @@ public abstract class CachingConverter implements Converter 
{
         */
        @Override
        @SuppressWarnings("unchecked")
-       public <T> T to(Object o, Object memberOf, Class<T> type) {
+       public <T> T to(Object o, Object memberOf, ConverterSession session, 
Class<T> type) {
                if (o == null)
                        return null;
                var inType = o.getClass();
@@ -185,15 +189,19 @@ public abstract class CachingConverter implements 
Converter {
                var fn = (Conversion<Object, T>) lookupConversion(inType, type);
                if (fn == null)
                        throw new InvalidConversionException(inType, type);
-               return fn.to(o, memberOf);
+               return fn.to(o, memberOf, session);
        }
 
        /**
-        * Converts the specified object to the specified parameterized type, 
passing the outer instance for
-        * non-static inner class construction.
+        * Converts the specified object to the specified parameterized type, 
using the given outer instance and converter session.
+        *
+        * <p>
+        * The session is forwarded to the cached {@link Conversion} function 
so conversion lambdas can access
+        * contextual objects such as {@link java.util.TimeZone} or {@link 
java.util.Locale}.
         *
         * @param o The object to convert. Can be <jk>null</jk>.
         * @param memberOf The outer instance for non-static inner class 
construction, or <jk>null</jk>.
+        * @param session The converter session providing contextual objects, 
or <jk>null</jk>.
         * @param mainType The target type. May be a {@link Class} or {@link 
ParameterizedType}.
         * @param args The type arguments of the target type (e.g. element type 
for collections).
         * @param <T> The target type.
@@ -202,7 +210,7 @@ public abstract class CachingConverter implements Converter 
{
         */
        @Override
        @SuppressWarnings("unchecked")
-       public <T> T to(Object o, Object memberOf, Type mainType, Type... args) 
{
+       public <T> T to(Object o, Object memberOf, ConverterSession session, 
Type mainType, Type... args) {
                if (o == null)
                        return null;
                var rawType = (Class<T>) (mainType instanceof ParameterizedType 
pt ? pt.getRawType() : (Class<?>) mainType);
@@ -213,6 +221,6 @@ public abstract class CachingConverter implements Converter 
{
                var fn = (Conversion<Object, T>) lookupConversion(inType, 
rawType);
                if (fn == null)
                        throw new InvalidConversionException(inType, rawType);
-               return fn.to(o, memberOf, argClasses);
+               return fn.to(o, memberOf, session, argClasses);
        }
 }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/ConfigurableConverter.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/ConfigurableConverter.java
index 243014ac36..6cfddb85a4 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/ConfigurableConverter.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/ConfigurableConverter.java
@@ -86,6 +86,21 @@ public class ConfigurableConverter extends BasicConverter {
                return this;
        }
 
+       /**
+        * Returns <jk>true</jk> if a custom conversion has been explicitly 
registered for the specified type pair.
+        *
+        * <p>
+        * This only checks the user-registered conversions, not the built-in 
{@link BasicConverter} reflection logic.
+        *
+        * @param inType The input type class.
+        * @param outType The output type class.
+        * @return <jk>true</jk> if a custom conversion exists for the 
specified type pair.
+        */
+       public boolean hasCustomConversion(Class<?> inType, Class<?> outType) {
+               var inner = registered.get(inType);
+               return inner != null && inner.containsKey(outType);
+       }
+
        @Override
        protected <I, O> Conversion<I, O> findConversion(Class<I> inType, 
Class<O> outType) {
                var inner = registered.get(inType);
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/Conversion.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/Conversion.java
index b1480aff73..ae37854402 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/Conversion.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/Conversion.java
@@ -41,8 +41,10 @@ public interface Conversion<I, O> {
         *
         * @param in The input object.
         * @param memberOf The outer instance for non-static inner class 
construction, or <jk>null</jk>.
+        * @param session The converter session providing contextual objects 
such as {@link java.util.TimeZone} or
+        *      {@link java.util.Locale}, or <jk>null</jk> if no session is 
available.
         * @param args Optional type arguments for parameterized output types 
(e.g. element type for collections).
         * @return The converted object.
         */
-       O to(I in, Object memberOf, Class<?>... args);
+       O to(I in, Object memberOf, ConverterSession session, Class<?>... args);
 }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/Converter.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/Converter.java
index 7db0a003f2..0a0d958340 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/Converter.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/Converter.java
@@ -54,25 +54,6 @@ public interface Converter {
         */
        <T> T to(Object o, Class<T> type);
 
-       /**
-        * Converts the specified object to the specified type, using the given 
outer instance for
-        * non-static inner class construction.
-        *
-        * <p>
-        * Returns <jk>null</jk> only when the input object is <jk>null</jk>.
-        * Throws {@link InvalidConversionException} when no conversion path 
exists.
-        *
-        * @param o The object to convert.
-        * @param memberOf The outer instance for non-static inner class 
construction, or <jk>null</jk>.
-        * @param type The type to convert to.
-        * @param <T> The type to convert to.
-        * @return The converted object, or <jk>null</jk> if the input is 
<jk>null</jk>.
-        * @throws InvalidConversionException If no conversion path exists from 
the input type to the target type.
-        */
-       default <T> T to(Object o, Object memberOf, Class<T> type) {
-               return to(o, type);
-       }
-
        /**
         * Converts the specified object to the specified parameterized type.
         *
@@ -103,22 +84,43 @@ public interface Converter {
        <T> T to(Object o, Type mainType, Type...args);
 
        /**
-        * Converts the specified object to the specified parameterized type, 
using the given outer instance for
-        * non-static inner class construction.
+        * Converts the specified object to the specified type, using the given 
outer instance and converter session.
         *
         * <p>
-        * Returns <jk>null</jk> only when the input object is <jk>null</jk>.
-        * Throws {@link InvalidConversionException} when no conversion path 
exists.
+        * The default implementation ignores both {@code memberOf} and {@code 
session} and delegates to
+        * {@link #to(Object, Class)}.
+        * Override in subclasses that wish to thread these into {@link 
Conversion#to(Object, Object, ConverterSession, Class[])}.
+        *
+        * @param o The object to convert.
+        * @param memberOf The outer instance for non-static inner class 
construction, or <jk>null</jk>.
+        * @param session The converter session providing contextual objects, 
or <jk>null</jk>.
+        * @param type The type to convert to.
+        * @param <T> The type to convert to.
+        * @return The converted object, or <jk>null</jk> if the input is 
<jk>null</jk>.
+        * @throws InvalidConversionException If no conversion path exists from 
the input type to the target type.
+        */
+       default <T> T to(Object o, Object memberOf, ConverterSession session, 
Class<T> type) {
+               return to(o, type);
+       }
+
+       /**
+        * Converts the specified object to the specified parameterized type, 
using the given outer instance and converter session.
+        *
+        * <p>
+        * The default implementation ignores both {@code memberOf} and {@code 
session} and delegates to
+        * {@link #to(Object, Type, Type[])}.
+        * Override in subclasses that wish to thread these into {@link 
Conversion#to(Object, Object, ConverterSession, Class[])}.
         *
         * @param o The object to convert.
         * @param memberOf The outer instance for non-static inner class 
construction, or <jk>null</jk>.
+        * @param session The converter session providing contextual objects, 
or <jk>null</jk>.
         * @param mainType The main type to convert to.
         * @param args The type parameters of the main type.
         * @param <T> The type to convert to.
         * @return The converted object, or <jk>null</jk> if the input is 
<jk>null</jk>.
         * @throws InvalidConversionException If no conversion path exists from 
the input type to the target type.
         */
-       default <T> T to(Object o, Object memberOf, Type mainType, Type...args) 
{
+       default <T> T to(Object o, Object memberOf, ConverterSession session, 
Type mainType, Type...args) {
                return to(o, mainType, args);
        }
 }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/ConverterSession.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/ConverterSession.java
new file mode 100644
index 0000000000..d9884ddd3a
--- /dev/null
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/conversion/ConverterSession.java
@@ -0,0 +1,76 @@
+/*
+ * 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.commons.conversion;
+
+import java.util.*;
+
+/**
+ * Provides contextual objects to {@link Conversion} functions at call time.
+ *
+ * <p>
+ * A {@code ConverterSession} is passed through the {@link 
Conversion#to(Object, Object, ConverterSession, Class[])}
+ * method and allows conversion lambdas to retrieve session-scoped objects 
such as a {@link java.util.TimeZone},
+ * {@link java.util.Locale}, or media type without requiring those objects to 
be captured in the lambda closure.
+ *
+ * <p>
+ * Implementations look up contextual objects by type (and optionally by 
name). If no matching object is
+ * registered for the requested type the method returns {@link 
Optional#empty()}.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ *     <jc>// A conversion that uses the session's TimeZone.</jc>
+ *     BeanContext <jv>bc</jv> = BeanContext
+ *             .<jsm>create</jsm>()
+ *             .addConverter(String.<jk>class</jk>, Calendar.<jk>class</jk>, 
(<jv>in</jv>, <jv>memberOf</jv>, <jv>session</jv>, <jv>args</jv>) -&gt; {
+ *                     var <jv>tz</jv> = 
<jv>session</jv>.get(TimeZone.<jk>class</jk>).orElse(TimeZone.<jsm>getDefault</jsm>());
+ *                     <jc>// ... parse string using tz ...</jc>
+ *             })
+ *             .build();
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ *     <li class='jc'>{@link Conversion}
+ *     <li class='jc'>{@link Converter}
+ * </ul>
+ */
+public interface ConverterSession {
+
+       /**
+        * Returns a contextual object of the specified type, or {@link 
Optional#empty()} if none is registered.
+        *
+        * @param <T> The object type.
+        * @param c The object type to look up.
+        * @return The contextual object wrapped in an {@link Optional}, or 
{@link Optional#empty()} if not available.
+        */
+       <T> Optional<T> get(Class<T> c);
+
+       /**
+        * Returns a named contextual object of the specified type, or {@link 
Optional#empty()} if none is registered.
+        *
+        * <p>
+        * The default implementation ignores the name and delegates to {@link 
#get(Class)}.
+        * Override this method when multiple objects of the same type need to 
be distinguished by name.
+        *
+        * @param <T> The object type.
+        * @param name The object name, or <jk>null</jk> to match any.
+        * @param c The object type to look up.
+        * @return The contextual object wrapped in an {@link Optional}, or 
{@link Optional#empty()} if not available.
+        */
+       default <T> Optional<T> get(String name, Class<T> c) {
+               return get(c);
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
index a867940270..ba4ba41dba 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
@@ -33,6 +33,7 @@ import java.util.stream.*;
 
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.commons.collections.*;
+import org.apache.juneau.commons.conversion.*;
 import org.apache.juneau.commons.function.*;
 import org.apache.juneau.commons.reflect.*;
 import org.apache.juneau.commons.reflect.Visibility;
@@ -257,6 +258,7 @@ public class BeanContext extends Context {
                private Set<ClassInfo> notBeanClasses;
 
                private Set<String> notBeanPackages;
+               private ConfigurableConverter converter;
 
                /**
                 * Constructor.
@@ -312,6 +314,7 @@ public class BeanContext extends Context {
                        beansRequireDefaultConstructor = 
copyFrom.beansRequireDefaultConstructor;
                        beansRequireSerializable = 
copyFrom.beansRequireSerializable;
                        beansRequireSettersForGetters = 
copyFrom.beansRequireSettersForGetters;
+                       converter = copyFrom.converter;
                        disableBeansRequireSomeProperties = ! 
copyFrom.beansRequireSomeProperties;
                        disableIgnoreMissingSetters = ! 
copyFrom.ignoreMissingSetters;
                        disableIgnoreTransientFields = ! 
copyFrom.ignoreTransientFields;
@@ -351,6 +354,7 @@ public class BeanContext extends Context {
                        beansRequireDefaultConstructor = 
copyFrom.beansRequireDefaultConstructor;
                        beansRequireSerializable = 
copyFrom.beansRequireSerializable;
                        beansRequireSettersForGetters = 
copyFrom.beansRequireSettersForGetters;
+                       converter = copyFrom.converter;
                        disableBeansRequireSomeProperties = 
copyFrom.disableBeansRequireSomeProperties;
                        disableIgnoreMissingSetters = 
copyFrom.disableIgnoreMissingSetters;
                        disableIgnoreTransientFields = 
copyFrom.disableIgnoreTransientFields;
@@ -398,6 +402,37 @@ public class BeanContext extends Context {
                        return this;
                }
 
+               /**
+                * Registers a custom type conversion on this context.
+                *
+                * <p>
+                * User-registered conversions take priority over built-in type 
dispatch in
+                * {@link BeanSession#convertToMemberType(Object, Object, 
ClassMeta)}, but are checked after
+                * {@link ObjectSwap} processing.
+                *
+                * <h5 class='section'>Example:</h5>
+                * <p class='bjava'>
+                *      <jc>// Register a custom String-to-MyBean 
conversion.</jc>
+                *      BeanContext <jv>bc</jv> = BeanContext
+                *              .<jsm>create</jsm>()
+                *              .addConverter(String.<jk>class</jk>, 
MyBean.<jk>class</jk>, (<jv>in</jv>, <jv>memberOf</jv>, <jv>session</jv>, 
<jv>args</jv>) -&gt; MyBean.fromString(<jv>in</jv>))
+                *              .build();
+                * </p>
+                *
+                * @param <I> The input type.
+                * @param <O> The output type.
+                * @param inType The input type class.
+                * @param outType The output type class.
+                * @param conversion The conversion function.
+                * @return This object.
+                */
+               public <I, O> Builder addConverter(Class<I> inType, Class<O> 
outType, Conversion<I, O> conversion) {
+                       if (converter == null)
+                               converter = new ConfigurableConverter();
+                       converter.add(inType, outType, conversion);
+                       return this;
+               }
+
                /**
                 * Minimum bean class visibility.
                 *
@@ -2226,6 +2261,7 @@ public class BeanContext extends Context {
                                beanMethodVisibility,
                                beanFieldVisibility,
                                beanDictionary,
+                               converter != null ? 
System.identityHashCode(converter) : 0,
                                swaps,
                                notBeanClasses,
                                notBeanPackages,
@@ -3628,6 +3664,7 @@ public class BeanContext extends Context {
        private final NullableSupplier<WriterSerializer> beanToStringSerializer;
        private final BeanRegistry beanRegistry;
        private final BeanSession defaultSession;
+       private final ConfigurableConverter converter;
        private final boolean beanMapPutReturnsOldValue;
        private final boolean beansRequireDefaultConstructor;
        private final boolean beansRequireSerializable;
@@ -3685,6 +3722,7 @@ public class BeanContext extends Context {
                beansRequireSerializable = builder.beansRequireSerializable;
                beansRequireSettersForGetters = 
builder.beansRequireSettersForGetters;
                beansRequireSomeProperties = ! 
builder.disableBeansRequireSomeProperties;
+               converter = builder.converter;
                findFluentSetters = builder.findFluentSetters;
                hashKey = builder.hashKey();
                ignoreInvocationExceptionsOnGetters = 
builder.ignoreInvocationExceptionsOnGetters;
@@ -3807,6 +3845,14 @@ public class BeanContext extends Context {
         */
        public final Visibility getBeanFieldVisibility() { return 
beanFieldVisibility; }
 
+       /**
+        * Returns the custom converter registered on this context, or 
<jk>null</jk> if none.
+        *
+        * @see BeanContext.Builder#addConverter(Class, Class, Conversion)
+        * @return The custom converter, or <jk>null</jk> if no custom 
conversions have been registered.
+        */
+       public final ConfigurableConverter getConverter() { return converter; }
+
        /**
         * Returns the {@link BeanMeta} class for the specified class.
         *
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
index 4aafb9e8cf..b8d141a09d 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
@@ -36,6 +36,7 @@ import java.util.logging.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.collections.*;
 import org.apache.juneau.commons.collections.FluentMap;
+import org.apache.juneau.commons.conversion.*;
 import org.apache.juneau.commons.lang.*;
 import org.apache.juneau.commons.reflect.*;
 import org.apache.juneau.commons.time.*;
@@ -58,7 +59,7 @@ import org.apache.juneau.utils.*;
        "java:S115", // Constants use UPPER_snakeCase naming convention
        "java:S1452"  // Wildcard required - ClassMeta<?>, ObjectSwap<?,?>, 
etc. for bean metadata
 })
-public class BeanSession extends ContextSession {
+public class BeanSession extends ContextSession implements ConverterSession {
 
        // Property name constants
        private static final String PROP_locale = "locale";
@@ -266,8 +267,11 @@ public class BeanSession extends ContextSession {
                return 1;
        }
 
-       private static boolean hasMutater(ClassMeta<?> from, ClassMeta<?> to) {
-               return to.hasMutaterFrom(from) || from.hasMutaterTo(to);
+       private boolean hasMutater(ClassMeta<?> from, ClassMeta<?> to) {
+               if (to.hasMutaterFrom(from) || from.hasMutaterTo(to))
+                       return true;
+               var c = ctx.getConverter();
+               return c != null && c.hasCustomConversion(from.inner(), 
to.inner());
        }
 
        private static final boolean isNullOrEmpty(Object o) {
@@ -292,6 +296,14 @@ public class BeanSession extends ContextSession {
                timeZone = opt(builder.timeZone).orElse(builder.timeZone);
        }
 
+       @Override /* ConverterSession */
+       public <T> Optional<T> get(Class<T> c) {
+               if (c == TimeZone.class) return opt(c.cast(timeZone));
+               if (c == Locale.class) return opt(c.cast(locale));
+               if (c == MediaType.class) return opt(c.cast(mediaType));
+               return opte();
+       }
+
        /**
         * Logs a warning message.
         *
@@ -1220,6 +1232,10 @@ public class BeanSession extends ContextSession {
                                        return (T)swap.swap(this, value);
                        }
 
+                       var ctxConverter = ctx.getConverter();
+                       if (ctxConverter != null && 
ctxConverter.hasCustomConversion(from.inner(), tc))
+                               return ctxConverter.to(value, outer, this, tc);
+
                        if (to.isCharSequence() && 
(from.isDateOrCalendarOrTemporal() || from.isDuration()))
                                return (T) Iso8601Utils.format(value, from, 
getTimeZone());
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/BeanContextConverter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/BeanContextConverter.java
index ea70f714af..81e0fd6704 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/BeanContextConverter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/BeanContextConverter.java
@@ -126,13 +126,14 @@ public class BeanContextConverter implements Converter {
         *
         * @param o The object to convert.
         * @param memberOf The outer instance for non-static inner class 
construction, or <jk>null</jk>.
+        * @param session The converter session, or <jk>null</jk>.
         * @param type The target class type.
         * @param <T> The target type to convert to.
         * @return The converted object, or <jk>null</jk> if the input object 
is <jk>null</jk>.
         * @throws InvalidDataConversionException If the object cannot be 
converted to the specified type.
         */
        @Override
-       public <T> T to(Object o, Object memberOf, Class<T> type) {
+       public <T> T to(Object o, Object memberOf, ConverterSession session, 
Class<T> type) {
                return 
BeanContext.DEFAULT_SESSION.convertToMemberType(memberOf, o, type);
        }
 
@@ -142,6 +143,7 @@ public class BeanContextConverter implements Converter {
         *
         * @param o The object to convert.
         * @param memberOf The outer instance for non-static inner class 
construction, or <jk>null</jk>.
+        * @param session The converter session, or <jk>null</jk>.
         * @param mainType The main type to convert to.
         * @param args The type parameters of the main type.
         * @param <T> The target type to convert to.
@@ -150,7 +152,7 @@ public class BeanContextConverter implements Converter {
         */
        @SuppressWarnings("unchecked")
        @Override
-       public <T> T to(Object o, Object memberOf, Type mainType, Type... args) 
{
+       public <T> T to(Object o, Object memberOf, ConverterSession session, 
Type mainType, Type... args) {
                var rawType = (Class<T>) (mainType instanceof ParameterizedType 
pt ? pt.getRawType() : (Class<?>) mainType);
                return 
BeanContext.DEFAULT_SESSION.convertToMemberType(memberOf, o, rawType);
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/BasicConverter_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/BasicConverter_Test.java
index cdf2875c02..5ae2aede09 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/BasicConverter_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/BasicConverter_Test.java
@@ -781,9 +781,9 @@ class BasicConverter_Test extends TestBase {
        }
 
        @Test void p01_innerClassConstructorWithMemberOf() {
-               // to(o, memberOf, Class) routes through the inner-class path 
in findConstructorConversion;
+               // to(o, memberOf, session, Class) routes through the 
inner-class path in findConstructorConversion;
                // memberOf (this) is passed as the synthetic outer-instance 
parameter.
-               var x = C.to("hello", this, P01_Inner.class);
+               var x = C.to("hello", this, (ConverterSession)null, 
P01_Inner.class);
                assertEquals("hello", x.value);
        }
 
@@ -791,7 +791,7 @@ class BasicConverter_Test extends TestBase {
                // The inner-class branch is entered (P02 has an enclosing 
class) but the 2-param ctor
                // (BasicConverter_Test, String) does not exist — falls through 
to the 1-param check which also
                // fails → InvalidConversionException.
-               assertThrows(InvalidConversionException.class, () -> 
C.to("hello", this, P02_InnerNoMatch.class));
+               assertThrows(InvalidConversionException.class, () -> 
C.to("hello", this, (ConverterSession)null, P02_InnerNoMatch.class));
        }
 
        
//====================================================================================================
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/CachingConverter_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/CachingConverter_Test.java
index 7501573dac..814ce48612 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/CachingConverter_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/CachingConverter_Test.java
@@ -107,40 +107,40 @@ class CachingConverter_Test extends TestBase {
        }
 
        
//====================================================================================================
-       // c - to(Object, Object, Class)
+       // c - to(Object, Object, ConverterSession, Class)
        
//====================================================================================================
 
        @Test void c01_toMemberOfNullInput() {
-               assertNull(C.to(null, "ignored", String.class));
+               assertNull(C.to(null, "ignored", (ConverterSession)null, 
String.class));
        }
 
        @Test void c02_toMemberOfSameType() {
                var s = "hello";
-               assertSame(s, C.to(s, null, String.class));
+               assertSame(s, C.to(s, null, (ConverterSession)null, 
String.class));
        }
 
        @Test void c03_toMemberOfConversion() {
-               assertEquals("42", C.to(42, null, String.class));
+               assertEquals("42", C.to(42, null, (ConverterSession)null, 
String.class));
        }
 
        @Test void c04_toMemberOfNoConversion() {
-               assertThrows(InvalidConversionException.class, () -> C.to(new 
StringBuilder("x"), null, java.net.URI.class));
+               assertThrows(InvalidConversionException.class, () -> C.to(new 
StringBuilder("x"), null, (ConverterSession)null, java.net.URI.class));
        }
 
        
//====================================================================================================
-       // d - to(Object, Object, Type, Type...)
+       // d - to(Object, Object, ConverterSession, Type, Type...)
        
//====================================================================================================
 
        // Sentinel used as the memberOf argument to avoid null-vs-Type 
overload ambiguity.
        private static final Object D_MEMBER_OF = new Object();
 
        @Test void d01_toMemberOfTypeNullInput() {
-               assertNull(C.to(null, D_MEMBER_OF, (Type) String.class));
+               assertNull(C.to(null, D_MEMBER_OF, (ConverterSession)null, 
(Type) String.class));
        }
 
        @Test void d02_toMemberOfTypePlainClass() {
                // mainType is a plain Class (false branch of ParameterizedType 
ternary)
-               assertEquals("42", C.to(42, D_MEMBER_OF, (Type) String.class));
+               assertEquals("42", C.to(42, D_MEMBER_OF, 
(ConverterSession)null, (Type) String.class));
        }
 
        @SuppressWarnings("unused")
@@ -156,25 +156,25 @@ class CachingConverter_Test extends TestBase {
 
        @Test void d03_toMemberOfTypeWithParameterizedType() {
                // mainType is a ParameterizedType (true branch of ternary); 
raw type List is extracted
-               var result = C.to(List.of("a", "b"), D_MEMBER_OF, 
D03_LIST_TYPE);
+               var result = C.to(List.of("a", "b"), D_MEMBER_OF, 
(ConverterSession)null, D03_LIST_TYPE);
                assertNotNull(result);
                assertInstanceOf(List.class, result);
        }
 
        @Test void d04_toMemberOfTypeWithPlainClassArg() {
                // args contains a plain Class (false branch of arg ternary)
-               var result = C.to(List.of("1", "2"), D_MEMBER_OF, (Type) 
List.class, (Type) Integer.class);
+               var result = C.to(List.of("1", "2"), D_MEMBER_OF, 
(ConverterSession)null, (Type) List.class, (Type) Integer.class);
                assertEquals(List.of(1, 2), result);
        }
 
        @Test void d05_toMemberOfTypeWithParameterizedTypeArg() {
                // args contains a ParameterizedType (true branch of arg 
ternary); raw type List is extracted
-               var result = C.to(List.of(List.of("a", "b")), D_MEMBER_OF, 
(Type) List.class, LIST_ARG_TYPE);
+               var result = C.to(List.of(List.of("a", "b")), D_MEMBER_OF, 
(ConverterSession)null, (Type) List.class, LIST_ARG_TYPE);
                assertNotNull(result);
                assertInstanceOf(List.class, result);
        }
 
        @Test void d06_toMemberOfTypeNoConversion() {
-               assertThrows(InvalidConversionException.class, () -> C.to(new 
StringBuilder("x"), D_MEMBER_OF, (Type) java.net.URI.class));
+               assertThrows(InvalidConversionException.class, () -> C.to(new 
StringBuilder("x"), D_MEMBER_OF, (ConverterSession)null, (Type) 
java.net.URI.class));
        }
 }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/ConfigurableConverter_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/ConfigurableConverter_Test.java
index 5933c12b0d..d3106f6d4c 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/ConfigurableConverter_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/ConfigurableConverter_Test.java
@@ -42,7 +42,7 @@ public class ConfigurableConverter_Test {
 
        @Test void a01_registeredConversionIsUsed() {
                var c = new ConfigurableConverter()
-                       .add(String.class, A01_Value.class, (in, memberOf, 
args) -> new A01_Value(in));
+                       .add(String.class, A01_Value.class, (in, memberOf, 
session, args) -> new A01_Value(in));
                var result = c.to("hello", A01_Value.class);
                assertNotNull(result);
                assertEquals("hello", result.raw);
@@ -51,7 +51,7 @@ public class ConfigurableConverter_Test {
        @Test void a02_registeredConversionTakesPriorityOverBuiltIn() {
                // Integer→String has a built-in conversion; register a custom 
one that wraps with brackets
                var c = new ConfigurableConverter()
-                       .add(Integer.class, String.class, (in, memberOf, args) 
-> "[" + in + "]");
+                       .add(Integer.class, String.class, (in, memberOf, 
session, args) -> "[" + in + "]");
                assertEquals("[42]", c.to(42, String.class));
        }
 
@@ -63,13 +63,13 @@ public class ConfigurableConverter_Test {
 
        @Test void a04_nullInputReturnsNull() {
                var c = new ConfigurableConverter()
-                       .add(String.class, A01_Value.class, (in, memberOf, 
args) -> new A01_Value(in));
+                       .add(String.class, A01_Value.class, (in, memberOf, 
session, args) -> new A01_Value(in));
                assertNull(c.to(null, A01_Value.class));
        }
 
        @Test void a05_canConvertReturnsTrueForRegisteredType() {
                var c = new ConfigurableConverter()
-                       .add(String.class, A01_Value.class, (in, memberOf, 
args) -> new A01_Value(in));
+                       .add(String.class, A01_Value.class, (in, memberOf, 
session, args) -> new A01_Value(in));
                assertTrue(c.canConvert(String.class, A01_Value.class));
        }
 
@@ -85,8 +85,8 @@ public class ConfigurableConverter_Test {
 
        @Test void a08_multipleRegistrationsOnSameConverter() {
                var c = new ConfigurableConverter()
-                       .add(String.class, A01_Value.class, (in, memberOf, 
args) -> new A01_Value(in))
-                       .add(Integer.class, A01_Value.class, (in, memberOf, 
args) -> new A01_Value(String.valueOf(in)));
+                       .add(String.class, A01_Value.class, (in, memberOf, 
session, args) -> new A01_Value(in))
+                       .add(Integer.class, A01_Value.class, (in, memberOf, 
session, args) -> new A01_Value(String.valueOf(in)));
                assertEquals("hello", c.to("hello", A01_Value.class).raw);
                assertEquals("42", c.to(42, A01_Value.class).raw);
        }
@@ -101,7 +101,7 @@ public class ConfigurableConverter_Test {
 
        @Test void a09_registrationDoesNotAffectOtherInstances() {
                var c1 = new ConfigurableConverter()
-                       .add(String.class, A09_Value.class, (in, memberOf, 
args) -> new A09_Value(in));
+                       .add(String.class, A09_Value.class, (in, memberOf, 
session, args) -> new A09_Value(in));
                var c2 = new ConfigurableConverter();
                assertNotNull(c1.to("x", A09_Value.class));
                assertFalse(c2.canConvert(String.class, A09_Value.class));
@@ -116,8 +116,8 @@ public class ConfigurableConverter_Test {
                var memberOf = new Object();
                var captured = new AtomicReference<Object>();
                var c = new ConfigurableConverter()
-                       .add(String.class, Integer.class, (in, m, args) -> { 
captured.set(m); return Integer.parseInt(in); });
-               c.to("42", memberOf, Integer.class);
+                       .add(String.class, Integer.class, (in, m, session, 
args) -> { captured.set(m); return Integer.parseInt(in); });
+               c.to("42", memberOf, (ConverterSession)null, Integer.class);
                assertSame(memberOf, captured.get());
        }
 
@@ -144,7 +144,7 @@ public class ConfigurableConverter_Test {
                        pool.submit(() -> {
                                try {
                                        latch.await();
-                                       c.add(String.class, C01_Value.class, 
(in, m, args) -> new C01_Value(in.toUpperCase()));
+                                       c.add(String.class, C01_Value.class, 
(in, m, session, args) -> new C01_Value(in.toUpperCase()));
                                } catch (Exception e) {
                                        errors.incrementAndGet();
                                }
@@ -161,7 +161,7 @@ public class ConfigurableConverter_Test {
 
        @Test void c02_concurrentConvertsAreThreadSafe() throws Exception {
                var c = new ConfigurableConverter()
-                       .add(String.class, Integer.class, (in, m, args) -> 
Integer.parseInt(in) * 2);
+                       .add(String.class, Integer.class, (in, m, session, 
args) -> Integer.parseInt(in) * 2);
                var threads = 32;
                var latch = new CountDownLatch(1);
                var errors = new AtomicInteger(0);
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/Converter_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/Converter_Test.java
index ddb072a68e..ff7036d098 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/Converter_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/conversion/Converter_Test.java
@@ -51,14 +51,14 @@ class Converter_Test extends TestBase {
                @Override public <T> T to(Object o, Type mainType, Type... 
args) { return BasicConverter.INSTANCE.to(o, mainType, args); }
        };
 
-       @Test void b01_defaultToMemberOfClass() {
-               // default to(o, memberOf, Class) ignores memberOf and 
delegates to to(o, Class)
-               assertEquals(Integer.valueOf(42), B_STUB.to("42", new Object(), 
Integer.class));
+       @Test void b01_defaultToSessionClass() {
+               // default to(o, memberOf, session, Class) ignores memberOf and 
session and delegates to to(o, Class)
+               assertEquals(Integer.valueOf(42), B_STUB.to("42", new Object(), 
(ConverterSession)null, Integer.class));
        }
 
-       @Test void b02_defaultToMemberOfType() {
-               // default to(o, memberOf, Type, Type...) ignores memberOf and 
delegates to to(o, Type, Type...)
-               var result = B_STUB.to(List.of("1", "2"), new Object(), (Type) 
List.class, new Type[]{Integer.class});
+       @Test void b02_defaultToSessionType() {
+               // default to(o, memberOf, session, Type, Type...) ignores 
memberOf and session and delegates to to(o, Type, Type...)
+               var result = B_STUB.to(List.of("1", "2"), new Object(), 
(ConverterSession)null, (Type) List.class, new Type[]{Integer.class});
                assertEquals(List.of(1, 2), result);
        }
 }

Reply via email to