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 34bc06c849 New BasicConverter class
34bc06c849 is described below
commit 34bc06c84979ae537eb71d57037373402d231810
Author: James Bognar <[email protected]>
AuthorDate: Thu Apr 2 08:05:57 2026 -0700
New BasicConverter class
---
.../juneau/commons/conversion/BasicConverter.java | 102 ++++++++++++---------
.../commons/conversion/CachingConverter.java | 61 +++++++++++-
.../juneau/commons/conversion/Conversion.java | 3 +-
.../juneau/commons/conversion/Converter.java | 39 ++++++++
.../apache/juneau/utils/BeanContextConverter.java | 35 +++++++
.../commons/conversion/BasicConverter_Test.java | 51 ++++++++++-
.../commons/conversion/CachingConverter_Test.java | 72 +++++++++++++++
.../juneau/commons/conversion/Converter_Test.java | 23 +++++
8 files changed, 333 insertions(+), 53 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 822b43ffe6..9bb4e16e99 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, args) -> (O) in;
+ return (in, memberOf, 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, args) -> (O)
Integer.valueOf(((Number) in).intValue());
- if (outType == Long.class) return (in, args) -> (O)
Long.valueOf(((Number) in).longValue());
- if (outType == Short.class) return (in, args) -> (O)
Short.valueOf(((Number) in).shortValue());
- if (outType == Float.class) return (in, args) -> (O)
Float.valueOf(((Number) in).floatValue());
- if (outType == Double.class) return (in, args) -> (O)
Double.valueOf(((Number) in).doubleValue());
- if (outType == Byte.class) return (in, args) -> (O)
Byte.valueOf(((Number) in).byteValue());
- if (outType == AtomicInteger.class) return (in, args) -> (O)
new AtomicInteger(((Number) in).intValue());
- if (outType == AtomicLong.class) return (in, args) -> (O) new
AtomicLong(((Number) in).longValue());
+ 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());
return null;
}
private <O> Conversion<Boolean, O> findNumberFromBoolean(Class<O>
outType) {
- if (outType == Integer.class) return (in, args) -> (O)
Integer.valueOf(in.booleanValue() ? 1 : 0);
- if (outType == Long.class) return (in, args) -> (O)
Long.valueOf(in.booleanValue() ? 1L : 0L);
- if (outType == Short.class) return (in, args) -> (O)
Short.valueOf(in.booleanValue() ? (short) 1 : (short) 0);
- if (outType == Float.class) return (in, args) -> (O)
Float.valueOf(in.booleanValue() ? 1f : 0f);
- if (outType == Double.class) return (in, args) -> (O)
Double.valueOf(in.booleanValue() ? 1d : 0d);
- if (outType == Byte.class) return (in, args) -> (O)
Byte.valueOf(in.booleanValue() ? (byte) 1 : (byte) 0);
- if (outType == AtomicInteger.class) return (in, args) -> (O)
new AtomicInteger(in.booleanValue() ? 1 : 0);
- if (outType == AtomicLong.class) return (in, args) -> (O) new
AtomicLong(in.booleanValue() ? 1L : 0L);
+ 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);
return null;
}
private <O> Conversion<CharSequence, O> findNumberFromString(Class<O>
outType) {
- return (in, args) -> (O) parseNumber(in.toString(), (Class<?
extends Number>) outType);
+ return (in, memberOf, 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, args) -> (O) Boolean.valueOf(((Number)
in).intValue() != 0);
+ return (in, memberOf, 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, args) -> {
+ return (in, memberOf, args) -> {
var s = in.toString();
return s.length() == 1 ? (O)
Character.valueOf(s.charAt(0)) : null;
};
if (Number.class.isAssignableFrom(inType))
- return (in, args) -> (O) Character.valueOf((char)
((Number) in).intValue());
+ return (in, memberOf, 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, args) ->
Arrays.toString((int[]) in);
- if (inType == long[].class) return (in, args) ->
Arrays.toString((long[]) in);
- if (inType == double[].class) return (in, args) ->
Arrays.toString((double[]) in);
- if (inType == float[].class) return (in, args) ->
Arrays.toString((float[]) in);
- if (inType == boolean[].class) return (in, args) ->
Arrays.toString((boolean[]) in);
- if (inType == byte[].class) return (in, args) ->
Arrays.toString((byte[]) in);
- if (inType == short[].class) return (in, args) ->
Arrays.toString((short[]) in);
- if (inType == char[].class) return (in, args) ->
Arrays.toString((char[]) in);
- return (in, args) -> Arrays.deepToString((Object[]) in);
+ 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);
}
- return (in, args) -> in.toString();
+ return (in, memberOf, 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, args) -> (O) Enum.valueOf((Class<Enum>)
outType, in.toString());
+ return (in, memberOf, 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, args) -> {
+ return (in, memberOf, 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, args) -> {
+ return (in, memberOf, args) -> {
var keyType = args.length > 0 ? args[0] : null;
var valType = args.length > 1 ? args[1] : null;
if (keyType == null &&
outType.isAssignableFrom(inType))
@@ -383,7 +383,7 @@ public class BasicConverter extends CachingConverter {
private <I, O> Conversion<I, O> findArrayConversion(Class<I> inType,
Class<O> outType) {
if (Collection.class.isAssignableFrom(inType) ||
inType.isArray()) {
var componentType = outType.getComponentType();
- return (in, args) -> {
+ return (in, memberOf, args) -> {
if (Collection.class.isAssignableFrom(inType)) {
var list = (Collection<?>) in;
var arr =
Array.newInstance(componentType, list.size());
@@ -408,13 +408,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, args) -> TimeZone.getTimeZone(in);
+ return (Conversion<I, O>) (Conversion<String,
TimeZone>) (in, memberOf, args) -> TimeZone.getTimeZone(in);
if (TimeZone.class.isAssignableFrom(inType) && outType ==
String.class)
- return (Conversion<I, O>) (Conversion<TimeZone,
String>) (in, args) -> in.getID();
+ return (Conversion<I, O>) (Conversion<TimeZone,
String>) (in, memberOf, args) -> in.getID();
if (inType == String.class && outType == Locale.class)
- return (Conversion<I, O>) (Conversion<String, Locale>)
(in, args) -> Locale.forLanguageTag(in.replace('_', '-'));
+ return (Conversion<I, O>) (Conversion<String, Locale>)
(in, memberOf, args) -> Locale.forLanguageTag(in.replace('_', '-'));
if (CharSequence.class.isAssignableFrom(inType) && outType ==
Boolean.class)
- return (in, args) -> {
+ return (in, memberOf, args) -> {
var s = in.toString();
if (s.isEmpty() || "null".equals(s))
return null;
@@ -433,14 +433,14 @@ public class BasicConverter extends CachingConverter {
for (var name : FACTORY_METHOD_NAMES) {
var opt = findStaticMethod(ci, name, inType, outType);
if (opt.isPresent())
- return (in, args) -> opt.get().invoke(null, in);
+ return (in, memberOf, args) ->
opt.get().invoke(null, in);
}
var inName = inType.getSimpleName();
for (var prefix : new String[]{"from", "for", "parse"}) {
var opt = findStaticMethod(ci, prefix + inName, inType,
outType);
if (opt.isPresent())
- return (in, args) -> opt.get().invoke(null, in);
+ return (in, memberOf, args) ->
opt.get().invoke(null, in);
}
return null;
@@ -462,6 +462,20 @@ public class BasicConverter extends CachingConverter {
//-----------------------------------------------------------------------------------------------------------------
private <I, O> Conversion<I, O> findConstructorConversion(Class<I>
inType, Class<O> outType) {
+ if (outType.getEnclosingClass() != null &&
!Modifier.isStatic(outType.getModifiers())) {
+ var enclosing = outType.getEnclosingClass();
+ var opt = info(outType).getPublicConstructor(c ->
+ c.getParameterCount() == 2
+ && c.isNotDeprecated()
+ && c.getParameterTypes().get(0).inner() ==
enclosing
+ &&
c.getParameterTypes().get(1).isAssignableFrom(inType)
+ );
+ if (opt.isPresent()) {
+ var ctor = opt.get();
+ return (in, memberOf, args) ->
ctor.newInstance(memberOf, in);
+ }
+ }
+
var opt = info(outType).getPublicConstructor(c ->
c.getParameterCount() == 1
&& c.isNotDeprecated()
@@ -469,7 +483,7 @@ public class BasicConverter extends CachingConverter {
);
if (opt.isPresent()) {
var ctor = opt.get();
- return (in, args) -> ctor.newInstance(in);
+ return (in, memberOf, args) -> ctor.newInstance(in);
}
return null;
}
@@ -489,7 +503,7 @@ public class BasicConverter extends CachingConverter {
);
if (opt.isPresent()) {
var method = opt.get();
- return (in, args) -> method.invoke(in);
+ return (in, memberOf, args) -> method.invoke(in);
}
return null;
}
@@ -504,7 +518,7 @@ public class BasicConverter extends CachingConverter {
var ctor = opt.get();
try {
return ctor.newInstance();
- } catch (@SuppressWarnings("unused") Exception e) {
+ } catch (@SuppressWarnings("unused") Exception e) { // HTT:
constructor would have to throw to reach here
return defaultSupplier.get(); // HTT
}
}
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 6f9a94ee38..cdd37efa1c 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
@@ -39,7 +39,7 @@ import java.util.stream.*;
* <ja>@Override</ja>
* <jk>protected</jk> <I,O> Conversion<I,O>
findConversion(Class<I> inType, Class<O> outType) {
* <jk>if</jk> (inType == String.<jk>class</jk> &&
outType == Integer.<jk>class</jk>)
- * <jk>return</jk> (Conversion<I,O>)
(Conversion<String,Integer>) (<jv>s</jv>, <jv>args</jv>) ->
Integer.<jsm>valueOf</jsm>(<jv>s</jv>);
+ * <jk>return</jk> (Conversion<I,O>)
(Conversion<String,Integer>) (<jv>s</jv>, <jv>memberOf</jv>,
<jv>args</jv>) -> Integer.<jsm>valueOf</jsm>(<jv>s</jv>);
* <jk>return null</jk>;
* }
* }
@@ -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, args) -> null;
+ 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
// 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);
+ return fn.to(o, null);
}
/**
@@ -160,6 +160,59 @@ 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, argClasses);
+ return fn.to(o, null, argClasses);
+ }
+
+ /**
+ * Converts the specified object to the specified type, passing the
outer instance for
+ * non-static inner class construction.
+ *
+ * @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 type The target type.
+ * @param <T> The target type.
+ * @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.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T to(Object o, Object memberOf, Class<T> type) {
+ if (o == null)
+ return null;
+ var inType = o.getClass();
+ if (inType == type)
+ return (T) o;
+ var fn = (Conversion<Object, T>) lookupConversion(inType, type);
+ if (fn == null)
+ throw new InvalidConversionException(inType, type);
+ return fn.to(o, memberOf);
+ }
+
+ /**
+ * Converts the specified object to the specified parameterized type,
passing the outer instance for
+ * non-static inner class construction.
+ *
+ * @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 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.
+ * @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.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T to(Object o, Object memberOf, Type mainType, Type... args)
{
+ if (o == null)
+ return null;
+ var rawType = (Class<T>) (mainType instanceof ParameterizedType
pt ? pt.getRawType() : (Class<?>) mainType);
+ var argClasses = Stream.of(args)
+ .map(t -> (Class<?>) (t instanceof ParameterizedType
pt2 ? pt2.getRawType() : t))
+ .toArray(Class[]::new);
+ var inType = o.getClass();
+ var fn = (Conversion<Object, T>) lookupConversion(inType,
rawType);
+ if (fn == null)
+ throw new InvalidConversionException(inType, rawType);
+ return fn.to(o, memberOf, argClasses);
}
}
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 f758fd8e0b..b1480aff73 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
@@ -40,8 +40,9 @@ public interface Conversion<I, O> {
* Converts the input object to the output type.
*
* @param in The input object.
+ * @param memberOf The outer instance for non-static inner class
construction, or <jk>null</jk>.
* @param args Optional type arguments for parameterized output types
(e.g. element type for collections).
* @return The converted object.
*/
- O to(I in, Class<?>... args);
+ O to(I in, Object memberOf, 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 f67b158512..7db0a003f2 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,6 +54,25 @@ 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.
*
@@ -82,4 +101,24 @@ public interface Converter {
* @throws InvalidConversionException If no conversion path exists from
the input type to the target type.
*/
<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.
+ *
+ * <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 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)
{
+ return to(o, mainType, args);
+ }
}
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 c6c5385dcb..ea70f714af 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
@@ -119,4 +119,39 @@ public class BeanContextConverter implements Converter {
public <T> T to(Object o, Type mainType, Type... args) {
return BeanContext.DEFAULT_SESSION.convertToType(o, mainType,
args);
}
+
+ /**
+ * Converts the specified object to the specified type, using the given
outer instance for
+ * non-static inner class construction.
+ *
+ * @param o The object to convert.
+ * @param memberOf The outer instance for non-static inner class
construction, 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) {
+ return
BeanContext.DEFAULT_SESSION.convertToMemberType(memberOf, o, type);
+ }
+
+ /**
+ * Converts the specified object to the specified parameterized type,
using the given outer instance for
+ * non-static inner class construction.
+ *
+ * @param o The object to convert.
+ * @param memberOf The outer instance for non-static inner class
construction, 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.
+ * @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.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T to(Object o, Object memberOf, 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 be46a4a37c..cdf2875c02 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
@@ -18,6 +18,7 @@ package org.apache.juneau.commons.conversion;
import static org.junit.jupiter.api.Assertions.*;
+import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
@@ -195,7 +196,7 @@ class BasicConverter_Test extends TestBase {
}
@Test void f02_listToListTyped() {
- var a = C.to(List.of("1", "2", "3"), List.class, Integer.class);
+ var a = C.to(List.of("1", "2", "3"), (Type) List.class, new
Type[]{Integer.class});
assertNotNull(a);
assertEquals(3, ((List<?>) a).size());
assertEquals(1, ((List<?>) a).get(0));
@@ -211,7 +212,7 @@ class BasicConverter_Test extends TestBase {
@Test void f03b_arrayToTypedList() {
// array branch with elemType != null — covers line 326 true
branch of ternary
- var a = C.to(new String[]{"1", "2", "3"}, List.class,
Integer.class);
+ var a = C.to(new String[]{"1", "2", "3"}, (Type) List.class,
new Type[]{Integer.class});
assertNotNull(a);
assertEquals(List.of(1, 2, 3), a);
}
@@ -318,7 +319,7 @@ class BasicConverter_Test extends TestBase {
}
@Test void g02_mapToMapTyped() {
- var a = C.to(Map.of(1, "100", 2, "200"), Map.class,
String.class, Integer.class);
+ var a = C.to(Map.of(1, "100", 2, "200"), (Type) Map.class, new
Type[]{String.class, Integer.class});
assertNotNull(a);
var map = (Map<?, ?>) a;
assertTrue(map.containsKey("1") || map.containsKey("2"));
@@ -369,7 +370,7 @@ class BasicConverter_Test extends TestBase {
@Test void g08_mapToAbstractMap() {
// Hits line 370 C=true branch (AbstractMap.class); use typed
args to bypass short-circuit
- var a = (Map<?, ?>) C.to(Map.of("a", 1), AbstractMap.class,
String.class, Integer.class);
+ var a = (Map<?, ?>) C.to(Map.of("a", 1), (Type)
AbstractMap.class, new Type[]{String.class, Integer.class});
assertNotNull(a);
assertInstanceOf(AbstractMap.class, a);
assertEquals(1, a.size());
@@ -762,4 +763,46 @@ class BasicConverter_Test extends TestBase {
@Test void o01_noConversion() {
assertThrows(InvalidConversionException.class, () -> C.to(new
Object(), BasicConverter_Test.class));
}
+
+
//====================================================================================================
+ // p - Non-static inner class constructor (memberOf path)
+
//====================================================================================================
+
+ // Non-static inner class with a String constructor; the JVM
synthesizes Inner(BasicConverter_Test, String).
+ public class P01_Inner {
+ public final String value;
+ public P01_Inner(String s) { this.value = s; }
+ }
+
+ // Non-static inner class whose only constructor takes a different
input type (no match for String).
+ public class P02_InnerNoMatch {
+ public final int value;
+ public P02_InnerNoMatch(int n) { this.value = n; }
+ }
+
+ @Test void p01_innerClassConstructorWithMemberOf() {
+ // to(o, memberOf, 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);
+ assertEquals("hello", x.value);
+ }
+
+ @Test void p02_innerClassNoCtorMatchFallsThrough() {
+ // 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));
+ }
+
+
//====================================================================================================
+ // q - newCollection AbstractList branch
+
//====================================================================================================
+
+ @Test void q01_collectionToAbstractList() {
+ // Hits the AbstractList.class branch in newCollection, which
returns a new ArrayList.
+ var result = C.to(List.of("a", "b"), (Type) AbstractList.class);
+ assertNotNull(result);
+ assertInstanceOf(List.class, result);
+ assertEquals(2, ((List<?>) result).size());
+ }
}
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 91caefca46..7501573dac 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
@@ -105,4 +105,76 @@ class CachingConverter_Test extends TestBase {
assertNotNull(result);
assertInstanceOf(List.class, result);
}
+
+
//====================================================================================================
+ // c - to(Object, Object, Class)
+
//====================================================================================================
+
+ @Test void c01_toMemberOfNullInput() {
+ assertNull(C.to(null, "ignored", String.class));
+ }
+
+ @Test void c02_toMemberOfSameType() {
+ var s = "hello";
+ assertSame(s, C.to(s, null, String.class));
+ }
+
+ @Test void c03_toMemberOfConversion() {
+ assertEquals("42", C.to(42, null, String.class));
+ }
+
+ @Test void c04_toMemberOfNoConversion() {
+ assertThrows(InvalidConversionException.class, () -> C.to(new
StringBuilder("x"), null, java.net.URI.class));
+ }
+
+
//====================================================================================================
+ // d - to(Object, Object, 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));
+ }
+
+ @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));
+ }
+
+ @SuppressWarnings("unused")
+ private List<String> d03_listField;
+ private static final Type D03_LIST_TYPE;
+ static {
+ try {
+ D03_LIST_TYPE =
CachingConverter_Test.class.getDeclaredField("d03_listField").getGenericType();
+ } catch (NoSuchFieldException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ @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);
+ 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);
+ 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);
+ 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));
+ }
}
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 a592d83fc5..ddb072a68e 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
@@ -19,6 +19,7 @@ package org.apache.juneau.commons.conversion;
import static org.junit.jupiter.api.Assertions.*;
import java.lang.reflect.*;
+import java.util.*;
import org.apache.juneau.*;
import org.junit.jupiter.api.*;
@@ -38,4 +39,26 @@ class Converter_Test extends TestBase {
assertTrue(STUB.canConvert(String.class, String.class));
assertTrue(STUB.canConvert(String.class, Integer.class));
}
+
+
//====================================================================================================
+ // b - default memberOf methods
+
//====================================================================================================
+
+ // Minimal converter that uses BasicConverter internally but does NOT
override the memberOf overloads,
+ // so the default methods on the Converter interface are exercised
directly.
+ private static final Converter B_STUB = new Converter() {
+ @Override public <T> T to(Object o, Class<T> type) { return
BasicConverter.INSTANCE.to(o, type); }
+ @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 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});
+ assertEquals(List.of(1, 2), result);
+ }
}