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.PrintWriter;
016import java.io.Reader;
017import java.time.LocalDate;
018import java.time.LocalDateTime;
019import java.time.LocalTime;
020import java.time.OffsetDateTime;
021import java.util.Collections;
022import java.util.Date;
023import java.util.LinkedHashMap;
024import java.util.Map;
025
026/**
027 * Represents configuration, utilising TOML.
028 *
029 * @since 1.0
030 * @author MrIvanPlays
031 */
032public final class TomlConfig {
033
034  private static final TomlMapper DEFAULT_TOML_MAPPER =
035      TomlMapper.builder().configure(TomlReadFeature.PARSE_JAVA_TIME, true).build();
036
037  /** Returns the key on which the mapper is stored. */
038  public static final String MAPPER_KEY = "mapper";
039
040  private static ConfigResolver configResolver;
041
042  /**
043   * Returns the {@link ConfigResolver} instance for toml config.
044   *
045   * @return config resolver
046   */
047  public static ConfigResolver getConfigResolver() {
048    if (configResolver == null) {
049      generateConfigResolver();
050    }
051    return configResolver;
052  }
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(new TomlValueWriter(DEFAULT_TOML_MAPPER))
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
141  private static final class TomlValueWriter implements ValueWriter {
142
143    private final TomlMapper defaultMapper;
144
145    TomlValueWriter(TomlMapper defaultMapper) {
146      this.defaultMapper = defaultMapper;
147    }
148
149    @Override
150    public void write(
151        String key, Object value, PrintWriter writer, CustomOptions options, boolean sectionExists)
152        throws IOException {
153      TomlMapper tomlMapper =
154          options.getAsOr(TomlConfig.MAPPER_KEY, TomlMapper.class, defaultMapper);
155      writer.println(tomlMapper.writeValueAsString(Collections.singletonMap(key, value)));
156    }
157  }
158}