Class ConfigurationBuilder<C>

java.lang.Object
space.arim.dazzleconf.ConfigurationBuilder<C>
Type Parameters:
C - the configuration type

public final class ConfigurationBuilder<C> extends Object
A builder for Configuration. The builder allows changing how the configuration is defined, read, serialized, and instantiated.

Construction

A builder can be made either through the factory methods like Configuration.defaultBuilder(java.lang.Class<C>) or by direct construction. If constructed directly, the builder is empty and type liaisons will need to be added to it.

Direct construction can be useful if a clean slate is desired, without the default liaisons.

Type Liaisons

Several methods add TypeLiaison instances. This builder stores type liaisons in the order they are added, which is significant because later type liaisons will be queried first in the resulting configuration:

Callers can add liaisons for any type or types by adding their own TypeLiaison implementations. Because liaisons added later will take precedence, existing liaisons can be overidden by adding another liaison that covers the same type.
  • Constructor Details

    • ConfigurationBuilder

      public ConfigurationBuilder(@NonNull TypeToken<C> configType)
      Creates for the specified interface type.

      This creates an empty configuration builder. No type liaisons are added to it, not even primitive types. To add liaisons, please use one of the appropriate methods.

      Parameters:
      configType - the config interface type
  • Method Details

    • locale

      public @NonNull ConfigurationBuilder<C> locale(@NonNull Locale locale)
      Sets the locale for displaying error messages. Version 2 preview: this method is currently a no-op, except when assertions are enabled, until DazzleConf enters full release.

      If not set, defaults to the system locale.

      Parameters:
      locale - the locale, nonnull
      Returns:
      this builder
    • addTypeLiaisons

      public @NonNull ConfigurationBuilder<C> addTypeLiaisons(@NonNull TypeLiaison @NonNull ... typeLiaisons)
      Adds the following type liaisons to this builder.

      The order is significant. Type liaisons added later will be queried first to handle configuration types. This matters because it lets the caller override existing liaisons.

      Parameters:
      typeLiaisons - the type liaisons
      Returns:
      this builder
    • addTypeLiaisons

      public @NonNull ConfigurationBuilder<C> addTypeLiaisons(@NonNull List<@NonNull TypeLiaison> typeLiaisons)
      Adds the following type liaisons to this builder.

      The order is significant. Type liaisons added later will be queried first to handle configuration types. This matters because it lets the caller override existing liaisons.

      Parameters:
      typeLiaisons - the type liaisons
      Returns:
      this builder
    • addPrimitiveTypeLiaisons

      public @NonNull ConfigurationBuilder<C> addPrimitiveTypeLiaisons()
      Adds type liaisons for primitives and String to this builder.

      These type liaisons are part of the default set. However, unlike addDefaultTypeLiaisons(), enum types, collections, and configuration subsections are not covered by this method.

      Types Handled

      The type liaisons added by this method cover boolean/Boolean, char/Character, byte/Byte, short/Short, int/Integer, long/Long, float/Float, double/Double, and String. Using a boxed type is treated identically to using the primitive type (and nulls are rejected in either case).

      Notable annotations

      The liaisons added by this method support the following annotations to modify their behavior:

      • StringLiaison: @StringDefault
      • BooleanLiaison: @BooleanDefault
      • LongLiaison: @LongRange and @LongDefault
      • IntegerLiaison: @IntegerRange and @IntegerDefault
      • ShortLiaison: @ShortRange and @ShortDefault
      • ByteLiaison: @ByteRange and @ByteDefault
      • DoubleLiaison: @DoubleRange and @DoubleDefault
      • FloatLiaison: @FloatRange and @FloatDefault

      The "Range" annotations for numeric types provide bounds checking for a specified range. If the user input falls outside that range, it is rejected with an error message.

      The "Default" annotations provide default values. There is mostly no difference between using default methods and the default value-providing annotations, but the annotations provide additional capabilities, like specifying an "if missing" value or being passed to dependent liaisons.

      The annotations mentioned here can also be depended upon by other liaisons, not just the default liaisons.

      Returns:
      this builder
    • addDefaultTypeLiaisons

      public @NonNull ConfigurationBuilder<C> addDefaultTypeLiaisons()
      Adds the default type liaisons to this builder.

      This method is called automatically if you are using Configuration.defaultBuilder.

      Types Handled

      The default type liaisons are capable of serializing primitive types, Strings, enum types, collections (Collection/List/Set), and configuration subsections.

      The full list of types handled by this method: boolean/Boolean, char/Character, byte/Byte, short/Short, int/Integer, long/Long, float/Float, double/Double, String, types for which Class#isEnum is true, Collection, List, Set, Map, and interface types where the type usage is annotated with SubSection.

      Notable annotations

      The default liaisons support the following annotations to modify their behavior:

      • StringLiaison: @StringDefault
      • BooleanLiaison: @BooleanDefault
      • LongLiaison: @LongRange and @LongDefault
      • IntegerLiaison: @IntegerRange and @IntegerDefault
      • ShortLiaison: @ShortRange and @ShortDefault
      • ByteLiaison: @ByteRange and @ByteDefault
      • DoubleLiaison: @DoubleRange and @DoubleDefault
      • FloatLiaison: @FloatRange and @FloatDefault

      The "Range" annotations for numeric types provide bounds checking for a specified range. If the user input falls outside that range, it is rejected with an error message.

      The "Default" annotations provide default values. There is mostly no difference between using default methods and the default value-providing annotations, but the annotations provide additional capabilities, like specifying an "if missing" value or being passed to dependent liaisons.

      The annotations mentioned here can also be depended upon by other liaisons, not just the default liaisons.

      Returns:
      this builder
    • addSimpleSerializer

      public <V> @NonNull ConfigurationBuilder<C> addSimpleSerializer(@NonNull TypeToken<V> typeToken, @NonNull SerializeDeserialize<V> serializeDeserialize)
      Adds a simple type liaison based on a single serializer.

      This method creates a simple type liaison pointing to the given SerializeDeserialize and paired with the type specified. As such, it cannot load subsections or handle a range of types. Also, you will need to use default methods in your configuration interface to supply default values.

      API Note

      This method is closest to version 1's method of handling custom types. It is slightly more limited, as it cannot allow you to depend on other serializers in the same way.

      Type Parameters:
      V - the type being handled by the serializer
      Parameters:
      typeToken - the type to handle. Annotations will be matched exactly, meaning that the provided serializer will NOT apply to usage of the type with different annotations
      serializeDeserialize - the serialization for that type
      Returns:
      this builder
    • clearTypeLiaisons

      public @NonNull ConfigurationBuilder<C> clearTypeLiaisons()
      Clears the type liaisons currently set on this builder
      Returns:
      this builder
    • keyMapper

      public @NonNull ConfigurationBuilder<C> keyMapper(@Nullable KeyMapper keyMapper)
      Sets the key mapper
      Parameters:
      keyMapper - the key mapper, or null to clear
      Returns:
      this builder
    • lookup

      public @NonNull ConfigurationBuilder<C> lookup(@NonNull MethodHandles.Lookup lookup)
      Changes the lookup used by the library for reflective access.

      If running on the module path, specifying a lookup can also bypass the need to open the configuration interface's module to the space.arim.dazzleconf module.

      Purpose

      Passing the lookup object allows library users to transmit their own privileged access to the library for reflective purposes. In return, the library promises it will not expose this lookup in a public interface.

      This lookup may be used for reflective access to the configuration interface and its methods. Also, the lookup is passed to methods on Instantiator, meaning it might be used for class generation. Accordingly, the lookup should have full privileged access, such that it is usable with MethodHandles.privateLookupIn.

      If this method is never called, the library will use its own lookup object. If running on the module path, the library lookup may be limited by the access granted to the library module, requiring library users to declare opens <theirpackage> to space.arim.dazzleconf in their JPMS module descriptor.

      Parameters:
      lookup - the privileged lookup. For most library users, MethodHandles.lookup() will suffice. Note that the lookup must have full privileged access
      Returns:
      this builder
    • reflectionService

      public @NonNull ConfigurationBuilder<C> reflectionService(@NonNull ReflectionService reflectionService)
      Sets the reflection service.

      The reflection service is used by the library to find methods, call methods, and generate implementations for the configuration interface.

      Parameters:
      reflectionService - the reflection service
      Returns:
      this builder
    • addMigration

      public @NonNull ConfigurationBuilder<C> addMigration(@NonNull Migration<?,C> migration)
      Adds the following migration to this builder.

      Migrations are not trivial. By using this method, you affirm that you have read the package javadoc for space.arim.dazzleconf.migration, and you understand the "perpetual migration" trap.

      The order is significant, because migrations are checked in the order in which they are declared. Thus, migrations that come first need to ensure they aren't wrongly handling different or overlapping versions.

      Parameters:
      migration - the migration
      Returns:
      this builder
    • addMigrations

      public @NonNull ConfigurationBuilder<C> addMigrations(@NonNull List<@NonNull Migration<?,C>> migrations)
      Adds the following migrations to this builder.

      Migrations are not trivial. By using this method, you affirm that you have read the package javadoc for space.arim.dazzleconf.migration, and you understand the "perpetual migration" trap.

      The order is significant, because migrations are checked in the order in which they are declared. Thus, migrations that come first need to ensure they aren't wrongly handling different or overlapping versions.

      Parameters:
      migrations - the migrations
      Returns:
      this builder
    • build

      @SideEffectFree public @NonNull Configuration<C> build()
      Builds into a fully fledged configuration.

      This function is side effect free: it does not modify this builder, which can be re-used after the call.

      Returns:
      the configuration
      Throws:
      DeveloperMistakeException - if the combination or usage of different library features is in error