Simple Java/POJO config library written with love and Lombok

Overview

Okaeri Configs

License Total lines Repo size Contributors Discord

Supported platforms (general use)

General implementations based on standard format libraries directly.

  • HJSON
    • 🌟 hjson-java: Human JSON is the best choice for your JSON configuration, small (core+55kB) but yet powerful
  • JSON
    • 🌟 Google GSON: ideal for GSON lovers, best suited for in-app storage or advanced user configurations
    • json-simple: fairly limited but still working, no pretty print, probably best suited for simple in-app storage
  • Binary
  • HOCON

Supported platforms (environment dependant)

Special implementations for safe use in specific environment, eg. gameservers.

  • Bukkit/Spigot/PaperSpigot (Minecraft server)
  • Velocity (Minecraft proxy), Sponge (Minecraft server)
    • currently no ready adapters, but use with Google GSON, Lightbend (HOCON) Config is possible (remember to exclude format specific dependencies (eg. gson) when shading, these should be provided by the environment directly).

Validation extensions

  • 🌟 Okaeri Validator: simple validator with jakrataee-like annotations but much less code (+15kB)
  • Jakarta EE: hibernate-validator based with full Jakarta Bean Validation 3.0 support

Recommendations

For standalone platforms hjson module is the best choice, it supports all the features, eg. full comment support. Combine it with Okaeri Validator for the best config experience. Total of only ~135kB, less than half of just the latest snakeyaml 1.28 which is 319kB!

For any platform if some form of config validation is applicable (eg. requiring that integer is positive) it is recommended to use Okaeri Validator when possible. Only few kilobytes but makes for a lot better experience for the end-user and developer too.

Genesis

Okaeri's configuration library is an easy way to use java classes as config adapters:

  • Supports different environments with minimal hassle and relatively small footprint
  • Allows for even complex types to be serialized/deserialized
  • Enhances your configs with durable comments and strongly-typed fields
  • Provides ability to access typed fields with the classic getters and setters
  • Core library is just ~60kB in size, most of the adapters require only ~100 lines of code

Example

@Header("################################################################")
@Header("#                                                              #")
@Header("#    okaeri-configs test                                       #")
@Header("#                                                              #")
@Header("#    Nie wiesz jak uzywac? Zerknij do dokumentacji!            #")
@Header("#    https://github.com/OkaeriPoland/okaeri-configs            #")
@Header("#                                                              #")
@Header("#    Trouble using? Check out the documentation!               #")
@Header("#    https://github.com/OkaeriPoland/okaeri-configs            #")
@Header("#                                                              #")
@Header("################################################################")
@Names(strategy = NameStrategy.HYPHEN_CASE, modifier = NameModifier.TO_LOWER_CASE)
public class TestConfig extends OkaeriConfig {

    @Variable("APP_TOKEN") // use jvm property or environment variable if available
    @Comment({"Klucz prywatny API", "API secret"})
    private String token = "";

    @CustomKey("example-list")
    @Comment({"Example list", "providing @CustomKey demonstration"})
    private List<String> exampleList = Collections.singletonList("127.0.0.1");

    @Comment({"Simple maps", "ready to go"})
    private Map<String, String> messages = Collections.singletonMap("test", "testing");

    @Comment({"Test complex map 1", "looks like complex maps are working too"})
    private Map<String, Map<String, Integer>> complexMap = Collections.singletonMap("aa", Collections.singletonMap("bb", 222));

    @Comment("Test complex map 2")
    private Map<String, Map<Integer, String>> complexMap2 = Collections.singletonMap("bb", Collections.singletonMap(232, "aadda"));

    @Comment("Custom objects can be serialized")
    private Location spawn = new Location(null, 1, 2, 3, 4, 5);

    @Comment("Non-string map keys")
    private Map<Integer, String> levels = Collections.singletonMap(1, "aaaaaa");

    @Comment("okaeri-configs likes classes more than primitives")
    private Integer version = 2;

    @Comment({"Test enum", "very nice", "right?"})
    private TestEnum testEnum = TestEnum.ONE_THO_THREE;

    @Comment("Test enum list")
    private List<TestEnum> testEnumList = Arrays.asList(TestEnum.ONE, TestEnum.ONE_THO_THREE);

    @Comment("Test enum set")
    private Set<TestEnum> testEnumSet = new HashSet<>(Arrays.asList(TestEnum.ONE, TestEnum.ONE_THO_THREE));

    @Comment("Test custom object list")
    @Comment(".. and repeating comments")
    private List<Location> testLocationList = Arrays.asList(
            new Location(null, 1, 2, 3, 4, 5),
            new Location(null, 3, 3, 5, 6, 9)
    );

