Configuration des Nodes

En mémoire, la configuration est représentée à l’aide de ConfigurationNodes. Un ConfigurationNode contient soit une valeur (comme un nombre, une chaîne ou une liste), soit un node enfant, ce qui donne une structure en arborescence. Quand vous utilisez un ConfigurationLoader pour charger ou crées de nouvelles configurations, il retournera le node racine. Nous recommandons que vous gardiez toujours une référence vers ce node racine stockée quelque part, afin d’éviter de charger la configuration à chaque vous que vous en avez besoin. En effet secondaire, ça va garder les commentaires qui étaient présents dans le fichier. Alternativement, vous pouvez stocker une référence vers une instance de configuration sérialisable qui contient la configuration entière de votre plugin.

Note

Selon le ConfigurationLoader utilisé, vous pouvez même obtenir un CommentedConfigurationNode, qui, en plus du comportement normal du ConfigurationNode est capable de conserver un commentaire qui va persister sur le fichier de configuration enregistré.

Valeurs

Valeurs basiques

Les types de valeurs basiques comme les int, double, boolean ou String ou chacun leur propre méthode getter qui retournera valeur ou une valeur par défaut si la node ne contient pas de valeur de ce type. Vérifions si l’administrateur du serveur veut que le module blockCheats de notre plugin soit activé en vérifiant la valeur au chemin modules.blockCheats.enabled.

boolean shouldEnable = rootNode.getNode("modules", "blockCheats", "enabled").getBoolean();

Oui, c’est vraiment aussi simple que cela. Comme dans l’exemple ci-dessus, les méthodes comme ConfigurationNode#getInt(), ConfigurationNode#getDouble() ou ConfigurationNode#getString() existent pour vous permettre de récupérer facilement une valeur de ce type.

Pour définir une valeur basique à une node, utilisez simplement la méthode ConfigurationNode#setValue(Object). Ne soyez pas confus par le fait qu’elle accepte un Object - cela signifie qu’elle peut prendre n’importe quoi et qu’elle va déterminer comment procéder par lui-même.

Imaginez que le module blockCheats soit désactivé par une commande de l’utilisateur. Ce changement devra être réflété dans la configuration et peut se faire comme suit :

rootNode.getNode("modules", "blockCheats", "enabled").setValue(false);

Avertissement

Les autres types que les types de valeurs de base ne peuvent pas être traités par ces fonctions basiques, et doivent à la place être lus et écrits en utilisant la méthode de (dé)sérialisation décrite ci-dessous. Les types basiques sont ceux qui sont gérés nativements par l’implémentation sous-jacente du format de fichier utilisé par le ConfigurationLoader, mais généralement incluent les types de données primitifs, les Strings ainsi que les Lists et les Maps de types basiques.

(Dé)Sérialisation

Si vous essayez de lire ou d’écrire un objet qui n’est pas un des types de bases mentionnés ci-dessus, vous devrez passer tout d’abord à travers la désérialisation. Dans le ConfigurationOptions utilisé pour créer votre ConfigurationNode racine, il y a une collection de TypeSerializers que Configurate utilise pour convertir vos objets en un ConfigurationNode et vice versa.

Afin de dire à Configurate quel type il doit traiter, nous devons fournir un TypeToken de guava. Imaginez que nous voulons lire l”UUID d’un joueur à partir du node de configuration towns.aFLARDia.mayor. Pour faire ceci, nous devron appeler la méthode getValue(…) tout en fournissant un TypeToken représentant la classe UUID.

import java.util.UUID;

UUID mayor = rootNode.getNode("towns", "aFLARDia", "mayor").getValue(TypeToken.of(UUID.class));

Cela demande à Configurate de localiser le bon TypeSerializer pour les UUIDs et de l’utiliser pour convertir la valeur stockée en un UUID. Le TypeSerializer (et par extension la méthode ci-dessus) peut lever une ObjectMappingException si il rencontre des données incomplètes ou invalides.

Maintenant si nous voulons écrire un nouvel UUID vers ce node de configuration, la syntaxe est très similaire. Utilisez la méthode setValue(…) avec un TypeToken et l’objet que vous souhaitez sérialiser.

rootNode.getNode("towns","aFLARDia", "mayor").setValue(TypeToken.of(UUID.class), newUuid);

Note

Sérialiser une valeur va lever une ObjectMappingException si aucun TypeSerializer pour le TypeToken donné ne peut être trouvé.

