diff --git a/common/common/src/main/java/io/helidon/common/GenericType.java b/common/common/src/main/java/io/helidon/common/GenericType.java index 1149aef515d..4e71f034055 100644 --- a/common/common/src/main/java/io/helidon/common/GenericType.java +++ b/common/common/src/main/java/io/helidon/common/GenericType.java @@ -243,7 +243,7 @@ public Builder baseType(Class baseType) { } public Builder addGenericParameter(GenericType genericParameter) { - this.genericParameters.add(genericParameter); + this.genericParameters.add(genericParameter.type()); return this; } diff --git a/common/common/src/main/java/io/helidon/common/HelidonParameterizedType.java b/common/common/src/main/java/io/helidon/common/HelidonParameterizedType.java index 12994c846ed..2670087fbec 100644 --- a/common/common/src/main/java/io/helidon/common/HelidonParameterizedType.java +++ b/common/common/src/main/java/io/helidon/common/HelidonParameterizedType.java @@ -32,32 +32,36 @@ public Type getOwnerType() { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { + if (o instanceof ParameterizedType) { + // Check that information is equivalent + ParameterizedType that = (ParameterizedType) o; + + if (this == that) + return true; + + Type thatRawType = that.getRawType(); + + return Objects.equals(type, thatRawType) && + Arrays.equals(typeArgs, + that.getActualTypeArguments()); + } else { return false; } - HelidonParameterizedType that = (HelidonParameterizedType) o; - return Objects.equals(type, that.type) - && Arrays.equals(typeArgs, that.typeArgs); } @Override public int hashCode() { - int result = Objects.hash(type); - result = 31 * result + Arrays.hashCode(typeArgs); - return result; + return Arrays.hashCode(typeArgs) ^ Objects.hashCode(type); } @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(type.toString()); + sb.append(type.getName()); if (typeArgs.length > 0) { sb.append("<"); for (Type typeArg : typeArgs) { - sb.append(typeArg); + sb.append(typeArg.getTypeName()); } sb.append(">"); } diff --git a/json/binding/src/main/java/io/helidon/json/binding/BindingFactoryConverter.java b/json/binding/src/main/java/io/helidon/json/binding/BindingFactoryConverter.java new file mode 100644 index 00000000000..151dc95ce47 --- /dev/null +++ b/json/binding/src/main/java/io/helidon/json/binding/BindingFactoryConverter.java @@ -0,0 +1,4 @@ +package io.helidon.json.binding; + +public interface BindingFactoryConverter extends BindingFactorySerializer, BindingFactoryDeserializer { +} diff --git a/json/binding/src/main/java/io/helidon/json/binding/BindingFactoryDeserializer.java b/json/binding/src/main/java/io/helidon/json/binding/BindingFactoryDeserializer.java new file mode 100644 index 00000000000..80eb66ba2d6 --- /dev/null +++ b/json/binding/src/main/java/io/helidon/json/binding/BindingFactoryDeserializer.java @@ -0,0 +1,9 @@ +package io.helidon.json.binding; + +import java.lang.reflect.Type; + +public interface BindingFactoryDeserializer extends JsonDeserializer { + + void configure(JsonBindingConfigurer jsonBindingConfigurer, Type type); + +} diff --git a/json/binding/src/main/java/io/helidon/json/binding/BindingFactorySerializer.java b/json/binding/src/main/java/io/helidon/json/binding/BindingFactorySerializer.java new file mode 100644 index 00000000000..eaec6d29db3 --- /dev/null +++ b/json/binding/src/main/java/io/helidon/json/binding/BindingFactorySerializer.java @@ -0,0 +1,9 @@ +package io.helidon.json.binding; + +import java.lang.reflect.Type; + +public interface BindingFactorySerializer extends JsonSerializer { + + void configure(JsonBindingConfigurer jsonBindingConfigurer, Type type); + +} diff --git a/json/binding/src/main/java/io/helidon/json/binding/JsonBindingFactory.java b/json/binding/src/main/java/io/helidon/json/binding/JsonBindingFactory.java index 8cfcbaf9dba..bac5bba21a1 100644 --- a/json/binding/src/main/java/io/helidon/json/binding/JsonBindingFactory.java +++ b/json/binding/src/main/java/io/helidon/json/binding/JsonBindingFactory.java @@ -1,13 +1,8 @@ package io.helidon.json.binding; -import java.lang.reflect.Type; - public interface JsonBindingFactory { -// ConfigurableJsonDeserializer createDeserializer(JsonBinding jsonBinding, Type type); -// ConfigurableJsonSerializer createSerializer(JsonBinding jsonBinding, Type type); - - JsonDeserializer createDeserializer(JsonBindingConfigurer jsonBindingConfigurer, Type type); - JsonSerializer createSerializer(JsonBindingConfigurer jsonBindingConfigurer, Type type); + BindingFactoryDeserializer createDeserializer(); + BindingFactorySerializer createSerializer(); } \ No newline at end of file diff --git a/json/binding/src/main/java/io/helidon/json/binding/JsonBindingImpl.java b/json/binding/src/main/java/io/helidon/json/binding/JsonBindingImpl.java index 4625176ca84..f6a633f0fa3 100644 --- a/json/binding/src/main/java/io/helidon/json/binding/JsonBindingImpl.java +++ b/json/binding/src/main/java/io/helidon/json/binding/JsonBindingImpl.java @@ -1,12 +1,11 @@ package io.helidon.json.binding; import java.io.ByteArrayOutputStream; -import java.lang.reflect.Parameter; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import io.helidon.common.GenericType; import io.helidon.json.processor.Generator; @@ -17,13 +16,16 @@ final class JsonBindingImpl implements JsonBinding, JsonBindingConfigurer { static final JsonBinding DEFAULT_INSTANCE = JsonBinding.builder().build(); private final JsonBindingConfig config; - private final Map, JsonSerializer> identitySerializers = new HashMap<>(); - private final Map, JsonDeserializer> identityDeserializers = new HashMap<>(); - private final Map, JsonBindingFactory> bindingFactories = new HashMap<>(); + private final Map, JsonSerializer> identitySerializers = new IdentityHashMap<>(); + private final Map, JsonDeserializer> identityDeserializers = new IdentityHashMap<>(); + private final Map, JsonBindingFactory> bindingFactories = new IdentityHashMap<>(); private final Map> serializers = new HashMap<>(); private final Map> deserializers = new HashMap<>(); + private final Map> serializersNotConfigured = new HashMap<>(); private final Map> deserializersNotConfigured = new HashMap<>(); + private final ReentrantLock serNotConfiguredLock = new ReentrantLock(); + private final ReentrantLock desNotConfiguredLock = new ReentrantLock(); JsonBindingImpl(JsonBindingConfig config) { this.config = config; @@ -79,7 +81,7 @@ public String toJson(T obj, Class type) { } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try (Generator generator = Generator.create(outputStream)) { - JsonSerializer converter = getSerializer(type); + JsonSerializer converter = getFinishedSerializer(type); converter.toJson(generator, obj); } catch (Exception e) { throw new RuntimeException(e); @@ -94,7 +96,7 @@ public String toJson(T obj, GenericType type) { } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try (Generator generator = Generator.create(outputStream)) { - JsonSerializer converter = getSerializer(type); + JsonSerializer converter = getFinishedSerializer(type); converter.toJson(generator, obj); } catch (Exception e) { throw new RuntimeException(e); @@ -104,7 +106,7 @@ public String toJson(T obj, GenericType type) { @Override public T fromJson(String jsonStr, Class type) { - JsonDeserializer deserializer = getDeserializer(type); + JsonDeserializer deserializer = getFinishedDeserializer(type); JsonParser parser = JsonParser.createParser(jsonStr); parser.nextToken(); return deserializer.fromJson(parser); @@ -112,27 +114,14 @@ public T fromJson(String jsonStr, Class type) { @Override public T fromJson(String jsonStr, GenericType type) { - JsonDeserializer deserializer = getDeserializer(type); + JsonDeserializer deserializer = getFinishedDeserializer(type); JsonParser parser = JsonParser.createParser(jsonStr); parser.nextToken(); return deserializer.fromJson(parser); } - @Override - @SuppressWarnings("unchecked") - public JsonDeserializer getDeserializer(Type type) { - if (type instanceof Class clazz) { - return (JsonDeserializer) getDeserializer(clazz); - } else if (type instanceof GenericType genericType) { - return getDeserializer(genericType); - } else { - return getDeserializer(GenericType.create(type)); - } - } - - @Override @SuppressWarnings("unchecked") - public JsonDeserializer getDeserializer(Class type) { + private JsonDeserializer getFinishedDeserializer(Class type) { JsonDeserializer deserializer = (JsonDeserializer) identityDeserializers.get(type); if (deserializer != null) { return deserializer; @@ -142,15 +131,19 @@ public JsonDeserializer getDeserializer(Class type) { throw new IllegalStateException("Deserializer/Converter/BindingFactory for type " + type + " is not registered."); } - deserializer = factory.createDeserializer(this, type); - deserializers.putIfAbsent(type, deserializer); - identityDeserializers.putIfAbsent(type, deserializer); - return deserializer; + BindingFactoryDeserializer factoryDeserializer = factory.createDeserializer(); + + deserializersNotConfigured.putIfAbsent(type, factoryDeserializer); + factoryDeserializer.configure(this, type); + deserializersNotConfigured.remove(type); + + deserializers.putIfAbsent(type, factoryDeserializer); + identityDeserializers.putIfAbsent(type, factoryDeserializer); + return factoryDeserializer; } - @Override @SuppressWarnings("unchecked") - public JsonDeserializer getDeserializer(GenericType type) { + private JsonDeserializer getFinishedDeserializer(GenericType type) { JsonDeserializer deserializer = (JsonDeserializer) deserializers.get(type); if (deserializer == null) { JsonBindingFactory factory = (JsonBindingFactory) bindingFactories.get(type.rawType()); @@ -158,31 +151,24 @@ public JsonDeserializer getDeserializer(GenericType type) { throw new IllegalStateException("Deserializer/Converter/BindingFactory for type " + type + " is not registered."); } - deserializer = factory.createDeserializer(this, type.type()); - deserializers.putIfAbsent(type, deserializer); - deserializers.putIfAbsent(type.type(), deserializer); + BindingFactoryDeserializer factoryDeserializer = factory.createDeserializer(); + + deserializersNotConfigured.putIfAbsent(type, factoryDeserializer); + factoryDeserializer.configure(this, type.type()); + deserializersNotConfigured.remove(type); + + deserializers.putIfAbsent(type, factoryDeserializer); + deserializers.putIfAbsent(type.type(), factoryDeserializer); if (type.isClass()) { - identityDeserializers.putIfAbsent(type.rawType(), deserializer); + identityDeserializers.putIfAbsent(type.rawType(), factoryDeserializer); } + return factoryDeserializer; } return deserializer; } - @Override @SuppressWarnings("unchecked") - public JsonSerializer getSerializer(Type type) { - if (type instanceof Class clazz) { - return (JsonSerializer) getSerializer(clazz); - } else if (type instanceof GenericType genericType) { - return getSerializer(genericType); - } else { - return getSerializer(GenericType.create(type)); - } - } - - @Override - @SuppressWarnings("unchecked") - public JsonSerializer getSerializer(Class type) { + private JsonSerializer getFinishedSerializer(Class type) { JsonSerializer serializer = (JsonSerializer) identitySerializers.get(type); if (serializer == null) { JsonBindingFactory factory = (JsonBindingFactory) bindingFactories.get(type); @@ -190,16 +176,20 @@ public JsonSerializer getSerializer(Class type) { throw new IllegalStateException("Serializer/Converter/BindingFactory for type " + type + " is not registered."); } - serializer = factory.createSerializer(this, type); - serializers.putIfAbsent(type, serializer); - identitySerializers.putIfAbsent(type, serializer); + BindingFactorySerializer factorySerializer = factory.createSerializer(); + serializersNotConfigured.putIfAbsent(type, factorySerializer); + factorySerializer.configure(this, type); + serializersNotConfigured.remove(type); + + serializers.putIfAbsent(type, factorySerializer); + identitySerializers.putIfAbsent(type, factorySerializer); + return factorySerializer; } return serializer; } - @Override @SuppressWarnings("unchecked") - public JsonSerializer getSerializer(GenericType type) { + private JsonSerializer getFinishedSerializer(GenericType type) { JsonSerializer serializer = (JsonSerializer) serializers.get(type); if (serializer == null) { JsonBindingFactory factory = (JsonBindingFactory) bindingFactories.get(type.rawType()); @@ -207,13 +197,78 @@ public JsonSerializer getSerializer(GenericType type) { throw new IllegalStateException("Serializer/Converter/BindingFactory for type " + type + " is not registered."); } - serializer = factory.createSerializer(this, type.type()); - serializers.putIfAbsent(type, serializer); - serializers.putIfAbsent(type.type(), serializer); + BindingFactorySerializer factorySerializer = factory.createSerializer(); + serializersNotConfigured.putIfAbsent(type.type(), factorySerializer); + factorySerializer.configure(this, type.type()); + serializersNotConfigured.remove(type.type()); + + serializers.putIfAbsent(type, factorySerializer); + serializers.putIfAbsent(type.type(), factorySerializer); if (type.isClass()) { - identitySerializers.putIfAbsent(type.rawType(), serializer); + identitySerializers.putIfAbsent(type.rawType(), factorySerializer); } + return factorySerializer; } return serializer; } + + @Override + @SuppressWarnings("unchecked") + public JsonDeserializer getDeserializer(Type type) { + return switch (type) { + case Class clazz -> (JsonDeserializer) getDeserializer(clazz); + case GenericType genericType -> getDeserializer(genericType); + case null, default -> getDeserializer(GenericType.create(type)); + }; + } + + @Override + @SuppressWarnings("unchecked") + public JsonDeserializer getDeserializer(Class type) { + JsonDeserializer deserializer = (JsonDeserializer) deserializersNotConfigured.get(type); + if (deserializer != null) { + return deserializer; + } + return getFinishedDeserializer(type); + } + + @Override + @SuppressWarnings("unchecked") + public JsonDeserializer getDeserializer(GenericType type) { + JsonDeserializer deserializer = (JsonDeserializer) deserializersNotConfigured.get(type); + if (deserializer != null) { + return deserializer; + } + return getFinishedDeserializer(type); + } + + @Override + @SuppressWarnings("unchecked") + public JsonSerializer getSerializer(Type type) { + return switch (type) { + case Class clazz -> (JsonSerializer) getSerializer(clazz); + case GenericType genericType -> getSerializer(genericType); + case null, default -> getSerializer(GenericType.create(type)); + }; + } + + @Override + @SuppressWarnings("unchecked") + public JsonSerializer getSerializer(Class type) { + JsonSerializer serializer = (JsonSerializer) serializersNotConfigured.get(type); + if (serializer != null) { + return serializer; + } + return getFinishedSerializer(type); + } + + @Override + @SuppressWarnings("unchecked") + public JsonSerializer getSerializer(GenericType type) { + JsonSerializer serializer = (JsonSerializer) serializersNotConfigured.get(type.type()); + if (serializer != null) { + return serializer; + } + return getFinishedSerializer(type); + } } diff --git a/json/binding/src/main/java/io/helidon/json/binding/JsonSerializer.java b/json/binding/src/main/java/io/helidon/json/binding/JsonSerializer.java index 3a5a6e245cd..f1ebbdd48a2 100644 --- a/json/binding/src/main/java/io/helidon/json/binding/JsonSerializer.java +++ b/json/binding/src/main/java/io/helidon/json/binding/JsonSerializer.java @@ -1,6 +1,5 @@ package io.helidon.json.binding; -import io.helidon.common.GenericType; import io.helidon.json.processor.Generator; public interface JsonSerializer { diff --git a/json/binding/src/main/java/io/helidon/json/binding/factories/ListBindingFactory.java b/json/binding/src/main/java/io/helidon/json/binding/factories/ListBindingFactory.java index 6cc4dfb5658..9a171245037 100644 --- a/json/binding/src/main/java/io/helidon/json/binding/factories/ListBindingFactory.java +++ b/json/binding/src/main/java/io/helidon/json/binding/factories/ListBindingFactory.java @@ -8,9 +8,10 @@ import io.helidon.common.GenericType; import io.helidon.common.Weight; import io.helidon.common.Weighted; -import io.helidon.json.binding.JsonBinding; +import io.helidon.json.binding.BindingFactoryConverter; +import io.helidon.json.binding.BindingFactoryDeserializer; +import io.helidon.json.binding.BindingFactorySerializer; import io.helidon.json.binding.JsonBindingConfigurer; -import io.helidon.json.binding.JsonConverter; import io.helidon.json.binding.JsonDeserializer; import io.helidon.json.binding.JsonSerializer; import io.helidon.json.binding.TypedJsonBindingFactory; @@ -24,13 +25,13 @@ final class ListBindingFactory implements TypedJsonBindingFactory> { @Override - public JsonDeserializer> createDeserializer(JsonBindingConfigurer jsonBindingConfigurer, Type type) { - return new ListConverter<>(jsonBindingConfigurer, type); + public BindingFactoryDeserializer> createDeserializer() { + return new ListConverter<>(); } @Override - public JsonSerializer> createSerializer(JsonBindingConfigurer jsonBindingConfigurer, Type type) { - return new ListConverter<>(jsonBindingConfigurer, type); + public BindingFactorySerializer> createSerializer() { + return new ListConverter<>(); } @Override @@ -38,21 +39,10 @@ public Class type() { return List.class; } - private static final class ListConverter implements JsonConverter> { + private static final class ListConverter implements BindingFactoryConverter> { - private final JsonDeserializer deserializer; - private final JsonSerializer serializer; - - @SuppressWarnings("unchecked") - private ListConverter(JsonBindingConfigurer jsonBindingConfigurer, Type type) { - if (type instanceof ParameterizedType parameterizedType) { - deserializer = jsonBindingConfigurer.getDeserializer(parameterizedType.getActualTypeArguments()[0]); - serializer = jsonBindingConfigurer.getSerializer(parameterizedType.getActualTypeArguments()[0]); - } else { - deserializer = (JsonDeserializer) jsonBindingConfigurer.getDeserializer(GenericType.OBJECT); - serializer = (JsonSerializer) jsonBindingConfigurer.getSerializer(GenericType.OBJECT); - } - } + private JsonDeserializer deserializer; + private JsonSerializer serializer; @Override public void toJson(Generator generator, List instance) { @@ -100,6 +90,15 @@ public List fromJson(JsonParser parser) { return list; } + @Override + public void configure(JsonBindingConfigurer jsonBindingConfigurer, Type type) { + if (type instanceof ParameterizedType parameterizedType) { + deserializer = jsonBindingConfigurer.getDeserializer(parameterizedType.getActualTypeArguments()[0]); + serializer = jsonBindingConfigurer.getSerializer(parameterizedType.getActualTypeArguments()[0]); + } else { + deserializer = jsonBindingConfigurer.getDeserializer(GenericType.OBJECT); + serializer = jsonBindingConfigurer.getSerializer(GenericType.OBJECT); + } + } } - } diff --git a/json/binding/src/main/java/io/helidon/json/binding/factories/SetBindingFactory.java b/json/binding/src/main/java/io/helidon/json/binding/factories/SetBindingFactory.java index 7b069a5387e..e92eecbbafb 100644 --- a/json/binding/src/main/java/io/helidon/json/binding/factories/SetBindingFactory.java +++ b/json/binding/src/main/java/io/helidon/json/binding/factories/SetBindingFactory.java @@ -8,13 +8,13 @@ import io.helidon.common.GenericType; import io.helidon.common.Weight; import io.helidon.common.Weighted; -import io.helidon.json.binding.JsonBinding; +import io.helidon.json.binding.BindingFactoryConverter; +import io.helidon.json.binding.BindingFactoryDeserializer; +import io.helidon.json.binding.BindingFactorySerializer; import io.helidon.json.binding.JsonBindingConfigurer; -import io.helidon.json.binding.JsonConverter; import io.helidon.json.binding.JsonDeserializer; import io.helidon.json.binding.JsonSerializer; import io.helidon.json.binding.TypedJsonBindingFactory; -import io.helidon.json.binding.TypedJsonConverter; import io.helidon.json.processor.Generator; import io.helidon.json.processor.JsonException; import io.helidon.json.processor.JsonParser; @@ -25,13 +25,13 @@ final class SetBindingFactory implements TypedJsonBindingFactory> { @Override - public JsonDeserializer> createDeserializer(JsonBindingConfigurer jsonBindingConfigurer, Type type) { - return new SetConverter<>(jsonBindingConfigurer, type); + public BindingFactoryDeserializer> createDeserializer() { + return new SetConverter<>(); } @Override - public JsonSerializer> createSerializer(JsonBindingConfigurer jsonBindingConfigurer, Type type) { - return new SetConverter<>(jsonBindingConfigurer, type); + public BindingFactorySerializer> createSerializer() { + return new SetConverter<>(); } @Override @@ -39,21 +39,10 @@ public Class type() { return Set.class; } - private static final class SetConverter implements JsonConverter> { + private static final class SetConverter implements BindingFactoryConverter> { - private final JsonDeserializer deserializer; - private final JsonSerializer serializer; - - @SuppressWarnings("unchecked") - private SetConverter(JsonBindingConfigurer jsonBindingConfigurer, Type type) { - if (type instanceof ParameterizedType parameterizedType) { - deserializer = jsonBindingConfigurer.getDeserializer(parameterizedType.getActualTypeArguments()[0]); - serializer = jsonBindingConfigurer.getSerializer(parameterizedType.getActualTypeArguments()[0]); - } else { - deserializer = (JsonDeserializer) jsonBindingConfigurer.getDeserializer(GenericType.OBJECT); - serializer = (JsonSerializer) jsonBindingConfigurer.getSerializer(GenericType.OBJECT); - } - } + private JsonDeserializer deserializer; + private JsonSerializer serializer; @Override public void toJson(Generator generator, Set instance) { @@ -101,6 +90,16 @@ public Set fromJson(JsonParser parser) { return set; } + @Override + public void configure(JsonBindingConfigurer jsonBindingConfigurer, Type type) { + if (type instanceof ParameterizedType parameterizedType) { + deserializer = jsonBindingConfigurer.getDeserializer(parameterizedType.getActualTypeArguments()[0]); + serializer = jsonBindingConfigurer.getSerializer(parameterizedType.getActualTypeArguments()[0]); + } else { + deserializer = jsonBindingConfigurer.getDeserializer(GenericType.OBJECT); + serializer = jsonBindingConfigurer.getSerializer(GenericType.OBJECT); + } + } } } diff --git a/json/codegen/src/main/java/io/helidon/json/codegen/JsonBindingFactoryGenerator.java b/json/codegen/src/main/java/io/helidon/json/codegen/JsonBindingFactoryGenerator.java index 631b2f015df..d7152734714 100644 --- a/json/codegen/src/main/java/io/helidon/json/codegen/JsonBindingFactoryGenerator.java +++ b/json/codegen/src/main/java/io/helidon/json/codegen/JsonBindingFactoryGenerator.java @@ -51,28 +51,24 @@ private static void addCreateDeserializerMethod(Method.Builder method, Converted method.name("createDeserializer") .addAnnotation(Annotation.create(Override.class)) .returnType(builder -> builder.type(TypeName.builder() - .from(Types.JSON_DESERIALIZER_TYPE) + .from(Types.JSON_FACTORY_DESERIALIZER_TYPE) .addTypeArgument(convertedTypeInfo.originalType()) .build())) - .addParameter(builder -> builder.type(Types.JSON_BINDING_CONFIGURER).name(CONFIGURE_PARAM)) - .addParameter(builder -> builder.type(Type.class).name("type")) .addContent("return new ") .addContent(convertedTypeInfo.converterType()) - .addContentLine("(" + CONFIGURE_PARAM + ", type);"); + .addContentLine("<>();"); } private static void addCreateSerializerMethod(Method.Builder method, ConvertedTypeInfo convertedTypeInfo) { method.name("createSerializer") .addAnnotation(Annotation.create(Override.class)) .returnType(builder -> builder.type(TypeName.builder() - .from(Types.JSON_SERIALIZER_TYPE) + .from(Types.JSON_FACTORY_SERIALIZER_TYPE) .addTypeArgument(convertedTypeInfo.originalType()) .build())) - .addParameter(builder -> builder.type(Types.JSON_BINDING_CONFIGURER).name(CONFIGURE_PARAM)) - .addParameter(builder -> builder.type(Type.class).name("type")) .addContent("return new ") .addContent(convertedTypeInfo.converterType()) - .addContentLine("(" + CONFIGURE_PARAM + ", type);"); + .addContentLine("<>();"); } private static void addTypeMethod(Method.Builder method, ConvertedTypeInfo convertedTypeInfo) { diff --git a/json/codegen/src/main/java/io/helidon/json/codegen/JsonConverterGenerator.java b/json/codegen/src/main/java/io/helidon/json/codegen/JsonConverterGenerator.java index d3901b76a58..eb2ed06bf74 100644 --- a/json/codegen/src/main/java/io/helidon/json/codegen/JsonConverterGenerator.java +++ b/json/codegen/src/main/java/io/helidon/json/codegen/JsonConverterGenerator.java @@ -50,10 +50,10 @@ private JsonConverterGenerator() { static void generateConverter(ClassBase.Builder classBuilder, ConvertedTypeInfo converterInfo, TypeInfo annotatedType, - boolean constructorConfiguration, + boolean factoryConfiguration, boolean typedConverter) { TypeName converterInterfaceType = TypeName.builder() - .from(typedConverter ? Types.TYPED_JSON_CONVERTER_TYPE : Types.JSON_CONVERTER_TYPE) + .from(typedConverter ? Types.TYPED_JSON_CONVERTER_TYPE : Types.JSON_FACTORY_CONVERTER_TYPE) .addTypeArgument(annotatedType.typeName()) .build(); @@ -63,16 +63,16 @@ static void generateConverter(ClassBase.Builder classBuilder, .addMethod(method -> generateToJsonMethod(classBuilder, method, converterInfo, - constructorConfiguration, + factoryConfiguration, toConfigure)) .addMethod(method -> generateFromJsonMethod(classBuilder, method, converterInfo, - constructorConfiguration, + factoryConfiguration, toConfigure)); - if (constructorConfiguration) { - classBuilder.addConstructor(method -> addConfigurationConstructor(method, toConfigure)); + if (factoryConfiguration) { + classBuilder.addMethod(method -> addConfigurationFactory(method, toConfigure)); } else { classBuilder.addInterface(Types.JSON_CONFIGURABLE) .addMethod(method -> addConfigurationMethod(method, toConfigure)); @@ -90,92 +90,93 @@ private static void addConfigurationMethod(Method.Builder method, Map toConfigure) { - constructor.accessModifier(AccessModifier.PRIVATE) + private static void addConfigurationFactory(Method.Builder method, Map toConfigure) { + method.name("configure") + .addAnnotation(Annotation.create(Override.class)) .addParameter(builder -> builder.type(Types.JSON_BINDING_CONFIGURER).name(CONFIGURE_PARAM)) .addParameter(builder -> builder.type(Type.class).name("type")); - initializeNoRuntimeResolving(constructor, toConfigure); + initializeNoRuntimeResolving(method, toConfigure); List needsRuntimeResolving = toConfigure.values() .stream() .filter(it -> needsResolving(it.resolved)) .toList(); - constructor.addContent("if (type instanceof ") + method.addContent("if (type instanceof ") .addContent(ParameterizedType.class) .addContentLine(" parameterizedType) {"); - Map> createdTypeSetters = new HashMap<>(); + Map> createdTypeSetters = new HashMap<>(); MethodNameCounter counter = new MethodNameCounter(); for (TypeToConfigure typeToConfigure : needsRuntimeResolving) { String fieldName = typeToConfigure.fieldName(); TypeName typeName = typeToConfigure.resolved; String obtainMethod = typeToConfigure.mode.method; if (typeName.typeArguments().isEmpty()) { - constructor.addContent(fieldName + " = " + CONFIGURE_PARAM + "." + obtainMethod + "(") + method.addContent(fieldName + " = " + CONFIGURE_PARAM + "." + obtainMethod + "(") .addContentLine("parameterizedType.getActualTypeArguments()[0]);"); } else { - Consumer builderConsumer; + Consumer builderConsumer; if (createdTypeSetters.containsKey(typeName.resolvedName())) { builderConsumer = createdTypeSetters.get(typeName.resolvedName()); } else { - builderConsumer = constructComplexGenericType(constructor, typeName, createdTypeSetters, counter); + builderConsumer = constructComplexGenericType(method, typeName, createdTypeSetters, counter); createdTypeSetters.put(typeName.resolvedName(), builderConsumer); } - constructor.addContent(fieldName + " = " + CONFIGURE_PARAM + "." + obtainMethod + "("); - builderConsumer.accept(constructor); - constructor.addContentLine(");"); + method.addContent(fieldName + " = " + CONFIGURE_PARAM + "." + obtainMethod + "("); + builderConsumer.accept(method); + method.addContentLine(");"); } } - constructor.addContent("}").addContentLine(" else {"); + method.addContent("}").addContentLine(" else {"); for (TypeToConfigure typeToConfigure : needsRuntimeResolving) { String fieldName = typeToConfigure.fieldName(); TypeName typeName = typeToConfigure.resolved; String obtainMethod = typeToConfigure.mode.method; if (typeName.typeArguments().isEmpty()) { - constructor.addContent(fieldName + " = (") + method.addContent(fieldName + " = (") .addContent(typeToConfigure.fieldType) .addContent(") " + CONFIGURE_PARAM + "." + obtainMethod + "(") .addContent(Object.class) .addContentLine(".class);"); } else { - constructor.addContent(fieldName + " = " + CONFIGURE_PARAM + "." + obtainMethod + "(") + method.addContent(fieldName + " = " + CONFIGURE_PARAM + "." + obtainMethod + "(") .addContent("new ").addContent(TypeNames.GENERIC_TYPE).addContent("<"); - buildSimpleGenericTypeWithObject(constructor, typeName); - constructor.addContentLine(">() {});"); + buildSimpleGenericTypeWithObject(method, typeName); + method.addContentLine(">() {});"); } } - constructor.addContentLine("}"); + method.addContentLine("}"); } - private static void buildSimpleGenericTypeWithObject(Constructor.Builder constructor, TypeName typeName) { + private static void buildSimpleGenericTypeWithObject(Method.Builder method, TypeName typeName) { if (typeName.typeArguments().isEmpty()) { //We have no more generics available if (needsResolving(typeName)) { - constructor.addContent(Object.class); + method.addContent(Object.class); } else { - constructor.addContent(typeName); + method.addContent(typeName); } } else { boolean first = true; - constructor.addContent(typeName.genericTypeName()).addContent("<"); + method.addContent(typeName.genericTypeName()).addContent("<"); for (TypeName typeArgument : typeName.typeArguments()) { if (first) { first = false; } else { - constructor.addContent(","); + method.addContent(","); } - buildSimpleGenericTypeWithObject(constructor, typeArgument); + buildSimpleGenericTypeWithObject(method, typeArgument); } - constructor.addContent(">"); + method.addContent(">"); } } - private static Consumer constructComplexGenericType(Constructor.Builder constructor, - TypeName typeName, - Map> createdTypeSetters, - MethodNameCounter counter) { + private static Consumer constructComplexGenericType(Method.Builder method, + TypeName typeName, + Map> createdTypeSetters, + MethodNameCounter counter) { if (typeName.typeArguments().isEmpty()) { if (needsResolving(typeName)) { return builder -> builder.addContent(TypeNames.GENERIC_TYPE) @@ -185,34 +186,32 @@ private static Consumer constructComplexGenericType(Constru .addContent(".create(").addContent(typeName).addContent(")"); } } else { - List> parameterValueSetters = new ArrayList<>(); + List> parameterValueSetters = new ArrayList<>(); for (TypeName typeArgument : typeName.typeArguments()) { if (createdTypeSetters.containsKey(typeArgument.resolvedName())) { parameterValueSetters.add(createdTypeSetters.get(typeArgument.resolvedName())); } else { - Consumer parameterValue = constructComplexGenericType(constructor, - typeArgument, - createdTypeSetters, - counter); + Consumer parameterValue = constructComplexGenericType(method, + typeArgument, + createdTypeSetters, + counter); parameterValueSetters.add(parameterValue); createdTypeSetters.putIfAbsent(typeArgument.resolvedName(), parameterValue); } } String variableName = "genericType" + counter.count++; - constructor.addContent("var " + variableName + " = ") + method.addContent("var " + variableName + " = ") .addContent(TypeNames.GENERIC_TYPE) - //TODO UPRAVIT ??? -// .addContent(".<").addContent(typeName).addContentLine(">builder()") .addContent(".<").addContent(TypeNames.OBJECT).addContentLine(">builder()") .increaseContentPadding() .increaseContentPadding() .addContent(".baseType(").addContent(typeName.genericTypeName()).addContentLine(".class)"); - for (Consumer parameterValue : parameterValueSetters) { - constructor.addContent(".addGenericParameter("); - parameterValue.accept(constructor); - constructor.addContentLine(")"); + for (Consumer parameterValue : parameterValueSetters) { + method.addContent(".addGenericParameter("); + parameterValue.accept(method); + method.addContentLine(")"); } - constructor.addContentLine(".build();") + method.addContentLine(".build();") .decreaseContentPadding() .decreaseContentPadding(); @@ -284,7 +283,6 @@ private static void generateToJsonMethod(ClassBase.Builder classBuilder, .addTypeArgument(resolved) .build(); classBuilder.addField(fieldBuilder -> fieldBuilder.name(fieldName) - .isFinal(useConstructorToConfigure) .type(converterType)); toConfigure.putIfAbsent(fieldName, new TypeToConfigure(TypeConfigMode.SERIALIZATION, @@ -469,7 +467,6 @@ private static void createTypeDeserializer(JsonProperty jsonProperty, String fieldName = "deserializer" + ensureUpperStart(jsonProperty.deserializationName().orElseThrow()); TypeName fieldType = TypeName.builder(Types.JSON_DESERIALIZER_TYPE).addTypeArgument(resolvedType).build(); classBuilder.addField(builder -> builder.name(fieldName) - .isFinal(useConstructorToConfigure) .type(fieldType)); toConfigure.putIfAbsent(fieldName, new TypeToConfigure(TypeConfigMode.DESERIALIZATION, fieldName, @@ -484,7 +481,6 @@ private static void createTypeDeserializer(JsonProperty jsonProperty, processedTypes.add(type); //To ensure deserializer reusability TypeName fieldType = TypeName.builder(Types.JSON_DESERIALIZER_TYPE).addTypeArgument(resolvedType).build(); classBuilder.addField(builder -> builder.name(converterFieldName) - .isFinal(useConstructorToConfigure) .type(fieldType)); toConfigure.putIfAbsent(converterFieldName, new TypeToConfigure(TypeConfigMode.DESERIALIZATION, converterFieldName, diff --git a/json/codegen/src/main/java/io/helidon/json/codegen/Types.java b/json/codegen/src/main/java/io/helidon/json/codegen/Types.java index c54c389d2bb..7b2486eb5c2 100644 --- a/json/codegen/src/main/java/io/helidon/json/codegen/Types.java +++ b/json/codegen/src/main/java/io/helidon/json/codegen/Types.java @@ -37,8 +37,11 @@ final class Types { //Types static final TypeName JSON_DESERIALIZER_TYPE = TypeName.create("io.helidon.json.binding.JsonDeserializer"); + static final TypeName JSON_FACTORY_DESERIALIZER_TYPE = TypeName.create("io.helidon.json.binding.BindingFactoryDeserializer"); static final TypeName JSON_SERIALIZER_TYPE = TypeName.create("io.helidon.json.binding.JsonSerializer"); + static final TypeName JSON_FACTORY_SERIALIZER_TYPE = TypeName.create("io.helidon.json.binding.BindingFactorySerializer"); static final TypeName JSON_CONVERTER_TYPE = TypeName.create("io.helidon.json.binding.JsonConverter"); + static final TypeName JSON_FACTORY_CONVERTER_TYPE = TypeName.create("io.helidon.json.binding.BindingFactoryConverter"); static final TypeName TYPED_JSON_DESERIALIZER_TYPE = TypeName.create("io.helidon.json.binding.TypedJsonDeserializer"); static final TypeName TYPED_JSON_SERIALIZER_TYPE = TypeName.create("io.helidon.json.binding.TypedJsonSerializer"); static final TypeName TYPED_JSON_CONVERTER_TYPE = TypeName.create("io.helidon.json.binding.TypedJsonConverter"); diff --git a/json/tests/src/main/java/io/helidon/json/tests/ClassWithGenerics.java b/json/tests/src/main/java/io/helidon/json/tests/ClassWithGenerics.java index 9854837ac95..8bbbb338a4b 100644 --- a/json/tests/src/main/java/io/helidon/json/tests/ClassWithGenerics.java +++ b/json/tests/src/main/java/io/helidon/json/tests/ClassWithGenerics.java @@ -11,7 +11,7 @@ public class ClassWithGenerics { private T field; private List collection = new ArrayList<>(); private List> collection2 = new ArrayList<>(); -// public ClassWithGenerics test; + public ClassWithGenerics test; private List collection23 = new ArrayList<>(); //widlcard //upper/lower bounds