    @Comment("Uber-complex-map test")
    private Map<TestEnum, Location> enumToLocationMap = Collections.singletonMap(TestEnum.THREE, new Location(null, 1, 2, 3, 4, 5));

    @CustomKey("list-to-uber-complex-map")
    @Comment("List-to-Uber-complex-map test")
    private List<Map<TestEnum, Location>> listMapEnumToLocationMap = Arrays.asList(
            Collections.singletonMap(TestEnum.THREE, new Location(null, 1, 2, 3, 4, 5)),
            Collections.singletonMap(TestEnum.ONE_THO_THREE, new Location(null, 3, 2, 3, 4, 5))
    );

    @Comment("Math test")
    private BigInteger bigInteger = new BigInteger("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999876543210");

    @Exclude
    private Instant start = Instant.now();

    /* ... */
}

Usage

With create(clazz, initializer)

TestConfig config = ConfigManager.create(TestConfig.class, (it) -> {
    it.withConfigurer(new YamlBukkitConfigurer(), new SerdesBukkit()); // specify configurer implementation, optionally additional serdes packages
    it.withBindFile("config.yml"); // specify File or pathname
    it.saveDefaults(); // save file if does not exists
    it.load(true); // load and save to update comments/new fields 
});

With create(clazz)

TestConfig config = (TestConfig) ConfigManager.create(TestConfig.class)
    .withConfigurer(new YamlBukkitConfigurer(), new SerdesBukkit()) // specify configurer implementation, optionally additional serdes packages
    .withBindFile("config.yml") // specify File or pathname
    .saveDefaults() // save file if does not exists
    .load(true); // load and save to update comments/new fields

Supported types

  • Subconfigs: Any config can be used in the fields of another (OkaeriConfig)
  • Basic Java types: Boolean, Byte, Character, Double, Float, Integer, Long, Short, String
  • Primitives: boolean, byte, char, double, float, int, long, short
  • Math types: java.math.BigInteger, java.math.BigDecimal
  • Complex types:
    • Map: results in LinkedHashMap
    • Set: results in HashSet
    • List: results in ArrayList
    • Any type assignable from Map or Collection if non-interface type is used and default constructor is available
  • Enum types: any enum is automatically transformed using valueOf() (with case-insensitive fallback) and name()
  • Custom types using ObjectSerializer/ObjectTransformer (see in supported platforms)
