Simple HOCON object mapping utility

Hey everyone, this is just a simple utility I designed for use with my bukkit/sponge projects. Originally I was using GSON to map to/from objects, and albeit it was less code, but unfortunately comments and custom paths were not possible, so I decided to rely on zml2008’s configurate library (the one used in sponge) to manage my config objects.

Its pretty simple though, all you need to do is extend this class and then you can use the static load method to load the existing config (or create the config for the first time), and you’ll get an instance of your config object back. From that instance you can save and load (in case of wanting to reload from file). Make sure your fields are annotated with the Settings annotation and likewise your custom objects with the ConfigSerializeable annotation. Any fields that should not be serialized should of course be marked transient.

I more or less threw this together last night real quick, but from my testing it works as expected. Please feel free to let me know if you come across any bugs and I’ll look into fixing them to the best of my ability.

Cheers,
Favorlock

/**
 * Simple configurate utility for HOCON configs.
 */
public class HoconConfig {
    private transient HoconConfigurationLoader loader;
    private transient ObjectMapper<? extends HoconConfig>.BoundInstance boundInstance;
    private transient ConfigurationNode node;

    /**
     * Loads a config from file and maps it to an object
     * of the specified class.
     *
     * @param clazz The class to map.
     * @param file The file to read/write from/to.
     * @param <T> The type to return as.
     * @return Object of type T
     */
    public static <T extends  HoconConfig> T load(Class<T> clazz, File file) {
        Objects.requireNonNull(clazz, "Config clazz cannot be null.");
        Objects.requireNonNull(file, "Config file cannot be null.");

        ObjectMapper<T> mapper = null;

        try {
            mapper = ObjectMapper.forClass(clazz);

            if (mapper != null) {
                HoconConfigurationLoader.Builder builder = HoconConfigurationLoader.builder().setFile(file);
                HoconConfigurationLoader loader = builder.build();
                ConfigurationNode node = loader.load();

                ObjectMapper<T>.BoundInstance boundInstance = mapper.bindToNew();
                T instance = boundInstance.populate(node);

                if (!file.exists()) {
                    if (!file.getParentFile().exists()) {
                        file.mkdirs();
                    }

                    file.createNewFile();
                    loader = builder.setFile(file).build();
                }

                instance.init(loader, node, boundInstance);
                instance.save();

                return instance;
            }
        } catch (ObjectMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public void load() {
        if (node != null && boundInstance != null) {
            try {
                boundInstance.populate(node);
            } catch (ObjectMappingException e) {
                e.printStackTrace();
            }
        }
    }

    public void save() {
        if (loader != null && node != null) {
            try {
                boundInstance.serialize(node);
                loader.save(node);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ObjectMappingException e) {
                e.printStackTrace();
            }
        }
    }

    protected void init(HoconConfigurationLoader loader, ConfigurationNode node, ObjectMapper.BoundInstance boundInstance) {
        this.loader = loader;
        this.node = node;
        this.boundInstance = boundInstance;

        load();
    }
}

So it uses the built-in object mapping of configurate?

Yes it does, I just made it a simple utility that makes managing configs a bit easier and simpler.