Add documantation about ConfigurationSerializable support

dev
Exlll 2 years ago
parent 3ea01199db
commit c2c35daab2

@ -1,23 +1,28 @@
# ConfigLib
**A Minecraft library for mapping objects to YAML configuration files.**
**A Minecraft library for saving, loading, updating, and commenting YAML configuration files.**
This library facilitates creating, saving, loading, updating, and documenting YAML configuration
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 then transformed into YAML and saved to some specified file.
which are first transformed into YAML and then saved to some specified file.
For a step-by-step tutorial that shows most features of this library in action check out
the [Tutorial](https://github.com/Exlll/ConfigLib/wiki/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 `BigInteger`, `BigDecimal`, `LocalDate`, `LocalTime`, and `LocalDateTime`
* Support for (nested) lists, sets, arrays, and maps
* Support for enums and POJOs (+ inheritance!)
* Support for Bukkit's `ConfigurationSerializable` types (e.g. `ItemStack`)
* Option to exclude fields from being converted
* Option to add explanatory comments by annotating fields
* Option to format field names before conversion
* Option to customize null handling
* Option to provide custom serializers
* Option to customize serialization by providing your own serializers
* Option to add headers and footers to configuration files
* ...and a few more!
## Usage example
@ -26,6 +31,12 @@ This section contains a short usage example to get you started. The whole range
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](https://github.com/Exlll/ConfigLib/wiki/Tutorial) page on the wiki.
If you want support Bukkit classes like `ItemStack`, check out
the [Configuration properties](#configuration-properties) section.
```java
public final class Example {
// To create a configuration annotate the class with @Configuration and make sure that
@ -69,21 +80,20 @@ public final class Example {
}
public static void main(String[] args) {
final var props = YamlConfigurationProperties.newBuilder().build();
final var store = new YamlConfigurationStore<>(UserConfiguration.class, props);
final var configFile = Paths.get("/tmp/config.yml");
var configFile = Paths.get("/tmp/config.yml");
var config = new UserConfiguration();
// Save a new instance to the configuration file
store.save(new UserConfiguration(), configFile);
Configurations.saveYamlConfiguration(configFile, UserConfiguration.class, config);
// Load a new instance from the configuration file
UserConfiguration configuration = store.load(configFile);
System.out.println(configuration.admin.username);
System.out.println(configuration.blockedUsers);
config = Configurations.loadYamlConfiguration(configFile, UserConfiguration.class);
System.out.println(config.admin.username);
System.out.println(config.blockedUsers);
// Modify and save the configuration file
configuration.blockedUsers.add(new User("user3", "pass3"));
store.save(configuration, configFile);
config.blockedUsers.add(new User("user3", "pass3"));
Configurations.saveYamlConfiguration(configFile, UserConfiguration.class, config);
}
}
```
@ -137,23 +147,23 @@ them null. Handling of null values is discussed in one of the sections further b
A configuration type may only contain fields 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` |
| Enums | Any Java enum |
| Configurations | Any configuration type |
| Collections | (Nested) Lists, sets, maps*, or arrays of previously listed 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` |
| Enums | Any Java enum |
| Configurations | Any configuration type |
| `ConfigurationSerializable` | All Bukkit classes that implement this interface, like `ItemStack` |
| Collections | (Nested) Lists, sets, maps*, or arrays of previously listed types |
(*) Map keys cannot be in the `Configurations` or `Collections` type class.
(*) Map keys can only be of simple or enum type, i.e. they cannot be in the `Collections`,
`Configurations`, or `ConfigurationSerializable` type class.
<details>
<summary>Examples of supported types</summary>
#### Examples of supported types
The following class contains examples of types that this library supports:
@ -161,23 +171,29 @@ The following class contains examples of types that this library supports:
public final class SupportedTypes {
boolean supported;
Character supported;
ExampleEnum supported; // where 'ExampleEnum' is some Java enum type
ExampleConf supported; // where 'ExampleConf' is another configuration type
java.awt.Point supported; // only if a custom serializer is registered
String supported;
ExampleEnum supported; // where 'ExampleEnum' is some Java enum type
ExampleConf supported; // where 'ExampleConf' is another configuration type
/* collection types */
List<BigInteger> supported;
Set<Double> supported;
LocalDate[] supported;
Map<ExampleEnum, ExampleConf> supported;
/* nested collection types */
List<Map<ExampleEnum, LocalDate>> supported;
int[][] supported;
Map<Integer, List<Map<Short, Set<ExampleConf>>>> 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;
}
```
</details>
<details>
<summary>Examples of unsupported types</summary>
@ -298,78 +314,31 @@ Config c2 = Configurations.loadYamlConfiguration(
);
```
#### Configuration properties
### Configuration properties
Instances of the `ConfigurationProperties` class allow customization of how configurations are
stored and loaded. To create such an instance, instantiate a new builder using
the `YamlConfigurationProperties.newBuilder()` method, configure it, and finally call its `build()`
method. Check out the several methods of the builder to see which configuration options are
available.
method. Alternatively, you can use the `toBuilder()` method of an
existing `YamlConfigurationProperties` to create a new builder that is initialized with values
takes from the properties object.
#### Ignoring and filtering fields
Check out the methods of the builder class to see which configuration options are available.
Fields that are `final`, `static`, `transient` or annotated with `@Ignore` are neither serialized
nor updated during deserialization. You can filter out additional fields by providing an instance of
`FieldFilter` to the configuration properties.
#### Handling of `null` values
#### Support for Bukkit classes like `ItemStack`
Configuration properties let you configure how `null` values are handled when serializing and
deserializing a configuration:
* By setting `outputNulls` to false, fields and collection elements that are null are not output.
Any comments that belong to such fields are also not written.
* By setting `inputNulls` to false, fields and collection elements that are null are not input. That
means that fields will keep their default values.
<details>
<summary>Example <code>null</code> handling configuration</summary>
The following code forbids null values to be output but allows null values to be input. By default,
both are forbidden which makes the call to `outputNulls` in this case redundant.
There is a special `YamlConfigurationProperties` object with name `BUKKIT_DEFAULT_PROPERTIES`
that adds support for Bukkit's `ConfigurationSerializable` types. If you want to use any of these
types in your configuration, you have to use that object as a starting point:
```java
YamlConfigurationProperties.newBuilder()
.outputNulls(false)
.inputNulls(true)
.build();
```
</details>
#### Field formatting
You can define how fields are formatted by configuring the configuration properties with a custom
formatter. Field formatters are implementations of the `FieldFormatter` interface. You can implement
this interface yourself or use one of the several formatters this library provides. These
pre-defined formatters can be found in the `FieldFormatters` class.
<details>
<summary>Example <code>FieldFormatter</code> configuration</summary>
The following code formats fields using the `IDENTITY` formatter (which is the default).
```java
YamlConfigurationProperties.newBuilder()
.setFieldFormatter(FieldFormatters.IDENTITY)
.build();
YamlConfigurationProperties properties = ConfigLib.BUKKIT_DEFAULT_PROPERTIES.toBuilder()
// ...further configure the builder...
.build();
```
</details>
### Subclassing
Subclassing of configurations types is supported. Subclasses of configuration types don't need to be
annotated with `@Configuration`. When a configuration is written, the fields of parent classes
are written before the fields of the child in a top to bottom manner. Parent configurations can
be `abstract`.
#### Shadowing of fields
Shadowing of fields refers to the situation where a subclass of configuration has a field that has
the same name as a field in one of its super classes. Shadowing of fields is currently not
supported. (This restriction might easily be lifted. If you need this feature, please open an issue
and describe how to handle name clashes.)
To get access to this object, you have to import `configlib-paper` instead of `configlib-core` as
described in the [Import](#import) section.
### Comments
@ -377,9 +346,6 @@ The fields of a configuration can be annotated with the `@Comment` annotation. T
an array of strings. Each of these strings is written onto a new line as a comment. Empty strings
are written as newlines.
<details>
<summary>Example of <code>@Comment</code> usage </summary>
Serializing the following configuration as YAML ...
```java
@ -400,44 +366,64 @@ public final class ExampleConfiguration {
commentedField: commented field
```
</details>
If a configuration type _C_ that defines comments is used (as a field) within another configuration
type, the comments of _C_ are written with the proper indentation. However, if instances of _C_ are
stored inside a collection, their comments are not printed when the collection is written.
### Recursive type definitions
### Subclassing
Recursive type definitions are currently not allowed but might be supported in a future version if
this feature is requested.
Subclassing of configurations types is supported. Subclasses of configuration types don't need to be
annotated with `@Configuration`. When a configuration is written, the fields of parent classes
are written before the fields of the child in a top to bottom manner. Parent configurations can
be `abstract`.
<details>
<summary>Examples of recursive type definitions</summary>
#### Shadowing of fields
Neither direct nor indirect recursive type definitions are supported.
Shadowing of fields refers to the situation where a subclass of configuration has a field that has
the same name as a field in one of its super classes. Shadowing of fields is currently not
supported. (This restriction might easily be lifted. If you need this feature, please open an issue
and describe how to handle name clashes.)
```java
public final class RecursiveTypDefinitions {
// Direct recursive definition
@Configuration
static final class R {
R r;
}
### Ignoring and filtering fields
// Indirect recursive definition
@Configuration
static final class R1 {
R2 r2;
}
Fields that are `final`, `static`, `transient` or annotated with `@Ignore` are neither serialized
nor updated during deserialization. You can filter out additional fields by providing an instance of
`FieldFilter` to the configuration properties.
@Configuration
static final class R2 {
R1 r1;
}
}
### Handling of `null` values
Configuration properties let you configure how `null` values are handled when serializing and
deserializing a configuration:
* By setting `outputNulls` to false, fields and collection elements that are null are not output.
Any comments that belong to such fields are also not written.
* By setting `inputNulls` to false, fields and collection elements that are null are not input. That
means that fields will keep their default values.
The following code forbids null values to be output but allows null values to be input. By default,
both are forbidden which makes the call to `outputNulls` in this case redundant.
```java
YamlConfigurationProperties.newBuilder()
.outputNulls(false)
.inputNulls(true)
.build();
```
</details>
### Field formatting
You can define how fields are formatted by configuring the configuration properties with a custom
formatter. Field formatters are implementations of the `FieldFormatter` interface. You can implement
this interface yourself or use one of the several formatters this library provides. These
pre-defined formatters can be found in the `FieldFormatters` class.
The following code formats fields using the `IDENTITY` formatter (which is the default).
```java
YamlConfigurationProperties.newBuilder()
.setFieldFormatter(FieldFormatters.IDENTITY)
.build();
```
### Type conversion and custom serializers
@ -445,20 +431,21 @@ Before instances of the types listed in the [supported types](#supported-types)
stored, they need to be converted into serializable types (i.e. into types the underlying YAML
library knows how to handle). The conversion happens according to the following table:
| Source type | Target type |
|------------------------|------------------|
| Boolean types | `Boolean` |
| Integer types | `Long` |
| Floating point types | `Double` |
| Characters and strings | `String` |
| Big numeric types | `String` |
| Time related types | `String` |
| Enums | `String` |
| Configurations | `Map<String, ?>` |
| `Set<S>` | `List<T>`* |
| `List<S>` | `List<T>` |
| `S[]` | `List<T>` |
| `Map<S1, S2>` | `Map<T1, T2>` |
| Source type | Target type |
|-----------------------------|------------------|
| Boolean types | `Boolean` |
| Integer types | `Long` |
| Floating point types | `Double` |
| Characters and strings | `String` |
| Big numeric types | `String` |
| Time related types | `String` |
| Enums | `String` |
| Configurations | `Map<String, ?>` |
| `Set<S>` | `List<T>`* |
| `List<S>` | `List<T>` |
| `S[]` | `List<T>` |
| `Map<S1, S2>` | `Map<T1, T2>` |
| `ConfigurationSerializable` | `String` |
(*) By default, sets are serialized as lists. This can be changed through the configuration
properties. This also means that `Set`s are valid target types.
@ -471,6 +458,9 @@ the `de.exlll.configlib.Serializer` interface and are selected based on `S`. Put
serializers are always selected based on the compile-time type of `F` and never on the runtime type
of its value.
<details>
<summary>Why should I care about this?</summary>
This distinction makes a difference (and might lead to confusion) when you have fields whose type is
a configuration type or a collection of some configuration type, and you extend that configuration
type. Concretely, assume you have and written two configuration types `A` and `B`
@ -479,6 +469,8 @@ a `A` will be stored when you save your main configuration. That is because the
field `a` was selected based on the compile-time type of `a` which is `A` and not `B`. The same
happens if you have a `List<A>` and put instances of `B` (or some other subclass of `A`) in it.
</details>
#### Custom serializers
If you want to add support for a type whose class is not annotated with `@Configuration`, you can
@ -487,9 +479,6 @@ interface. When implementing that interface you have to make sure that you conve
into one of the valid target types listed in the table above. The serializer then has to be
registered through a `ConfigurationProperties` object.
<details>
<summary>Example <code>Serializer</code> implementation</summary>
The following `Serializer` serializes instances of `java.awt.Point` into strings.
```java
@ -509,8 +498,6 @@ public final class PointSerializer implements Serializer<Point, String> {
}
```
</details>
Custom serializers takes precedence over the serializers provided by this library.
### Changing the type of fields
@ -519,6 +506,39 @@ Changing the type of fields is not supported. If you change the type of one of y
configuration file still contains a value of the old type, a type mismatch will occur when reading
that file. Instead, remove the old field and add a new one with a different name.
### Recursive type definitions
Recursive type definitions are currently not allowed but might be supported in a future version if
this feature is requested.
<details>
<summary>Examples of recursive type definitions</summary>
Neither direct nor indirect recursive type definitions are supported.
```java
public final class RecursiveTypDefinitions {
// Direct recursive definition
@Configuration
static final class R {
R r;
}
// Indirect recursive definition
@Configuration
static final class R1 {
R2 r2;
}
@Configuration
static final class R2 {
R1 r1;
}
}
```
</details>
## Import
**INFO:** I'm currently looking for an easier way for you to import this library that does not
@ -543,6 +563,10 @@ dependencies array (for Velocity) of your own plugin.
Alternatively, if you don't want to use an extra plugin, you can shade the `-core` version and the
YAML parser yourself.
**NOTE:** If you want serialization support for Bukkit classes like `ItemStack`,
replace `configlib-core` with `configlib-paper`
(see [here](#support-for-bukkit-classes-like-itemstack)).
#### Maven
```xml

Loading…
Cancel
Save