Comments
  • Access to the parent Map in the ObjectSerializer

    Access to the parent Map in the ObjectSerializer

    How can I access the key of a Map<String, Object> when serializing a custom object?

    I have a Channel object where I want to map the key of the map to the identifier of the channel. How can I do this?

        @Override
        public boolean supports(final @NonNull Class<? super Channel> type) {
            return Channel.class.isAssignableFrom(type);
        }
    
        @Override
        public void serialize(final @NonNull Channel object, final @NonNull SerializationData data) {
            data.add("id", object.uuid());
            data.add("identifier", object.identifier()); // <--- set the identifier as the Map.key instead
            data.add("name", object.displayName());
            data.add("format", Objects.requireNonNullElse(object.format(), Channel.DEFAULT_CHANNEL_MESSAGE_FORMAT).getOrNull(Format.MINI_MESSAGE_TEMPLATE));
    
            for (final Setting<?> setting : object.settings().settings()) {
                data.add(setting.key().asString(), object.getOrNull(setting));
            }
        }
    
        @Override
        public Channel deserialize(final @NonNull DeserializationData data, final @NonNull GenericsDeclaration generics) {
            final Channel.Builder builder = Channel.channel();
    
            if (data.containsKey("id"))
                builder.uuid(data.get("id", UUID.class));
            if (data.containsKey("identifier")) // <--- this should come from the Map.key
                builder.identifier(data.get("identifier", String.class));
            if (data.containsKey("name"))
                builder.displayName(data.get("name", Component.class));
            if (data.containsKey("format"))
                builder.format(Format.miniMessageFormat(data.get("format", String.class)));
    
            for (final Setting<?> setting : Channel.SETTINGS) {
                addSetting(data, builder, setting.key().asString().replace(SChat.NAMESPACE + ":", ""), setting);
            }
    
            return builder.build();
        }
    
        private <V> void addSetting(final DeserializationData data, final Channel.Builder builder, final String key, final Setting<V> setting) {
            if (data.containsKey(key))
                builder.setting(setting, data.get(key, setting.type()));
        }
    
    enhancement 
    opened by Silthus 19
  • Null value after config update

    Null value after config update

    When config file is updated with new variable, it sets null value for it and in the config class I have something different in my initializer.

    Steps to reproduce the behavior:

    Create configuration file with built-in class. Initialize class and assign it to variable at same time to create an instance of object to the config. After doing that update your configuration file by adding new String variable to constructor params and initialize it in the class also (class variable defined earlier). Try to update your configuration with preset presented on the bottom. Your updated String variable will be null after updating the config with newly added custom class variable.

    All used libs are versioned in 3.4.2.

    Config classes and setup routine

    public PluginConfiguration createPluginConfiguration(File pluginConfigurationFile) {
            return ConfigManager.create(PluginConfiguration.class, (it) -> {
                it.withConfigurer(new OkaeriValidator(new YamlBukkitConfigurer(SectionSeparator.NEW_LINE), true), new SerdesBukkit());
                it.withBindFile(pluginConfigurationFile);
                it.saveDefaults();
                it.load(true);
            });
        }
    

    Try to update this config file with java class file attached. bug-files.zip

    invalid 
    opened by plytki 7
  • Not compatible with Java 17

    Not compatible with Java 17

    Describe the bug Config generator printing null error with Java 17 when launching newest 1.18 minecraft version (paper).

    To Reproduce Steps to reproduce the behavior:

    1. Create config with ConfigurationFactory and YamlBukkitConfigurer.
    2. Launch minecraft server.
    3. See error.

    Expected behavior It wont create configuration file.

    Library version I've tried 3.1.0, 3.4.2 and 4.0.0-beta2.

    Config classes and setup routine Add config files source code and setup routine (the part with ConfigManager.create(...)).

    invalid 
    opened by plytki 5
  • Empty string is added each time the configuration is loaded.

    Empty string is added each time the configuration is loaded.

    Describe the bug New lines adds every time config loaded.

    To Reproduce

    TestConfig testConfig = ConfigManager.create(TestConfig.class, (it) -> {
                it.withConfigurer(new YamlBukkitConfigurer(SectionSeparator.NEW_LINE), new SerdesBukkit());
                it.withBindFile(getDataFolder().toPath().resolve("settings.yml"));
                it.withRemoveOrphans(true);
                it.saveDefaults();
                it.load(true);
            });
    

    Each loading config will add extra empty line after header.

    Expected behavior Line shouldn't be added if already exists.

    Library version

        implementation 'eu.okaeri:okaeri-configs-yaml-bukkit:4.0.6'
        implementation 'eu.okaeri:okaeri-configs-serdes-commons:4.0.6'
        implementation 'eu.okaeri:okaeri-configs-serdes-bukkit:4.0.6'
    
    wontfix 
    opened by Brikster 4
  • Newly added variables are not in the proper order

    Newly added variables are not in the proper order

    Newly added variables are not in proper order

    Steps to reproduce the behavior:

    1. Create configuration file with configurer presented below.
    2. Add new variable to your config class.
    3. Update your config.

    Your newly added variables won't be placed in order as you initialized them in your config class.

    All libs are versioned in 3.4.2.

    ConfigManager.create(PluginConfiguration.class, (it) -> {
                it.withConfigurer(new OkaeriValidator(new YamlBukkitConfigurer(SectionSeparator.NEW_LINE), true), new SerdesBukkit());
                it.withBindFile(pluginConfigurationFile);
                it.saveDefaults();
                it.load(true);
            });
    
    bug 
    opened by plytki 4
  • Problem z mapą objektów z rozserzeniem OkaeriConfigs

    Problem z mapą objektów z rozserzeniem OkaeriConfigs

    java.lang.NullPointerException: Cannot invoke "java.util.Map.put(Object, Object)" because "this.this$0.flags" is null
    	at pl.minecodes.mineplots.data.configuration.PlotsConfiguration$1.<init>(PlotsConfiguration.java:50) ~[?:?]
    	at pl.minecodes.mineplots.data.configuration.PlotsConfiguration.<init>(PlotsConfiguration.java:48) ~[?:?]
    	at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?]
    	at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:78) ~[?:?]
    	at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?]
    	at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?]
    	at java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:128) ~[?:?]
    	at jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:350) ~[?:?]
    	at java.lang.Class.newInstance(Class.java:642) ~[?:?]
    	at pl.minecodes.mineplots.data.okaeriConfigsConfigManager.create(okaeriConfigsConfigManager.java:23) ~[?:?]
    	at pl.minecodes.mineplots.data.okaeriConfigsConfigManager.create(okaeriConfigsConfigManager.java:50) ~[?:?]
    	at pl.minecodes.mineplots.Plots.onEnable(Plots.java:19) ~[?:?]
    	at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:263) ~[patched_1.17.jar:git-Paper-74]
    	at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:370) ~[patched_1.17.jar:git-Paper-74]
    	at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:500) ~[patched_1.17.jar:git-Paper-74]
    	at org.bukkit.craftbukkit.v1_17_R1.CraftServer.enablePlugin(CraftServer.java:518) ~[patched_1.17.jar:git-Paper-74]
    	at org.bukkit.craftbukkit.v1_17_R1.CraftServer.enablePlugins(CraftServer.java:432) ~[patched_1.17.jar:git-Paper-74]
    	at org.bukkit.craftbukkit.v1_17_R1.CraftServer.reload(CraftServer.java:957) ~[patched_1.17.jar:git-Paper-74]
    	at org.bukkit.Bukkit.reload(Bukkit.java:769) ~[patched_1.17.jar:git-Paper-74]
    	at org.bukkit.command.defaults.ReloadCommand.execute(ReloadCommand.java:54) ~[patched_1.17.jar:git-Paper-74]
    	at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:159) ~[patched_1.17.jar:git-Paper-74]
    	at org.bukkit.craftbukkit.v1_17_R1.CraftServer.dispatchCommand(CraftServer.java:821) ~[patched_1.17.jar:git-Paper-74]
    	at org.bukkit.craftbukkit.v1_17_R1.CraftServer.dispatchServerCommand(CraftServer.java:784) ~[patched_1.17.jar:git-Paper-74]
    	at net.minecraft.server.dedicated.DedicatedServer.handleConsoleInputs(DedicatedServer.java:516) ~[patched_1.17.jar:git-Paper-74]
    	at net.minecraft.server.dedicated.DedicatedServer.tickChildren(DedicatedServer.java:478) ~[patched_1.17.jar:git-Paper-74]
    	at net.minecraft.server.MinecraftServer.tickServer(MinecraftServer.java:1404) ~[patched_1.17.jar:git-Paper-74]
    	at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1180) ~[patched_1.17.jar:git-Paper-74]
    	at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:320) ~[patched_1.17.jar:git-Paper-74]
    	at java.lang.Thread.run(Thread.java:831) [?:?]
    
    
    invalid 
    opened by arthurr0 4
  • JSON Gson configs do not encode special characters

    JSON Gson configs do not encode special characters

    Describe the bug JsonGsonConfigurer does not encode characters like ó, ę, ś, ą, ń, ć, », &

    To Reproduce Steps to reproduce the behavior: Create a normal config using the JsonGsonConfigurer

    Expected behavior Characters should be encoded normally instead they're replaced as ? or an escaped java unicode character like \u0026

    Library version json gson configs ver 3.4.2

    Config classes and setup routine coreConfig = ConfigManager.create(CoreConfig.class, (it) -> it.withConfigurer(new JsonGsonConfigurer()) .withBindFile(new File(this.getDataFolder(), "config.json")) .saveDefaults() .load(true));

    bug 
    opened by oskarscot 3
  • No comments for all options

    No comments for all options

    Describe the bug No comments for all configuration options. To Reproduce No errors in logs.

    Library version 3.4.2

    Config classes and setup routine

    Configuration class
    @Data
    @EqualsAndHashCode(callSuper = false)
    @Names(strategy = NameStrategy.HYPHEN_CASE, modifier = NameModifier.TO_LOWER_CASE)
    public class LocaleMessages extends OkaeriConfig {
    
        @Exclude
        private MiniMessage miniMessage = MiniMessage.miniMessage();
    
        @Comment({
                "PL: Gdy użytkownik nie posiada uprawnień do komendy.",
                "EN: "
        })
        private Component dontHavePermission = this.miniMessage.parse("<red>You are not permitted to perform this command! <dark_gray>(<yellow>{permission}<dark_gray>)");
    
        @Comment({
                "PL: Gdy użytkownik nie istnieje w bazie danych.",
                "EN: "
        })
        private Component userDoesNotExist = this.miniMessage.parse("<red>User does not exist in the database.");
    
        @Comment({
                "PL: Gdy plugin wykryje konto premium w niedozwolonej akcji dla tego typu konta.",
                "EN: "
        })
        private Component premiumAccountDetected = this.miniMessage.parse("<red>Premium account detected!");
    
        @Comment({
                "PL: Gdy plugin wykryje konto cracked w niedozwolonej akcji dla tego typu konta.",
                "EN: "
        })
        private Component crackedAccountDetected = this.miniMessage.parse("<red>Cracked account detected!");
       
    ...
    
    CofnigManager configuration
            return ConfigManager.create(clazz, it -> it
                    .withConfigurer(new YamlBungeeConfigurer("#", SectionSeparator.NEW_LINE), registry -> {
                        registry.register(new StringToComponentTransformer());
                    })
                    .withBindFile(file)
                    .saveDefaults()
                    .load(true)
            );
    

    image

    bug 
    opened by arthurr0 3
  • System do migracji / wprowadzania zmian w konfiguracji

    System do migracji / wprowadzania zmian w konfiguracji

    Propozycja dodania adnotacji @Migrate

    Działanie Adnotacja umożliwiłaby migracje pól np. ze starych systemów do zarządzania konfiguracją do nowych lub pozwoliłaby na zmianę lokalizacji pola. Jak by to miało wyglądać? Mamy przypadek, gdzie wartość trzymana jest pod kluczem np. new_key więc plik jak jest ładowany to po nim szuka. Jeśli damy w takim przypadku do załadowania stary plik gdzie ta sama wartość była trzymana pod kluczem np. old_key to jej nie znajdzie. Adnotacja Migrate miałaby działać na takiej zasadzie, że jeśli przy ładowaniu nie znajdzie nigdzie wartości w configu pod nowym (aktualnym) kluczem to spróbuje ją znaleźć po starym kluczu z adnotacji Migrate. Dodatkowo jeśli znajdzie wartość pod starym kluczem to się pozbędzie się starego pola z pliku i przypisze jego wartość nowemu kluczowi.

    config.yml (wersja 1):

    old_key: value
    

    config.yml (wersja 2):

    new_key: value
    

    Configuration.java

    public class Configuration extends OkaeriConfig {
    
      @Migrate("old_key")
      @CustomKey("new_key")
      public String value = "Lorem ipsum";
    
    }
    

    Nie jestem pewien czy wszystko dobrze opisałem ale mam nadzieje, że tak.

    enhancement 
    opened by P3ridot 2
  • Comments inside subconfigs from collections and maps don't work

    Comments inside subconfigs from collections and maps don't work

    Describe the bug

    Created yaml file doesn't have comments on the field with subconfigs. Here is the code example:

     // That's field in OkaeriConfig with Map of subconfigs (another OkaeriConfig impl.)
     private Map<String, ChatNotificationChannelConfig> channels = new LinkedHashMap<String, ChatNotificationChannelConfig>() {{
        put("default", new ChatNotificationChannelConfig());
        put("donate", new ChatNotificationChannelConfig(
           30,
           Lists.newArrayList("some message"),
           false,
           false
        ));
     }};
    
     @Getter
     @AllArgsConstructor
     @NoArgsConstructor
     @SuppressWarnings("FieldMayBeFinal")
     @Names(strategy = NameStrategy.HYPHEN_CASE, modifier = NameModifier.TO_LOWER_CASE)
     public static class ChatNotificationChannelConfig extends OkaeriConfig {
    
        @Comment("Time in seconds for periodically broadcasting")
        private int time = 60;
    
        @Comment
        @Comment({"line1", "line2"})
        private List<String> messages = Lists.newArrayList(
         "&8===================================\n" +
         "text\n" +
         "&8==================================="
        );
    
        @Comment
        @Comment("Enable this, if you want to restrict channel by permission")
        private boolean permissionRequired = false;
    
        @Comment
        @Comment("Enable this, if you want messages to be sent randomly")
        private boolean randomOrder = false;
    
     }
    

    Config is loaded with:

    ConfigManager.create(configClass, config -> {
        config.withConfigurer(new OkaeriValidator(new YamlBukkitConfigurer(), true), new SerdesCommons(), new SerdesBukkit());
        config.withBindFile(dataFolderPath.resolve(fileName));
        config.withRemoveOrphans(true);
        config.saveDefaults();
        config.load(true);
    });
    

    The final yaml file looks like this (there are no comments):

    chat:
      enable: true
      channels:
        default:
          time: 60
          messages:
          - |-
            &8===================================
            text
            &8===================================
          permission-required: false
          random-order: false
        donate:
          time: 30
          messages:
          - some message
          permission-required: false
          random-order: false
    
    duplicate wontfix 
    opened by Brikster 1
  • Nested Object Serialization

    Nested Object Serialization

    Describe the bug I have the following config and want to use the already registered serializer for the message text of the object:

    public class ConfigTest extends OkaeriConfig {
    
        List<Message> messages = new ArrayList<>(List.of(
                new Message(text("Hi")),
                new Message(text("there!"))
        ));
    }
    
    @Data
    public class Message {
    
        private final Component text;
    
        public Message(Component text) {
            this.text = text;
        }
    }
    
    final class MessageSerializer implements ObjectSerializer<Message> {
    
        @Override
        public boolean supports(Class<? super Message> type) {
            return Message.class.isAssignableFrom(type);
        }
    
        @Override
        public void serialize(Message object, SerializationData data) {
            data.add("text", object.getText());
        }
    
        @Override
        public Message deserialize(DeserializationData data, GenericsDeclaration generics) {
            return new Message(data.get("text", Component.class));
        }
    }
    
    final class MiniMessageComponentSerializer extends BidirectionalTransformer<String, Component> {
    
        private final MiniMessage parser = MiniMessage.miniMessage();
    
        @Override
        public GenericsPair<String, Component> getPair() {
            return new GenericsPair<>(GenericsDeclaration.of(String.class), GenericsDeclaration.of(Component.class));
        }
    
        @Override
        public Component leftToRight(final @NonNull String data, final @NonNull SerdesContext serdesContext) {
            return parser.deserialize(data);
        }
    
        @Override
        public String rightToLeft(final @NonNull Component data, final @NonNull SerdesContext serdesContext) {
            return parser.serialize(data);
        }
    }
    
    @PluginMain
    public class TemplatePlugin extends JavaPlugin {
    
        @Getter
        @Accessors(fluent = true)
        private ConfigTest config;
    
        public TemplatePlugin() {
        }
    
        public TemplatePlugin(
                JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) {
            super(loader, description, dataFolder, file);
        }
    
        @Override
        public void onEnable() {
            loadConfig();
        }
    
        private void loadConfig() {
            this.config = ConfigManager.create(ConfigTest.class, config -> config
                    .withConfigurer(new YamlBukkitConfigurer(), new SerdesBukkit(), new SerdesCommons(), new ExamplePack())
                    .withBindFile("config.yml")
                    .saveDefaults()
                    .load(true));
        }
    }
    

    To Reproduce See the following repository with a failing test: https://github.com/sVoxelDev/okaeri-nested-serialization-bug

    Expected behavior The serializers should leverage already existing serializers.

    good first issue invalid 
    opened by Silthus 1
  • Give access to root object in serializer

    Give access to root object in serializer

    I'd like to access root object in serializer. I'll give an exmaple:

    I have object... For eg. ComplexMessage that contains ChatMessage & ActionbarMessage . In normal serializer I would serialize it to something like that:

    message:
      chat: "Chat message"
      actionbar: "Actionbar message"
    

    which is fine when I have 2 messages types used

    But when I have only 1 message type (for eg. chat) I ends up with

    message:
      chat: "Chat message"
    

    but I wants it's to be simpler - and serialize it as String:

    message: "Chat message"
    
    opened by P3ridot 0
  • Implement comments for maps and collections with snakeyaml

    Implement comments for maps and collections with snakeyaml

    Fix issue #17.

    Rewritten ConfigPostprocessor to provide first line of collection to a walker (previously, lines that begins from "-" were written as is). YamlSectionWalker's implementations now resolve subtypes of Map and Collection and append comments of their fields.

    Now there is an ability to create commented Lists, Maps, Lists of Maps, Maps of Lists etc.

    Here you can see an example:

    import eu.okaeri.configs.OkaeriConfig;
    import eu.okaeri.configs.annotation.*;
    
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    
    import java.util.*;
    
    @Getter
    @SuppressWarnings("FieldMayBeFinal")
    @Names(strategy = NameStrategy.HYPHEN_CASE, modifier = NameModifier.TO_LOWER_CASE)
    public class ExampleConfig extends OkaeriConfig {
    
        @Exclude
        private static final ExampleSubConfig anotherExampleSubConfigValue = new ExampleSubConfig("Custom awesome value", 2022);
    
        @Comment
        private List<ExampleSubConfig> subConfigsList = Arrays.asList(new ExampleSubConfig(), anotherExampleSubConfigValue);
    
        @Comment
        private Map<String, ExampleSubConfig> subConfigsMap = new HashMap<String, ExampleSubConfig>() {{
            put("first-map-key", new ExampleSubConfig());
            put("second-map-key", anotherExampleSubConfigValue);
        }};
    
        @Comment
        private List<Map<String, ExampleSubConfig>> subConfigsMapsList = Collections.singletonList(new HashMap<String, ExampleSubConfig>() {{
            put("first-map-key", new ExampleSubConfig());
            put("second-map-key", anotherExampleSubConfigValue);
        }});
    
        @Comment
        private Map<String, List<ExampleSubConfig>> subConfigsListsMap = new HashMap<String, List<ExampleSubConfig>>() {{
            put("first-map-key", Arrays.asList(new ExampleSubConfig(), anotherExampleSubConfigValue));
            put("second-map-key", Arrays.asList(new ExampleSubConfig(), anotherExampleSubConfigValue));
        }};
    
        @Getter
        @SuppressWarnings("FieldMayBeFinal")
        @AllArgsConstructor
        @NoArgsConstructor
        @Names(strategy = NameStrategy.HYPHEN_CASE, modifier = NameModifier.TO_LOWER_CASE)
        public static class ExampleSubConfig extends OkaeriConfig {
    
            @Comment("You can see this comment inside maps and collections")
            private String someKeyFirst = "Awesome value";
    
            @Comment("And this comment too")
            private int someKeySecond = 1337;
    
        }
    
    }
    

    And config file:

    sub-configs-list:
    - # You can see this comment inside maps and collections
      some-key-first: Awesome value
      # And this comment too
      some-key-second: 1337
    - # You can see this comment inside maps and collections
      some-key-first: Custom awesome value
      # And this comment too
      some-key-second: 2022
    
    sub-configs-map:
      second-map-key:
        # You can see this comment inside maps and collections
        some-key-first: Custom awesome value
        # And this comment too
        some-key-second: 2022
      first-map-key:
        # You can see this comment inside maps and collections
        some-key-first: Awesome value
        # And this comment too
        some-key-second: 1337
    
    sub-configs-maps-list:
    - second-map-key:
        # You can see this comment inside maps and collections
        some-key-first: Custom awesome value
        # And this comment too
        some-key-second: 2022
      first-map-key:
        # You can see this comment inside maps and collections
        some-key-first: Awesome value
        # And this comment too
        some-key-second: 1337
    
    sub-configs-lists-map:
      second-map-key:
      - # You can see this comment inside maps and collections
        some-key-first: Awesome value
        # And this comment too
        some-key-second: 1337
      - # You can see this comment inside maps and collections
        some-key-first: Custom awesome value
        # And this comment too
        some-key-second: 2022
      first-map-key:
      - # You can see this comment inside maps and collections
        some-key-first: Awesome value
        # And this comment too
        some-key-second: 1337
      - # You can see this comment inside maps and collections
        some-key-first: Custom awesome value
        # And this comment too
        some-key-second: 2022
    
    
    opened by Brikster 1
  • Subconfigurations validation seem to be not implemented

    Subconfigurations validation seem to be not implemented

    Describe the bug I have such code:

    @Getter
    @SuppressWarnings("FieldMayBeFinal")
    @Names(strategy = NameStrategy.HYPHEN_CASE, modifier = NameModifier.TO_LOWER_CASE)
    public class ModerationConfig extends OkaeriConfig {
    
        private CapsModerationConfig caps = new CapsModerationConfig();
    
        @Positive
        private int exampleField = 6;
    
        @Getter
        @SuppressWarnings("FieldMayBeFinal")
        @Names(strategy = NameStrategy.HYPHEN_CASE, modifier = NameModifier.TO_LOWER_CASE)
        public static class CapsModerationConfig extends OkaeriConfig {
    
            private boolean enable = true;
    
            @Positive
            private int length = 6;
    
            @Min(0) @Max(100)
            private int percent = 80;
    
            private boolean block = true;
    
        }
    
    }
    

    If I set "exampleField" and "length" to zeros, I get exception only for the field from topper class. If I set to zero "length" only, result is the same.

    Library version

    implementation 'eu.okaeri:okaeri-configs-yaml-bukkit:4.0.6'
    implementation 'eu.okaeri:okaeri-configs-serdes-commons:4.0.6'
    implementation 'eu.okaeri:okaeri-configs-serdes-bukkit:4.0.6'
    implementation 'eu.okaeri:okaeri-configs-validator-okaeri:4.0.6'
    
    bug 
    opened by Brikster 0
Owner
Okaeri
Okaeri
BRCC(better remote config center)是一个分布式配置中心,用于统一管理应用服务的配置信息,避免各类资源散落在各个项目中,简化资源配置的维护成本。作为一种轻量级的解决方案,部署简单,同时支持多环境、多版本、多角色的资源管理,可以在不改变应用源码的情况下无缝切换和实时生效配置信息。

BRCC:Better Remote Config Center 简介   BRCC是一个分布式配置中心,用于统一管理应用服务的配置信息,避免各类资源散落在各个项目中,简化资源配置的维护成本。作为一种轻量级的解决方案,部署简单,同时支持多环境、多版本、多角色的资源管理,可以在不改变应用源码的情况下无

Baidu 367 Dec 24, 2022
Modern configuration library for distributed apps written in Java.

Overview cfg4j ("configuration for Java") is a configuration library for Java distributed apps (and more). Features: Open source Easy to use Auto-relo

cfg4j 544 Nov 23, 2022
Simple Ini Parser for Java or SIni4j is a simple INI parse made in Java

Simple Ini Parser for Java or SIni4j is a simple INI parse made in Java, built for provide a simple and easiest way to load INI files in Java

Synonware 2 Mar 18, 2022
A simple configuration library for Java applications providing a node structure, a variety of formats, and tools for transformation

Configurate Configurate is a simple configuration library for Java applications that provides a node-based representation of data, able to handle a wi

SpongePowered 274 Jan 3, 2023
configuration library for JVM languages using HOCON files

Configuration library for JVM languages. If you have questions or are working on a pull request or just curious, please feel welcome to join the chat

Lightbend 5.8k Jan 4, 2023
configuration library for JVM languages using HOCON files

Configuration library for JVM languages. If you have questions or are working on a pull request or just curious, please feel welcome to join the chat

Lightbend 5.8k Jan 4, 2023
Library for configuration management API

Archaius Features Archaius includes a set of configuration management APIs used by Netflix. It provides the following functionality: Dynamic, Typed Pr

Netflix, Inc. 2.4k Dec 22, 2022
KickAss Configuration. An annotation-based configuration system for Java and Kotlin

KAConf 2016-2020 Mario Macías KickAss Configuration v0.9.0 is an Annotation-based configuration system inspired in the wonderful Spring Boot. Its stro

Mario Macías 53 Nov 21, 2022
Highly-available version-controlled service configuration repository based on Git, ZooKeeper and HTTP/2

Visit the official web site for more information. Central Dogma Central Dogma is an open-source, highly-available and version-controlled service confi

LINE 503 Dec 20, 2022
Oyvey skid with decent ca and pastebin hwid lock lmao

McDonald-1.1.7-Cracked It wasn't obfuscated so it isn't really a "crack", just had to remove the HWID authentication from the main class. Uhm, the gui

null 6 Dec 2, 2022
prob isn't but uhhh there you go, it works on .cc and i am happy with that :)

TickShift (prob isn't but uhhh there you go, it works on .cc and i am happy with that :)) Credits Codex#4562: [rudimentary shit code] Doogie13: [expla

noat 9 Dec 2, 2022
Nacos: Dynamic Naming and Configuration Service

An easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.

Alibaba 25.1k Jan 3, 2023
COMMA Config API is an API allowing you to easily create complex and stylish config menu screens for Minecraft Fabric mods

COMMA Config API is an API allowing you to easily create complex and stylish config menu screens for Minecraft Fabric mods. COMMA stands for Configurable Options Menu Modding API

Dr. Rubisco 1 Jan 13, 2022
MapNeat is a JVM library written in Kotlin that provides an easy to use DSL (Domain Specific Language) for transforming JSON to JSON, XML to JSON, POJO to JSON in a declarative way.

MapNeat is a JVM library written in Kotlin that provides an easy to use DSL (Domain Specific Language) for transforming JSON to JSON, XML to JSON, POJ

Andrei Ciobanu 59 Sep 17, 2022
An AI companion who is eager to learn and would love to see the world through your eyes. Gedir is always ready to chat when you need an empathetic friend

Gedir We are currently developing the core. Ideas, feedback, and contributions to code are all very welcome. An AI companion who is eager to learn and

SZ 3 Sep 3, 2022
Simple, efficient Excel to POJO library for Java

ZeroCell ZeroCell provides a simple API for loading data from Excel sheets into Plain Old Java Objects (POJOs) using annotations to map columns from a

Credit Data CRB 68 Dec 8, 2022
基于RuoYi-Vue集成 Lombok+Mybatis-Plus+Undertow+knife4j+Hutool+Feign 重写所有原生业务 定期与RuoYi-Vue同步

平台简介 RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 分布式集群 场景升级 定期与 RuoYi-Vue 同步 集成 Lock4j dynamic-datasource 等分布式场景解决方案 集成 Mybatis-Plus Lombok Hutool 等便捷开发工具 适配重写相关业

CrazyLionLi 110 Jan 4, 2023
Fast and Easy mapping from database and csv to POJO. A java micro ORM, lightweight alternative to iBatis and Hibernate. Fast Csv Parser and Csv Mapper

Simple Flat Mapper Release Notes Getting Started Docs Building it The build is using Maven. git clone https://github.com/arnaudroger/SimpleFlatMapper.

Arnaud Roger 418 Dec 17, 2022
A twelve-factor configuration (12factor.net/config) library for Java 8+

dotenv A twelve-factor configuration library for Java 8+. Features: seamless integration with Guice (prefer Spring? see here); zero dependencies; avai

Stanley Shyiko 42 Oct 1, 2022