-
-
Notifications
You must be signed in to change notification settings - Fork 68
Getting Started
Configurate is designed to be easy to get started with. For our examples here we're going to be using the HOCON loader, but any of the other loaders will work similarly.
In Gradle:
repositories {
maven {
url = "https://repo.spongepowered.org/maven"
name = "sponge"
}
}
dependencies {
implementation("org.spongepowered:configurate-hocon:3.7")
}
In Maven:
<repositories>
<repository>
<id>sponge</id>
<url>https://repo.spongepowered.org/maven</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>configurate-hocon</artifactId>
<version>3.7</version> <!-- this may not be the latest available version -->
</dependency>
</dependencies>
Make sure to check the releases page for the latest release.
We'll assume we're going to load a configuration from the working directory named myproject.conf
, with the contents
name = "Alice"
messages {
# Alice hasn't checked their messages in a while
count = 20
mood = CONFUSED
}
Then, we'll load myproject.conf
:
HoconConfigurationLoader loader = HoconConfigurationLoader.builder()
.setPath(Path.of("myproject.conf")) // Set where we will load and save to
.build();
CommentedConfigurationNode root;
try {
root = loader.load();
} catch (IOException e) {
System.err.println("An error occurred while loading this configuration: " + e.getMessage());
if (e.getCause() != null) {
e.getCause().printStackTrace();
}
System.exit(1);
return;
}
Configurate's loaders will provide empty nodes if the requested file does not exist. When saving, the loader will create any necessary parent directories.
Let's pretend we are writing information to a messages file. For this example,we'll have an enum class Mood
, with the following contents:
/**
* A mood that a message may have
*/
enum Mood {
HAPPY, SAD, CONFUSED, NEUTRAL;
public static final TypeToken<Mood> TYPE = TypeToken.of(Mood.class); // Keep track of our generic type, to avoid reinitialization
}
Continuing from where we left off after loading the configuration, we'll retrieve some values from the configuration and print them to the user.
ConfigurationNode countNode = root.getNode("messages", "count"),
moodNode = root.getNode("messages", "mood");
String name = root.getNode("name").getString();
int count = countNode.getInt(Integer.MIN_VALUE);
Mood mood = moodNode.getValue(Mood.TYPE);
if (name == null || count == Integer.MIN_VALUE || mood == null) {
System.err.println("Invalid configuration");
System.exit(2);
return;
}
System.out.println("Hello, " + name + "!");
System.out.println("You have " + count + " " + mood + " messages!");
System.out.println("Thanks for viewing your messages");
// Update values
countNode.setValue(0);
moodNode.setValue(Mood.TYPE, Mood.NEUTRAL);
root.getNode("accesses").act(n -> { // perform actions with the node at key "accesses" available at `n`
n.setComment("The times messages have been accessed, in milliseconds since the epoch");
n.appendListNode().setValue(System.currentTimeMillis());
});
There are a few important things to note here. First off, it is possible to skip through levels of hierarchy within configuration nodes by providing each level as an element to the varargs method getNode()
. While any object is a valid key, there may be limitations depending on the loader used to save the node. Additionally, using any key that is not a valid Map key will result in undefined behaviour.
Another important distinction shown in the above example is between native value types and serialized value types. Native value types are those that are directly supported by the loader and appear differently depending on the file format, while serialized types are any supported by the object mapper and can generally be serialized to any output. To take advantage of serialized types, the variant of ConfigurationNode.setValue()
that takes a TypeToken must be used.
// And save the node back to the file
try {
loader.save(root);
} catch (IOException e) {
System.err.println("Unable to save your messages configuration! Sorry! " + e.getMessage());
System.exit(1);
}
This will save the contents of the node root
to the location originally specified for the loader we created at the beginning of this example. While the node we are saving (root
) was originally created by this loader, any implementation of ConfigurationNode
must be accepted by any loader.
Some formats place specific restrictions on the structure of output data (for example, requiring the root node to be in map format), so there can be conversion restrictions, but those schema violations must be documented in each loader.
Hopefully this gives a brief taste of how to use Configurate for basic configuration needs. For more complicated setups, take a look at the Object Mapper which allows representing configuration structures as objects, rather than accessing the node directly. As your project evolves, Configuration Transformations allow making changes to your configuration as a whole, including versioning the configuration structure. For a more in-depth look at the behaviour of any section of Configurate, the [Javadocs] have a thorough description of every class.
For any further questions, feel free to be in touch in the SpongePowered Discord