This is an automated email from the ASF dual-hosted git repository.
rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git
The following commit(s) were added to refs/heads/master by this push:
new 0b8c0d1 [JOHNZON-317] lazy lookup of implicit converters
0b8c0d1 is described below
commit 0b8c0d13efad98654d258bee1a1ef623ac091ebb
Author: Romain Manni-Bucau <[email protected]>
AuthorDate: Mon Jul 13 13:04:52 2020 +0200
[JOHNZON-317] lazy lookup of implicit converters
---
.../org/apache/johnzon/jsonb/JohnzonBuilder.java | 515 ++---------------
.../org/apache/johnzon/jsonb/JsonbAccessMode.java | 16 +-
.../jsonb/converter/JohnzonJsonbAdapter.java | 8 +
.../org/apache/johnzon/jsonb/JohnzonJsonbTest.java | 28 +
.../org/apache/johnzon/mapper/MapperBuilder.java | 90 ++-
.../org/apache/johnzon/mapper/MapperConfig.java | 11 +-
.../apache/johnzon/mapper/MappingParserImpl.java | 18 +-
.../java/org/apache/johnzon/mapper/Mappings.java | 42 +-
.../apache/johnzon/mapper/TypeAwareAdapter.java | 6 +
.../johnzon/mapper/access/BaseAccessMode.java | 2 +-
.../mapper/converter/DateWithCopyConverter.java | 2 +-
.../johnzon/mapper/converter/LocaleConverter.java | 23 +-
.../johnzon/mapper/internal/ConverterAdapter.java | 25 +-
.../johnzon/mapper/map/LazyConverterMap.java | 637 +++++++++++++++++++++
.../apache/johnzon/mapper/MapperConfigTest.java | 4 +-
.../apache/johnzon/mapper/ObjectConverterTest.java | 17 +
.../test/java/org/superbiz/ExtendMappingTest.java | 4 +-
17 files changed, 872 insertions(+), 576 deletions(-)
diff --git
a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
index 26f380b..5bbfa2c 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
@@ -18,72 +18,6 @@
*/
package org.apache.johnzon.jsonb;
-import static java.time.format.DateTimeFormatter.ofPattern;
-import static java.time.temporal.ChronoField.DAY_OF_MONTH;
-import static java.time.temporal.ChronoField.HOUR_OF_DAY;
-import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
-import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
-import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
-import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
-import static java.time.temporal.ChronoField.YEAR;
-import static java.util.Collections.emptyMap;
-import static java.util.Optional.ofNullable;
-import static javax.json.bind.config.PropertyNamingStrategy.IDENTITY;
-import static javax.json.bind.config.PropertyOrderStrategy.ANY;
-
-import java.io.Closeable;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.nio.charset.StandardCharsets;
-import java.time.Duration;
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.OffsetDateTime;
-import java.time.OffsetTime;
-import java.time.Period;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
-import java.time.temporal.TemporalAccessor;
-import java.time.temporal.TemporalQueries;
-import java.util.Base64;
-import java.util.Calendar;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.SimpleTimeZone;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Stream;
-
-import javax.json.JsonBuilderFactory;
-import javax.json.bind.Jsonb;
-import javax.json.bind.JsonbBuilder;
-import javax.json.bind.JsonbConfig;
-import javax.json.bind.JsonbException;
-import javax.json.bind.adapter.JsonbAdapter;
-import javax.json.bind.config.BinaryDataStrategy;
-import javax.json.bind.config.PropertyNamingStrategy;
-import javax.json.bind.config.PropertyVisibilityStrategy;
-import javax.json.bind.serializer.JsonbDeserializer;
-import javax.json.bind.serializer.JsonbSerializer;
-import javax.json.spi.JsonProvider;
-import javax.json.stream.JsonGenerator;
-import javax.json.stream.JsonParserFactory;
-
import org.apache.johnzon.core.AbstractJsonFactory;
import org.apache.johnzon.core.JsonGeneratorFactoryImpl;
import org.apache.johnzon.core.JsonParserFactoryImpl;
@@ -95,7 +29,6 @@ import
org.apache.johnzon.jsonb.factory.SimpleJohnzonAdapterFactory;
import org.apache.johnzon.jsonb.serializer.JohnzonDeserializationContext;
import org.apache.johnzon.jsonb.serializer.JohnzonSerializationContext;
import org.apache.johnzon.jsonb.spi.JohnzonAdapterFactory;
-import org.apache.johnzon.mapper.Adapter;
import org.apache.johnzon.mapper.Converter;
import org.apache.johnzon.mapper.Mapper;
import org.apache.johnzon.mapper.MapperBuilder;
@@ -103,8 +36,42 @@ import org.apache.johnzon.mapper.ObjectConverter;
import org.apache.johnzon.mapper.SerializeValueFilter;
import org.apache.johnzon.mapper.access.AccessMode;
import org.apache.johnzon.mapper.access.FieldAndMethodAccessMode;
+import org.apache.johnzon.mapper.converter.LocaleConverter;
import org.apache.johnzon.mapper.internal.AdapterKey;
-import org.apache.johnzon.mapper.internal.ConverterAdapter;
+
+import javax.json.JsonBuilderFactory;
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import javax.json.bind.adapter.JsonbAdapter;
+import javax.json.bind.config.BinaryDataStrategy;
+import javax.json.bind.config.PropertyNamingStrategy;
+import javax.json.bind.config.PropertyVisibilityStrategy;
+import javax.json.bind.serializer.JsonbDeserializer;
+import javax.json.bind.serializer.JsonbSerializer;
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonParserFactory;
+import java.io.Closeable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import static java.time.format.DateTimeFormatter.ofPattern;
+import static java.util.Collections.emptyMap;
+import static java.util.Optional.ofNullable;
+import static javax.json.bind.config.PropertyNamingStrategy.IDENTITY;
+import static javax.json.bind.config.PropertyOrderStrategy.ANY;
public class JohnzonBuilder implements JsonbBuilder {
private static final Object NO_BM = new Object();
@@ -212,8 +179,13 @@ public class JohnzonBuilder implements JsonbBuilder {
builder.setUseJsRange(toBool( //
https://github.com/eclipse-ee4j/jsonb-api/issues/180
System.getProperty("johnzon.use-js-range",
config.getProperty("johnzon.use-js-range")
.map(String::valueOf).orElse("false"))));
-
- final Map<AdapterKey, Adapter<?, ?>> defaultConverters =
createJava8Converters(builder);
+ builder.setUseShortISO8601Format(false);
+ config.getProperty(JsonbConfig.DATE_FORMAT)
+ .map(String.class::cast)
+ .ifPresent(dateFormat ->
builder.setAdaptersDateTimeFormatter(config.getProperty(JsonbConfig.LOCALE)
+ .map(it -> String.class.isInstance(it) ? new
LocaleConverter().to(it.toString()) : Locale.class.cast(it))
+ .map(value -> ofPattern(dateFormat, value))
+ .orElseGet(() -> ofPattern(dateFormat))));
final JohnzonAdapterFactory factory =
config.getProperty("johnzon.factory").map(val -> {
if (JohnzonAdapterFactory.class.isInstance(val)) {
@@ -241,7 +213,7 @@ public class JohnzonBuilder implements JsonbBuilder {
.orElseGet(() -> new JsonbAccessMode(
propertyNamingStrategy, orderValue, visibilityStrategy,
!namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE),
- defaultConverters,
+ builder.getAdapters(),
factory, jsonp, builderFactorySupplier,
parserFactoryProvider,
config.getProperty("johnzon.accessModeDelegate")
.map(this::toAccessMode)
@@ -266,7 +238,7 @@ public class JohnzonBuilder implements JsonbBuilder {
final Type[] args = pt.getActualTypeArguments();
final JohnzonJsonbAdapter johnzonJsonbAdapter = new
JohnzonJsonbAdapter(adapter, args[0], args[1]);
builder.addAdapter(args[0], args[1], johnzonJsonbAdapter);
- defaultConverters.put(new AdapterKey(args[0], args[1]),
johnzonJsonbAdapter);
+ builder.getAdapters().put(new AdapterKey(args[0], args[1]),
johnzonJsonbAdapter);
}));
ofNullable(config.getProperty("johnzon.fail-on-unknown-properties")
@@ -448,405 +420,6 @@ public class JohnzonBuilder implements JsonbBuilder {
return
ofNullable(Thread.currentThread().getContextClassLoader()).orElseGet(ClassLoader::getSystemClassLoader);
}
- // TODO: move these converters in converter package
- private Map<AdapterKey, Adapter<?, ?>> createJava8Converters(final
MapperBuilder builder) {
- final Map<AdapterKey, Adapter<?, ?>> converters = new HashMap<>();
-
- final ZoneId zoneIDUTC = ZoneId.of("UTC");
-
- // built-in converters not in mapper
- converters.put(new AdapterKey(Period.class, String.class), new
ConverterAdapter<>(new Converter<Period>() {
- @Override
- public String toString(final Period instance) {
- return instance.toString();
- }
-
- @Override
- public Period fromString(final String text) {
- return Period.parse(text);
- }
- }));
- converters.put(new AdapterKey(Duration.class, String.class), new
ConverterAdapter<>(new Converter<Duration>() {
- @Override
- public String toString(final Duration instance) {
- return instance.toString();
- }
-
- @Override
- public Duration fromString(final String text) {
- return Duration.parse(text);
- }
- }));
- converters.put(new AdapterKey(Date.class, String.class), new
ConverterAdapter<>(new Converter<Date>() {
- @Override
- public String toString(final Date instance) {
- return ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC)
- .format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
- }
-
- @Override
- public Date fromString(final String text) {
- try {
- return Date.from(ZonedDateTime.parse(text).toInstant());
- } catch (final DateTimeParseException dte) {
- return
Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC));
- }
- }
- }));
- converters.put(new AdapterKey(Calendar.class, String.class), new
ConverterAdapter<>(new Converter<Calendar>() {
- @Override
- public String toString(final Calendar instance) {
- return toStringCalendar(instance);
- }
-
- @Override
- public Calendar fromString(final String text) {
- return fromCalendar(text, zdt -> {
- final Calendar instance = Calendar.getInstance();
- instance.clear();
- instance.setTimeZone(TimeZone.getTimeZone(zdt.getZone()));
- instance.setTimeInMillis(zdt.toInstant().toEpochMilli());
- return instance;
- });
- }
- }));
- converters.put(new AdapterKey(GregorianCalendar.class, String.class),
new ConverterAdapter<>(new Converter<GregorianCalendar>() {
- @Override
- public String toString(final GregorianCalendar instance) {
- return toStringCalendar(instance);
- }
-
- @Override
- public GregorianCalendar fromString(final String text) {
- return fromCalendar(text, GregorianCalendar::from);
- }
- }));
- converters.put(new AdapterKey(TimeZone.class, String.class), new
ConverterAdapter<>(new Converter<TimeZone>() {
- @Override
- public String toString(final TimeZone instance) {
- return instance.getID();
- }
-
- @Override
- public TimeZone fromString(final String text) {
- checkForDeprecatedTimeZone(text);
- return TimeZone.getTimeZone(text);
- }
- }));
- converters.put(new AdapterKey(ZoneId.class, String.class), new
ConverterAdapter<>(new Converter<ZoneId>() {
- @Override
- public String toString(final ZoneId instance) {
- return instance.getId();
- }
-
- @Override
- public ZoneId fromString(final String text) {
- return ZoneId.of(text);
- }
- }));
- converters.put(new AdapterKey(ZoneOffset.class, String.class), new
ConverterAdapter<>(new Converter<ZoneOffset>() {
- @Override
- public String toString(final ZoneOffset instance) {
- return instance.getId();
- }
-
- @Override
- public ZoneOffset fromString(final String text) {
- return ZoneOffset.of(text);
- }
- }));
- converters.put(new AdapterKey(SimpleTimeZone.class, String.class), new
ConverterAdapter<>(new Converter<SimpleTimeZone>() {
- @Override
- public String toString(final SimpleTimeZone instance) {
- return instance.getID();
- }
-
- @Override
- public SimpleTimeZone fromString(final String text) {
- checkForDeprecatedTimeZone(text);
- final TimeZone timeZone = TimeZone.getTimeZone(text);
- return new SimpleTimeZone(timeZone.getRawOffset(),
timeZone.getID());
- }
- }));
- converters.put(new AdapterKey(Instant.class, String.class), new
ConverterAdapter<>(new Converter<Instant>() {
- @Override
- public String toString(final Instant instance) {
- return instance.toString();
- }
-
- @Override
- public Instant fromString(final String text) {
- return Instant.parse(text);
- }
- }));
- converters.put(new AdapterKey(LocalDate.class, String.class), new
ConverterAdapter<>(new Converter<LocalDate>() {
- @Override
- public String toString(final LocalDate instance) {
- return instance.toString();
- }
-
- @Override
- public LocalDate fromString(final String text) {
- return LocalDate.parse(text);
- }
- }));
- converters.put(new AdapterKey(LocalTime.class, String.class), new
ConverterAdapter<>(new Converter<LocalTime>() {
- @Override
- public String toString(final LocalTime instance) {
- return instance.toString();
- }
-
- @Override
- public LocalTime fromString(final String text) {
- return LocalTime.parse(text);
- }
- }));
- converters.put(new AdapterKey(LocalDateTime.class, String.class), new
ConverterAdapter<>(new Converter<LocalDateTime>() {
- @Override
- public String toString(final LocalDateTime instance) {
- return instance.toString();
- }
-
- @Override
- public LocalDateTime fromString(final String text) {
- return LocalDateTime.parse(text);
- }
- }));
- converters.put(new AdapterKey(ZonedDateTime.class, String.class), new
ConverterAdapter<>(new Converter<ZonedDateTime>() {
- @Override
- public String toString(final ZonedDateTime instance) {
- return instance.toString();
- }
-
- @Override
- public ZonedDateTime fromString(final String text) {
- return ZonedDateTime.parse(text);
- }
- }));
- converters.put(new AdapterKey(OffsetDateTime.class, String.class), new
ConverterAdapter<>(new Converter<OffsetDateTime>() {
- @Override
- public String toString(final OffsetDateTime instance) {
- return instance.toString();
- }
-
- @Override
- public OffsetDateTime fromString(final String text) {
- return OffsetDateTime.parse(text);
- }
- }));
- converters.put(new AdapterKey(OffsetTime.class, String.class), new
ConverterAdapter<>(new Converter<OffsetTime>() {
- @Override
- public String toString(final OffsetTime instance) {
- return instance.toString();
- }
-
- @Override
- public OffsetTime fromString(final String text) {
- return OffsetTime.parse(text);
- }
- }));
- addDateFormatConfigConverters(converters, zoneIDUTC);
-
- converters.forEach((k, v) -> builder.addAdapter(k.getFrom(),
k.getTo(), v));
- return converters;
- }
-
- private String toStringCalendar(final Calendar instance) {
- if (!hasTime(instance)) { // spec
- final LocalDate localDate = LocalDate.of(
- instance.get(Calendar.YEAR),
- instance.get(Calendar.MONTH) + 1,
- instance.get(Calendar.DAY_OF_MONTH));
- return localDate.toString() +
- (instance.getTimeZone() != null ?
- instance.getTimeZone().toZoneId().getRules()
-
.getOffset(Instant.ofEpochMilli(TimeUnit.DAYS.toMillis(localDate.toEpochDay())))
: "");
- }
- return ZonedDateTime.ofInstant(instance.toInstant(),
instance.getTimeZone().toZoneId())
- .format(DateTimeFormatter.ISO_DATE_TIME);
- }
-
- private boolean hasTime(final Calendar instance) {
- if (!instance.isSet(Calendar.HOUR_OF_DAY)) {
- return false;
- }
- return instance.get(Calendar.HOUR_OF_DAY) != 0 ||
- (instance.isSet(Calendar.MINUTE)&& instance.get(Calendar.MINUTE)
!= 0) ||
- (instance.isSet(Calendar.SECOND) && instance.get(Calendar.SECOND)
!= 0);
- }
-
- private <T extends Calendar> T fromCalendar(final String text, final
Function<ZonedDateTime, T> calendarSupplier) {
- switch (text.length()) {
- case 10: {
- final ZonedDateTime date = LocalDate.parse(text)
- .atTime(0, 0, 0)
- .atZone(ZoneId.of("UTC"));
- return calendarSupplier.apply(date);
- }
- default:
- final ZonedDateTime zonedDateTime = ZonedDateTime.parse(text);
- return calendarSupplier.apply(zonedDateTime);
- }
- }
-
-
- private void addDateFormatConfigConverters(final Map<AdapterKey,
Adapter<?, ?>> converters, final ZoneId zoneIDUTC) {
- // config, override defaults
-
config.getProperty(JsonbConfig.DATE_FORMAT).map(String.class::cast).ifPresent(dateFormat
-> {
- final Optional<Locale> locale =
config.getProperty(JsonbConfig.LOCALE).map(Locale.class::cast);
- final DateTimeFormatter formatter = locale.isPresent() ?
ofPattern(dateFormat, locale.get()) : ofPattern(dateFormat);
-
- converters.put(new AdapterKey(Date.class, String.class), new
ConverterAdapter<>(new Converter<Date>() {
-
- @Override
- public String toString(final Date instance) {
- return
formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC));
- }
-
- @Override
- public Date fromString(final String text) {
- try {
- return Date.from(parseZonedDateTime(text, formatter,
zoneIDUTC).toInstant());
- } catch (final DateTimeParseException dpe) {
- return
Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC));
- }
- }
- }));
- converters.put(new AdapterKey(LocalDateTime.class, String.class),
new ConverterAdapter<>(new Converter<LocalDateTime>() {
-
- @Override
- public String toString(final LocalDateTime instance) {
- return
formatter.format(ZonedDateTime.ofInstant(instance.toInstant(ZoneOffset.UTC),
zoneIDUTC));
- }
-
- @Override
- public LocalDateTime fromString(final String text) {
- try {
- return parseZonedDateTime(text, formatter,
zoneIDUTC).toLocalDateTime();
- } catch (final DateTimeParseException dpe) {
- return LocalDateTime.parse(text);
- }
- }
- }));
- converters.put(new AdapterKey(LocalDate.class, String.class), new
ConverterAdapter<>(new Converter<LocalDate>() {
-
- @Override
- public String toString(final LocalDate instance) {
- return
formatter.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(TimeUnit.DAYS.toMillis(instance.toEpochDay())),
zoneIDUTC));
- }
-
- @Override
- public LocalDate fromString(final String text) {
- try {
- return parseZonedDateTime(text, formatter,
zoneIDUTC).toLocalDate();
- } catch (final DateTimeParseException dpe) {
- return LocalDate.parse(text);
- }
- }
- }));
- converters.put(new AdapterKey(OffsetDateTime.class, String.class),
new ConverterAdapter<>(new Converter<OffsetDateTime>() {
-
- @Override
- public String toString(final OffsetDateTime instance) {
- return
formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC));
- }
-
- @Override
- public OffsetDateTime fromString(final String text) {
- try {
- return parseZonedDateTime(text, formatter,
zoneIDUTC).toOffsetDateTime();
- } catch (final DateTimeParseException dpe) {
- return OffsetDateTime.parse(text);
- }
- }
- }));
- converters.put(new AdapterKey(ZonedDateTime.class, String.class),
new ConverterAdapter<>(new Converter<ZonedDateTime>() {
-
- @Override
- public String toString(final ZonedDateTime instance) {
- return
formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC));
- }
-
- @Override
- public ZonedDateTime fromString(final String text) {
- try {
- return parseZonedDateTime(text, formatter, zoneIDUTC);
- } catch (final DateTimeParseException dpe) {
- return ZonedDateTime.parse(text);
- }
- }
- }));
- converters.put(new AdapterKey(Calendar.class, String.class), new
ConverterAdapter<>(new Converter<Calendar>() {
-
- @Override
- public String toString(final Calendar instance) {
- return
formatter.format(ZonedDateTime.ofInstant(instance.toInstant(),
instance.getTimeZone().toZoneId()));
- }
-
- @Override
- public Calendar fromString(final String text) {
- final ZonedDateTime zonedDateTime =
parseZonedDateTime(text, formatter, zoneIDUTC);
- final Calendar instance = Calendar.getInstance();
-
instance.setTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone()));
- instance.setTime(Date.from(zonedDateTime.toInstant()));
- return instance;
- }
- }));
- converters.put(new AdapterKey(GregorianCalendar.class,
String.class), new ConverterAdapter<>(new Converter<GregorianCalendar>() {
-
- @Override
- public String toString(final GregorianCalendar instance) {
- return
formatter.format(ZonedDateTime.ofInstant(instance.toInstant(),
instance.getTimeZone().toZoneId()));
- }
-
- @Override
- public GregorianCalendar fromString(final String text) {
- final ZonedDateTime zonedDateTime =
parseZonedDateTime(text, formatter, zoneIDUTC);
- final Calendar instance = GregorianCalendar.getInstance();
-
instance.setTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone()));
- instance.setTime(Date.from(zonedDateTime.toInstant()));
- return GregorianCalendar.class.cast(instance);
- }
- }));
- converters.put(new AdapterKey(Instant.class, String.class), new
ConverterAdapter<>(new Converter<Instant>() {
-
- @Override
- public String toString(final Instant instance) {
- return formatter.format(ZonedDateTime.ofInstant(instance,
zoneIDUTC));
- }
-
- @Override
- public Instant fromString(final String text) {
- return parseZonedDateTime(text, formatter,
zoneIDUTC).toInstant();
- }
- }));
- });
- }
-
- private static ZonedDateTime parseZonedDateTime(final String text, final
DateTimeFormatter formatter, final ZoneId defaultZone){
- TemporalAccessor parse = formatter.parse(text);
- ZoneId zone = parse.query(TemporalQueries.zone());
- if (Objects.isNull(zone)) {
- zone = defaultZone;
- }
- int year = parse.isSupported(YEAR) ? parse.get(YEAR) : 0;
- int month = parse.isSupported(MONTH_OF_YEAR) ?
parse.get(MONTH_OF_YEAR) : 0;
- int day = parse.isSupported(DAY_OF_MONTH) ? parse.get(DAY_OF_MONTH) :
0;
- int hour = parse.isSupported(HOUR_OF_DAY) ? parse.get(HOUR_OF_DAY) : 0;
- int minute = parse.isSupported(MINUTE_OF_HOUR) ?
parse.get(MINUTE_OF_HOUR) : 0;
- int second = parse.isSupported(SECOND_OF_MINUTE) ?
parse.get(SECOND_OF_MINUTE) : 0;
- int millisecond = parse.isSupported(MILLI_OF_SECOND) ?
parse.get(MILLI_OF_SECOND) : 0;
- return ZonedDateTime.of(year, month, day, hour, minute, second,
millisecond, zone);
- }
-
- private static void checkForDeprecatedTimeZone(final String text) {
- switch (text) {
- case "CST": // really for TCK, this sucks for end users so we
don't fail for all deprecated zones
- throw new JsonbException("Deprecated timezone: '" + text +
'"');
- default:
- }
- }
-
private Map<String, ?> generatorConfig() {
final Map<String, Object> map = new HashMap<>();
if (config == null) {
diff --git
a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
index 1130f67..363f9b6 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
@@ -441,20 +441,20 @@ public class JsonbAccessMode implements AccessMode,
Closeable {
converter = new JohnzonJsonbAdapter(instance.getValue(),
actualTypeArguments[0], actualTypeArguments[1]);
} else if (dateFormat != null) { // TODO: support lists, LocalDate?
if (Date.class == type) {
- converter = new ConverterAdapter<>(new
JsonbDateConverter(dateFormat));
+ converter = new ConverterAdapter<>(new
JsonbDateConverter(dateFormat), Date.class);
} else if (LocalDateTime.class == type) {
- converter = new ConverterAdapter<>(new
JsonbLocalDateTimeConverter(dateFormat));
+ converter = new ConverterAdapter<>(new
JsonbLocalDateTimeConverter(dateFormat), LocalDateTime.class);
} else if (LocalDate.class == type) {
- converter = new ConverterAdapter<>(new
JsonbLocalDateConverter(dateFormat));
+ converter = new ConverterAdapter<>(new
JsonbLocalDateConverter(dateFormat), LocalDate.class);
} else if (ZonedDateTime.class == type) {
- converter = new ConverterAdapter<>(new
JsonbZonedDateTimeConverter(dateFormat));
+ converter = new ConverterAdapter<>(new
JsonbZonedDateTimeConverter(dateFormat), ZonedDateTime.class);
} else { // can happen if set on the class, todo: refine the checks
converter = null; // todo: should we fallback on numberformat?
}
} else if (numberFormat != null) { // TODO: support lists?
- converter = new ConverterAdapter<>(new
JsonbNumberConverter(numberFormat));
+ converter = new ConverterAdapter<>(new
JsonbNumberConverter(numberFormat), Number.class);
} else {
- converter = new ConverterAdapter<>(new JsonbValueConverter());
+ converter = new ConverterAdapter<>(new JsonbValueConverter(),
Object.class);
}
return converter;
}
@@ -985,7 +985,7 @@ public class JsonbAccessMode implements AccessMode,
Closeable {
try {
MapperConverter mapperConverter =
johnzonConverter.value().newInstance();
if (mapperConverter instanceof Converter) {
- converter = new ConverterAdapter<>((Converter)
mapperConverter);
+ converter = new ConverterAdapter<>((Converter)
mapperConverter, annotationHolder.getType());
} else if (mapperConverter instanceof
ObjectConverter.Reader) {
reader = (ObjectConverter.Reader) mapperConverter;
}
@@ -1045,7 +1045,7 @@ public class JsonbAccessMode implements AccessMode,
Closeable {
try {
MapperConverter mapperConverter =
johnzonConverter.value().newInstance();
if (mapperConverter instanceof Converter) {
- converter = new ConverterAdapter<>((Converter)
mapperConverter) ;
+ converter = new ConverterAdapter<>((Converter)
mapperConverter, reader.getType()) ;
} else if (mapperConverter instanceof
ObjectConverter.Writer) {
writer = (ObjectConverter.Writer) mapperConverter;
}
diff --git
a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JohnzonJsonbAdapter.java
b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JohnzonJsonbAdapter.java
index 7fc505f..079c782 100644
---
a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JohnzonJsonbAdapter.java
+++
b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JohnzonJsonbAdapter.java
@@ -19,6 +19,7 @@
package org.apache.johnzon.jsonb.converter;
import org.apache.johnzon.mapper.TypeAwareAdapter;
+import org.apache.johnzon.mapper.internal.AdapterKey;
import javax.json.bind.JsonbException;
import javax.json.bind.adapter.JsonbAdapter;
@@ -28,11 +29,13 @@ public class JohnzonJsonbAdapter<JsonType, OriginalType>
implements TypeAwareAda
private final JsonbAdapter<OriginalType, JsonType> delegate;
private final Type from;
private final Type to;
+ private final AdapterKey key;
public JohnzonJsonbAdapter(final JsonbAdapter<OriginalType, JsonType>
delegate, final Type from, final Type to) {
this.delegate = delegate;
this.from = from;
this.to = to;
+ this.key = new AdapterKey(from, to);
}
@Override
@@ -60,6 +63,11 @@ public class JohnzonJsonbAdapter<JsonType, OriginalType>
implements TypeAwareAda
}
@Override
+ public AdapterKey getKey() {
+ return key;
+ }
+
+ @Override
public Type getTo() {
return to;
}
diff --git
a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonJsonbTest.java
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonJsonbTest.java
index debf27f..757359b 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonJsonbTest.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonJsonbTest.java
@@ -24,9 +24,14 @@ import javax.json.JsonArray;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
+import org.apache.johnzon.jsonb.test.JsonbRule;
+import org.junit.Rule;
import org.junit.Test;
public class JohnzonJsonbTest {
+ @Rule
+ public final JsonbRule rule = new JsonbRule();
+
@Test
public void jsonArray() throws Exception {
try (final Jsonb jsonb = JsonbBuilder.create()) {
@@ -35,4 +40,27 @@ public class JohnzonJsonbTest {
assertEquals(json, array.toString());
}
}
+
+ @Test
+ public void longBounds() {
+ final String max = rule.toJson(new LongWrapper(Long.MAX_VALUE));
+ assertEquals("{\"value\":9223372036854775807}", max);
+ assertEquals(Long.MAX_VALUE, rule.fromJson(max,
LongWrapper.class).value, 0);
+
+ final String min = rule.toJson(new LongWrapper(Long.MIN_VALUE));
+ assertEquals("{\"value\":-9223372036854775808}", min);
+ assertEquals(Long.MIN_VALUE, rule.fromJson(min,
LongWrapper.class).value, 0);
+ }
+
+ public static class LongWrapper {
+ public Long value;
+
+ public LongWrapper() {
+ // no-op
+ }
+
+ public LongWrapper(final Long value) {
+ this.value = value;
+ }
+ }
}
diff --git
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
index 81cc049..ade0e26 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
@@ -28,25 +28,18 @@ import org.apache.johnzon.mapper.access.BaseAccessMode;
import org.apache.johnzon.mapper.access.FieldAccessMode;
import org.apache.johnzon.mapper.access.FieldAndMethodAccessMode;
import org.apache.johnzon.mapper.access.MethodAccessMode;
-import org.apache.johnzon.mapper.converter.BigDecimalConverter;
-import org.apache.johnzon.mapper.converter.BigIntegerConverter;
import org.apache.johnzon.mapper.converter.BooleanConverter;
import org.apache.johnzon.mapper.converter.ByteConverter;
import org.apache.johnzon.mapper.converter.CachedDelegateConverter;
import org.apache.johnzon.mapper.converter.CharacterConverter;
-import org.apache.johnzon.mapper.converter.ClassConverter;
-import org.apache.johnzon.mapper.converter.DateConverter;
import org.apache.johnzon.mapper.converter.DoubleConverter;
import org.apache.johnzon.mapper.converter.FloatConverter;
import org.apache.johnzon.mapper.converter.IntegerConverter;
-import org.apache.johnzon.mapper.converter.LocaleConverter;
import org.apache.johnzon.mapper.converter.LongConverter;
import org.apache.johnzon.mapper.converter.ShortConverter;
-import org.apache.johnzon.mapper.converter.StringConverter;
-import org.apache.johnzon.mapper.converter.URIConverter;
-import org.apache.johnzon.mapper.converter.URLConverter;
import org.apache.johnzon.mapper.internal.AdapterKey;
import org.apache.johnzon.mapper.internal.ConverterAdapter;
+import org.apache.johnzon.mapper.map.LazyConverterMap;
import javax.json.JsonBuilderFactory;
import javax.json.JsonReaderFactory;
@@ -56,58 +49,20 @@ import javax.json.stream.JsonGeneratorFactory;
import java.io.Closeable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.net.URI;
-import java.net.URL;
import java.nio.charset.Charset;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
-import java.util.Date;
import java.util.HashMap;
-import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.function.Predicate;
// this class is responsible to hold any needed config
// to build the runtime
public class MapperBuilder {
- private static final Map<AdapterKey, Adapter<?, ?>> DEFAULT_CONVERTERS =
new HashMap<AdapterKey, Adapter<?, ?>>(24);
-
- static {
- //DEFAULT_CONVERTERS.put(Date.class, new
DateConverter("yyyy-MM-dd'T'HH:mm:ssZ")); // ISO8601 long RFC822 zone
- DEFAULT_CONVERTERS.put(new AdapterKey(Date.class, String.class), new
ConverterAdapter<Date>(new DateConverter("yyyyMMddHHmmssZ"))); // ISO8601 short
- DEFAULT_CONVERTERS.put(new AdapterKey(URL.class, String.class), new
ConverterAdapter<URL>(new URLConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(URI.class, String.class), new
ConverterAdapter<URI>(new URIConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(Class.class, String.class), new
ConverterAdapter<Class<?>>(new ClassConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(String.class, String.class), new
ConverterAdapter<String>(new StringConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(BigDecimal.class, String.class),
new ConverterAdapter<BigDecimal>(new BigDecimalConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(BigInteger.class, String.class),
new ConverterAdapter<BigInteger>(new BigIntegerConverter()));
- /* primitives should be hanlded low level and adapters will wrap them
in string which is unlikely
- DEFAULT_CONVERTERS.put(new AdapterKey(Byte.class, String.class), new
ConverterAdapter<Byte>(new CachedDelegateConverter<Byte>(new ByteConverter())));
- DEFAULT_CONVERTERS.put(new AdapterKey(Character.class, String.class),
new ConverterAdapter<Character>(new CharacterConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(Double.class, String.class), new
ConverterAdapter<Double>(new DoubleConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(Float.class, String.class), new
ConverterAdapter<Float>(new FloatConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(Integer.class, String.class),
new ConverterAdapter<Integer>(new IntegerConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(Long.class, String.class), new
ConverterAdapter<Long>(new LongConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(Short.class, String.class), new
ConverterAdapter<Short>(new ShortConverter()));
- DEFAULT_CONVERTERS.put(new AdapterKey(Boolean.class, String.class),
new ConverterAdapter<Boolean>(new CachedDelegateConverter<Boolean>(new
BooleanConverter())));
- DEFAULT_CONVERTERS.put(new AdapterKey(byte.class, String.class),
DEFAULT_CONVERTERS.get(new AdapterKey(Byte.class, String.class)));
- DEFAULT_CONVERTERS.put(new AdapterKey(char.class, String.class),
DEFAULT_CONVERTERS.get(new AdapterKey(Character.class, String.class)));
- DEFAULT_CONVERTERS.put(new AdapterKey(double.class, String.class),
DEFAULT_CONVERTERS.get(new AdapterKey(Double.class, String.class)));
- DEFAULT_CONVERTERS.put(new AdapterKey(float.class, String.class),
DEFAULT_CONVERTERS.get(new AdapterKey(Float.class, String.class)));
- DEFAULT_CONVERTERS.put(new AdapterKey(int.class, String.class),
DEFAULT_CONVERTERS.get(new AdapterKey(Integer.class, String.class)));
- DEFAULT_CONVERTERS.put(new AdapterKey(long.class, String.class),
DEFAULT_CONVERTERS.get(new AdapterKey(Long.class, String.class)));
- DEFAULT_CONVERTERS.put(new AdapterKey(short.class, String.class),
DEFAULT_CONVERTERS.get(new AdapterKey(Short.class, String.class)));
- DEFAULT_CONVERTERS.put(new AdapterKey(boolean.class, String.class),
DEFAULT_CONVERTERS.get(new AdapterKey(Boolean.class, String.class)));
- */
- DEFAULT_CONVERTERS.put(new AdapterKey(Locale.class, String.class), new
LocaleConverter());
- }
-
private JsonReaderFactory readerFactory;
private JsonGeneratorFactory generatorFactory;
private JsonProvider provider;
@@ -134,7 +89,7 @@ public class MapperBuilder {
private boolean enforceQuoteString;
private AccessMode accessMode;
private Charset encoding =
Charset.forName(System.getProperty("johnzon.mapper.encoding", "UTF-8"));
- private ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters = new
ConcurrentHashMap<AdapterKey, Adapter<?, ?>>(DEFAULT_CONVERTERS);
+ private LazyConverterMap adapters = new LazyConverterMap();
private Map<Class<?>, ObjectConverter.Reader<?>> objectConverterReaders =
new HashMap<Class<?>, ObjectConverter.Reader<?>>();
private Map<Class<?>, ObjectConverter.Writer<?>> objectConverterWriters =
new HashMap<Class<?>, ObjectConverter.Writer<?>>();
private Map<Class<?>, String[]> ignoredForFields = new HashMap<Class<?>,
String[]>();
@@ -244,14 +199,14 @@ public class MapperBuilder {
}
if (primitiveConverters) {
- adapters.put(new AdapterKey(Byte.class, String.class), new
ConverterAdapter<Byte>(new CachedDelegateConverter<Byte>(new ByteConverter())));
- adapters.put(new AdapterKey(Character.class, String.class), new
ConverterAdapter<Character>(new CharacterConverter()));
- adapters.put(new AdapterKey(Double.class, String.class), new
ConverterAdapter<Double>(new DoubleConverter()));
- adapters.put(new AdapterKey(Float.class, String.class), new
ConverterAdapter<Float>(new FloatConverter()));
- adapters.put(new AdapterKey(Integer.class, String.class), new
ConverterAdapter<Integer>(new IntegerConverter()));
- adapters.put(new AdapterKey(Long.class, String.class), new
ConverterAdapter<Long>(new LongConverter()));
- adapters.put(new AdapterKey(Short.class, String.class), new
ConverterAdapter<Short>(new ShortConverter()));
- adapters.put(new AdapterKey(Boolean.class, String.class), new
ConverterAdapter<Boolean>(new CachedDelegateConverter<Boolean>(new
BooleanConverter())));
+ adapters.put(new AdapterKey(Byte.class, String.class), new
ConverterAdapter<>(new CachedDelegateConverter<>(new ByteConverter()),
Byte.class));
+ adapters.put(new AdapterKey(Character.class, String.class), new
ConverterAdapter<>(new CharacterConverter(), Character.class));
+ adapters.put(new AdapterKey(Double.class, String.class), new
ConverterAdapter<>(new DoubleConverter(), Double.class));
+ adapters.put(new AdapterKey(Float.class, String.class), new
ConverterAdapter<>(new FloatConverter(), Float.class));
+ adapters.put(new AdapterKey(Integer.class, String.class), new
ConverterAdapter<>(new IntegerConverter(), Integer.class));
+ adapters.put(new AdapterKey(Long.class, String.class), new
ConverterAdapter<>(new LongConverter(), Long.class));
+ adapters.put(new AdapterKey(Short.class, String.class), new
ConverterAdapter<>(new ShortConverter(), Short.class));
+ adapters.put(new AdapterKey(Boolean.class, String.class), new
ConverterAdapter<>(new CachedDelegateConverter<>(new BooleanConverter()),
Boolean.class));
adapters.put(new AdapterKey(byte.class, String.class),
adapters.get(new AdapterKey(Byte.class, String.class)));
adapters.put(new AdapterKey(char.class, String.class),
adapters.get(new AdapterKey(Character.class, String.class)));
adapters.put(new AdapterKey(double.class, String.class),
adapters.get(new AdapterKey(Double.class, String.class)));
@@ -278,6 +233,25 @@ public class MapperBuilder {
closeables);
}
+ public ConcurrentHashMap<AdapterKey, Adapter<?,?>> getAdapters() {
+ return adapters;
+ }
+
+ public MapperBuilder setUseShortISO8601Format(final boolean
useShortISO8601Format) {
+ adapters.setUseShortISO8601Format(useShortISO8601Format);
+ return this;
+ }
+
+ public MapperBuilder setAdaptersDateTimeFormatter(final DateTimeFormatter
dateTimeFormatter) {
+ adapters.setDateTimeFormatter(dateTimeFormatter);
+ return this;
+ }
+
+ public MapperBuilder setAdaptersDateTimeFormatterString(final String
dateTimeFormatter) {
+
adapters.setDateTimeFormatter(DateTimeFormatter.ofPattern(dateTimeFormatter));
+ return this;
+ }
+
public MapperBuilder setInterfaceImplementationMapping(final Map<Class<?>,
Class<?>> interfaceImplementationMapping) {
this.interfaceImplementationMapping = interfaceImplementationMapping;
return this;
@@ -395,13 +369,13 @@ public class MapperBuilder {
@Deprecated // use addAdapter
public MapperBuilder addPropertyEditor(final Class<?> clazz, final
Converter<?> converter) {
- adapters.put(new AdapterKey(clazz, String.class), new
ConverterAdapter(converter));
+ adapters.put(new AdapterKey(clazz, String.class), new
ConverterAdapter(converter, clazz));
return this;
}
@Deprecated // use addAdapter
public MapperBuilder addConverter(final Type clazz, final Converter<?>
converter) {
- adapters.put(new AdapterKey(clazz, String.class), new
ConverterAdapter(converter));
+ adapters.put(new AdapterKey(clazz, String.class), new
ConverterAdapter(converter, clazz));
return this;
}
diff --git
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
index 3046376..d0b17d5 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
@@ -22,6 +22,7 @@ import org.apache.johnzon.mapper.access.AccessMode;
import org.apache.johnzon.mapper.converter.EnumConverter;
import org.apache.johnzon.mapper.internal.AdapterKey;
import org.apache.johnzon.mapper.internal.ConverterAdapter;
+import org.apache.johnzon.mapper.map.LazyConverterMap;
import javax.json.JsonValue;
import java.lang.reflect.Type;
@@ -67,7 +68,7 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig
implements Cloneable {
private final boolean supportEnumMapDeserialization; // for tck
private final AccessMode accessMode;
private final Charset encoding;
- private final ConcurrentMap<AdapterKey, Adapter<?, ?>> adapters;
+ private final LazyConverterMap adapters;
private final ConcurrentMap<Adapter<?, ?>, AdapterKey> reverseAdapters;
private final Map<Class<?>, ObjectConverter.Writer<?>>
objectConverterWriters;
@@ -95,7 +96,7 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig
implements Cloneable {
//disable checkstyle for 10+ parameters
//CHECKSTYLE:OFF
- public MapperConfig(final ConcurrentMap<AdapterKey, Adapter<?, ?>>
adapters,
+ public MapperConfig(final LazyConverterMap adapters,
final Map<Class<?>, ObjectConverter.Writer<?>>
objectConverterWriters,
final Map<Class<?>, ObjectConverter.Reader<?>>
objectConverterReaders,
final int version, final boolean close,
@@ -218,12 +219,12 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig
implements Cloneable {
if (Class.class.isInstance(aClass)) {
final Class<?> clazz = Class.class.cast(aClass);
if (Enum.class.isAssignableFrom(clazz)) {
- final Adapter<?, ?> enumConverter = new ConverterAdapter(new
EnumConverter(clazz));
+ final Adapter<?, ?> enumConverter = new ConverterAdapter(new
EnumConverter(clazz), clazz);
adapters.putIfAbsent(new AdapterKey(String.class, aClass),
enumConverter);
return enumConverter;
}
}
- final List<AdapterKey> matched = adapters.keySet().stream()
+ final List<AdapterKey> matched = adapters.adapterKeys().stream()
.filter(k -> k.isAssignableFrom(aClass))
.collect(toList());
if (matched.size() == 1) {
@@ -382,7 +383,7 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig
implements Cloneable {
return encoding;
}
- public ConcurrentMap<AdapterKey, Adapter<?, ?>> getAdapters() {
+ public LazyConverterMap getAdapters() {
return adapters;
}
diff --git
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
index 1e90acf..25a5134 100644
---
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
+++
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
@@ -503,15 +503,9 @@ public class MappingParserImpl implements MappingParser {
if (JsonObject.class == key.getTo() || JsonStructure.class ==
key.getTo()) {
return converter.to(jsonValue.asJsonObject());
}
-
- //X TODO maybe we can put this into MapperConfig?
- //X config.getAdapter(AdapterKey)
- //X config.getAdapterKey(Adapter)
- final AdapterKey adapterKey = getAdapterKey(converter);
-
final Object param;
try {
- Type to = adapterKey.getTo();
+ Type to = key.getTo();
param = buildObject(to, JsonObject.class.cast(jsonValue), to
instanceof Class, jsonPointer);
} catch (final Exception e) {
throw new MapperException(e);
@@ -575,9 +569,7 @@ public class MappingParserImpl implements MappingParser {
if (adapterKey == null) {
if (converter instanceof TypeAwareAdapter) {
- TypeAwareAdapter typeAwareAdapter = (TypeAwareAdapter)
converter;
- adapterKey = new AdapterKey(typeAwareAdapter.getFrom(),
typeAwareAdapter.getTo());
- config.getReverseAdapters().putIfAbsent(converter, adapterKey);
+ return TypeAwareAdapter.class.cast(converter).getKey();
} else {
Class<?> current = converter.getClass();
@@ -655,7 +647,7 @@ public class MappingParserImpl implements MappingParser {
if (JsonObject.class == type || JsonStructure.class == type) {
return jsonValue;
}
- final boolean typedAdapter =
TypeAwareAdapter.class.isInstance(itemConverter);
+ final boolean typedAdapter =
!ConverterAdapter.class.isInstance(itemConverter) &&
TypeAwareAdapter.class.isInstance(itemConverter);
final Object object = buildObject(
baseInstance != null ? baseInstance.getClass() : (
typedAdapter ?
TypeAwareAdapter.class.cast(itemConverter).getTo() : type),
@@ -1136,12 +1128,12 @@ public class MappingParserImpl implements MappingParser
{
if (Class.class.isInstance(aClass)) {
final Class<?> clazz = Class.class.cast(aClass);
if (Enum.class.isAssignableFrom(clazz)) {
- final Adapter<?, ?> enumConverter = new ConverterAdapter(new
EnumConverter(clazz));
+ final Adapter<?, ?> enumConverter = new ConverterAdapter(new
EnumConverter(clazz), clazz);
config.getAdapters().putIfAbsent(new AdapterKey(String.class,
aClass), enumConverter);
return enumConverter;
}
}
- final List<AdapterKey> matched = config.getAdapters().keySet().stream()
+ final List<AdapterKey> matched =
config.getAdapters().adapterKeys().stream()
.filter(k -> k.isAssignableFrom(aClass))
.collect(toList());
if (matched.size() == 1) {
diff --git
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
index 1c5db42..1c5b045 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
@@ -160,16 +160,27 @@ public class Mappings {
}
if (theObjectConverter == null) {
Adapter adapter;
+ final Type readerType = reader.getType();
if (converter instanceof Converter) {
- adapter = new ConverterAdapter((Converter) converter);
+ adapter = new ConverterAdapter((Converter) converter,
readerType);
} else {
adapter = (Adapter) converter;
}
- if (matches(reader.getType(), adapter)) {
+ if (matches(readerType, adapter)) {
theConverter = adapter;
} else {
- theItemConverter = adapter;
+ if (converter instanceof Converter) {
+ if (ParameterizedType.class.isInstance(readerType)
&& ParameterizedType.class.cast(readerType).getActualTypeArguments().length >
0) {
+ final Type[] args =
ParameterizedType.class.cast(readerType).getActualTypeArguments();
+ // List<A> or Map<String, A> lead to read the
last arg in all cases
+ theItemConverter = new
ConverterAdapter((Converter) converter, args[args.length - 1]);
+ } else {
+ theItemConverter = adapter;
+ }
+ } else {
+ theItemConverter = adapter;
+ }
}
}
}
@@ -237,18 +248,29 @@ public class Mappings {
if (converter instanceof ObjectConverter.Reader) {
theObjectConverter = (ObjectConverter.Reader) converter;
}
+ final Type writerType = writer.getType();
if (theObjectConverter == null) {
Adapter adapter;
if (converter instanceof Converter) {
- adapter = new ConverterAdapter((Converter) converter);
+ adapter = new ConverterAdapter((Converter) converter,
writerType);
} else {
adapter = (Adapter) converter;
}
- if (matches(writer.getType(), adapter)) {
+ if (matches(writerType, adapter)) {
theConverter = adapter;
} else {
- theItemConverter = adapter;
+ if (converter instanceof Converter) {
+ if (ParameterizedType.class.isInstance(writerType)
&& ParameterizedType.class.cast(writerType).getActualTypeArguments().length >
0) {
+ final Type[] args =
ParameterizedType.class.cast(writerType).getActualTypeArguments();
+ // List<A> or Map<String, A> lead to read the
last arg in all cases
+ theItemConverter = new
ConverterAdapter((Converter) converter, args[args.length - 1]);
+ } else {
+ theItemConverter = adapter;
+ }
+ } else {
+ theItemConverter = adapter;
+ }
}
}
}
@@ -677,7 +699,7 @@ public class Mappings {
final AdapterKey key = new AdapterKey(String.class, type);
converter = adapters.get(key); // first ensure user didnt
override it
if (converter == null) {
- converter = new ConverterAdapter(new EnumConverter(type));
+ converter = new ConverterAdapter(new EnumConverter(type),
type);
adapters.put(key, (Adapter<?, ?>) converter);
}
}
@@ -694,12 +716,12 @@ public class Mappings {
public MapBuilderReader(final Map<String, Getter> objectGetters, final
String[] paths, final int version) {
this.getters = objectGetters;
this.paths = paths;
- this.template = new LinkedHashMap<String, Object>();
+ this.template = new LinkedHashMap<>();
this.version = version;
Map<String, Object> last = this.template;
for (int i = 1; i < paths.length; i++) {
- final Map<String, Object> newLast = new LinkedHashMap<String,
Object>();
+ final Map<String, Object> newLast = new LinkedHashMap<>();
last.put(paths[i], newLast);
last = newLast;
}
@@ -707,7 +729,7 @@ public class Mappings {
@Override
public Object read(final Object instance) {
- final Map<String, Object> map = new LinkedHashMap<String,
Object>(template);
+ final Map<String, Object> map = new LinkedHashMap<>(template);
Map<String, Object> nested = map;
for (int i = 1; i < paths.length; i++) {
nested = Map.class.cast(nested.get(paths[i]));
diff --git
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/TypeAwareAdapter.java
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/TypeAwareAdapter.java
index cff2fc0..b956803 100644
---
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/TypeAwareAdapter.java
+++
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/TypeAwareAdapter.java
@@ -18,9 +18,15 @@
*/
package org.apache.johnzon.mapper;
+import org.apache.johnzon.mapper.internal.AdapterKey;
+
import java.lang.reflect.Type;
public interface TypeAwareAdapter<A, B> extends Adapter<A, B> {
Type getTo();
Type getFrom();
+
+ default AdapterKey getKey() {
+ return new AdapterKey(getFrom(), getTo());
+ }
}
diff --git
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
index edbc379..7bd011e 100644
---
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
+++
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
@@ -197,7 +197,7 @@ public abstract class BaseAccessMode implements AccessMode {
try {
MapperConverter mapperConverter =
JohnzonConverter.class.cast(a).value().newInstance();
if (mapperConverter instanceof Converter) {
- final Adapter<?, ?> converter = new
ConverterAdapter((Converter) mapperConverter);
+ final Adapter<?, ?> converter = new
ConverterAdapter((Converter) mapperConverter,
constructor.getGenericParameterTypes()[i]);
if
(matches(constructor.getParameterTypes()[i], converter)) {
constructorParameterConverters[i] =
converter;
constructorItemParameterConverters[i] =
null;
diff --git
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/converter/DateWithCopyConverter.java
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/converter/DateWithCopyConverter.java
index 431f133..85abb26 100644
---
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/converter/DateWithCopyConverter.java
+++
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/converter/DateWithCopyConverter.java
@@ -28,7 +28,7 @@ public class DateWithCopyConverter implements Adapter<Date,
String> {
private final Adapter<Date, String> delegate;
public DateWithCopyConverter(final Adapter<Date, String> delegate) {
- this.delegate = delegate == null ? new ConverterAdapter<Date>(new
DateConverter("yyyyMMddHHmmssZ")) : delegate;
+ this.delegate = delegate == null ? new ConverterAdapter<>(new
DateConverter("yyyyMMddHHmmssZ"), Date.class) : delegate;
}
@Override
diff --git
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/converter/LocaleConverter.java
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/converter/LocaleConverter.java
index a03b2a1..6dd330f 100644
---
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/converter/LocaleConverter.java
+++
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/converter/LocaleConverter.java
@@ -18,12 +18,31 @@
*/
package org.apache.johnzon.mapper.converter;
-import org.apache.johnzon.mapper.Adapter;
+import org.apache.johnzon.mapper.TypeAwareAdapter;
+import org.apache.johnzon.mapper.internal.AdapterKey;
+import java.lang.reflect.Type;
import java.util.Locale;
// from [lang]
-public class LocaleConverter implements Adapter<Locale, String> {
+public class LocaleConverter implements TypeAwareAdapter<Locale, String> {
+ private final AdapterKey key = new AdapterKey(Locale.class, String.class);
+
+ @Override
+ public Type getTo() {
+ return key.getTo();
+ }
+
+ @Override
+ public Type getFrom() {
+ return key.getFrom();
+ }
+
+ @Override
+ public AdapterKey getKey() {
+ return key;
+ }
+
@Override
public String from(final Locale instance) {
return instance.toString();
diff --git
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/ConverterAdapter.java
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/ConverterAdapter.java
index c3bb90e..afea76b 100644
---
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/ConverterAdapter.java
+++
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/ConverterAdapter.java
@@ -18,14 +18,18 @@
*/
package org.apache.johnzon.mapper.internal;
-import org.apache.johnzon.mapper.Adapter;
import org.apache.johnzon.mapper.Converter;
+import org.apache.johnzon.mapper.TypeAwareAdapter;
-public class ConverterAdapter<A> implements Adapter<A, String> {
+import java.lang.reflect.Type;
+
+public class ConverterAdapter<A> implements TypeAwareAdapter<A, String> {
private final Converter<A> converter;
+ private final AdapterKey key;
- public ConverterAdapter(final Converter<A> converter) {
+ public ConverterAdapter(final Converter<A> converter, final Type from) {
this.converter = converter;
+ this.key = new AdapterKey(from, String.class);
}
public Converter<A> getConverter() {
@@ -41,4 +45,19 @@ public class ConverterAdapter<A> implements Adapter<A,
String> {
public String from(final A a) {
return converter.toString(a);
}
+
+ @Override
+ public Type getTo() {
+ return key.getTo();
+ }
+
+ @Override
+ public Type getFrom() {
+ return key.getFrom();
+ }
+
+ @Override
+ public AdapterKey getKey() {
+ return key;
+ }
}
diff --git
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/map/LazyConverterMap.java
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/map/LazyConverterMap.java
new file mode 100644
index 0000000..f1a4e42
--- /dev/null
+++
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/map/LazyConverterMap.java
@@ -0,0 +1,637 @@
+/*
+ * 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.johnzon.mapper.map;
+
+import org.apache.johnzon.mapper.Adapter;
+import org.apache.johnzon.mapper.Converter;
+import org.apache.johnzon.mapper.MapperException;
+import org.apache.johnzon.mapper.converter.BigDecimalConverter;
+import org.apache.johnzon.mapper.converter.BigIntegerConverter;
+import org.apache.johnzon.mapper.converter.ClassConverter;
+import org.apache.johnzon.mapper.converter.DateConverter;
+import org.apache.johnzon.mapper.converter.LocaleConverter;
+import org.apache.johnzon.mapper.converter.StringConverter;
+import org.apache.johnzon.mapper.converter.URIConverter;
+import org.apache.johnzon.mapper.converter.URLConverter;
+import org.apache.johnzon.mapper.internal.AdapterKey;
+import org.apache.johnzon.mapper.internal.ConverterAdapter;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URL;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalQueries;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.HOUR_OF_DAY;
+import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
+import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
+import static java.time.temporal.ChronoField.YEAR;
+import static java.util.stream.Collectors.toSet;
+
+// important: override all usages,
+// mainly org.apache.johnzon.mapper.MapperConfig.findAdapter and
+// org.apache.johnzon.mapper.MappingParserImpl.findAdapter today
+public class LazyConverterMap extends ConcurrentHashMap<AdapterKey, Adapter<?,
?>> {
+ private static final Adapter<?, ?> NO_ADAPTER = new Adapter<Object,
Object>() {
+ @Override
+ public Object to(final Object b) {
+ throw new UnsupportedOperationException("shouldn't be called");
+ }
+
+ @Override
+ public Object from(final Object a) {
+ return to(null); // just fail
+ }
+ };
+
+ private boolean useShortISO8601Format = true;
+ private DateTimeFormatter dateTimeFormatter;
+
+ public void setUseShortISO8601Format(final boolean useShortISO8601Format) {
+ this.useShortISO8601Format = useShortISO8601Format;
+ }
+
+ public void setDateTimeFormatter(final DateTimeFormatter
dateTimeFormatter) {
+ this.dateTimeFormatter = dateTimeFormatter;
+ }
+
+ @Override
+ public Adapter<?, ?> get(final Object key) {
+ final Adapter<?, ?> found = super.get(key);
+ if (found == NO_ADAPTER) {
+ return null;
+ }
+ if (found == null) {
+ if (!AdapterKey.class.isInstance(key)) {
+ return null;
+ }
+ final AdapterKey k = AdapterKey.class.cast(key);
+ if (k.getTo() == String.class) {
+ final Adapter<?, ?> adapter = doLazyLookup(k);
+ if (adapter != null) {
+ return adapter;
+ } // else let's cache we don't need to go through lazy lookups
+ }
+ add(k, NO_ADAPTER);
+ return null;
+ }
+ return found;
+ }
+
+ @Override
+ public Set<Entry<AdapterKey, Adapter<?, ?>>> entrySet() {
+ return super.entrySet().stream()
+ .filter(it -> it.getValue() != NO_ADAPTER)
+ .collect(toSet());
+ }
+
+ public Set<AdapterKey> adapterKeys() {
+ return Stream.concat(
+ super.keySet().stream()
+ .filter(it -> super.get(it) != NO_ADAPTER),
+ Stream.of(Date.class, URI.class, URL.class, Class.class,
String.class, BigDecimal.class, BigInteger.class,
+ Locale.class, Period.class, Duration.class,
Calendar.class, GregorianCalendar.class, TimeZone.class,
+ ZoneId.class, ZoneOffset.class, SimpleTimeZone.class,
Instant.class, LocalDateTime.class, LocalDate.class,
+ ZonedDateTime.class, OffsetDateTime.class,
OffsetTime.class)
+ .map(it -> new AdapterKey(it, String.class, true)))
+ .collect(toSet());
+ }
+
+ private Adapter<?, ?> doLazyLookup(final AdapterKey key) {
+ final Type from = key.getFrom();
+ if (from == Date.class) {
+ return addDateConverter(key);
+ }
+ if (from == URI.class) {
+ return add(key, new ConverterAdapter<>(new URIConverter(),
URI.class));
+ }
+ if (from == URL.class) {
+ return add(key, new ConverterAdapter<>(new URLConverter(),
URL.class));
+ }
+ if (from == Class.class) {
+ return add(key, new ConverterAdapter<>(new ClassConverter(),
Class.class));
+ }
+ if (from == String.class) {
+ return add(key, new ConverterAdapter<>(new StringConverter(),
String.class));
+ }
+ if (from == BigDecimal.class) {
+ return add(key, new ConverterAdapter<>(new BigDecimalConverter(),
BigDecimal.class));
+ }
+ if (from == BigInteger.class) {
+ return add(key, new ConverterAdapter<>(new BigIntegerConverter(),
BigInteger.class));
+ }
+ if (from == Locale.class) {
+ return add(key, new LocaleConverter());
+ }
+ if (from == Period.class) {
+ return add(key, new ConverterAdapter<>(new Converter<Period>() {
+ @Override
+ public String toString(final Period instance) {
+ return instance.toString();
+ }
+
+ @Override
+ public Period fromString(final String text) {
+ return Period.parse(text);
+ }
+ }, Period.class));
+ }
+ if (from == Duration.class) {
+ return add(key, new ConverterAdapter<>(new Converter<Duration>() {
+ @Override
+ public String toString(final Duration instance) {
+ return instance.toString();
+ }
+
+ @Override
+ public Duration fromString(final String text) {
+ return Duration.parse(text);
+ }
+ }, Duration.class));
+ }
+ if (from == Calendar.class) {
+ return addCalendarConverter(key);
+ }
+ if (from == GregorianCalendar.class) {
+ return addGregorianCalendar(key);
+ }
+ if (from == TimeZone.class) {
+ return add(key, new ConverterAdapter<>(new Converter<TimeZone>() {
+ @Override
+ public String toString(final TimeZone instance) {
+ return instance.getID();
+ }
+
+ @Override
+ public TimeZone fromString(final String text) {
+ checkForDeprecatedTimeZone(text);
+ return TimeZone.getTimeZone(text);
+ }
+ }, TimeZone.class));
+ }
+ if (from == ZoneId.class) {
+ return add(key, new ConverterAdapter<>(new Converter<ZoneId>() {
+ @Override
+ public String toString(final ZoneId instance) {
+ return instance.getId();
+ }
+
+ @Override
+ public ZoneId fromString(final String text) {
+ return ZoneId.of(text);
+ }
+ }, ZoneId.class));
+ }
+ if (from == ZoneOffset.class) {
+ return add(key, new ConverterAdapter<>(new Converter<ZoneOffset>()
{
+ @Override
+ public String toString(final ZoneOffset instance) {
+ return instance.getId();
+ }
+
+ @Override
+ public ZoneOffset fromString(final String text) {
+ return ZoneOffset.of(text);
+ }
+ }, ZoneOffset.class));
+ }
+ if (from == SimpleTimeZone.class) {
+ return add(key, new ConverterAdapter<>(new
Converter<SimpleTimeZone>() {
+ @Override
+ public String toString(final SimpleTimeZone instance) {
+ return instance.getID();
+ }
+
+ @Override
+ public SimpleTimeZone fromString(final String text) {
+ checkForDeprecatedTimeZone(text);
+ final TimeZone timeZone = TimeZone.getTimeZone(text);
+ return new SimpleTimeZone(timeZone.getRawOffset(),
timeZone.getID());
+ }
+ }, SimpleTimeZone.class));
+ }
+ if (from == Instant.class) {
+ return addInstantConverter(key);
+ }
+ if (from == LocalDate.class) {
+ return addLocalDateConverter(key);
+ }
+ if (from == LocalTime.class) {
+ return add(key, new ConverterAdapter<>(new Converter<LocalTime>() {
+ @Override
+ public String toString(final LocalTime instance) {
+ return instance.toString();
+ }
+
+ @Override
+ public LocalTime fromString(final String text) {
+ return LocalTime.parse(text);
+ }
+ }, LocalTime.class));
+ }
+ if (from == LocalDateTime.class) {
+ return addLocalDateTimeConverter(key);
+ }
+ if (from == ZonedDateTime.class) {
+ return addZonedDateTimeConverter(key);
+ }
+ if (from == OffsetDateTime.class) {
+ return addOffsetDateTimeConverter(key);
+ }
+ if (from == OffsetTime.class) {
+ return add(key, new ConverterAdapter<>(new Converter<OffsetTime>()
{
+ @Override
+ public String toString(final OffsetTime instance) {
+ return instance.toString();
+ }
+
+ @Override
+ public OffsetTime fromString(final String text) {
+ return OffsetTime.parse(text);
+ }
+ }, OffsetTime.class));
+ }
+ return null;
+ }
+
+ private Adapter<?, ?> addOffsetDateTimeConverter(final AdapterKey key) {
+ if (dateTimeFormatter != null) {
+ final ZoneId zoneIDUTC = ZoneId.of("UTC");
+ return add(key, new ConverterAdapter<>(new
Converter<OffsetDateTime>() {
+ @Override
+ public String toString(final OffsetDateTime instance) {
+ return
dateTimeFormatter.format(ZonedDateTime.ofInstant(instance.toInstant(),
zoneIDUTC));
+ }
+
+ @Override
+ public OffsetDateTime fromString(final String text) {
+ try {
+ return parseZonedDateTime(text, dateTimeFormatter,
zoneIDUTC).toOffsetDateTime();
+ } catch (final DateTimeParseException dpe) {
+ return OffsetDateTime.parse(text);
+ }
+ }
+ }, OffsetDateTime.class));
+ }
+ return add(key, new ConverterAdapter<>(new Converter<OffsetDateTime>()
{
+ @Override
+ public String toString(final OffsetDateTime instance) {
+ return instance.toString();
+ }
+
+ @Override
+ public OffsetDateTime fromString(final String text) {
+ return OffsetDateTime.parse(text);
+ }
+ }, OffsetDateTime.class));
+ }
+
+ private Adapter<?, ?> addZonedDateTimeConverter(final AdapterKey key) {
+ if (dateTimeFormatter != null) {
+ final ZoneId zoneIDUTC = ZoneId.of("UTC");
+ return add(key, new ConverterAdapter<>(new
Converter<ZonedDateTime>() {
+ @Override
+ public String toString(final ZonedDateTime instance) {
+ return
dateTimeFormatter.format(ZonedDateTime.ofInstant(instance.toInstant(),
zoneIDUTC));
+ }
+
+ @Override
+ public ZonedDateTime fromString(final String text) {
+ try {
+ return parseZonedDateTime(text, dateTimeFormatter,
zoneIDUTC);
+ } catch (final DateTimeParseException dpe) {
+ return ZonedDateTime.parse(text);
+ }
+ }
+ }, ZonedDateTime.class));
+ }
+ return add(key, new ConverterAdapter<>(new Converter<ZonedDateTime>() {
+ @Override
+ public String toString(final ZonedDateTime instance) {
+ return instance.toString();
+ }
+
+ @Override
+ public ZonedDateTime fromString(final String text) {
+ return ZonedDateTime.parse(text);
+ }
+ }, ZonedDateTime.class));
+ }
+
+ private Adapter<?, ?> addLocalDateTimeConverter(final AdapterKey key) {
+ if (dateTimeFormatter != null) {
+ final ZoneId zoneIDUTC = ZoneId.of("UTC");
+ return add(key, new ConverterAdapter<>(new
Converter<LocalDateTime>() {
+
+ @Override
+ public String toString(final LocalDateTime instance) {
+ return
dateTimeFormatter.format(ZonedDateTime.ofInstant(instance.toInstant(ZoneOffset.UTC),
zoneIDUTC));
+ }
+
+ @Override
+ public LocalDateTime fromString(final String text) {
+ try {
+ return parseZonedDateTime(text, dateTimeFormatter,
zoneIDUTC).toLocalDateTime();
+ } catch (final DateTimeParseException dpe) {
+ return LocalDateTime.parse(text);
+ }
+ }
+ }, LocalDateTime.class));
+ }
+ return add(key, new ConverterAdapter<>(new Converter<LocalDateTime>() {
+ @Override
+ public String toString(final LocalDateTime instance) {
+ return instance.toString();
+ }
+
+ @Override
+ public LocalDateTime fromString(final String text) {
+ return LocalDateTime.parse(text);
+ }
+ }, LocalDateTime.class));
+ }
+
+ private Adapter<?, ?> addLocalDateConverter(final AdapterKey key) {
+ if (dateTimeFormatter != null) {
+ final ZoneId zoneIDUTC = ZoneId.of("UTC");
+ return add(key, new ConverterAdapter<>(new Converter<LocalDate>() {
+ @Override
+ public String toString(final LocalDate instance) {
+ return
dateTimeFormatter.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(TimeUnit.DAYS.toMillis(instance.toEpochDay())),
zoneIDUTC));
+ }
+
+ @Override
+ public LocalDate fromString(final String text) {
+ try {
+ return parseZonedDateTime(text, dateTimeFormatter,
zoneIDUTC).toLocalDate();
+ } catch (final DateTimeParseException dpe) {
+ return LocalDate.parse(text);
+ }
+ }
+ }, LocalDate.class));
+ }
+ return add(key, new ConverterAdapter<>(new Converter<LocalDate>() {
+ @Override
+ public String toString(final LocalDate instance) {
+ return instance.toString();
+ }
+
+ @Override
+ public LocalDate fromString(final String text) {
+ return LocalDate.parse(text);
+ }
+ }, LocalDate.class));
+ }
+
+ private Adapter<?, ?> addInstantConverter(final AdapterKey key) {
+ if (dateTimeFormatter != null) {
+ final ZoneId zoneIDUTC = ZoneId.of("UTC");
+ return add(key, new ConverterAdapter<>(new Converter<Instant>() {
+ @Override
+ public String toString(final Instant instance) {
+ return
dateTimeFormatter.format(ZonedDateTime.ofInstant(instance, zoneIDUTC));
+ }
+
+ @Override
+ public Instant fromString(final String text) {
+ return parseZonedDateTime(text, dateTimeFormatter,
zoneIDUTC).toInstant();
+ }
+ }, Instant.class));
+ }
+ return add(key, new ConverterAdapter<>(new Converter<Instant>() {
+ @Override
+ public String toString(final Instant instance) {
+ return instance.toString();
+ }
+
+ @Override
+ public Instant fromString(final String text) {
+ return Instant.parse(text);
+ }
+ }, Instant.class));
+ }
+
+ private Adapter<?, ?> addGregorianCalendar(final AdapterKey key) {
+ if (dateTimeFormatter != null) {
+ final ZoneId zoneIDUTC = ZoneId.of("UTC");
+ return add(key, new ConverterAdapter<>(new
Converter<GregorianCalendar>() {
+ @Override
+ public String toString(final GregorianCalendar instance) {
+ return
dateTimeFormatter.format(ZonedDateTime.ofInstant(instance.toInstant(),
instance.getTimeZone().toZoneId()));
+ }
+
+ @Override
+ public GregorianCalendar fromString(final String text) {
+ final ZonedDateTime zonedDateTime =
parseZonedDateTime(text, dateTimeFormatter, zoneIDUTC);
+ final Calendar instance = GregorianCalendar.getInstance();
+
instance.setTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone()));
+ instance.setTime(Date.from(zonedDateTime.toInstant()));
+ return GregorianCalendar.class.cast(instance);
+ }
+ }, GregorianCalendar.class));
+ }
+ return add(key, new ConverterAdapter<>(new
Converter<GregorianCalendar>() {
+ @Override
+ public String toString(final GregorianCalendar instance) {
+ return toStringCalendar(instance);
+ }
+
+ @Override
+ public GregorianCalendar fromString(final String text) {
+ return fromCalendar(text, GregorianCalendar::from);
+ }
+ }, GregorianCalendar.class));
+ }
+
+ private Adapter<?, ?> addCalendarConverter(final AdapterKey key) {
+ if (dateTimeFormatter != null) {
+ final ZoneId zoneIDUTC = ZoneId.of("UTC");
+ return add(key, new ConverterAdapter<>(new Converter<Calendar>() {
+ @Override
+ public String toString(final Calendar instance) {
+ return
dateTimeFormatter.format(ZonedDateTime.ofInstant(instance.toInstant(),
instance.getTimeZone().toZoneId()));
+ }
+
+ @Override
+ public Calendar fromString(final String text) {
+ final ZonedDateTime zonedDateTime =
parseZonedDateTime(text, dateTimeFormatter, zoneIDUTC);
+ final Calendar instance = Calendar.getInstance();
+
instance.setTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone()));
+ instance.setTime(Date.from(zonedDateTime.toInstant()));
+ return instance;
+ }
+ }, Calendar.class));
+ }
+ return add(key, new ConverterAdapter<>(new Converter<Calendar>() {
+ @Override
+ public String toString(final Calendar instance) {
+ return toStringCalendar(instance);
+ }
+
+ @Override
+ public Calendar fromString(final String text) {
+ return fromCalendar(text, zdt -> {
+ final Calendar instance = Calendar.getInstance();
+ instance.clear();
+ instance.setTimeZone(TimeZone.getTimeZone(zdt.getZone()));
+ instance.setTimeInMillis(zdt.toInstant().toEpochMilli());
+ return instance;
+ });
+ }
+ }, Calendar.class));
+ }
+
+ private Adapter<?, ?> addDateConverter(final AdapterKey key) {
+ if (useShortISO8601Format) {
+ return add(key, new ConverterAdapter<>(new
DateConverter("yyyyMMddHHmmssZ"), Date.class));
+ }
+ final ZoneId zoneIDUTC = ZoneId.of("UTC");
+ if (dateTimeFormatter != null) {
+ return add(key, new ConverterAdapter<>(new Converter<Date>() {
+
+ @Override
+ public String toString(final Date instance) {
+ return
dateTimeFormatter.format(ZonedDateTime.ofInstant(instance.toInstant(),
zoneIDUTC));
+ }
+
+ @Override
+ public Date fromString(final String text) {
+ try {
+ return Date.from(parseZonedDateTime(text,
dateTimeFormatter, zoneIDUTC).toInstant());
+ } catch (final DateTimeParseException dpe) {
+ return
Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC));
+ }
+ }
+ }, Date.class));
+ }
+ return add(key, new ConverterAdapter<>(new Converter<Date>() {
+ @Override
+ public String toString(final Date instance) {
+ return ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC)
+ .format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
+ }
+
+ @Override
+ public Date fromString(final String text) {
+ try {
+ return Date.from(ZonedDateTime.parse(text).toInstant());
+ } catch (final DateTimeParseException dte) {
+ return
Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC));
+ }
+ }
+ }, Date.class));
+ }
+
+ private static ZonedDateTime parseZonedDateTime(final String text, final
DateTimeFormatter formatter, final ZoneId defaultZone) {
+ final TemporalAccessor parse = formatter.parse(text);
+ ZoneId zone = parse.query(TemporalQueries.zone());
+ if (Objects.isNull(zone)) {
+ zone = defaultZone;
+ }
+ final int year = parse.isSupported(YEAR) ? parse.get(YEAR) : 0;
+ final int month = parse.isSupported(MONTH_OF_YEAR) ?
parse.get(MONTH_OF_YEAR) : 0;
+ final int day = parse.isSupported(DAY_OF_MONTH) ?
parse.get(DAY_OF_MONTH) : 0;
+ final int hour = parse.isSupported(HOUR_OF_DAY) ?
parse.get(HOUR_OF_DAY) : 0;
+ final int minute = parse.isSupported(MINUTE_OF_HOUR) ?
parse.get(MINUTE_OF_HOUR) : 0;
+ final int second = parse.isSupported(SECOND_OF_MINUTE) ?
parse.get(SECOND_OF_MINUTE) : 0;
+ final int millisecond = parse.isSupported(MILLI_OF_SECOND) ?
parse.get(MILLI_OF_SECOND) : 0;
+ return ZonedDateTime.of(year, month, day, hour, minute, second,
millisecond, zone);
+ }
+
+ private static void checkForDeprecatedTimeZone(final String text) {
+ switch (text) {
+ case "CST": // really for TCK, this sucks for end users so we
don't fail for all deprecated zones
+ throw new MapperException("Deprecated timezone: '" + text +
'"');
+ default:
+ }
+ }
+
+ private String toStringCalendar(final Calendar instance) {
+ if (!hasTime(instance)) { // spec
+ final LocalDate localDate = LocalDate.of(
+ instance.get(Calendar.YEAR),
+ instance.get(Calendar.MONTH) + 1,
+ instance.get(Calendar.DAY_OF_MONTH));
+ return localDate.toString() +
+ (instance.getTimeZone() != null ?
+ instance.getTimeZone().toZoneId().getRules()
+
.getOffset(Instant.ofEpochMilli(TimeUnit.DAYS.toMillis(localDate.toEpochDay())))
: "");
+ }
+ return ZonedDateTime.ofInstant(instance.toInstant(),
instance.getTimeZone().toZoneId())
+ .format(DateTimeFormatter.ISO_DATE_TIME);
+ }
+
+ private boolean hasTime(final Calendar instance) {
+ if (!instance.isSet(Calendar.HOUR_OF_DAY)) {
+ return false;
+ }
+ return instance.get(Calendar.HOUR_OF_DAY) != 0 ||
+ (instance.isSet(Calendar.MINUTE) &&
instance.get(Calendar.MINUTE) != 0) ||
+ (instance.isSet(Calendar.SECOND) &&
instance.get(Calendar.SECOND) != 0);
+ }
+
+ private <T extends Calendar> T fromCalendar(final String text, final
Function<ZonedDateTime, T> calendarSupplier) {
+ switch (text.length()) {
+ case 10: {
+ final ZonedDateTime date = LocalDate.parse(text)
+ .atTime(0, 0, 0)
+ .atZone(ZoneId.of("UTC"));
+ return calendarSupplier.apply(date);
+ }
+ default:
+ final ZonedDateTime zonedDateTime = ZonedDateTime.parse(text);
+ return calendarSupplier.apply(zonedDateTime);
+ }
+ }
+
+ private Adapter<?, ?> add(final AdapterKey key, final Adapter<?, ?>
converter) {
+ final Adapter<?, ?> existing = putIfAbsent(key, converter);
+ return existing == null ? converter : existing;
+ }
+}
diff --git
a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java
b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java
index f0f4020..4044781 100644
---
a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java
+++
b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java
@@ -21,6 +21,7 @@ package org.apache.johnzon.mapper;
import static java.util.Collections.emptyMap;
import org.apache.johnzon.mapper.access.FieldAccessMode;
+import org.apache.johnzon.mapper.map.LazyConverterMap;
import org.junit.Assert;
import org.junit.Test;
@@ -31,7 +32,6 @@ import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
public class MapperConfigTest {
@@ -156,7 +156,7 @@ public class MapperConfigTest {
private MapperConfig createConfig(Map<Class<?>, ObjectConverter.Codec<?>>
converter) {
- return new MapperConfig(new ConcurrentHashMap<>(0),
+ return new MapperConfig(new LazyConverterMap(),
Map.class.cast(converter),
Map.class.cast(converter),
-1,
true,
diff --git
a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterTest.java
b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterTest.java
index 62e9495..8e5e059 100644
---
a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterTest.java
+++
b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterTest.java
@@ -108,6 +108,15 @@ public class ObjectConverterTest {
result = 31 * result + Arrays.hashCode(linkedPersonsArray);
return result;
}
+
+ @Override
+ public String toString() {
+ return "Contact{" +
+ "linkedPersons=" + linkedPersons +
+ ", linkedPersonsArray=" +
Arrays.toString(linkedPersonsArray) +
+ ", personMap=" + personMap +
+ '}';
+ }
}
public static class Person {
@@ -156,5 +165,13 @@ public class ObjectConverterTest {
result = 31 * result + (lastName != null ? lastName.hashCode() :
0);
return result;
}
+
+ @Override
+ public String toString() {
+ return "Person{" +
+ "firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ '}';
+ }
}
}
\ No newline at end of file
diff --git a/johnzon-mapper/src/test/java/org/superbiz/ExtendMappingTest.java
b/johnzon-mapper/src/test/java/org/superbiz/ExtendMappingTest.java
index 8e5264c..27fdd0d 100644
--- a/johnzon-mapper/src/test/java/org/superbiz/ExtendMappingTest.java
+++ b/johnzon-mapper/src/test/java/org/superbiz/ExtendMappingTest.java
@@ -21,13 +21,13 @@ package org.superbiz;
import org.apache.johnzon.mapper.MapperConfig;
import org.apache.johnzon.mapper.Mappings;
import org.apache.johnzon.mapper.access.FieldAccessMode;
+import org.apache.johnzon.mapper.map.LazyConverterMap;
import org.junit.Test;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
import static java.util.Collections.emptyMap;
import static org.junit.Assert.assertEquals;
@@ -57,7 +57,7 @@ public class ExtendMappingTest {
public static class MyMappings extends Mappings {
public MyMappings() {
super(new MapperConfig(
- new ConcurrentHashMap<>(), new HashMap<>(), new
HashMap<>(),
+ new LazyConverterMap(), new HashMap<>(), new HashMap<>(),
-1, true, true, true, false, false, false,
new FieldAccessMode(false, false),
StandardCharsets.UTF_8, String::compareTo, false, false,
null, false, false,