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 32d510f55 Test modernization
32d510f55 is described below
commit 32d510f5585921ee3dd0bbc4adf5ab38252a5984
Author: James Bognar <[email protected]>
AuthorDate: Tue Sep 9 13:51:38 2025 -0400
Test modernization
---
.../src/test/java/org/apache/juneau/TestUtils.java | 2 +-
.../a/rttests/RoundTripTransformBeans_Test.java | 40 ++--
.../apache/juneau/junit/BasicBeanConverter.java | 234 ++++++++-------------
.../org/apache/juneau/junit/BeanConverter.java | 66 +++---
.../java/org/apache/juneau/junit/Listifier.java | 2 +-
.../org/apache/juneau/junit/NestedTokenizer.java | 18 +-
.../{Listifier.java => PropertyExtractor.java} | 9 +-
.../apache/juneau/junit/PropertyExtractors.java | 94 +++++++++
.../java/org/apache/juneau/junit/Stringifier.java | 2 +-
.../test/java/org/apache/juneau/junit/Swapper.java | 2 +-
.../{Stringifier.java => ThrowingSupplier.java} | 9 +-
.../juneau/junit/{Listifier.java => Utils.java} | 38 +++-
12 files changed, 282 insertions(+), 234 deletions(-)
diff --git a/juneau-utest/src/test/java/org/apache/juneau/TestUtils.java
b/juneau-utest/src/test/java/org/apache/juneau/TestUtils.java
index a94c30586..37fc5131a 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/TestUtils.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/TestUtils.java
@@ -936,7 +936,7 @@ public class TestUtils extends Utils2 {
var i = name.indexOf("{");
var pn = i == -1 ? name : name.substring(0, i);
var spn = i == -1 ? null : splitNestedInner(name);
- var e = ofNullable(converter.getEntry(o, pn.equals("<NULL>") ?
null : pn)).orElse(null);
+ var e = ofNullable(converter.getProperty(o, pn.equals("<NULL>")
? null : pn)).orElse(null);
if (spn == null || e == null) return converter.stringify(e);
return spn.stream().map(x -> getEntry(converter, e,
x)).map(converter::stringify).collect(joining(",","{","}"));
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeans_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeans_Test.java
index b1538a5cb..5613c3b7e 100755
---
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeans_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeans_Test.java
@@ -541,14 +541,14 @@ class RoundTripTransformBeans_Test extends SimpleTestBase
{
public static class F1 {
- @Swap(TemporalCalendarSwap.IsoLocalDateTime.class)
+ @Swap(TemporalCalendarSwap.IsoInstant.class)
private Calendar c;
public Calendar getC() { return c; }
public void setC(Calendar v) { c = v; }
public static F1 create() {
var x = new F1();
- x.setC(parseISO8601Calendar("2018-12-12T05:12:00"));
+ x.setC(parseISO8601Calendar("2018-12-12T05:12:00Z"));
return x;
}
}
@@ -562,17 +562,17 @@ class RoundTripTransformBeans_Test extends SimpleTestBase
{
var x = F1.create();
var r = s.serialize(x);
- assertEquals("{c:'2018-12-12T05:12:00'}", r);
+ assertEquals("{c:'2018-12-12T05:12:00Z'}", r);
x = p.parse(r, F1.class);
- assertBean(x, "c", "2018-12-12T10:12:00Z");
+ assertBean(x, "c", "2018-12-12T05:12:00Z");
t.roundTrip(x, F1.class);
}
- @Swap(on="Dummy1.c", value=TemporalCalendarSwap.IsoLocalDateTime.class)
- @Swap(on="F1c.c", value=TemporalCalendarSwap.IsoLocalDateTime.class)
- @Swap(on="Dummy2.c", value=TemporalCalendarSwap.IsoLocalDateTime.class)
+ @Swap(on="Dummy1.c", value=TemporalCalendarSwap.IsoInstant.class)
+ @Swap(on="F1c.c", value=TemporalCalendarSwap.IsoInstant.class)
+ @Swap(on="Dummy2.c", value=TemporalCalendarSwap.IsoInstant.class)
private static class F1cConfig {}
public static class F1c {
@@ -583,7 +583,7 @@ class RoundTripTransformBeans_Test extends SimpleTestBase {
public static F1c create() {
var x = new F1c();
- x.setC(parseISO8601Calendar("2018-12-12T05:12:00"));
+ x.setC(parseISO8601Calendar("2018-12-12T05:12:00Z"));
return x;
}
}
@@ -597,16 +597,16 @@ class RoundTripTransformBeans_Test extends SimpleTestBase
{
var x = F1c.create();
var r = s.serialize(x);
- assertEquals("{c:'2018-12-12T05:12:00'}", r);
+ assertEquals("{c:'2018-12-12T05:12:00Z'}", r);
x = p.parse(r, F1c.class);
- assertSerialized(x, s, "{c:'2018-12-12T05:12:00'}");
+ assertSerialized(x, s, "{c:'2018-12-12T05:12:00Z'}");
t.roundTrip(x, F1c.class);
}
public static class F2a {
- @Swap(TemporalCalendarSwap.IsoLocalDateTime.class)
+ @Swap(TemporalCalendarSwap.IsoInstant.class)
protected Calendar c;
}
@@ -617,7 +617,7 @@ class RoundTripTransformBeans_Test extends SimpleTestBase {
public static F2 create() {
var x = new F2();
- x.setC(parseISO8601Calendar("2018-12-12T05:12:00"));
+ x.setC(parseISO8601Calendar("2018-12-12T05:12:00Z"));
return x;
}
}
@@ -631,17 +631,17 @@ class RoundTripTransformBeans_Test extends SimpleTestBase
{
var x = F2.create();
var r = s.serialize(x);
- assertEquals("{c:'2018-12-12T05:12:00'}", r);
+ assertEquals("{c:'2018-12-12T05:12:00Z'}", r);
x = p.parse(r, F2.class);
- assertBean(x, "c", "2018-12-12T10:12:00Z");
+ assertBean(x, "c", "2018-12-12T05:12:00Z");
t.roundTrip(x, F2.class);
}
- @Swap(on="Dummy1.c", value=TemporalCalendarSwap.IsoLocalDateTime.class)
- @Swap(on="F2ac.c", value=TemporalCalendarSwap.IsoLocalDateTime.class)
- @Swap(on="Dummy2.c", value=TemporalCalendarSwap.IsoLocalDateTime.class)
+ @Swap(on="Dummy1.c", value=TemporalCalendarSwap.IsoInstant.class)
+ @Swap(on="F2ac.c", value=TemporalCalendarSwap.IsoInstant.class)
+ @Swap(on="Dummy2.c", value=TemporalCalendarSwap.IsoInstant.class)
private static class F2acConfig {}
public static class F2ac {
@@ -655,7 +655,7 @@ class RoundTripTransformBeans_Test extends SimpleTestBase {
public static F2c create() {
var x = new F2c();
- x.setC(parseISO8601Calendar("2018-12-12T05:12:00"));
+ x.setC(parseISO8601Calendar("2018-12-12T05:12:00Z"));
return x;
}
}
@@ -669,10 +669,10 @@ class RoundTripTransformBeans_Test extends SimpleTestBase
{
var x = F2.create();
var r = s.serialize(x);
- assertEquals("{c:'2018-12-12T05:12:00'}", r);
+ assertEquals("{c:'2018-12-12T05:12:00Z'}", r);
x = p.parse(r, F2.class);
- assertBean(x, "c", "2018-12-12T10:12:00Z");
+ assertBean(x, "c", "2018-12-12T05:12:00Z");
t.roundTrip(x, F2.class);
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter.java
b/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter.java
index 315ee3200..43796bfe0 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter.java
@@ -14,10 +14,10 @@ package org.apache.juneau.junit;
import static java.util.stream.Collectors.*;
import static java.util.stream.StreamSupport.*;
-import static java.lang.Integer.*;
import static java.time.format.DateTimeFormatter.*;
import static java.util.Collections.*;
import static java.util.Optional.*;
+import static org.apache.juneau.junit.Utils.*;
import java.io.*;
@@ -25,14 +25,12 @@ import static java.util.Spliterators.*;
import java.lang.reflect.*;
import java.nio.file.*;
-import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.stream.*;
import org.apache.juneau.*;
-import org.apache.juneau.common.internal.*;
/**
* Default implementation of {@link BeanConverter} for Bean-Centric Test (BCT)
object conversion.
@@ -191,7 +189,6 @@ import org.apache.juneau.common.internal.*;
* </ul>
*
* @see BeanConverter
- * @see TestUtils
*/
@SuppressWarnings({"unchecked","rawtypes"})
public class BasicBeanConverter implements BeanConverter {
@@ -213,6 +210,7 @@ public class BasicBeanConverter implements BeanConverter {
private final List<StringifierEntry<?>> stringifiers;
private final List<ListifierEntry<?>> listifiers;
private final List<SwapperEntry<?>> swappers;
+ private final List<PropertyExtractor> propertyExtractors;
private final Map<String,Object> settings;
private final ConcurrentHashMap<Class,Optional<Stringifier<?>>>
stringifierMap = new ConcurrentHashMap<>();
@@ -223,10 +221,12 @@ public class BasicBeanConverter implements BeanConverter {
stringifiers = new ArrayList<>(b.stringifiers);
listifiers = new ArrayList<>(b.listifiers);
swappers = new ArrayList<>(b.swappers);
+ propertyExtractors = new ArrayList<>(b.propertyExtractors);
settings = new HashMap<>(b.settings);
Collections.reverse(stringifiers);
Collections.reverse(listifiers);
Collections.reverse(swappers);
+ Collections.reverse(propertyExtractors);
}
/**
@@ -246,29 +246,23 @@ public class BasicBeanConverter implements BeanConverter {
o = swap(o);
if (o == null)
return getSetting(SETTING_nullValue, null);
- Class<?> c = o.getClass();
+ var c = o.getClass();
var stringifier = stringifierMap.computeIfAbsent(c,
this::findStringifier);
if (stringifier.isEmpty()) {
- if (c.isArray())
- return stringify(arrayToList(o));
- if (canListify(o)) {
- stringifier = of((o2, bs) ->
bs.stringify(bs.listify(o2)));
- } else {
- stringifier = of((o2, bs) -> o2.toString());
- }
+ stringifier = of(canListify(o) ? (bc, o2) ->
bc.stringify(bc.listify(o2)) : (bc, o2) -> o2.toString());
stringifierMap.putIfAbsent(c, stringifier);
}
var o2 = o;
- return stringifier.map(x -> (Stringifier)x).map(x ->
x.apply(o2, this)).map(Object::toString).orElse(null);
+ return stringifier.map(x -> (Stringifier)x).map(x ->
x.apply(this, o2)).map(Object::toString).orElse(null);
}
@Override
public Object swap(Object o) {
if (o == null) return null;
- Class<?> c = o.getClass();
+ var c = o.getClass();
var swapper = swapperMap.computeIfAbsent(c, this::findSwapper);
if (swapper.isPresent())
- return swap(swapper.map(x -> (Swapper)x).map(x ->
x.apply(o, this)).orElse(null));
+ return swap(swapper.map(x -> (Swapper)x).map(x ->
x.apply(this, o)).orElse(null));
return o;
}
@@ -277,21 +271,22 @@ public class BasicBeanConverter implements BeanConverter {
o = swap(o);
if (o == null)
return null;
- Class<?> c = o.getClass();
- if (c.isArray())
- return arrayToList(o);
if (o instanceof List)
return (List<Object>)o;
+ var c = o.getClass();
+ if (c.isArray())
+ return arrayToList(o);
var o2 = o;
- return listifierMap.computeIfAbsent(c,
this::findListifier).map(x -> (Listifier)x).map(x -> (List<Object>)x.apply(o2,
this)).orElse(null);
+ return listifierMap.computeIfAbsent(c,
this::findListifier).map(x -> (Listifier)x).map(x ->
(List<Object>)x.apply(this, o2)).orElse(null);
}
@Override
public boolean canListify(Object o) {
o = swap(o);
- if (o == null) return false;
- Class<?> c = o.getClass();
- return c.isArray() || listifierMap.computeIfAbsent(c,
this::findListifier).isPresent();
+ if (o == null)
+ return false;
+ var c = o.getClass();
+ return o instanceof List || c.isArray() ||
listifierMap.computeIfAbsent(c, this::findListifier).isPresent();
}
@Override
@@ -300,57 +295,14 @@ public class BasicBeanConverter implements BeanConverter {
}
@Override
- public Object getEntry(Object object, String name) {
- return safe(() -> {
- var o = swap(object);
- if (o instanceof Map o2) {
- if (o2.containsKey(name)) return o2.get(name);
- if ("size".equals(name)) return o2.size();
- } else if (canListify(o)) {
- var l = listify(o);
- if (name.matches("-?\\d+"))
- return l.get(parseInt(name));
- if (o.getClass().isArray()) {
- if ("length".equals(name)) return
l.size();
- if ("size".equals(name)) return
l.size();
- } else {
- if ("size".equals(name)) return
l.size();
- }
- }
- var f = (Field)null;
- var c = o.getClass();
- var n = Character.toUpperCase(name.charAt(0)) +
name.substring(1);
- var m = Arrays.stream(c.getMethods()).filter(x ->
x.getName().equals("is"+n) && x.getParameterCount() ==
0).findFirst().orElse(null);
- if (m != null) {
- m.setAccessible(true);
- return m.invoke(o);
- }
- m = Arrays.stream(c.getMethods()).filter(x ->
x.getName().equals("get"+n) && x.getParameterCount() ==
0).findFirst().orElse(null);
- if (m != null) {
- m.setAccessible(true);
- return m.invoke(o);
- }
- m = Arrays.stream(c.getMethods()).filter(x ->
x.getName().equals("get") && x.getParameterCount() == 1 &&
x.getParameterTypes()[0] == String.class).findFirst().orElse(null);
- if (m != null) {
- m.setAccessible(true);
- return m.invoke(o, name);
- }
- var c2 = c;
- while (f == null && c2 != null) {
- f =
Arrays.stream(c2.getDeclaredFields()).filter(x ->
x.getName().equals(name)).findFirst().orElse(null);
- c2 = c2.getSuperclass();
- }
- if (f != null) {
- f.setAccessible(true);
- return f.get(o);
- }
- m = Arrays.stream(c.getMethods()).filter(x ->
x.getName().equals(name) && x.getParameterCount() ==
0).findFirst().orElse(null);
- if (m != null) {
- m.setAccessible(true);
- return m.invoke(o);
- }
- throw new RuntimeException(f("Property {0} not found on
object of type {1}", name, o.getClass().getSimpleName()));
- });
+ public Object getProperty(Object object, String name) {
+ var o = swap(object);
+ return propertyExtractors
+ .stream()
+ .filter(x -> x.canExtract(this, o, name))
+ .findFirst()
+ .orElseThrow(()->new RuntimeException(f("Could not find
extractor for object of type {0}", o.getClass().getName())))
+ .extract(this, o, name);
}
private Optional<Stringifier<?>> findStringifier(Class<?> c) {
@@ -395,6 +347,7 @@ public class BasicBeanConverter implements BeanConverter {
return findSwapper(c.getSuperclass());
}
+ @Override
public <T> T getSetting(String key, T def) {
return (T)settings.getOrDefault(key, def);
}
@@ -452,6 +405,10 @@ public class BasicBeanConverter implements BeanConverter {
*/
public static class Builder {
private Map<String,Object> settings = new HashMap<>();
+ private List<StringifierEntry<?>> stringifiers = new
ArrayList<>();
+ private List<ListifierEntry<?>> listifiers = new ArrayList<>();
+ private List<SwapperEntry<?>> swappers = new ArrayList<>();
+ private List<PropertyExtractor> propertyExtractors = new
ArrayList<>();
/**
* Adds a configuration setting to the converter.
@@ -462,8 +419,6 @@ public class BasicBeanConverter implements BeanConverter {
*/
public Builder addSetting(String key, Object value) {
settings.put(key, value); return this; }
- private List<StringifierEntry<?>> stringifiers = new
ArrayList<>();
-
/**
* Registers a custom stringifier for a specific type.
*
@@ -477,8 +432,6 @@ public class BasicBeanConverter implements BeanConverter {
*/
public <T> Builder addStringifier(Class<T> c, Stringifier<T> s)
{ stringifiers.add(new StringifierEntry<>(c, s)); return this; }
- private List<ListifierEntry<?>> listifiers = new ArrayList<>();
-
/**
* Registers a custom listifier for a specific type.
*
@@ -492,8 +445,6 @@ public class BasicBeanConverter implements BeanConverter {
*/
public <T> Builder addListifier(Class<T> c, Listifier<T> l) {
listifiers.add(new ListifierEntry<>(c, l)); return this; }
- private List<SwapperEntry<?>> swappers = new ArrayList<>();
-
/**
* Registers a custom swapper for a specific type.
*
@@ -508,6 +459,8 @@ public class BasicBeanConverter implements BeanConverter {
*/
public <T> Builder addSwapper(Class<T> c, Swapper<T> s) {
swappers.add(new SwapperEntry<>(c, s)); return this; }
+ public <T> Builder addPropertyExtractor(PropertyExtractor e) {
propertyExtractors.add(e); return this; }
+
/**
* Adds default handlers and settings for common Java types.
*
@@ -541,30 +494,34 @@ public class BasicBeanConverter implements BeanConverter {
addSetting(SETTING_emptyValue, "<empty>");
addSetting(SETTING_classNameFormat, "simple");
- addStringifier(Map.Entry.class, (o, bs) ->
bs.stringify(o.getKey()) + bs.getSetting(SETTING_mapEntrySeparator, "=") +
bs.stringify(o.getValue()));
- addStringifier(GregorianCalendar.class, (o, bs) ->
o.toZonedDateTime().format(bs.getSetting(SETTING_calendarFormat, ISO_INSTANT)));
- addStringifier(Date.class, (o, bs) ->
o.toInstant().toString());
- addStringifier(InputStream.class, (o, bs) ->
stringify(o));
- addStringifier(byte[].class, (o, bs) -> stringify(o));
- addStringifier(Reader.class, (o, bs) -> stringify(o));
- addStringifier(File.class, (o, bs) -> stringify(o));
- addStringifier(Enum.class, (o, bs) -> o.name());
- addStringifier(Class.class, (o, bs) -> stringify(o,
bs));
- addStringifier(Constructor.class, (o, bs) ->
stringify(o, bs));
- addStringifier(Method.class, (o, bs) -> stringify(o,
bs));
- addStringifier(List.class, (o, bs) ->
((List<?>)o).stream().map(bs::stringify).collect(joining(bs.getSetting(SETTING_fieldSeparator,
","), bs.getSetting(SETTING_collectionPrefix, "["),
bs.getSetting(SETTING_collectionSuffix, "]"))));
- addStringifier(Map.class, (o, bs) ->
((Map<?,?>)o).entrySet().stream().map(bs::stringify).collect(joining(bs.getSetting(SETTING_fieldSeparator,
","), bs.getSetting(SETTING_mapPrefix, "{"), bs.getSetting(SETTING_mapSuffix,
"}"))));
-
- addListifier(Collection.class, (o, bs) -> new
ArrayList<>(o));
- addListifier(Iterable.class, (o, bs) ->
stream(((Iterable<Object>)o).spliterator(), false).toList());
- addListifier(Iterator.class, (o, bs) ->
stream(spliteratorUnknownSize(o, 0), false).toList());
- addListifier(Enumeration.class, (o, bs) -> list(o));
- addListifier(Stream.class, (o, bs) -> o.toList());
- addListifier(Optional.class, (o, bs) -> o.isEmpty() ?
emptyList() : singletonList(o.get()));
- addListifier(Map.class, (o, bs) -> new
ArrayList<>(((Map<?,?>)o).entrySet()));
-
- addSwapper(Optional.class, (o, bs) -> o.orElse(null));
- addSwapper(Supplier.class, (o, bs) -> o.get());
+ addStringifier(Map.Entry.class, (bc, o) ->
bc.stringify(o.getKey()) + bc.getSetting(SETTING_mapEntrySeparator, "=") +
bc.stringify(o.getValue()));
+ addStringifier(GregorianCalendar.class, (bc, o) ->
o.toZonedDateTime().format(bc.getSetting(SETTING_calendarFormat, ISO_INSTANT)));
+ addStringifier(Date.class, (bc, o) ->
o.toInstant().toString());
+ addStringifier(InputStream.class, (bc, o) ->
stringify(o));
+ addStringifier(byte[].class, (bc, o) -> stringify(o));
+ addStringifier(Reader.class, (bc, o) -> stringify(o));
+ addStringifier(File.class, (bc, o) -> stringify(o));
+ addStringifier(Enum.class, (bc, o) -> o.name());
+ addStringifier(Class.class, (bc, o) -> stringify(bc,
o));
+ addStringifier(Constructor.class, (bc, o) ->
stringify(bc, o));
+ addStringifier(Method.class, (bc, o) -> stringify(bc,
o));
+ addStringifier(List.class, (bc, o) ->
((List<?>)o).stream().map(bc::stringify).collect(joining(bc.getSetting(SETTING_fieldSeparator,
","), bc.getSetting(SETTING_collectionPrefix, "["),
bc.getSetting(SETTING_collectionSuffix, "]"))));
+ addStringifier(Map.class, (bc, o) ->
((Map<?,?>)o).entrySet().stream().map(bc::stringify).collect(joining(bc.getSetting(SETTING_fieldSeparator,
","), bc.getSetting(SETTING_mapPrefix, "{"), bc.getSetting(SETTING_mapSuffix,
"}"))));
+
+ addListifier(Collection.class, (bc, o) -> new
ArrayList<>(o));
+ addListifier(Iterable.class, (bc, o) ->
stream(((Iterable<Object>)o).spliterator(), false).toList());
+ addListifier(Iterator.class, (bc, o) ->
stream(spliteratorUnknownSize(o, 0), false).toList());
+ addListifier(Enumeration.class, (bc, o) -> list(o));
+ addListifier(Stream.class, (bc, o) -> o.toList());
+ addListifier(Optional.class, (bc, o) -> o.isEmpty() ?
emptyList() : singletonList(o.get()));
+ addListifier(Map.class, (bc, o) -> new
ArrayList<>(((Map<?,?>)o).entrySet()));
+
+ addSwapper(Optional.class, (bc, o) -> o.orElse(null));
+ addSwapper(Supplier.class, (bc, o) -> o.get());
+
+ addPropertyExtractor(new
PropertyExtractors.ObjectPropertyExtractor());
+ addPropertyExtractor(new
PropertyExtractors.ListPropertyExtractor());
+ addPropertyExtractor(new
PropertyExtractors.MapPropertyExtractor());
return this;
}
@@ -619,22 +576,22 @@ public class BasicBeanConverter implements BeanConverter {
private static final char[] HEX = "0123456789ABCDEF".toCharArray();
- private static String stringify(byte[] bytes) {
- var sb = new StringBuilder(bytes.length * 2);
- for (var element : bytes) {
+ private static String stringify(byte[] o) {
+ var sb = new StringBuilder(o.length * 2);
+ for (var element : o) {
var v = element & 0xFF;
sb.append(HEX[v >>> 4]).append(HEX[v & 0x0F]);
}
return sb.toString();
}
- private static String stringify(InputStream is) {
+ private static String stringify(InputStream o) {
return safe(() -> {
- try (var is2 = is) {
+ try (var o2 = o) {
var buff = new ByteArrayOutputStream(1024);
var nRead = 0;
var b = new byte[1024];
- while ((nRead = is2.read(b, 0, b.length)) != -1)
+ while ((nRead = o2.read(b, 0, b.length)) != -1)
buff.write(b, 0, nRead);
buff.flush();
return stringify(buff.toByteArray());
@@ -642,67 +599,40 @@ public class BasicBeanConverter implements BeanConverter {
});
}
- private static String stringify(Reader in) {
+ private static String stringify(Reader o) {
return safe(() -> {
- try (var in2 = in) {
+ try (var o2 = o) {
var sb = new StringBuilder();
var buf = new char[1024];
var i = 0;
- while ((i = in2.read(buf)) != -1)
+ while ((i = o2.read(buf)) != -1)
sb.append(buf, 0, i);
return sb.toString();
}
});
}
- private static String stringify(File in) {
- return
safe(()->stringify(Files.newBufferedReader(in.toPath())));
+ private static String stringify(File o) {
+ return safe(()->stringify(Files.newBufferedReader(o.toPath())));
}
- private static String stringify(Class<?> in, BasicBeanConverter bs) {
- return switch(bs.getSetting(SETTING_classNameFormat, "")) {
- case "simple" -> in.getSimpleName();
- case "canonical" -> in.getCanonicalName();
- default -> in.getName();
+ private static String stringify(BeanConverter bc, Class<?> o) {
+ return switch(bc.getSetting(SETTING_classNameFormat,
"default")) {
+ case "simple" -> o.getSimpleName();
+ case "canonical" -> o.getCanonicalName();
+ default -> o.getName();
};
}
- private static String stringify(Constructor o, BasicBeanConverter bs) {
- return new
StringBuilder().append(stringify(o.getDeclaringClass(),
bs)).append('(').append(stringify(o.getParameterTypes(),
bs)).append(')').toString();
+ private static String stringify(BeanConverter bc, Constructor o) {
+ return new StringBuilder().append(stringify(bc,
o.getDeclaringClass())).append('(').append(stringify(bc,
o.getParameterTypes())).append(')').toString();
}
- private static String stringify(Method o, BasicBeanConverter bs) {
- return new
StringBuilder().append(o.getName()).append('(').append(stringify(o.getParameterTypes(),
bs)).append(')').toString();
- }
-
- private static String stringify(Class[] o, BasicBeanConverter bs) {
- var sb = new StringBuilder();
- for (int i = 0; i < o.length; i++) {
- if (i > 0)
- sb.append(',');
- sb.append(stringify(o[i], bs));
- }
- return sb.toString();
- }
-
- private static String f(String msg, Object...args) {
- return args.length == 0 ? msg : MessageFormat.format(msg, args);
- }
-
- private static <T> T safe(ThrowingSupplier<T> s) {
- try {
- return s.get();
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ private static String stringify(BeanConverter bc, Method o) {
+ return new
StringBuilder().append(o.getName()).append('(').append(stringify(bc,
o.getParameterTypes())).append(')').toString();
}
- private static List<Object> arrayToList(Object o) {
- var l = new ArrayList<>();
- for (var i = 0; i < Array.getLength(o); i++)
- l.add(Array.get(o, i));
- return l;
+ private static String stringify(BeanConverter bc, Class[] o) {
+ return Arrays.stream(o).map(x -> stringify(bc,
x)).collect(joining(","));
}
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/junit/BeanConverter.java
b/juneau-utest/src/test/java/org/apache/juneau/junit/BeanConverter.java
index 1a78bc72e..11845c097 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/BeanConverter.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/BeanConverter.java
@@ -18,12 +18,12 @@ import org.apache.juneau.*;
/**
* Abstract interface for Bean-Centric Test (BCT) object conversion and
property access.
- *
- * <p>This interface defines the core contract for converting objects to
strings and lists,
- * and for accessing object properties in a uniform way. It forms the
foundation of the BCT
- * testing framework, enabling consistent object introspection and value
extraction across
+ *
+ * <p>This interface defines the core contract for converting objects to
strings and lists,
+ * and for accessing object properties in a uniform way. It forms the
foundation of the BCT
+ * testing framework, enabling consistent object introspection and value
extraction across
* different object types and structures.</p>
- *
+ *
* <p>The BeanConverter is designed to handle a wide variety of Java objects
including:</p>
* <ul>
* <li><b>Primitives and wrappers</b> - Numbers, booleans, characters</li>
@@ -32,24 +32,24 @@ import org.apache.juneau.*;
* <li><b>Custom beans and POJOs</b> - JavaBean-style objects with
getters/setters</li>
* <li><b>Special objects</b> - Optional, Supplier, Date/Calendar, File,
streams</li>
* </ul>
- *
+ *
* <h5 class='section'>Core Conversion Operations:</h5>
* <dl>
* <dt><b>{@link #stringify(Object)}</b></dt>
* <dd>Converts any object to its string representation, handling nested
structures</dd>
- *
+ *
* <dt><b>{@link #listify(Object)}</b></dt>
* <dd>Converts collection-like objects (arrays, Collections, Iterables,
etc.) to List<Object></dd>
- *
+ *
* <dt><b>{@link #swap(Object)}</b></dt>
* <dd>Pre-processes objects before conversion (e.g., unwrapping Optional,
calling Supplier)</dd>
- *
- * <dt><b>{@link #getEntry(Object, String)}</b></dt>
+ *
+ * <dt><b>{@link #getProperty(Object, String)}</b></dt>
* <dd>Accesses object properties using multiple fallback mechanisms</dd>
* </dl>
- *
+ *
* <h5 class='section'>Property Access Strategy:</h5>
- * <p>The {@link #getEntry(Object, String)} method uses a comprehensive
fallback approach:</p>
+ * <p>The {@link #getProperty(Object, String)} method uses a comprehensive
fallback approach:</p>
* <ol>
* <li><b>Collection/Array access:</b> Numeric indices for
arrays/lists</li>
* <li><b>Universal size properties:</b> "length" and "size" for arrays,
collections, and maps</li>
@@ -60,7 +60,7 @@ import org.apache.juneau.*;
* <li><b>Public fields:</b> Direct field access via reflection</li>
* <li><b>No-arg methods:</b> Method calls with property names</li>
* </ol>
- *
+ *
* <h5 class='section'>Extensibility:</h5>
* <p>The interface is designed to be implemented by custom converters that
can:</p>
* <ul>
@@ -70,7 +70,7 @@ import org.apache.juneau.*;
* <li>Override property access mechanisms</li>
* <li>Configure formatting and display options</li>
* </ul>
- *
+ *
* <h5 class='section'>Primary Implementation:</h5>
* <p>The main implementation is {@link BasicBeanConverter}, which
provides:</p>
* <ul>
@@ -79,7 +79,7 @@ import org.apache.juneau.*;
* <li>Performance optimization through caching</li>
* <li>Comprehensive default handling for common Java types</li>
* </ul>
- *
+ *
* <h5 class='section'>Usage in BCT Framework:</h5>
* <p>This interface is used internally by BCT assertion methods like:</p>
* <ul>
@@ -89,7 +89,7 @@ import org.apache.juneau.*;
* <li>{@link TestUtils#assertList(List, Object...)}</li>
* <li>{@link TestUtils#assertBeans(Collection, String, String...)}</li>
* </ul>
- *
+ *
* @see BasicBeanConverter
* @see TestUtils
*/
@@ -97,7 +97,7 @@ public interface BeanConverter {
/**
* Converts an object to its string representation.
- *
+ *
* <p>This method applies swapping logic first via {@link
#swap(Object)}, then determines
* the appropriate string conversion based on the object's type. The
conversion handles:</p>
* <ul>
@@ -108,7 +108,7 @@ public interface BeanConverter {
* <li><b>Maps:</b> Converts to brace-delimited key-value
format</li>
* <li><b>Custom objects:</b> Uses registered stringifiers or
fallback toString()</li>
* </ul>
- *
+ *
* @param o The object to stringify
* @return The string representation of the object
*/
@@ -116,7 +116,7 @@ public interface BeanConverter {
/**
* Converts a collection-like object to a List<Object>.
- *
+ *
* <p>This method applies swapping logic first via {@link
#swap(Object)}, then converts
* collection-like objects to lists. Supported types include:</p>
* <ul>
@@ -129,7 +129,7 @@ public interface BeanConverter {
* <li><b>Optional:</b> Returns empty list or single-element
list</li>
* <li><b>Map:</b> Returns list of Map.Entry objects</li>
* </ul>
- *
+ *
* @param o The object to convert to a list
* @return A List containing the elements, or null if the object is null
*/
@@ -137,10 +137,10 @@ public interface BeanConverter {
/**
* Determines if an object can be converted to a list.
- *
+ *
* <p>This method checks if the object (after swapping) is a type that
can be
* meaningfully converted to a List<Object> via {@link
#listify(Object)}.</p>
- *
+ *
* @param o The object to test
* @return True if the object can be listified, false otherwise
*/
@@ -148,17 +148,17 @@ public interface BeanConverter {
/**
* Returns the string representation used for null values.
- *
+ *
* <p>This is used by {@link #stringify(Object)} when the object (after
swapping) is null.
* The default implementation typically returns "<null>" or
similar.</p>
- *
+ *
* @return The null value representation
*/
String nullValue();
/**
* Pre-processes objects before conversion operations.
- *
+ *
* <p>This method applies object transformations before stringification
or listification.
* Common swapping operations include:</p>
* <ul>
@@ -167,9 +167,9 @@ public interface BeanConverter {
* <li><b>Lazy objects:</b> Forces evaluation of deferred
computations</li>
* <li><b>Wrapper types:</b> Extracts wrapped values</li>
* </ul>
- *
+ *
* <p>The method may be called recursively if swapping produces another
swappable type.</p>
- *
+ *
* @param o The object to swap
* @return The swapped object, or the original object if no swapping is
needed
*/
@@ -177,10 +177,10 @@ public interface BeanConverter {
/**
* Accesses a named property or field from an object.
- *
+ *
* <p>This is the core property access method used by BCT assertions.
It employs
* multiple fallback strategies to extract values from objects:</p>
- *
+ *
* <h5 class='section'>Access Priority Order:</h5>
* <ol>
* <li><b>Map key access:</b> If object is a Map, looks up the
name as a key</li>
@@ -192,7 +192,7 @@ public interface BeanConverter {
* <li><b>Public fields:</b> Direct field access via
reflection</li>
* <li><b>No-arg methods:</b> Methods with the exact property
name</li>
* </ol>
- *
+ *
* <h5 class='section'>Special Property Names:</h5>
* <ul>
* <li><b>"length"/"size":</b> Returns size for arrays,
collections, maps</li>
@@ -200,11 +200,13 @@ public interface BeanConverter {
* <li><b>"<NULL>":</b> Accesses null key in maps</li>
* <li><b>"-1", "-2", etc.:</b> Negative indexing from end of
array/list</li>
* </ul>
- *
+ *
* @param object The object to access properties from
* @param name The property/field name to access
* @return The property value
* @throws RuntimeException if the property cannot be found or accessed
*/
- Object getEntry(Object object, String name);
+ Object getProperty(Object object, String name);
+
+ <T> T getSetting(String key, T defaultValue);
}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/junit/Listifier.java
b/juneau-utest/src/test/java/org/apache/juneau/junit/Listifier.java
index 92db0fdec..0d96801f1 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/Listifier.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Listifier.java
@@ -16,4 +16,4 @@ import java.util.*;
import java.util.function.*;
@FunctionalInterface
-public interface Listifier<T> extends
BiFunction<T,BasicBeanConverter,List<Object>> {}
+public interface Listifier<T> extends BiFunction<BeanConverter,T,List<Object>>
{}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/junit/NestedTokenizer.java
b/juneau-utest/src/test/java/org/apache/juneau/junit/NestedTokenizer.java
index c2d5f1371..62242db6b 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/NestedTokenizer.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/NestedTokenizer.java
@@ -14,10 +14,10 @@ package org.apache.juneau.junit;
import static java.util.Collections.*;
import static java.util.stream.Collectors.*;
+import static org.apache.juneau.junit.Utils.*;
import static org.apache.juneau.junit.NestedTokenizer.ParseState.*;
import java.util.*;
-import java.util.function.*;
/**
* Splits a nested comma-delimited string into a list of Token objects using a
state machine parser.
@@ -246,20 +246,4 @@ public class NestedTokenizer {
return Objects.hash(value, nested);
}
}
-
-
//---------------------------------------------------------------------------------------------
- // Helper methods.
-
//---------------------------------------------------------------------------------------------
-
- private static <T,U> boolean eq(T o1, U o2, BiPredicate<T,U> test) {
- if (o1 == null) { return o2 == null; }
- if (o2 == null) { return false; }
- if (o1 == o2) { return true; }
- return test.test(o1, o2);
- }
-
- @SuppressWarnings("unlikely-arg-type")
- private static <T,U> boolean eq(T o1, U o2) {
- return Objects.equals(o1, o2);
- }
}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/junit/Listifier.java
b/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractor.java
similarity index 90%
copy from juneau-utest/src/test/java/org/apache/juneau/junit/Listifier.java
copy to
juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractor.java
index 92db0fdec..95df43bda 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/Listifier.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractor.java
@@ -12,8 +12,9 @@
//
***************************************************************************************************************************
package org.apache.juneau.junit;
-import java.util.*;
-import java.util.function.*;
+public interface PropertyExtractor {
-@FunctionalInterface
-public interface Listifier<T> extends
BiFunction<T,BasicBeanConverter,List<Object>> {}
+ boolean canExtract(BeanConverter converter, Object o, String key);
+
+ Object extract(BeanConverter converter, Object o, String key);
+}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors.java
b/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors.java
new file mode 100644
index 000000000..2b56e58af
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors.java
@@ -0,0 +1,94 @@
+package org.apache.juneau.junit;
+
+import static java.lang.Integer.*;
+import static org.apache.juneau.junit.Utils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+public class PropertyExtractors {
+
+ public static class ObjectPropertyExtractor implements
PropertyExtractor {
+
+ @Override
+ public boolean canExtract(BeanConverter converter, Object o,
String name) {
+ return true;
+ }
+
+ @Override
+ public Object extract(BeanConverter converter, Object o, String
name) {
+ return
+ safe(() -> {
+ if (o == null)
+ return null;
+ var f = (Field)null;
+ var c = o.getClass();
+ var n =
Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ var m =
Arrays.stream(c.getMethods()).filter(x -> x.getName().equals("is"+n) &&
x.getParameterCount() == 0).findFirst().orElse(null);
+ if (m != null) {
+ m.setAccessible(true);
+ return m.invoke(o);
+ }
+ m =
Arrays.stream(c.getMethods()).filter(x -> x.getName().equals("get"+n) &&
x.getParameterCount() == 0).findFirst().orElse(null);
+ if (m != null) {
+ m.setAccessible(true);
+ return m.invoke(o);
+ }
+ m =
Arrays.stream(c.getMethods()).filter(x -> x.getName().equals("get") &&
x.getParameterCount() == 1 && x.getParameterTypes()[0] ==
String.class).findFirst().orElse(null);
+ if (m != null) {
+ m.setAccessible(true);
+ return m.invoke(o, name);
+ }
+ var c2 = c;
+ while (f == null && c2 != null) {
+ f =
Arrays.stream(c2.getDeclaredFields()).filter(x ->
x.getName().equals(name)).findFirst().orElse(null);
+ c2 = c2.getSuperclass();
+ }
+ if (f != null) {
+ f.setAccessible(true);
+ return f.get(o);
+ }
+ m =
Arrays.stream(c.getMethods()).filter(x -> x.getName().equals(name) &&
x.getParameterCount() == 0).findFirst().orElse(null);
+ if (m != null) {
+ m.setAccessible(true);
+ return m.invoke(o);
+ }
+ throw new RuntimeException(f("Property
{0} not found on object of type {1}", name, o.getClass().getSimpleName()));
+ });
+ }
+ }
+
+ public static class ListPropertyExtractor extends
ObjectPropertyExtractor {
+
+ @Override
+ public boolean canExtract(BeanConverter converter, Object o,
String name) {
+ return converter.canListify(o);
+ }
+
+ @Override
+ public Object extract(BeanConverter converter, Object o, String
name) {
+ var l = converter.listify(o);
+ if (name.matches("-?\\d+"))
+ return l.get(parseInt(name));
+ if ("length".equals(name)) return l.size();
+ if ("size".equals(name)) return l.size();
+ return super.extract(converter, o, name);
+ }
+ }
+
+ public static class MapPropertyExtractor extends
ObjectPropertyExtractor {
+
+ @Override
+ public boolean canExtract(BeanConverter converter, Object o,
String name) {
+ return o instanceof Map;
+ }
+
+ @Override
+ public Object extract(BeanConverter converter, Object o, String
name) {
+ var m = (Map<?,?>)o;
+ if (m.containsKey(name)) return m.get(name);
+ if ("size".equals(name)) return m.size();
+ return super.extract(converter, o, name);
+ }
+ }
+}
\ No newline at end of file
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifier.java
b/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifier.java
index ec7f8fee9..e86ee4b1d 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifier.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifier.java
@@ -15,4 +15,4 @@ package org.apache.juneau.junit;
import java.util.function.*;
@FunctionalInterface
-public interface Stringifier<T> extends
BiFunction<T,BasicBeanConverter,String> {}
+public interface Stringifier<T> extends BiFunction<BeanConverter,T,String> {}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/junit/Swapper.java
b/juneau-utest/src/test/java/org/apache/juneau/junit/Swapper.java
index c09a5b880..7d6227b4b 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/Swapper.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Swapper.java
@@ -15,4 +15,4 @@ package org.apache.juneau.junit;
import java.util.function.*;
@FunctionalInterface
-public interface Swapper<T> extends BiFunction<T,BasicBeanConverter,Object> {}
+public interface Swapper<T> extends BiFunction<BeanConverter,T,Object> {}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifier.java
b/juneau-utest/src/test/java/org/apache/juneau/junit/ThrowingSupplier.java
similarity index 93%
copy from juneau-utest/src/test/java/org/apache/juneau/junit/Stringifier.java
copy to juneau-utest/src/test/java/org/apache/juneau/junit/ThrowingSupplier.java
index ec7f8fee9..a050c8fbc 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifier.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/ThrowingSupplier.java
@@ -12,7 +12,10 @@
//
***************************************************************************************************************************
package org.apache.juneau.junit;
-import java.util.function.*;
-
+/**
+ * A supplier that throws an exception.
+ */
@FunctionalInterface
-public interface Stringifier<T> extends
BiFunction<T,BasicBeanConverter,String> {}
+public interface ThrowingSupplier<T> {
+ T get() throws Exception;
+}
\ No newline at end of file
diff --git a/juneau-utest/src/test/java/org/apache/juneau/junit/Listifier.java
b/juneau-utest/src/test/java/org/apache/juneau/junit/Utils.java
similarity index 64%
copy from juneau-utest/src/test/java/org/apache/juneau/junit/Listifier.java
copy to juneau-utest/src/test/java/org/apache/juneau/junit/Utils.java
index 92db0fdec..9b29b735c 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/Listifier.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Utils.java
@@ -12,8 +12,42 @@
//
***************************************************************************************************************************
package org.apache.juneau.junit;
+import java.lang.reflect.*;
+import java.text.*;
import java.util.*;
import java.util.function.*;
-@FunctionalInterface
-public interface Listifier<T> extends
BiFunction<T,BasicBeanConverter,List<Object>> {}
+public class Utils {
+ public static <T> T safe(ThrowingSupplier<T> s) {
+ try {
+ return s.get();
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String f(String msg, Object...args) {
+ return args.length == 0 ? msg : MessageFormat.format(msg, args);
+ }
+
+ public static List<Object> arrayToList(Object o) {
+ var l = new ArrayList<>();
+ for (var i = 0; i < Array.getLength(o); i++)
+ l.add(Array.get(o, i));
+ return l;
+ }
+
+ public static <T,U> boolean eq(T o1, U o2, BiPredicate<T,U> test) {
+ if (o1 == null) { return o2 == null; }
+ if (o2 == null) { return false; }
+ if (o1 == o2) { return true; }
+ return test.test(o1, o2);
+ }
+
+ @SuppressWarnings("unlikely-arg-type")
+ public static <T,U> boolean eq(T o1, U o2) {
+ return Objects.equals(o1, o2);
+ }
+}