Pour des classes simples comme UUID, vous pouvez simplement créer un TypeToken en utilisant la méthode statique TypeToken#of(Class). Cependant, les UUIDs et quelques autres types ont déjà une constante pour ça, comme TypeTokens#UUID_TOKEN, que vous devez utiliser à la place. Mais quand la classe que vous voulez utiliser possède ses propres types de paramètres (comme Map<String,UUID>), la syntaxe devient un peu plus compliquée. Dans la plupart des cas vous saurez exactement quel types de paramètres ça va être au moment de la compilation, donc vous pouvez juste créer le TypeToken en tant que classe anonyme : new TypeToken<Map<String, UUID>>() {}. De cette façon, même les types génériques peuvent aisément être écrits et lus.

Voir aussi

Pour plus d’informations à propos des TypeTokens, référez-vous à la documentation guava

Astuce

La SpongeAPI fournit une classe avec plusieurs type tokens prédéfinis que vous pouvez utiliser. Si les développeurs de plugins ont besoin de beaucoup ou de complexes TypeTokens, ou alors qu’ils les utilisent fréquemment, nous recommandons de créer une classe similaire pour eux-mêmes afin d’améliorer la lisibilité du code. (Attention, il n’est pas garanti que toutes ces entrées ont des TypeSerializers enregistrés).

Vous pouvez trouver une liste non-exhaustive des types supportés, et des façons d’ajouter un support pour de nouveaux types sur la page de configuration de la sérialisation.

Par défaut

Contrairement à SpongeAPI, la librairie Configurate n’utilise pas l”Optional pour les valeurs qui pourraient ne pas être présentes, mais plutôt null. Alors que les getters des méthodes primitives (comme getBoolean() ou getInt()) peuvent retourner false ou 0, ceux qui retournent un objet (comme getString()) vont retourner null si aucune valeur n’est présente. Si vous ne voulez pas gérer manuellement ces cas spéciaux, vous pouvez utiliser les valeurs par défaut. Chaque méthode getXXX() discutée plus haut a une forme surchargée acceptant un paramètre supplémentaire comme valeur par défaut.

Jetons un coup d’oeil à l’exemple pour la lecture d’une valeur booléenne à nouveau.

boolean shouldEnable = rootNode.getNode("modules", "blockCheats", "enabled").getBoolean();

Cet appel va retourner false si la valeur false est sauvegardé dans la configuration ou si la valeur n’est pas présente dans la configuration. Étant donné que ces deux cas ne sont pas distinguables nous n’avons pas de moyen simple de définir notre variable à false seulement si c’est la valeur spécifiée dans la configuration. Sauf si nous spécifions true comme valeur par défaut.

boolean shouldEnable = rootNode.getNode("modules", "blockCheats", "enabled").getBoolean(true);

De la même façon, vous pouvez spécifié des valeurs par défaut sur n’importe quelle valeur que vous récupérer depuis la configuration, évitant ainsi les null ou les ObjectMappingException causés par l’absence de la valeur entière. Cela peut aussi fonctionner sur la désérialisation de la méthode getValue(). Quelques exemples :

String greeting = rootNode.getNode("messages", "greeting")
        .getString("FLARD be with you good man!");

UUID mayor = rootNode.getNode("towns", "aFLARDia", "mayor")
        .getValue(TypeTokens.UUID_TOKEN, somePlayer.getUniqueId());

Une autre application utile de ces valeurs par défaut, c’est qu’ils peuvent être copiés vers votre configuration si nécessaire. Lors de la création de votre node racine de configuration, vous pouvez créer votre ConfigurationOptions avec setShouldCopyDefaults(true). Par la suite, quand vous fournirez une valeur par défaut, Configurate vérifiera d’abord si la valeur que vous essayerez de récupérer est présente, et si elle ne l’est pas, il écrira la valeur par défaut sur la node avant de retourner la valeur par défaut.

Supposons que votre plugin s’exécute pour la première fois et que le fichier de configuration n’existe pas encore. Vous essayez de le charger avec ConfigurationOptions qui permet de copier les valeurs par défaut et de récupérer une node de configuration vide. Maintenant vous exécutez la ligne rootNode.getNode("modules", "blockCheats", "enabled").getBoolean(true). Comme la node n’existe pas encore, Configurate la crée et écrit la valeur true dessus selon les directives du ConfigurationOptions avant de la retourner. Lorsque la configuration est alors terminée, la valeur true va rester sur la node sans jamais être définie explicitement.