Skip to content

MrIvanPlays/AnnotationConfig

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

license issues api version support

AnnotationConfig

Make configurations with ease

JavaDocs: https://jd.mrivanplays.com/annotationconfig/

Examples

WARNING: You may want to read CHANGELOG.md before seeing anything from this section.

Config example

import com.mrivanplays.annotationconfig.core.annotations.ConfigObject;
import com.mrivanplays.annotationconfig.core.annotations.Ignore;
import com.mrivanplays.annotationconfig.core.annotations.Key;
import com.mrivanplays.annotationconfig.core.annotations.Max;
import com.mrivanplays.annotationconfig.core.annotations.Min;
import com.mrivanplays.annotationconfig.core.annotations.comment.Comment;
import com.mrivanplays.annotationconfig.core.serialization.DataObject;
import com.mrivanplays.annotationconfig.core.serialization.FieldTypeSerializer;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Comment("Generated by AnnotatedConfig v2.1.0")
@Comment("This is a config example for developers.")
public class ExampleAnnotatedConfig {

  @Comment("This value can only be between 1 and 3 ( 1 and 3 included )")
  @Min(minInt = 1)
  @Max(maxInt = 3)
  private int foo = 2;

  @Comment("This string cannot be longer than 20 characters ( spaces are included )")
  @Max(maxInt = 20)
  private String bar = "This is some string";

  @ConfigObject private MessagesSection messages = new MessagesSection();

  @Comment("All configurable messages")
  public static final class MessagesSection {

    @Comment("The no permission message")
    @Key("no-permission")
    private String noPermission = "You don't have permission to perform this command";

    @Comment("The no spamming message")
    @Key("no-spamming")
    private String noSpamming = "You can't spam this";

    public String getNoPermission() {
      return noPermission;
    }

    public String getNoSpamming() {
      return noSpamming;
    }
  }

  @Ignore private String importantClass = "com.mrivanplays.something.Important"; // this is ignored

  @Comment("This is also going to be serialized as a config object,")
  @Comment("but it is much more controllable rather than @ConfigObject")
  private SomethingToSerialize serialize = new SomethingToSerialize("foo", 1, (byte) 0x2);

  public static final class SomethingToSerialize {

    private final String foo;
    private final int bar;
    private final byte baz;

    public SomethingToSerialize(String foo, int bar, byte baz) {
      this.foo = foo;
      this.bar = bar;
      this.baz = baz;
    }

    public String getFoo() {
      return foo;
    }

    public int getBar() {
      return bar;
    }

    public byte getBaz() {
      return baz;
    }
  }

  /**
   * This should be registered before calling the dump method for this annotated config using the
   * SerializerRegistry
   */
  public static final class SomethingToSerializeSerializer
      implements FieldTypeSerializer<SomethingToSerialize> {

    @Override
    public SomethingToSerialize deserialize(DataObject data, Field field) {
      return new SomethingToSerialize(
          data.get("foo").getAsString(), data.get("bar").getAsInt(), data.get("baz").getAsByte());
    }

    @Override
    public DataObject serialize(SomethingToSerialize value, Field field) {
      DataObject ret = new DataObject();
      ret.put("foo", value.getFoo());
      ret.put("bar", value.getBar());
      ret.put("baz", value.getBaz());
      return ret;
    }
  }

  @Comment("This cannot have a negative value")
  @Min(minDouble = 0)
  @Key("barxtwo") // you can also apply @Key to regular fields, not just in config objects
  private double baz = 0.2;

  @Comment("AnnotatedConfig can also read & write lists")
  @Key("foo-list")
  private List<String> fooList = Arrays.asList("This is", "a lore", "as an example", "for list");

  @Comment("Lists can be of all primitive types")
  @Key("bar-list")
  private List<Integer> barList = Arrays.asList(1, 2, 3, 4);

