001package com.mrivanplays.annotationconfig.toml;
002
003import com.fasterxml.jackson.dataformat.toml.TomlMapper;
004import com.fasterxml.jackson.dataformat.toml.TomlReadFeature;
005import com.mrivanplays.annotationconfig.core.resolver.ConfigResolver;
006import com.mrivanplays.annotationconfig.core.resolver.ValueReader;
007import com.mrivanplays.annotationconfig.core.resolver.ValueWriter;
008import com.mrivanplays.annotationconfig.core.resolver.options.CustomOptions;
009import com.mrivanplays.annotationconfig.core.resolver.options.Option;
010import com.mrivanplays.annotationconfig.core.resolver.settings.LoadSetting;
011import com.mrivanplays.annotationconfig.core.serialization.DataObject;
012import com.mrivanplays.annotationconfig.core.serialization.SerializerRegistry;
013import java.io.File;
014import java.io.IOException;
015import java.io.Reader;
016import java.time.LocalDate;
017import java.time.LocalDateTime;
018import java.time.LocalTime;
019import java.time.OffsetDateTime;
020import java.util.Date;
021import java.util.LinkedHashMap;
022import java.util.Map;
023
024/**
025 * Represents configuration, utilising TOML.
026 *
027 * @since 1.0
028 * @author MrIvanPlays
029 */
030public final class TomlConfig {
031
032  private static final TomlMapper DEFAULT_TOML_MAPPER =
033      TomlMapper.builder().configure(TomlReadFeature.PARSE_JAVA_TIME, true).build();
034
035  /** Returns the key on which the mapper is stored. */
036  public static final String MAPPER_KEY = "mapper";
037
038  private static ConfigResolver configResolver;
039
040  /**
041   * Returns the {@link ConfigResolver} instance for toml config.
042   *
043   * @return config resolver
044   */
045  public static ConfigResolver getConfigResolver() {
046    if (configResolver == null) {
047      generateConfigResolver();
048    }
049    return configResolver;
050  }
051
052  private static final ValueWriter TOML_VALUE_WRITER = new TomlValueWriter(DEFAULT_TOML_MAPPER);
053
054  private static void generateConfigResolver() {
055    configResolver =
056        ConfigResolver.newBuilder()
057            .withOption(MAPPER_KEY, Option.of(DEFAULT_TOML_MAPPER).markReplaceable())
058            .withLoadSetting(LoadSetting.GENERATE_NEW_OPTIONS, false)
059            .withValueWriter(TOML_VALUE_WRITER)
060            .withCommentPrefix("# ")
061            .shouldReverseFields(true)
062            .withValueReader(
063                new ValueReader() {
064                  @Override
065                  public Map<String, Object> read(Reader reader, CustomOptions options)
066                      throws IOException {
067                    return (Map<String, Object>)
068                        options
069                            .getAsOr(MAPPER_KEY, TomlMapper.class, DEFAULT_TOML_MAPPER)
070                            .reader()
071                            .readValue(reader, LinkedHashMap.class);
072                  }
073                })
074            .build();
075  }
076
077  static {
078    SerializerRegistry registry = SerializerRegistry.INSTANCE;
079    if (!registry.hasSerializer(Date.class)) {
080      registry.registerSerializer(Date.class, new DateResolver());
081    }
082    if (!registry.hasSerializer(OffsetDateTime.class)) {
083      registry.registerSerializer(
084          OffsetDateTime.class,
085          (data, field) -> OffsetDateTime.parse(data.getAsString()),
086          (value, field) -> new DataObject(value.toString()));
087    }
088    if (!registry.hasSerializer(LocalDateTime.class)) {
089      registry.registerSerializer(
090          LocalDateTime.class,
091          (data, field) -> LocalDateTime.parse(data.getAsString()),
092          (value, field) -> new DataObject(value.toString()));
093    }
094    if (!registry.hasSerializer(LocalDate.class)) {
095      registry.registerSerializer(
096          LocalDate.class,
097          (data, field) -> LocalDate.parse(data.getAsString()),
098          (value, field) -> new DataObject(value.toString()));
099    }
100    if (!registry.hasSerializer(LocalTime.class)) {
101      registry.registerSerializer(
102          LocalTime.class,
103          (data, field) -> LocalTime.parse(data.getAsString()),
104          (value, field) -> new DataObject(value.toString()));
105    }
106  }
107
108  /**
109   * Loads the config object from the file. If the file does not exist, it creates one.
110   *
111   * @param annotatedConfig annotated config
112   * @param file file
113   * @deprecated see {@link #load(Object, File, TomlMapper)}
114   */
115  @Deprecated
116  public static void load(Object annotatedConfig, File file) {
117    load(annotatedConfig, file, DEFAULT_TOML_MAPPER);
118  }
119
120  /**
121   * Loads the config object from the file. If the file does not exist, it creates one.
122   *
123   * @param annotatedConfig annotated config
124   * @param file file
125   * @param tomlMapper toml mapper
126   * @deprecated use {@link #getConfigResolver()}
127   */
128  @Deprecated
129  public static void load(Object annotatedConfig, File file, TomlMapper tomlMapper) {
130    ConfigResolver resolver = getConfigResolver();
131    if (!resolver.options().has(MAPPER_KEY)) {
132      resolver.options().put(MAPPER_KEY, Option.of(tomlMapper).markReplaceable());
133    } else {
134      if (resolver.options().isReplaceable(MAPPER_KEY).orElse(false)) {
135        resolver.options().put(MAPPER_KEY, Option.of(tomlMapper).markReplaceable());
136      }
137    }
138    resolver.loadOrDump(annotatedConfig, file);
139  }
140}