001package com.mrivanplays.annotationconfig.yaml;
002
003import com.mrivanplays.annotationconfig.core.AnnotationType;
004import com.mrivanplays.annotationconfig.core.CustomAnnotationRegistry;
005import com.mrivanplays.annotationconfig.core.internal.AnnotatedConfigResolver;
006import com.mrivanplays.annotationconfig.core.internal.AnnotationHolder;
007import java.io.File;
008import java.io.FileReader;
009import java.io.IOException;
010import java.io.PrintWriter;
011import java.io.Reader;
012import java.util.LinkedHashMap;
013import java.util.List;
014import java.util.Map;
015import org.yaml.snakeyaml.Yaml;
016
017/** Represents configuration, utilising YAML */
018public final class YamlConfig {
019
020  private static CustomAnnotationRegistry annotationRegistry = new CustomAnnotationRegistry();
021
022  /**
023   * Returns the {@link CustomAnnotationRegistry} for this config.
024   *
025   * @return custom annotation registry
026   */
027  public static CustomAnnotationRegistry getAnnotationRegistry() {
028    return annotationRegistry;
029  }
030
031  private static final Yaml YAML = new Yaml();
032  private static final AnnotatedConfigResolver.ValueWriter VALUE_WRITER = new YamlValueWriter();
033
034  /**
035   * Loads the config object from the file. If the file does not exist, it creates one.
036   *
037   * @param annotatedConfig annotated config
038   * @param file file
039   */
040  public static void load(Object annotatedConfig, File file) {
041    List<Map.Entry<AnnotationHolder, List<AnnotationType>>> map =
042        AnnotatedConfigResolver.resolveAnnotations(annotatedConfig, annotationRegistry, true);
043    if (!file.exists()) {
044      AnnotatedConfigResolver.dump(
045          annotatedConfig,
046          map,
047          file,
048          "# ",
049          VALUE_WRITER,
050          annotationRegistry,
051          YamlConfig.class,
052          true);
053    }
054
055    try (Reader reader = new FileReader(file)) {
056      Map<String, Object> values = YAML.loadAs(reader, LinkedHashMap.class);
057      if (values == null) {
058        return;
059      }
060      AnnotatedConfigResolver.setFields(
061          annotatedConfig,
062          values,
063          map,
064          annotationRegistry,
065          "# ",
066          VALUE_WRITER,
067          file,
068          true,
069          true,
070          YamlConfig.class);
071    } catch (IOException e) {
072      throw new RuntimeException(e);
073    }
074  }
075
076  private static final class YamlValueWriter implements AnnotatedConfigResolver.ValueWriter {
077
078    @Override
079    public void write(String key, Object value, PrintWriter writer) {
080      write(key, value, writer, 2);
081    }
082
083    private void write(String key, Object value, PrintWriter writer, int childIndents) {
084      StringBuilder intentPrefixBuilder = new StringBuilder();
085      for (int i = 0; i < childIndents; i++) {
086        intentPrefixBuilder.append(" ");
087      }
088      String intentPrefix = intentPrefixBuilder.toString();
089      if (value instanceof Map<?, ?>) {
090        writer.println(key + ":");
091        for (Map.Entry<String, Object> entry : ((Map<String, Object>) value).entrySet()) {
092          String mapKey = entry.getKey();
093          Object v = entry.getValue();
094          if (v instanceof List<?>) {
095            for (Object b : (List<?>) v) {
096              writer.println(intentPrefix + "  - " + b);
097            }
098          } else if (v instanceof Map<?, ?>) {
099            write(mapKey, v, writer, childIndents + 2);
100          } else {
101            writer.println(intentPrefix + mapKey + ": " + v);
102          }
103        }
104      } else if (value instanceof List<?>) {
105        writer.println(key + ":");
106        for (Object b : (List<?>) value) {
107          writer.println(intentPrefix + "- " + b);
108        }
109      } else {
110        writer.println(key + ": " + value);
111      }
112      writer.append('\n');
113    }
114
115    @Override
116    public void writeCustom(Object value, PrintWriter writer, String annoName) {
117      if (!(value instanceof String)
118          && !(value instanceof Character)
119          && !(value instanceof char[])) {
120        throw new IllegalArgumentException(
121            "Cannot write other than String, char and char[] for yaml config: annotation '"
122                + annoName
123                + "'");
124      }
125      writer.write(String.valueOf(value));
126    }
127  }
128}