  @Comment("Same for maps, but a map can only be Map<String, Object>")
  @Comment("otherwise you will need another object")
  @Key("foo-map")
  private Map<String, Object> fooMap =
      new LinkedHashMap<String, Object>() {
        {
          put("foo", 1);
          put("bar", "This is a section value");
          put("baz", 3);
        }
      };

  @Comment("This doesn't have a serializer registered")
  @Comment("so it gets serialized by the default serializer")
  @Key("default-serializer-example")
  private DefaultSerializationExample defaultSerExample =
      new DefaultSerializationExample("bar", 1, 5.6);

  public static final class DefaultSerializationExample {

    private String foo;
    private int bar;
    private double baz;

    public DefaultSerializationExample(String foo, int bar, double baz) {
      this.foo = foo;
      this.bar = bar;
      this.baz = baz;
    }

    public String getFoo() {
      return foo;
    }

    public int getBar() {
      return bar;
    }

    public double getBaz() {
      return baz;
    }
  }

  @Comment("You can create sections like this too")
  @Key("inside.section.foo")
  private String fooInsideSection = "foo";

  @ConfigObject
  @Key("section-inside-section")
  private SectionInsideSection insideSection = new SectionInsideSection();

  @Comment("This is a section inside a section example")
  public static class SectionInsideSection {

    @Comment("Comments are supported here too!")
    @Key("pp.baz")
    private int baz = 69420;

    @Comment("And here!")
    @Key("pp.foo")
    private double foo = 3.3;

    public int getBaz() {
      return baz;
    }

    public double getFoo() {
      return foo;
    }
  }

  // this is featured in the next example
  public static class SectionObject {

    private int aabb;
    private String ccdd;

    public SectionObject(int aabb, String ccdd) {
      this.aabb = aabb;
      this.ccdd = ccdd;
    }

    public int getAabb() {
      return aabb;
    }

    public String getCcdd() {
      return ccdd;
    }
  }

  @Comment("This is a section object list")
  private SectionObjectList<SectionObject> sectionObjectList =
      SectionObjectList.newBuilderForType(SectionObject.class)
          .defaultValue("asdfp", new SectionObject(1, "lorem"))
          .defaultValue("pepepepepe", new SectionObject(2, "ipsum"))
          .build();

  public int getFoo() {
    return foo;
  }

  public String getBar() {
    return bar;
  }

  public MessagesSection getMessages() {
    return messages;
  }

  public String getImportantClass() {
    return importantClass;
  }

  public SomethingToSerialize getSerialize() {
    return serialize;
  }

  public double getBaz() {
    return baz;
  }

  public List<String> getFooList() {
    return fooList;
  }

  public List<Integer> getBarList() {
    return barList;
  }

  public Map<String, Object> getFooMap() {
    return fooMap;
  }

  public DefaultSerializationExample getDefaultSerExample() {
    return defaultSerExample;
  }

  public String getFooInsideSection() {
    return fooInsideSection;
  }

  public SectionInsideSection getInsideSection() {
    return insideSection;
  }

  public SectionObjectList<SectionObject> getSectionObjectList() {
    return sectionObjectList;
  }
}

Config example output (YAML)

Keep in mind in order to show you all of the features of AnnotatedConfig, everything has been stuffed in 1 class. Don't forget that in Java you can do multiple classes ;) . Line count doesn't matter.

# Generated by AnnotatedConfig v2.1.0
# This is a config example for developers.

# This cannot have a negative value
barxtwo: 0.2

# This is a section object list
sectionObjectList:
  asdfp:
    aabb: 1
    ccdd: "lorem"
  pepepepepe:
    aabb: 2
    ccdd: "ipsum"

# This is also going to be serialized as a config object,
# but it is much more controllable rather than @ConfigObject
serialize:
  foo: "foo"
  bar: 1
  baz: 2

# This string cannot be longer than 20 characters ( spaces are included )
bar: "This is some string"

