Skip to content

Configurations

Guillaume R edited this page May 1, 2018 · 6 revisions

Concepts

A configuration is a data structure that maps keys to values. Each key is unique and associated to zero or one value. In essence, a configuration is very similar to a Map<String, Object>.

Configurations can be nested to create any structure you want.

Keys

Keys are strings that represent the path of a value. It can be a simple name, or a compound path in the case of nested configurations. Compound paths are expressed with dots, like a.b.c.

For instance, take the following json document:

{
    "a": "something",
    "b": {
        "c": 123
    },
    "key.with.dots": true
}
  • something is accessible with the key a
  • {"c": 123} is accessible with the key b
  • 123 is accessible with the compound key b.c. Here we say that b is a sub-configuration.
  • true is accessible with the simple key key.with.dots, which is not a compound path despite having dots. We'll see later how to tell NightConfig that such a key isn't compound.

Values

Values can be anything... until you write them in a particular format, for instance JSON. At this moment, you have to be sure that all the values are supported by the configuration format, typically:

  • Standard numbers (int, long, float, etc.)
  • Strings
  • Booleans
  • Sub-configurations
  • Lists of such values (Java Collection)

Map<String, Object> should be avoided and replaced by proper configuration objects.

In practice

Config types

NightConfig provides several config types. The most important ones are UnmodifiableConfig and Config.

Config is your basic, read-write configuration interface and, as the name suggests, UnmodifiableConfig is a read-only configuration. Beware that every modifiable Config inherits from UnmodifiableConfig. Therefore, one can obtain a ready-only view that reflects the changes made to the modifiable config. UnmodifiableConfigs aren't guaranteed to be immutable.

Creating Config objects

In memory

The Config class provides static methods to create new configurations. One of them is inMemory(), which creates an in-memory configuration which is independent of any configuration format (see Config formats).

By default, configurations aren't thread-safe. You have to ask for thread-safety when you create the configuration object.

Config config = Config.inMemory(); // in-memory, not thread-safe
UnmodifiableConfig configView = config; // read-only view of config

Config safeConfig = Config.inMemoryConcurrent(); // in-memory, thread-safe
UnmodifiableConfig safeView = safeConfig; // read-only view of safeConfig

From a file

With FileConfig.of(File) you can get a configuration from a file, providing that its name allows NightConfig to know its configuration format and that you've added the module corresponding to that format.

Here is an example with JSON:

File configFile = new File("path/to/my/config.json");
FileConfig config = FileConfig.of(configFile); // Associates the file to the config
// For now, the configuration is empty. We need to read its content:
config.load(); // Reads the file and populates the config

Manipulating values

In this paragraph, the examples assume that you've read the following JSON data with FileConfig:

{
    "a": "something",
    "b": {
        "c": 123
    },
    "key.with.dots": true
}

Get

UnmodifiableConfig defines the get and getOptional methods that allow you to read values from the config. You give the path as a String parameter. If it contains dots, the path is parsed as a compound path. Otherwise, it is a simple path.

String str = config.get("a");
Optional<String> strOptional = config.getOptional("a"); // Optional of "something"
Optional<String> notFound = config.getOptional("I'm not in the config"); // Empty optional

Config sub = config.get("b"); // sub configuration
int subN = sub.get("c");
int n = config.get("b.c"); // compound path
assert subN == n; // OK: subN and n are the same

Note that you don't have to specify the type of the value, because NightConfig automatically uses the type of the variable you assign the value to.

OK, so we can use compound paths by separating the different parts with dots. But how to use a simple key that contains dots? The answer is simple: bypass path parsing with a list.

boolean value = config.get(Arrays.of("key.with.dots"));

Each element of the list is a key. In our example, we create a list with only one element to access our simple value.

We could have used a list instead of b.c:

int n = config.get(Arrays.of("b", "c"));

Set

Use Config.set to replace existing values and to add new ones.

config.set("a", "new value");
config.set("new key", "new value");
config.set(Arrays.of("key.with.dots"), 12345);

Add and remove

Use Config.add to add new values without overwriting existing ones, and Config.remove to remove existing values.

config.add("a new key", "a new value"); // ok
config.add("a", "new value for existing key a"); // won't modify the value of a

config.remove("a");

As always, you can use List paths instead of String paths.