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 String
s ainsi que les List
s et les Map
s 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 UUID
s 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 UUID
s 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 TypeToken
s, 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 TypeToken
s, 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 TypeSerializer
s 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.