diff --git a/api/src/main/java/jakarta/json/bind/JsonbConfig.java b/api/src/main/java/jakarta/json/bind/JsonbConfig.java index 4ed66427..2c2a6e90 100644 --- a/api/src/main/java/jakarta/json/bind/JsonbConfig.java +++ b/api/src/main/java/jakarta/json/bind/JsonbConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -21,6 +21,7 @@ import jakarta.json.bind.config.PropertyNamingStrategy; import jakarta.json.bind.config.PropertyVisibilityStrategy; +import jakarta.json.bind.customization.TypeCustomization; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.bind.serializer.JsonbSerializer; @@ -126,6 +127,11 @@ public class JsonbConfig { */ public static final String DESERIALIZERS = "jsonb.derializers"; + /** + * Property used to specify runtime type customizations. + */ + public static final String TYPE_CUSTOMIZATIONS = "jsonb.type-customizations"; + /** * Property used to specify custom binary data strategy. */ @@ -360,6 +366,21 @@ public final JsonbConfig withDeserializers(final JsonbDeserializer... deserializ return this; } + /** + * Property used to specify custom runtime type customizations. + * + * Configures value of {@link #TYPE_CUSTOMIZATIONS} property. + * + * Calling withTypeCustomizations more than once will merge the type customizations with previous value. + * + * @param typeCustomizations Type customizations which affects deserialization and serialization of the customized types. + * @return This JsonbConfig instance. + */ + public final JsonbConfig withTypeCustomizations(final TypeCustomization... typeCustomizations) { + mergeProperties(TYPE_CUSTOMIZATIONS, typeCustomizations, TypeCustomization.class); + return this; + } + /** * Property used to specify custom binary data strategy. * diff --git a/api/src/main/java/jakarta/json/bind/customization/CreatorCustomization.java b/api/src/main/java/jakarta/json/bind/customization/CreatorCustomization.java new file mode 100644 index 00000000..68976e0b --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/CreatorCustomization.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.util.List; +import java.util.Optional; + +import jakarta.json.bind.spi.JsonbProvider; + +/** + * Decorated creator customization. + */ +public interface CreatorCustomization { + + /** + * Create new {@link CreatorCustomizationBuilder} instance. + * + * Builder created via this method targets constructors of the class it is bound to. + * + * @return new creator customization builder instance + */ + static CreatorCustomizationBuilder builder() { + return JsonbProvider.provider().newCreatorCustomizationBuilder(); + } + + /** + * Create new {@link CreatorCustomizationBuilder} instance based on creator method name. + * + * @return new creator customization builder instance + */ + static CreatorCustomizationBuilder builder(String methodName) { + return JsonbProvider.provider().newCreatorCustomizationBuilder(methodName); + } + + /** + * Return creator method name if has been specified. + * If the name is empty, it is handled as if it is null. + * + * @return specified creator method name, otherwise empty + */ + Optional getFactoryMethodName(); + + /** + * Return immutable {@link List} of the registered parameters. The order of the parameters needs to be the same as they were added. + * If no parameters were added, empty {@link List} is returned. + * + * @return creator parameters + */ + List getParams(); + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/CreatorCustomizationBuilder.java b/api/src/main/java/jakarta/json/bind/customization/CreatorCustomizationBuilder.java new file mode 100644 index 00000000..0e5678ac --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/CreatorCustomizationBuilder.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.util.function.Consumer; + +/** + * Builder of the {@link CreatorCustomization} instance. + * + * When no creator method name is specified, it is assumed that creator is constructor. + */ +public interface CreatorCustomizationBuilder { + + /** + * Add {@link ParamCustomization} instance. + * + * All creator parameters are required to be added in exact order as they are on decorated factory method/constructor. + * + * @param creatorParam creator parameter + * @return updated builder instance + */ + CreatorCustomizationBuilder addParam(ParamCustomization creatorParam); + + /** + * Add new {@link ParamCustomization} of the property. + *
+ * Shortcut method to the {@link #addParam(ParamCustomization)}. It is not required to create {@link CreatorCustomizationBuilder} + * since this method will create {@link ParamCustomization} based on the provided parameter class and json name and by calling + * {@link ParamCustomization#create(Class, String)} method. No further customizations will be applied. + *
+ * All creator parameters are required to be added in exact order as they are on decorated factory method/constructor. + * + * @param parameterClass class of the parameter + * @param jsonName json name of the parameter + * @return updated builder instance + */ + default CreatorCustomizationBuilder addParam(Class parameterClass, String jsonName) { + return addParam(ParamCustomization.create(parameterClass, jsonName)); + } + + /** + * Add new {@link PropertyCustomization} of the property. + *
+ * Shortcut method to the {@link #addParam(ParamCustomization)}. It is not required to create {@link CreatorCustomizationBuilder} + * since this method will create {@link CreatorCustomizationBuilder} based on the provided parameter class and json name. + * Created builder is provided over the paramBuilder. + *
+ * All creator parameters are required to be added in exact order as they are on decorated factory method/constructor. + *
+ * Example usage: + *
{@code
+     * creatorBuilder.addParameter(String.class, "jsonName", paramBuilder -> paramBuilder.nillable(true));
+     * }
+ * + * @param parameterClass class of the parameter + * @param jsonName json name of the parameter + * @param paramBuilder builder used to customize parameter + * @return updated builder instance + */ + default CreatorCustomizationBuilder addParam(Class parameterClass, + String jsonName, + Consumer paramBuilder) { + ParamCustomizationBuilder builder = ParamCustomization.builder(parameterClass, jsonName); + paramBuilder.accept(builder); + return addParam(builder.build()); + } + + /** + * Build the new instance of the {@link CreatorCustomization}. + * + * @return new {@link CreatorCustomization} instance + */ + CreatorCustomization build(); + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/JsonbCustomization.java b/api/src/main/java/jakarta/json/bind/customization/JsonbCustomization.java new file mode 100644 index 00000000..00ed7bdd --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/JsonbCustomization.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.util.Optional; + +import jakarta.json.bind.adapter.JsonbAdapter; +import jakarta.json.bind.serializer.JsonbDeserializer; + +/** + * Common interface for all the customizations. + */ +public interface JsonbCustomization { + + /** + * Return {@link JsonbDeserializer} of the component. + * + * @return component deserializer instance, otherwise empty + */ + Optional> getDeserializer(); + + /** + * Return {@link JsonbAdapter} of the component. + * + * @return component adapter instance, otherwise empty + */ + Optional> getAdapter(); + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/JsonbCustomizationBuilder.java b/api/src/main/java/jakarta/json/bind/customization/JsonbCustomizationBuilder.java new file mode 100644 index 00000000..d64c03be --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/JsonbCustomizationBuilder.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.Locale; + +import jakarta.json.bind.adapter.JsonbAdapter; +import jakarta.json.bind.serializer.JsonbDeserializer; + +/** + * Common interface for all customization builders. + */ +public interface JsonbCustomizationBuilder, B extends JsonbCustomization> { + + /** + * Whether this component can be nillable. + * + * @param nillable nillable component + * @return updated builder instance + */ + T nillable(boolean nillable); + + /** + * Ignore nillable customization of this component. + * + * @return updated builder instance + */ + T ignoreNillable(); + + /** + * Set {@link JsonbDeserializer} which should be used. + * + * @param deserializer component deserializer + * @return updated builder instance + */ + T deserializer(JsonbDeserializer deserializer); + + /** + * Ignore deserializer of this component. + * + * @return updated builder instance + */ + T ignoreDeserializer(); + + /** + * Set {@link JsonbAdapter} which should be used. + * + * @param adapter component adapter + * @return updated builder instance + */ + T adapter(JsonbAdapter adapter); + + /** + * Ignore adapter of this component. + * + * @return updated builder instance + */ + T ignoreAdapter(); + + /** + * Set number format and locale which should be used. + * + * @param numberFormat number format + * @param locale locale + * @return updated builder instance + */ + T numberFormat(String numberFormat, Locale locale); + + /** + * Set number format which should be used. + * + * @param numberFormat number format + * @return updated builder instance + */ + T numberFormat(String numberFormat); + + /** + * Set locale which should be used. + * + * @param locale locale + * @return updated builder instance + */ + T numberFormat(Locale locale); + + /** + * Set {@link NumberFormat} instance which should be used. + * + * @param numberFormat pre created NumberFormat instance + * @return updated builder instance + */ + T numberFormat(NumberFormat numberFormat); + + /** + * Ignore number format customization of this component. + * + * @return updated builder instance + */ + T ignoreNumberFormat(); + + /** + * Set date format and locale which should be used. + * + * @param dateFormat date format + * @param locale locale + * @return updated builder instance + */ + T dateFormat(String dateFormat, Locale locale); + + /** + * Set date format which should be used. + * + * @param dateFormat date format + * @return updated builder instance + */ + T dateFormat(String dateFormat); + + /** + * Set locale which should be used. + * + * @param locale locale + * @return updated builder instance + */ + T dateFormat(Locale locale); + + /** + * Set {@link DateFormat} instance which should be used. + * + * @param dateFormat pre created DateFormat instance + * @return updated builder instance + */ + T dateFormat(DateFormat dateFormat); + + /** + * Ignore date format customization of this component. + * + * @return updated builder instance + */ + T ignoreDateFormat(); + + /** + * Ignore all the customizations. + * + * @return updated builder instance + */ + T ignoreAllCustomizations(); + + /** + * Build the new instance from this builder. + * + * @return new instance + */ + B build(); + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/ParamCustomization.java b/api/src/main/java/jakarta/json/bind/customization/ParamCustomization.java new file mode 100644 index 00000000..770ef246 --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/ParamCustomization.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.util.Objects; + +import jakarta.json.bind.spi.JsonbProvider; + +/** + * Decorated parameter customization. + */ +public interface ParamCustomization extends JsonbCustomization, ScopelessCustomization { + + /** + * Create new {@link ParamCustomizationBuilder} instance base on parameter class and its json name. + * Both class and json name are required to be non-null or empty. + * + * @param paramClass parameter class + * @param jsonName parameter json name + * @return new parameter customization builder instance + */ + static ParamCustomizationBuilder builder(Class paramClass, String jsonName) { + Objects.requireNonNull(paramClass, "Parameter class cannot be null"); + if (jsonName == null || jsonName.isBlank()) { + throw new IllegalStateException("Json name cannot be null or empty"); + } + return JsonbProvider.provider().newParamCustomizationBuilder(paramClass, jsonName); + } + + /** + * Create new instance of {@link ParamCustomization} base on parameter class and its json name. + * Both parameter class and json name are required to be non-null or empty. + * + * @param paramClass parameter class + * @param jsonName parameter json name + * @return new parameter customization instance + */ + static ParamCustomization create(Class paramClass, String jsonName) { + Objects.requireNonNull(paramClass, "Parameter class cannot be null"); + if (jsonName == null || jsonName.isBlank()) { + throw new IllegalStateException("Json name cannot be null or empty"); + } + return builder(paramClass, jsonName).build(); + } + + /** + * Return parameter json name. + * + * @return parameter json name + */ + String getJsonName(); + + /** + * Return parameter class. + * + * @return parameter class + */ + Class getParameterClass(); + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/ParamCustomizationBuilder.java b/api/src/main/java/jakarta/json/bind/customization/ParamCustomizationBuilder.java new file mode 100644 index 00000000..4b529523 --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/ParamCustomizationBuilder.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +/** + * Builder of the {@link ParamCustomization} instance. + */ +public interface ParamCustomizationBuilder extends JsonbCustomizationBuilder { + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/PropertyCustomization.java b/api/src/main/java/jakarta/json/bind/customization/PropertyCustomization.java new file mode 100644 index 00000000..8c493958 --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/PropertyCustomization.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.util.Arrays; +import java.util.Optional; + +import jakarta.json.bind.spi.JsonbProvider; + +/** + * Decorated property customization. + */ +public interface PropertyCustomization extends ScopedCustomization { + + /** + * Create new {@link PropertyCustomizationBuilder} instance base on property name. + * Property name is required to be non-null or empty. + * + * @param propertyName name of the decorated property + * @return new property customization builder instance + */ + static PropertyCustomizationBuilder builder(String propertyName) { + if (propertyName == null || propertyName.isBlank()) { + throw new IllegalStateException("Property name cannot be null or empty"); + } + return JsonbProvider.provider().newPropertyCustomizationBuilder(propertyName); + } + + /** + * Return the original property name. + * + * @return property name + */ + String getPropertyName(); + + /** + * Return if the property is transient in the given {@link Scope}. + * + * @param scope required scope + * @return property transient state, otherwise empty + */ + Optional getTransientProperty(Scope scope); + + /** + * Return custom property name in the given {@link Scope}. + * + * @param scope required scope + * @return property custom name, otherwise empty + */ + Optional getName(Scope scope); + + default PropertyCustomizationBuilder toBuilder() { + PropertyCustomizationBuilder builder = builder(getPropertyName()); + getAdapter().ifPresent(builder::adapter); + getDeserializer().ifPresent(builder::deserializer); + getSerializer().ifPresent(builder::serializer); + Arrays.stream(Scope.values()) + .forEach(scope -> { + getTransientProperty(scope).ifPresent(value -> builder.transientProperty(value, scope)); + getDateFormat(scope).ifPresent(value -> builder.dateFormat(value, scope)); + getName(scope).ifPresent(value -> builder.name(value, scope)); + getNillable(scope).ifPresent(value -> builder.nillable(value, scope)); + getNumberFormat(scope).ifPresent(value -> builder.numberFormat(value, scope)); + }); + return builder; + } + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/PropertyCustomizationBuilder.java b/api/src/main/java/jakarta/json/bind/customization/PropertyCustomizationBuilder.java new file mode 100644 index 00000000..3e7c6c57 --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/PropertyCustomizationBuilder.java @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.Locale; + +import jakarta.json.bind.annotation.JsonbDateFormat; + +/** + * Builder of the specific class property customization. + *
+ * Customization of the property accessor methods is done over the methods with the {@link Scope} + * parameter. Methods without any specific {@link Scope} parameter are setting required customization + * to the deserialization and serialization. + *

+ * For example having getter method with custom name like this + *

{@code
+ * @JsonbProperty("customName")
+ * public String getExample() { .... }
+ * }
+ * is effectively the same as having it set like this using the builder + *
{@code
+ * builder.name("customName", Scope.DESERIALIZATION);
+ * }
+ *

+ */ +public interface PropertyCustomizationBuilder + extends SerializationCustomizationBuilder { + + /** + * Set custom property name for serialization and deserialization. + * + * @param name custom property name + * @return updated builder instance + */ + default PropertyCustomizationBuilder name(String name) { + return name(name, Scope.SERIALIZATION).name(name, Scope.DESERIALIZATION); + } + + /** + * Set custom property name bound to the selected {@link Scope}. + * + * @param name custom property name + * @param scope scope to which this name is bound + * @return updated builder instance + */ + PropertyCustomizationBuilder name(String name, Scope scope); + + /** + * Ignore custom property name. + * + * Custom property name will be ignored for serialization and deserialization. + * + * @return updated builder instance + */ + default PropertyCustomizationBuilder ignoreName() { + return ignoreName(Scope.SERIALIZATION).ignoreName(Scope.DESERIALIZATION); + } + + /** + * Ignore custom property name in the given {@link Scope}. + * + * @return updated builder instance + */ + PropertyCustomizationBuilder ignoreName(Scope scope); + + /** + * Whether this component can be nillable. + * + * This number format is used for serialization and deserialization of the property. + * + * @param nillable nillable component + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder nillable(boolean nillable) { + return nillable(nillable, Scope.SERIALIZATION).nillable(nillable, Scope.DESERIALIZATION); + } + + /** + * Whether this component should be nillable in the given {@link Scope}. + * + * @param nillable nillable component + * @param scope scope of the nillable + * @return updated builder instance + */ + PropertyCustomizationBuilder nillable(boolean nillable, Scope scope); + + /** + * Ignore nillable customization of this component. + * + * Nillable customization will be ignored for both serialization and deserialization. + * + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder ignoreNillable() { + return ignoreNillable(Scope.SERIALIZATION).ignoreNillable(Scope.DESERIALIZATION); + } + + /** + * Ignore nillable customization of this component in the given {@link Scope}. + * + * @param scope ignored scope + * @return updated builder instance + */ + PropertyCustomizationBuilder ignoreNillable(Scope scope); + + /** + * Set number format which should be used. + * + * This number format is used for serialization and deserialization of the property. + * + * @param numberFormat number format + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder numberFormat(String numberFormat) { + return numberFormat(numberFormat, Scope.SERIALIZATION).numberFormat(numberFormat, Scope.DESERIALIZATION); + } + + /** + * Set number format locale which should be used. + * + * This locale is used for serialization and deserialization of the property. + * + * @param locale locale + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder numberFormat(Locale locale) { + return numberFormat(locale, Scope.SERIALIZATION).numberFormat(locale, Scope.DESERIALIZATION); + } + + /** + * Set number format and locale which should be used. + * + * Both number format and locale are used for serialization and deserialization of the property. + * + * @param numberFormat number format + * @param locale locale + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder numberFormat(String numberFormat, Locale locale) { + return numberFormat(numberFormat, locale, Scope.SERIALIZATION).numberFormat(numberFormat, locale, Scope.DESERIALIZATION); + } + + /** + * Set {@link NumberFormat} instance which should be used. + * + * This {@link NumberFormat} instance is used for serialization and deserialization of the property. + * + * @param numberFormat pre created NumberFormat instance + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder numberFormat(NumberFormat numberFormat) { + return numberFormat(numberFormat, Scope.SERIALIZATION).numberFormat(numberFormat, Scope.DESERIALIZATION); + } + + /** + * Set number format and locale which should be used in the given {@link Scope}. + * + * @param numberFormat number format + * @param locale number format locale + * @param scope scope of the format + * @return updated builder instance + */ + PropertyCustomizationBuilder numberFormat(String numberFormat, Locale locale, Scope scope); + + /** + * Set {@link NumberFormat} instance which should be used in the given {@link Scope}. + * + * @param numberFormat pre created NumberFormat instance + * @param scope scope of the format + * @return updated builder instance + */ + PropertyCustomizationBuilder numberFormat(NumberFormat numberFormat, Scope scope); + + /** + * Set number format which should be used in the given {@link Scope}. + * + * @param numberFormat number format + * @param scope scope of the format + * @return updated builder instance + */ + default PropertyCustomizationBuilder numberFormat(String numberFormat, Scope scope) { + return numberFormat(numberFormat, Locale.getDefault(), scope); + } + + /** + * Set number format locale which should be used in the given {@link Scope}. + * + * @param locale number format locale + * @param scope scope of the format + * @return updated builder instance + */ + default PropertyCustomizationBuilder numberFormat(Locale locale, Scope scope) { + return numberFormat("", locale, scope); + } + + /** + * Ignore number format and locale customization of this property. + * + * Both number format and locale will be ignored for serialization and deserialization of the property. + * + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder ignoreNumberFormat() { + return ignoreNumberFormat(Scope.SERIALIZATION).ignoreTransient(Scope.DESERIALIZATION); + } + + /** + * Ignore number format customization of this property in the given {@link Scope}. + * + * @param scope ignored scope + * @return updated builder instance + */ + PropertyCustomizationBuilder ignoreNumberFormat(Scope scope); + + /** + * Set date format and locale which should be used. + * + * Both date format and locale are used for serialization and deserialization of the property. + * + * @param dateFormat date format + * @param locale locale + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder dateFormat(String dateFormat, Locale locale) { + return dateFormat(dateFormat, locale, Scope.SERIALIZATION).dateFormat(dateFormat, locale, Scope.DESERIALIZATION); + } + + /** + * Set date format which should be used. + * + * This date format is used for serialization and deserialization of the property. + * + * @param dateFormat date format + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder dateFormat(String dateFormat) { + return dateFormat(dateFormat, Locale.getDefault(), Scope.SERIALIZATION) + .dateFormat(dateFormat, Locale.getDefault(), Scope.DESERIALIZATION); + } + + + /** + * Set date format locale which should be used. + * + * This date format locale is used for serialization and deserialization of the property. + * + * @param locale locale + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder dateFormat(Locale locale) { + return dateFormat(JsonbDateFormat.DEFAULT_FORMAT, locale, Scope.SERIALIZATION) + .dateFormat(JsonbDateFormat.DEFAULT_FORMAT, locale, Scope.DESERIALIZATION); + } + + /** + * Set {@link DateFormat} instance which should be used. + * + * This {@link DateFormat} instance is used for serialization and deserialization of the property. + * + * @param dateFormat pre created DateFormat instance + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder dateFormat(DateFormat dateFormat) { + return dateFormat(dateFormat, Scope.SERIALIZATION).dateFormat(dateFormat, Scope.DESERIALIZATION); + } + + /** + * Set date format and locale which should be used in the given {@link Scope}. + * + * @param dateFormat date format + * @param locale date format locale + * @param scope scope of the format + * @return updated builder instance + */ + PropertyCustomizationBuilder dateFormat(String dateFormat, Locale locale, Scope scope); + + /** + * Set {@link DateFormat} instance which should be used in the given {@link Scope}. + * + * @param dateFormat pre created DateFormat instance + * @return updated builder instance + */ + PropertyCustomizationBuilder dateFormat(DateFormat dateFormat, Scope scope); + + /** + * Set date format which should be used in the given {@link Scope}. + * + * @param dateFormat date format + * @param scope scope of the format + * @return updated builder instance + */ + default PropertyCustomizationBuilder dateFormat(String dateFormat, Scope scope) { + return dateFormat(dateFormat, Locale.getDefault(), scope); + } + + /** + * Set date format locale which should be used in the given {@link Scope}. + * + * @param locale date format locale + * @param scope scope of the format + * @return updated builder instance + */ + default PropertyCustomizationBuilder dateFormat(Locale locale, Scope scope) { + return dateFormat(JsonbDateFormat.DEFAULT_FORMAT, locale, scope); + } + + /** + * Ignore date format customization of this property. + * + * The date format is ignored for serialization and deserialization of the property. + * + * @return updated builder instance + */ + @Override + default PropertyCustomizationBuilder ignoreDateFormat() { + return ignoreDateFormat(Scope.SERIALIZATION).ignoreDateFormat(Scope.DESERIALIZATION); + } + + /** + * Ignore date format customization of this property in the given {@link Scope}. + * + * @return updated builder instance + */ + PropertyCustomizationBuilder ignoreDateFormat(Scope scope); + + /** + * Whether the property is transient. + * + * This value of the property will be set for serialization and deserialization. + * + * @param isTransient transient property + * @return updated builder instance + */ + default PropertyCustomizationBuilder transientProperty(boolean isTransient) { + return transientProperty(isTransient, Scope.SERIALIZATION).transientProperty(isTransient, Scope.DESERIALIZATION); + } + + /** + * Whether the property is transient in the given {@link Scope}. + * + * @param isTransient transient property + * @param scope transient scope + * @return updated builder instance + */ + PropertyCustomizationBuilder transientProperty(boolean isTransient, Scope scope); + + /** + * Ignore transient status of this property. + * + * Transient will be ignored for serialization and deserialization. + * + * @return updated builder instance + */ + default PropertyCustomizationBuilder ignoreTransient() { + return ignoreTransient(Scope.SERIALIZATION).ignoreTransient(Scope.DESERIALIZATION); + } + + /** + * Ignore transient status of this property in the given {@link Scope}. + * + * @param scope transient ignore scope + * @return updated builder instance + */ + PropertyCustomizationBuilder ignoreTransient(Scope scope); + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/Scope.java b/api/src/main/java/jakarta/json/bind/customization/Scope.java new file mode 100644 index 00000000..2247d8a5 --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/Scope.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +/** + * Scope of upon which the customization should be used. + */ +public enum Scope { + + /** + * Transformation from Java object to the JSON. + */ + SERIALIZATION, + + /** + * Transformation from JSON to the Java object. + */ + DESERIALIZATION, + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/ScopedCustomization.java b/api/src/main/java/jakarta/json/bind/customization/ScopedCustomization.java new file mode 100644 index 00000000..684e4bde --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/ScopedCustomization.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.Optional; + +/** + * Customization methods with the scope. + */ +public interface ScopedCustomization extends SerializationCustomization { + + /** + * Return {@link NumberFormat} specified to the required {@link Scope}. + * + * @param scope required scope + * @return specified {@link NumberFormat} instance, otherwise empty + */ + Optional getNumberFormat(Scope scope); + + /** + * Return {@link DateFormat} specified to the required {@link Scope}. + * + * @param scope required scope + * @return specified {@link DateFormat} instance, otherwise empty + */ + Optional getDateFormat(Scope scope); + + /** + * Return whether the component can be nillable in the given {@link Scope}. + * If no explicit value has been set, empty optional is returned. + * + * @param scope required scope + * @return property nillable state, if not explicitly set empty is returned + */ + Optional getNillable(Scope scope); + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/ScopelessCustomization.java b/api/src/main/java/jakarta/json/bind/customization/ScopelessCustomization.java new file mode 100644 index 00000000..82dac7dc --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/ScopelessCustomization.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.Optional; + +/** + * Customization methods without the scope. + */ +public interface ScopelessCustomization extends JsonbCustomization { + + /** + * Return specified {@link NumberFormat}. + * + * @return specified {@link NumberFormat} instance, otherwise empty + */ + Optional getNumberFormat(); + + /** + * Return {@link DateFormat}. + * + * @return specified {@link DateFormat} instance, otherwise empty + */ + Optional getDateFormat(); + + /** + * Return whether this component can be nillable. + * If no explicit value has been set, empty optional is returned. + * + * @return property nillable state, if not explicitly set empty is returned + */ + Optional getNillable(); + + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/SerializationCustomization.java b/api/src/main/java/jakarta/json/bind/customization/SerializationCustomization.java new file mode 100644 index 00000000..1b151770 --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/SerializationCustomization.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.util.Optional; + +import jakarta.json.bind.serializer.JsonbSerializer; + +/** + * Extension of the {@link JsonbCustomization} over the serialization specific methods. + */ +public interface SerializationCustomization extends JsonbCustomization { + + /** + * Return {@link JsonbSerializer} of the component. + * + * @return component serializer instance, otherwise empty + */ + Optional> getSerializer(); + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/SerializationCustomizationBuilder.java b/api/src/main/java/jakarta/json/bind/customization/SerializationCustomizationBuilder.java new file mode 100644 index 00000000..c840e87d --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/SerializationCustomizationBuilder.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import jakarta.json.bind.serializer.JsonbSerializer; + +/** + * Extension of the {@link JsonbCustomizationBuilder} over the serialization specific methods. + */ +public interface SerializationCustomizationBuilder, B extends JsonbCustomization> + extends JsonbCustomizationBuilder { + + /** + * Set {@link JsonbSerializer} which should be used for this component. + * + * @param serializer component deserializer + * @return updated builder instance + */ + T serializer(JsonbSerializer serializer); + + /** + * Ignore serializer of this component. + * + * @return updated builder instance + */ + T ignoreSerializer(); + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/TypeCustomization.java b/api/src/main/java/jakarta/json/bind/customization/TypeCustomization.java new file mode 100644 index 00000000..e12fbbbe --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/TypeCustomization.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.util.Map; +import java.util.Optional; + +import jakarta.json.bind.config.PropertyOrderStrategy; +import jakarta.json.bind.config.PropertyVisibilityStrategy; +import jakarta.json.bind.spi.JsonbProvider; + +/** + * Type customization. + */ +public interface TypeCustomization extends SerializationCustomization, ScopelessCustomization { + + /** + * Create new {@link TypeCustomizationBuilder} instance base on type class. + * Parameter typeClass is required to be non-null. + * + * @param typeClass type class + * @return new type customization builder instance + */ + static TypeCustomizationBuilder builder(Class typeClass) { + return JsonbProvider.provider().newTypeCustomizationBuilder(typeClass); + } + + /** + * Return type which is this customization bound to. + * + * @return type which is this customization bound to + */ + Class getType(); + + /** + * Return {@link PropertyOrderStrategy} of the decorated type. + * + * @return property order strategy instance, otherwise empty + */ + Optional getPropertyOrderStrategy(); + + /** + * Return {@link PropertyVisibilityStrategy} of the decorated type. + * + * @return property visibility strategy instance, otherwise empty + */ + Optional getPropertyVisibilityStrategy(); + + /** + * Return registered property customizations. + * If no property is registered, empty {@link Map} is returned. + * + * @return map of the registered property customizations, otherwise empty map + */ + Map getProperties(); + + /** + * Return {@link CreatorCustomization} of the decorated type. + * + * @return creator customization instance, otherwise empty + */ + Optional getCreator(); + + /** + * Convert {@link TypeCustomization} to the {@link TypeCustomizationBuilder} with prefilled values from original object. + * + * @return new {@link TypeCustomizationBuilder} instance + */ + default TypeCustomizationBuilder toBuilder() { + TypeCustomizationBuilder builder = builder(getType()); + getNillable().ifPresent(builder::nillable); + getDeserializer().ifPresent(builder::deserializer); + getSerializer().ifPresent(builder::serializer); + getAdapter().ifPresent(builder::adapter); + getNumberFormat().ifPresent(builder::numberFormat); + getDateFormat().ifPresent(builder::dateFormat); + getCreator().ifPresent(builder::creator); + getPropertyOrderStrategy().ifPresent(builder::propertyOrderStrategy); + getPropertyVisibilityStrategy().ifPresent(builder::visibilityStrategy); + getProperties().values().forEach(builder::property); + return builder; + } + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/TypeCustomizationBuilder.java b/api/src/main/java/jakarta/json/bind/customization/TypeCustomizationBuilder.java new file mode 100644 index 00000000..26215161 --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/TypeCustomizationBuilder.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.json.bind.customization; + +import java.util.Objects; +import java.util.function.Consumer; + +import jakarta.json.bind.config.PropertyOrderStrategy; +import jakarta.json.bind.config.PropertyVisibilityStrategy; + +/** + * Builder of the specific type customization. + */ +public interface TypeCustomizationBuilder extends SerializationCustomizationBuilder { + + /** + * Set {@link PropertyOrderStrategy} which should be used. + * + * @param strategy property order strategy + * @return updated builder instance + */ + TypeCustomizationBuilder propertyOrderStrategy(PropertyOrderStrategy strategy); + + /** + * Ignore property order strategy. + * + * @return updated builder instance + */ + TypeCustomizationBuilder ignorePropertyOrderStrategy(); + + /** + * Set {@link PropertyVisibilityStrategy} which should be used. + * + * @param strategy property visibility strategy + * @return updated builder instance + */ + TypeCustomizationBuilder visibilityStrategy(PropertyVisibilityStrategy strategy); + + /** + * Ignore visibility strategy. + * + * @return updated builder instance + */ + TypeCustomizationBuilder ignoreVisibilityStrategy(); + + /** + * Add new {@link PropertyCustomization} of the property. + * + * @param propertyCustomization property customization + * @return updated builder instance + */ + TypeCustomizationBuilder property(PropertyCustomization propertyCustomization); + + /** + * Add new {@link PropertyCustomization} of the property. + *
+ * Shortcut method to the {@link #property(PropertyCustomization)}. It is not required to create {@link PropertyCustomizationBuilder} + * since this method will create it based on the provided property name and expose it over the propertyBuilder parameter. + *
+ * Example usage: + *
{@code
+     * typeBuilder.property("exampleProperty", propertyBuilder -> propertyBuilder.nillable(true));
+     * }
+ * @param propertyName name of the class property + * @param propertyBuilder builder used to customize property + * @return updated builder instance + */ + default TypeCustomizationBuilder property(String propertyName, Consumer propertyBuilder) { + PropertyCustomizationBuilder builder = PropertyCustomization.builder(Objects.requireNonNull(propertyName)); + propertyBuilder.accept(builder); + return property(builder.build()); + } + + /** + * Add new {@link CreatorCustomization} of the type. + * + * @param creatorCustomization creator customization + * @return updated builder instance + */ + TypeCustomizationBuilder creator(CreatorCustomization creatorCustomization); + + /** + * Add new {@link CreatorCustomization} of the type. + *
+ * Shortcut method to the {@link #creator(CreatorCustomization)}. It is not required to create {@link CreatorCustomizationBuilder} + * since this method will create it based on the creator method name and expose it over the creatorBuilder parameter. + *
+ * Example usage: + *
{@code
+     * typeBuilder.creator("factoryMethodName",
+     *                     creatorBuilder -> creatorBuilder.addParameter(String.class, "firstParameter"));
+     * }
+ * @param creatorMethodName creator method name + * @param creatorBuilder creator builder instance consumer + * @return updated builder instance + */ + default TypeCustomizationBuilder creator(String creatorMethodName, Consumer creatorBuilder) { + CreatorCustomizationBuilder builder = CreatorCustomization.builder(creatorMethodName); + creatorBuilder.accept(builder); + return creator(builder.build()); + } + + /** + * Add new {@link CreatorCustomization} of the type. + *
+ * Shortcut method to the {@link #creator(CreatorCustomization)}. It is not required to create {@link CreatorCustomizationBuilder} + * since this method will create it and expose it over the creatorBuilder parameter. Since no factory method name is + * provided, this creator builder is targeting constructors. + *
+ * Example usage: + *
{@code
+     * typeBuilder.creator(creatorBuilder -> creatorBuilder.addParameter(String.class, "firstParameter"));
+     * }
+ * @param creatorBuilder creator builder instance consumer + * @return updated builder instance + */ + default TypeCustomizationBuilder creator(Consumer creatorBuilder) { + CreatorCustomizationBuilder builder = CreatorCustomization.builder(); + creatorBuilder.accept(builder); + return creator(builder.build()); + } + +} diff --git a/api/src/main/java/jakarta/json/bind/customization/package-info.java b/api/src/main/java/jakarta/json/bind/customization/package-info.java new file mode 100644 index 00000000..c046db25 --- /dev/null +++ b/api/src/main/java/jakarta/json/bind/customization/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/** + *

+ * Defines customizations for different components to provide runtime customization with the same capability as + * annotation approach. + *

+ * + * @since JSON Binding 2.1 + */ +package jakarta.json.bind.customization; diff --git a/api/src/main/java/jakarta/json/bind/spi/JsonbProvider.java b/api/src/main/java/jakarta/json/bind/spi/JsonbProvider.java index 250a478c..56ffe34f 100644 --- a/api/src/main/java/jakarta/json/bind/spi/JsonbProvider.java +++ b/api/src/main/java/jakarta/json/bind/spi/JsonbProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -18,8 +18,13 @@ import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbException; +import jakarta.json.bind.customization.CreatorCustomizationBuilder; +import jakarta.json.bind.customization.ParamCustomizationBuilder; +import jakarta.json.bind.customization.PropertyCustomizationBuilder; +import jakarta.json.bind.customization.TypeCustomizationBuilder; import java.util.Iterator; +import java.util.Objects; import java.util.ServiceLoader; /** @@ -61,6 +66,7 @@ public abstract class JsonbProvider { */ private static final String DEFAULT_PROVIDER = "org.eclipse.yasson.JsonBindingProvider"; + private static volatile JsonbProvider instance = null; /** * Protected constructor. */ @@ -84,21 +90,28 @@ protected JsonbProvider() { */ @SuppressWarnings("UseSpecificCatch") public static JsonbProvider provider() { - ServiceLoader loader = ServiceLoader.load(JsonbProvider.class); - Iterator it = loader.iterator(); - if (it.hasNext()) { - return it.next(); - } - - try { - Class clazz = Class.forName(DEFAULT_PROVIDER); - return (JsonbProvider) clazz.newInstance(); - } catch (ClassNotFoundException x) { - throw new JsonbException("JSON Binding provider " + DEFAULT_PROVIDER + " not found", x); - } catch (Exception x) { - throw new JsonbException("JSON Binding provider " + DEFAULT_PROVIDER - + " could not be instantiated: " + x, x); + if (instance == null) { + synchronized (JsonbProvider.class) { + if (instance == null) { + ServiceLoader loader = ServiceLoader.load(JsonbProvider.class); + Iterator it = loader.iterator(); + if (it.hasNext()) { + instance = it.next(); + } else { + try { + Class clazz = Class.forName(DEFAULT_PROVIDER); + instance = (JsonbProvider) clazz.newInstance(); + } catch (ClassNotFoundException x) { + throw new JsonbException("JSON Binding provider " + DEFAULT_PROVIDER + " not found", x); + } catch (Exception x) { + throw new JsonbException("JSON Binding provider " + DEFAULT_PROVIDER + + " could not be instantiated: " + x, x); + } + } + } + } } + return instance; } /** @@ -123,13 +136,12 @@ public static JsonbProvider provider() { */ @SuppressWarnings("UseSpecificCatch") public static JsonbProvider provider(final String providerName) { - if (providerName == null) { - throw new IllegalArgumentException(); + Objects.requireNonNull(providerName, "providerName is required"); + if (instance != null && providerName.equals(instance.getClass().getName())) { + return instance; } ServiceLoader loader = ServiceLoader.load(JsonbProvider.class); - Iterator it = loader.iterator(); - while (it.hasNext()) { - JsonbProvider provider = it.next(); + for (JsonbProvider provider : loader) { if (providerName.equals(provider.getClass().getName())) { return provider; } @@ -157,4 +169,44 @@ public static JsonbProvider provider(final String providerName) { */ public abstract JsonbBuilder create(); + /** + * Return new {@link TypeCustomizationBuilder} instance based on customized type class. + * + * @param decoratedType customized type class + * @return new builder instance + */ + public abstract TypeCustomizationBuilder newTypeCustomizationBuilder(Class decoratedType); + + /** + * Return new {@link PropertyCustomizationBuilder} instance based on customized property name. + * + * @param propertyName customized property name + * @return new builder instance + */ + public abstract PropertyCustomizationBuilder newPropertyCustomizationBuilder(String propertyName); + + /** + * Return new {@link CreatorCustomizationBuilder}. + * + * @return new builder instance + */ + public abstract CreatorCustomizationBuilder newCreatorCustomizationBuilder(); + + /** + * Return new {@link CreatorCustomizationBuilder} instance based on creator method name. + * + * @param methodName customized creator method name + * @return new builder instance + */ + public abstract CreatorCustomizationBuilder newCreatorCustomizationBuilder(String methodName); + + /** + * Return new {@link ParamCustomizationBuilder} instance based on parameter class and its name in JSON document. + * + * @param paramClass customized parameter class + * @param jsonName customized parameter json name + * @return new builder instance + */ + public abstract ParamCustomizationBuilder newParamCustomizationBuilder(Class paramClass, String jsonName); + } diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java index 521faf9a..5edeb677 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -19,6 +19,7 @@ exports jakarta.json.bind.adapter; exports jakarta.json.bind.annotation; exports jakarta.json.bind.config; + exports jakarta.json.bind.customization; exports jakarta.json.bind.serializer; exports jakarta.json.bind.spi;