# Same for maps, but a map can only be Map<String, Object>
# otherwise you will need another object
foo-map:
  foo: 1
  bar: "This is a section value"
  baz: 3

# This value can only be between 1 and 3 ( 1 and 3 included )
foo: 2

# AnnotatedConfig can also read & write lists
foo-list:
  - "This is"
  - "a lore"
  - "as an example"
  - "for list"

# All configurable messages
messages:
  # The no spamming message
  no-spamming: "You can't spam this"
  # The no permission message
  no-permission: "You don't have permission to perform this command"

# This doesn't have a serializer registered
# so it gets serialized by the default serializer
default-serializer-example:
  foo: "bar"
  bar: 1
  baz: 5.6

# This is a section inside a section example
section-inside-section:
  pp:
    # Comments are supported here too!
    baz: 69420
    # And here!
    foo: 3.3

# Lists can be of all primitive types
bar-list:
  - 1
  - 2
  - 3
  - 4

inside:
  section:
    # You can create sections like this too
    foo: "foo"

Config dump/load

Keep in mind these are the simplest examples

Base code for all examples:

import com.mrivanplays.annotationconfig.utils.TypeToken;

File file = // ...
SerializerRegistry serializerRegistry = SerializerRegistry.INSTANCE;
serializerRegistry.registerSerializer(
    ExampleAnnotatedConfig.SomethingToSerialize.class,
    new ExampleAnnotatedConfig.SomethingToSerializeSerializer());
serializerRegistry.registerSerializer(
    new TypeToken<SectionObjectList<SectionObject>>() {}.getType(),
    new SectionObjectListSerializer<SectionObject>());
ExampleAnnotatedConfig annotatedConfig = new ExampleAnnotatedConfig();

YAML example:

YamlConfig.getConfigResolver().loadOrDump(anotatedConfig, file, /* loader settings */);

.conf/.properties example:

PropertyConfig.getConfigResolver().loadOrDump(annotatedConfig, file, /* loader settings */);

TOML example:

TomlConfig.getConfigResolver().loadOrDump(annotatedConfig, file, /* loader settings */);

Custom config type example:

// all values specified in the builder should be for the specific config type
ConfigResolver configResolver = ConfigResolver.newBuilder()
    /* other options inside the builder */
    .withCommentPrefix("# ") // comment prefix for the config type
    .withValueWriter(() -> /* insert value writer here */)
    .withValueReader(/* insert value reader here */)
    .shouldReverseFields(true /* should we reverse fields */)
    .build();

configResolver.loadOrDump(annotatedConfig, file, /* loader settings */);

Installation

Maven:

<build>
  <plugins>
    <plugin>
      <version>3.7.0</version>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <compilerArgs>
          <arg>-parameters</arg>
        </compilerArgs>
      </configuration>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-shade-plugin</artifactId>
      <version>3.1.1</version>
      <configuration>
        <relocations>
          <!-- Relocating is only necessary if you're shading for other library addition -->
          <relocation>
            <pattern>com.mrivanplays.annotationconfig</pattern>
            <shadedPattern>[YOUR PLUGIN PACKAGE].annotationconfig
            </shadedPattern> <!-- Replace this -->
          </relocation>
        </relocations>
      </configuration>
      <executions>
        <execution>
          <phase>package</phase>
          <goals>
            <goal>shade</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

<repositories>
  <repository>
    <id>ivan</id>
    <url>https://repo.mrivanplays.com/repository/ivan/</url>
  </repository>
</repositories>

<dependencies>
  <dependency>
    <groupId>com.mrivanplays</groupId>
    <!-- Types: toml, yaml -->
    <!-- If you want .conf/.properties, or custom implementation configuration, you can set the type to core -->
    <artifactId>annotationconfig-(type)</artifactId> <!-- Replace type -->
    <version>VERSION</version> <!-- Replace with latest version -->
    <scope>compile</scope>
  </dependency>
</dependencies>