All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.config.objectmapping.Value Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2018, 2021 Oracle and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.helidon.config.objectmapping;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.function.Supplier;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * Annotation used to customize behaviour of JavaBean deserialization support.
 * 

* The first option for generic Config to JavaBean deserialization works just with class with no-parameter constructor. * Each JavaBean property value is then set by value mapped from appropriate configuration node. * Each public setter method and public non-final fields are taken as JavaBean properties that will be set. * The deserialization process is applied recursively on each property. *

* Use {@link Transient} annotation to exclude setter or field from set of processed JavaBean properties. *

* By default JavaBean property name is used as config key to {@link io.helidon.config.Config#get(String) get} configuration node. * The config key can be customized by {@link #key()} attribute. Use the annotation on public setter or public field. * Annotation on method has precedence over annotation used on field. The second one is ignored. *

* If the appropriate configuration node does not exist it is possible to specify default value: *

    *
  • {@link #withDefaultSupplier()} - instance of supplier class is used to get default value of target type; or
  • *
  • {@link #withDefault()} - default value in {@code String} form that will be mapped to target type * by associated config mapping function
  • *
* In case of both default attributes are set the {@code withDefaultSupplier} is used * and {@code withDefault} is ignored. *

 * public class AppConfig {
 *     private String greeting;
 *     private int pageSize;
 *     private List{@literal } range;
 *
 *     public AppConfig() { // {@literal <1>}
 *     }
 *
 *     public void setGreeting(String greeting) { // {@literal <2>}
 *         this.greeting = greeting;
 *     }
 *
 *     {@literal @}Value(key = "page-size", withDefault = "10") // {@literal <3>}
 *     public void setPageSize(int pageSize) {
 *         this.pageSize = pageSize;
 *     }
 *
 *     {@literal @}Value(withDefaultSupplier = DefaultRangeSupplier.class) // {@literal <4>}
 *     public void setRange(List{@literal } basicRange) {
 *         this.range = basicRange;
 *     }
 *
 *     //...
 *
 *     public static class DefaultRangeSupplier // {@literal <5>}
 *                 implements Supplier{@literal >} {
 *         {@literal @}Override
 *         public List{@literal } get() {
 *             return List.of(0, 10);
 *         }
 *     }
 * }
 * 
*
    *
  1. public no-parameter constructor;
  2. *
  3. property {@code greeting} is not customized; will be set from config node with {@code greeting} key, if exists;
  4. *
  5. property {@code pageSize} customizes key of config node to {@code page-size}; * if the config node does not exist, value {@code "10"} will be mapped to {@code int}; *
  6. *
  7. property {@code range} will be set from config node with same {@code range} key; * if the config node does not exist, {@code DefaultRangeSupplier} instance will be used to get default value; *
  8. *
  9. {@code DefaultRangeSupplier} is used to supply {@code List} value.
  10. *
*

* The second option is to provide factory public static method {@code from} with parameters set from configuration. * Or public "factory" constructor with parameters can be used too. *


 * public class AppConfig {
 *     private final String greeting;
 *     private final int pageSize;
 *     private final List{@literal } basicRange;
 *
 *     private AppConfig(String greeting, int pageSize, List{@literal } basicRange) {
 *         this.greeting = greeting;
 *         this.pageSize = pageSize;
 *         this.basicRange = basicRange;
 *     }
 *
 *     //...
 *
 *     // FACTORY METHOD
 *     public static AppConfig create({@literal @}Value(key = "greeting", withDefault = "Hi")
 *                                  String greeting,
 *                                  {@literal @}Value(key = "page-size", withDefault = "10")
 *                                  int pageSize,
 *                                  {@literal @}Value(key = "basic-range",
 *                                          withDefaultSupplier = DefaultBasicRangeSupplier.class)
 *                                  List{@literal } basicRange) {
 *         return new AppConfig(greeting, pageSize, basicRange);
 *     }
 * }
 * 
*

* The third option is to provide Builder accessible by public static {@code builder()} method. * The Builder instances is initialized via public setters or fields, similar to the first deserialization option. * Finally, Builder has {@code build()} method that creates new instances of a bean. *


 * public class AppConfig {
 *     private final String greeting;
 *     private final int pageSize;
 *     private final List{@literal } basicRange;
 *
 *     private AppConfig(String greeting, int pageSize, List{@literal } basicRange) {
 *         this.greeting = greeting;
 *         this.pageSize = pageSize;
 *         this.basicRange = basicRange;
 *     }
 *
 *     // BUILDER METHOD
 *     public static Builder builder() {
 *         return new Builder();
 *     }
 *
 *     public static class Builder {
 *         private String greeting;
 *         private int pageSize;
 *         private List{@literal } basicRange;
 *
 *         private Builder() {
 *         }
 *
 *         {@literal @}Value(withDefault = "Hi")
 *         public void setGreeting(String greeting) {
 *             this.greeting = greeting;
 *         }
 *
 *         {@literal @}Value(key = "page-size", withDefault = "10")
 *         public void setPageSize(int pageSize) {
 *             this.pageSize = pageSize;
 *         }
 *
 *         {@literal @}Value(key = "basic-range",
 *                 withDefaultSupplier = DefaultBasicRangeSupplier.class)
 *         public void setBasicRange(List{@literal } basicRange) {
 *             this.basicRange = basicRange;
 *         }
 *
 *         // BUILD METHOD
 *         public AppConfig build() {
 *             return new AppConfig(greeting, pageSize, basicRange);
 *         }
 *     }
 * }
 * 
*

* Configuration example: *

{@code
 * {
 *     "app": {
 *         "greeting": "Hello",
 *         "page-size": 20,
 *         "range": [ -20, 20 ]
 *     }
 * }
 * }
* Getting {@code app} config node as {@code AppConfig} instance: *
{@code
 * AppConfig appConfig = config.get("app").as(AppConfig.class);
 * assert appConfig.getGreeting().equals("Hello");
 * assert appConfig.getPageSize() == 20;
 * assert appConfig.getRange().get(0) == -20;
 * assert appConfig.getRange().get(1) == 20;
 * }
* In this case default values where not used because JSON contains all expected nodes. *

* The annotation cannot be applied on same JavaBean property together with {@link Transient}. * * @see Transient */ @Retention(RUNTIME) @Target({METHOD, FIELD, PARAMETER}) public @interface Value { /** * Specifies a key of configuration node to be used to set JavaBean property value from. *

* If not specified original JavaBean property name is used. * * @return config property key */ String key() default ""; /** * Specifies default value in form of single String value * that will be used to set JavaBean property value in case configuration does not contain a config node * of appropriate config key. *

* In case {@link #withDefaultSupplier} is also used current value is ignored. * * @return single default value that will be converted into target type */ String withDefault() default None.VALUE; /** * Specifies supplier of default value * that will be used to set JavaBean property value in case configuration does not contain config node * of appropriate config key. *

* Default value is used in case appropriate config value is not set. * In case {@link #withDefault} is also used this one has higher priority and will be used. * * @return supplier that will provide default value in target type */ Class> withDefaultSupplier() default None.class; /** * Class that represents not-set default values. */ interface None extends Supplier { /** * Value to use when a {@code null} value should be the default. */ String VALUE = "io.helidon.config:default=null"; @Override default Void get() { return null; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy