Skip to content
 
 

Repository files navigation

Easy Config

Important

There currently is no Javadoc for any methods or classes.

AI Usage

AI is used when I am unfamiliar with a topic so that I can get basic knowledge and AI is used for Javadoc generation.

Easy Config is a Java library designed to bridge the gap between your code and configuration files. By utilizing Guava's TypeToken, it ensures that every piece of data you load is exactly the type you expect, even for complex generic structures like List<Map<String, Integer>>.

Note: Comments are implemented on Nodes and Sections, but Jackson doesn't allow writing comments to files.

Licenses used
  • Apache-2.0:

    • api-definition
    • api-implementation
    • api-fileformat-jackson-common
    • api-fileformat-json
    • api-fileformat-toml
    • api-fileformat-yaml-1.2
  • GPLv3:

    • api-serialization-bukkit (The Bukkit/Spigot API is GPLv3 licensed.)

Why use it?

  • Leveraging Guava's TypeToken for True Type-Safety.
  • Out-of-the-box support for YAML, JSON and TOML.
  • Fluent builder-based API that lets you define your configuration structure directly in code.
  • Extensible-first design to allow custom serializers, new file formats, or even a completely new backend.

Quick Start

Important

The AIO Package is now deprecated!
Use the BOM instead!

Add EasyConfig to your project, see Maven Central.

Usage & Examples

Config File

Open File
Open Implementation File

Usage Examples

Creating a Config File

Also see ConfigFileBuilder.java
Also see ConfigFileBuilderImpl.java
Also see ConfigSectionBuilderImpl.java
Also see ConfigNodeBuilderImpl.java

import java.util.List;

ConfigFile myConfig = new ConfigFile.builder()
        .node(" location", builder -> {
            builder.type(Location.class);
            builder.serializer(LocationSerializer.instance()); // Built-in serializers are singletons
            builder.defaultValue(new Location(x, y, z));
        })
        .node("names", builder -> {
            // Example: Map<String, Integer> becomes .type(Map.class, String.class, Integer.class)
            builder.type(new TypeToken<List<String>>() {
            });
        })
        .env("node_and_env_key", int.class)
        .env("node_key", "separate_env_key", String.class)
        .section("identity", builder -> {
            builder.node("name", String.class);
            builder.node("age", Integer.class);
        })
        .build();

Config Sections

Open File
Open Implementation File

Access an section
ConfigSection section = myConfig.section("my", "cool", "section");
Modify an section

Also see MutableConfigSection
Also see MutableConfigSectionImpl

try(MutableConfigSection mutable = section.mutable()){ // Implements AutoClosable, recommended usage
        mutable.addNode(myNode);
}

// Alternate way
var mutable = section.mutable();
mutable.addNode(myNode);
mutable.close();

Config Nodes

Open File
Open Implementation File

Access an node
ConfigNode node = myConfig.section("my", "cool", "section").<String>node("node");
Modify an node

Also see MutableConfigNode
Also see MutableConfigNodeImpl

try(MutableConfigNode mutable = node.mutable()){ // Implements AutoClosable, recommended usage
        mutable.setValue("Good day");
}

// Alternate way
var mutable = node.mutable();
mutable.setValue("Good day");
mutable.close();

Formats & Format Providers

Also see Format Also see FormatProvider Also see FileFormatProvider

Write a file to disk
CopiedEasyConfig easyConfig = EasyConfig.instance().copy();
ConfigFile myConfig = new ConfigFile.builder()
        .node(" location", builder -> {
          builder.type(Location.class);
          builder.serializer(LocationSerializer.instance()); // Built-in serializers are singletons
          builder.defaultValue(new Location(x, y, z));
        })
        .node("names", builder -> {
          // Example: Map<String, Integer> becomes .type(Map.class, String.class, Integer.class)
          builder.type(new TypeToken<List<String>>() {
          });
        })
        .section("identity", builder -> {
          builder.node("name", String.class);
          builder.node("age", Integer.class);
        })
        .build();

easyConfig.provider(YamlFileFormat.class).save(easyConfig, myConfig);
Read a file from disk
CopiedEasyConfig easyConfig = EasyConfig.instance().copy();
ConfigFile myConfig = new ConfigFile.builder()
        .node(" location", builder -> {
          builder.type(Location.class);
          builder.serializer(LocationSerializer.instance()); // Built-in serializers are singletons
          builder.defaultValue(new Location(x, y, z));
        })
        .node("names", builder -> {
          // Example: Map<String, Integer> becomes .type(Map.class, String.class, Integer.class)
          builder.type(new TypeToken<List<String>>() {
          });
        })
        .section("identity", builder -> {
          builder.node("name", String.class);
          builder.node("age", Integer.class);
        })
        .build();

easyConfig.provider(YamlFileFormat.class).load(easyConfig, myConfig); // This populates the myConfig variable.

Serializers

You can create your own serializers for custom types. The concrete implementations for built-in serializers are located in the com.pixelatedslice.easyconfig.impl.serialization.builtin package and are singletons.

Example

Open File

public final class LocationSerializerImpl implements BuiltInBukkitSerializer<Location> {
  private static final TypeToken<Location> typeToken = new TypeToken<Location>() {
  };

  private LocationSerializerImpl() {
  }

  public static LocationSerializerImpl instance() {
    return LocationSerializerImplHolder.INSTANCE;
  }

  @Override
  @NonNull
  public TypeToken<Location> forType() {
    return typeToken;
  }

  @Override
  public void serialize(@Nullable Location value, @NonNull ConfigSectionBuilder sectionBuilder) {
    Objects.requireNonNull(sectionBuilder);

    sectionBuilder.node(
            "world",
            ((value != null) && (value.getWorld() != null)) ? value.getWorld().getName() : null,
            String.class
    );
    sectionBuilder.node("x", (value != null) ? value.getX() : null, Double.class);
    sectionBuilder.node("y", (value != null) ? value.getY() : null, Double.class);
    sectionBuilder.node("z", (value != null) ? value.getZ() : null, Double.class);
    sectionBuilder.node("yaw", (value != null) ? value.getYaw() : null, Float.class);
    sectionBuilder.node("pitch", (value != null) ? value.getPitch() : null, Float.class);
  }

  @Override
  @NonNull
  public Location deserialize(@NonNull ConfigSection section) {
    Objects.requireNonNull(section);

    var world = section
            .node(String.class, "world")
            .flatMap(ConfigNode::value)
            .map(Bukkit::getWorld)
            .orElse(null);
    var x = section.node(Double.class, "x").flatMap(ConfigNode::value).orElseThrow();
    var y = section.node(Double.class, "y").flatMap(ConfigNode::value).orElseThrow();
    var z = section.node(Double.class, "z").flatMap(ConfigNode::value).orElseThrow();
    var yaw = section.node(Float.class, "yaw")
            .flatMap(ConfigNode::value)
            .orElseThrow();
    var pitch = section.node(Float.class, "pitch")
            .flatMap(ConfigNode::value)
            .orElseThrow();

    return new Location(
            world,
            x, y, z,
            yaw, pitch
    );
  }

  private static final class LocationSerializerImplHolder {
    private static final LocationSerializerImpl INSTANCE = new LocationSerializerImpl();

    private LocationSerializerImplHolder() {
    }
  }
}

About

Typesafe API to read and write Configs in Java

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages