MCPcopy Index your code
hub / github.com/Exlll/ConfigLib

github.com/Exlll/ConfigLib @v4.8.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v4.8.1 ↗ · + Follow
2,234 symbols 6,469 edges 96 files 114 documented · 5%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

ConfigLib

A Minecraft library for saving, loading, updating, and commenting YAML configuration files.

This library facilitates creating, saving, loading, updating, and commenting YAML configuration files. It does so by automatically mapping instances of configuration classes to serializable maps which are first transformed into YAML and then saved to some specified file.

Information on how to import this library can be found at the end of this documentation. For a step-by-step tutorial that shows most features of this library in action check out the Tutorial page on the wiki!

Features

  • Automatic creation, saving, loading, and updating of configuration files
  • Support for comments through annotations
  • Support for all primitive types, their wrapper types, and strings
  • Support for all Java enums, records, and POJOs (+ inheritance!)
  • Support for (nested) lists, sets, arrays, and maps
  • Support for BigInteger and BigDecimal
  • Support for LocalDate, LocalTime, LocalDateTime, and Instant
  • Support for UUID, File, Path, URL, and URI
  • Support for Bukkit's ConfigurationSerializable types (e.g. ItemStack)
  • Option to format the names of configuration elements
  • Option to exclude fields from being converted
  • Option to customize null handling
  • Option to customize serialization by providing your own serializers
  • Option to add headers and footers to configuration files
  • Option to overwrite configuration values with environment variables
  • ...and a few more!

Usage example

This section contains a short usage example to get you started. The whole range of features is discussed in the following sections. Information on how to import this library is located at the end of this documentation.

For a step-by-step tutorial with a more advanced example check out the Tutorial page on the wiki.

If you want support for Bukkit classes like ItemStack, check out the Configuration properties section.

public final class Example {
    // * To create a configuration annotate a class with @Configuration and make sure that
    //   it has a no-args constructor.
    // * Now add fields to that class and assign them default values.
    // * That's it! Fields can be private; setters are not required.
    @Configuration
    public static class BaseConfiguration {
        private String host = "127.0.0.1";
        private int port = 1234;
        // The library supports lists, sets, and maps.
        private Set<String> blockedAddresses = Set.of("8.8.8.8");
        // Fields can be ignored by making them final, transient, static or by
        // annotating them with @Ignore.
        private final double ignoreMe = 3.14;
    }

    // This library supports records; no @Configuration annotation required
    public record User(
            String username,
            @Comment("Please choose a strong password.")
            String password
    ) {}

    // Subclassing of configurations and nesting of configurations in other configurations
    // is also supported. Subclasses don't need to be annotated again.
    public static final class UserConfiguration extends BaseConfiguration {
        // You can add comments with the @Comment annotation. Each string in the comment
        // array is written (as a comment) on a new line.
        @Comment({"The admin user has full access.", "Choose a proper password!"})
        User admin = new User("root", "toor"); // The User class is a record!
        List<User> blockedUsers = List.of(
                new User("user1", null), // null values are supported
                new User("user2", null)
        );
    }

    public static void main(String[] args) {
        var configFile = Paths.get("/tmp/config.yml");
        var config = new UserConfiguration();

        // Save an instance to the configuration file
        YamlConfigurations.save(configFile, UserConfiguration.class, config);

        // Load a new instance from the configuration file
        config = YamlConfigurations.load(configFile, UserConfiguration.class);
        System.out.println(config.admin.username);
        System.out.println(config.blockedUsers);

        // Modify the configuration and save it again
        config.blockedUsers.add(new User("user3", "pass3"));
        YamlConfigurations.save(configFile, UserConfiguration.class, config);
    }
}

By running the above code, a new YAML configuration is created at /tmp/config.yml. Its content looks like this:

host: 127.0.0.1
port: 1234
blockedAddresses:
  - 8.8.8.8
# The admin user has full access.
# Choose a proper password!
admin:
  username: root
  # Please choose a strong password.
  password: toor
blockedUsers:
  - username: user1
  - username: user2
  - username: user3
    password: pass3

Two things are noticeable here:

  1. Not every user in the blockedUsers list has a password mapping. This is because null values are not output by default. That behavior can be changed by the builder.
  2. The password of the user with username user3 has no comment. This is due to limitations of the YAML library. Configurations in lists, sets, or maps cannot have their comments printed.

General information

In the following sections the term configuration type refers to any Java record type or to any non-generic class that is directly or indirectly (i.e. through subclassing) annotated with@de.exlll.configlib.Configuration. Accordingly, the term configuration refers to an instance of such a type. A configuration element is either a class field or a record component of a configuration type.

Declaring configuration types

To declare a configuration type, either define a Java record or annotate a class with @Configuration and make sure that it has a no-args constructor. The no-args constructor can be private. Inner classes (i.e. the ones that are nested but not static) have an implicit synthetic constructor with at least one argument and are, therefore, not supported.

Now simply add components to your record or fields to your class whose type is any of the supported types listed in the next section. You can (and should) initialize all fields of a configuration class with non-null default values.

Supported types

A configuration type may only contain configuration elements of the following types:

Type class Types
Boolean types boolean, and Boolean
Integer types byte, short, int, long, and their respective wrapper types
Floating point types float, double, and their respective wrapper types
Characters and strings char, Character, String
Big numeric types BigInteger, BigDecimal
Time related types LocalTime, LocalDate, LocalDateTime, Instant
Utility types UUID, File, Path, URL, URI
Enums Any Java enum
Configurations Any Java record or any class annotated with @Configuration
ConfigurationSerializable All Bukkit classes that implement this interface, like ItemStack
Collections (Nested) Lists, sets, maps*, or arrays of previously listed types

(*) Map keys can only be of simple or enum type, i.e. they cannot be in the Collections, Configurations, or ConfigurationSerializable type class.

For all types that are not listed in the table above, you can provide your own custom serializer.

Examples of supported types

The following class contains examples of types that this library supports:

public final class SupportedTypes {
    boolean supported;
    Character supported;
    String supported;
    LocalTime supported;
    UUID supported;
    ExampleEnum supported;    // where 'ExampleEnum' is some Java enum type
    ExampleConfig supported;  // where 'ExampleConfig' is a class annotated with @Configuration
    ExampleRecord supported;  // where 'ExampleRecord' is a Java record

    /* collection types */
    List<BigInteger> supported;
    Set<Double> supported;
    LocalDate[] supported;
    Map<ExampleEnum, ExampleConfig> supported;

    /* nested collection types */
    List<Map<ExampleEnum, LocalDate>> supported;
    int[][] supported;
    Map<Integer, List<Map<Short, Set<ExampleRecord>>>> supported;

    // supported if a custom serializer is registered
    java.awt.Point supported;

    // supported when a special properties object is used (explained further below)
    org.bukkit.inventory.ItemStack supported;
}

Examples of unsupported types

The following class contains examples of types that this library does (and will) not support:

public final class UnsupportedTypes<T> {
    Map<Point, String> unsupported;        // invalid map key
    Map<List<String>, String> unsupported; // invalid map key
    Box<String> unsupported;               // custom parameterized type
    List<? extends String> unsupported;    // wildcard type
    List<?> unsupported;                   // wildcard type
    List<?>[] unsupported;                 // wildcard type
    T unsupported;                         // type variable
    List unsupported;                      // raw type
    List[] unsupported;                    // raw type
    List<String>[] unsupported;            // generic array type
    Set<Integer>[] unsupported;            // generic array type
    Map<Byte, Byte>[] unsupported;         // generic array type
}

NOTE: Even though this library does not support these types, it is still possible to serialize them by providing a custom serializer via the @SerializeWith annotation. That serializer then has to be applied to top-level type (i.e. nesting must be set to 0, which is the default).

Loading and saving configurations

There are two ways to load and save configurations. Which way you choose depends on your liking. Both ways have five methods in common:

  • The save method converts a configuration to a string in YAML format and saves that string to a file. The file is created if it does not exist and is overwritten otherwise.
  • The load method creates a new configuration instance and populates it with values taken from a file. For classes, the no-args constructor is used to create a new instance. For records, the canonical constructor is called.
  • The update method is a combination of load and save and the method you'd usually want to use: it takes care of creating the configuration file if it does not exist and otherwise updates it to reflect changes to (the configuration elements of) the configuration type.
  • The write method works the same way as the save method but writes the string to a java.io.OutputStream.
  • The read method works the same way as the load method but reads the values from a java.io.InputStream.

Example of update behavior when configuration file exists

Let's say you have the following configuration type

@Configuration 
public final class C {
    int i = 10; 
    int j = 11; 
}

... and a YAML configuration file that contains:

i: 20
k: 30

Now, when you call the update method for that configuration type and file using any of the two options listed below, the configuration instance that update returns will have its i variable initialized to 20 and its j variable will have its default of 11. After the operation, the configuration file will contain the following content (note that k has been dropped):

i: 20
j: 11

To exemplify the usage of these five methods we assume for the following sections that you have implemented the configuration type below and have access to some regular java.nio.file.Path object configurationFile.

@Configuration
public final class Config { /* some fields */ }

Option 1

The first option is to create a YamlConfigurationStore instance and use it to save, load, or update configurations.

YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder().build();
YamlConfigurationStore<Config> store = new YamlConfigurationStore<>(Config.class, properties);

Config config1 = store.load(configurationFile);
store.save(config1, configurationFile);
Config config2 = store.update(configurationFile);

Using a YamlConfigurationStore directly is always more efficient than the second option show below, especially if you are calling any of its method multiple times.

Option 2

The second option is to use the static methods from the YamlConfigurations class.

Config config1 = YamlConfigurations.load(configurationFile, Config.class);
YamlConfigurations.save(configurationFile, Config.class, config1);
Config config2 = YamlConfigurations.update(configurationFile, Config.class);

Each of these methods has two additional overloads: One that takes a properties object and another that lets you configure a properties object builder. For example, the overloads of the load method are:

```java // overload 1 YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder().build(); Config config1 = YamlConfigurations.load(configurationFile, Config.class, properties);

// overload 2 Config config2 = YamlC

Extension points exported contracts — how you extend this code

Serializer (Interface)
Implementations of this interface convert instances of type T1 to a serializable type T2 and vice versa. [45 implementers]
configlib-core/src/main/java/de/exlll/configlib/Serializer.java
Environment (Interface)
(no doc) [4 implementers]
configlib-core/src/main/java/de/exlll/configlib/Environment.java
FileConfigurationStore (Interface)
Instances of this class save and load configurations using files. @param the configuration type [2 implementers]
configlib-core/src/main/java/de/exlll/configlib/FileConfigurationStore.java
IOStreamConfigurationStore (Interface)
Instances of this class read and write configurations from input streams and to output streams, respectively. The de [2 …
configlib-core/src/main/java/de/exlll/configlib/IOStreamConfigurationStore.java
FieldFilter (Interface)
Implementations of this interface test fields for specific conditions. [1 implementers]
configlib-core/src/main/java/de/exlll/configlib/FieldFilter.java

Core symbols most depended-on inside this repo

deserialize
called by 144
configlib-core/src/main/java/de/exlll/configlib/Serializer.java
select
called by 110
configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java
requireTargetType
called by 67
configlib-core/src/main/java/de/exlll/configlib/Validator.java
serialize
called by 56
configlib-core/src/main/java/de/exlll/configlib/Serializer.java
requireNonNull
called by 53
configlib-core/src/main/java/de/exlll/configlib/Validator.java
build
called by 45
configlib-core/src/main/java/de/exlll/configlib/ConfigurationProperties.java
extractCommentNodes
called by 40
configlib-core/src/main/java/de/exlll/configlib/CommentNodeExtractor.java
newBuilder
called by 39
configlib-core/src/main/java/de/exlll/configlib/ConfigurationProperties.java

Shape

Method 1,875
Class 333
Interface 16
Enum 10

Languages

Java100%

Modules by API surface

configlib-core/src/testFixtures/java/de/exlll/configlib/configurations/ExampleConfigurationA1.java384 symbols
configlib-core/src/testFixtures/java/de/exlll/configlib/configurations/ExampleConfigurationA2.java365 symbols
configlib-core/src/main/java/de/exlll/configlib/Serializers.java113 symbols
configlib-core/src/test/java/de/exlll/configlib/SerializerSelectorTest.java106 symbols
configlib-core/src/test/java/de/exlll/configlib/SerializersTest.java100 symbols
configlib-core/src/test/java/de/exlll/configlib/TypeSerializerTest.java73 symbols
configlib-core/src/test/java/de/exlll/configlib/ReflectTest.java59 symbols
configlib-core/src/testFixtures/java/de/exlll/configlib/configurations/ExampleConfigurationCustom.java58 symbols
configlib-core/src/test/java/de/exlll/configlib/PolymorphicSerializerTest.java56 symbols
configlib-yaml/src/test/java/de/exlll/configlib/YamlWriterTest.java54 symbols
configlib-core/src/testFixtures/java/de/exlll/configlib/TestUtils.java53 symbols
configlib-core/src/test/java/de/exlll/configlib/ConfigurationSerializerTest.java53 symbols

For agents

$ claude mcp add ConfigLib \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact