package com.electronwill.nightconfig.core;
import com.electronwill.nightconfig.core.utils.FakeCommentedConfig;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import static com.electronwill.nightconfig.core.utils.StringUtils.split;
/**
* A modifiable config that supports comments.
*
* @author TheElectronWill
*/
public interface CommentedConfig extends UnmodifiableCommentedConfig, Config {
/**
* Sets a config comment.
*
* @param path the comment's path, each part separated by a dot. Example "a.b.c"
* @param comment the comment to set
* @return the old comment if any, or {@code null}
*/
default String setComment(String path, String comment) {
return setComment(split(path, '.'), comment);
}
/**
* Sets a config comment.
*
* @param path the comment's path, each element of the list is a different part of the path.
* @param comment the comment to set
* @return the old comment if any, or {@code null}
*/
String setComment(List<String> path, String comment);
/**
* Removes a comment from the config.
*
* @param path the comment's path, each part separated by a dot. Example "a.b.c"
* @return the old comment if any, or {@code null}
*/
default String removeComment(String path) {
return removeComment(split(path, '.'));
}
/**
* Removes a comment from the config.
*
* @param path the comment's path, each element of the list is a different part of the path.
* @return the old comment if any, or {@code null}
*/
String removeComment(List<String> path);
/**
* Removes all the comments from the config.
*/
void clearComments();
/**
* Puts the comments in the given map to this config. Existing comments are replaced, missing
* comments are created.
*
* @param comments the comments to set
*/
default void putAllComments(Map<String, CommentNode> comments) {
for (Map.Entry<String, CommentNode> entry : comments.entrySet()) {
String key = entry.getKey();
CommentNode node = entry.getValue();
String comment = node.getComment();
if (comment != null) {
setComment(Collections.singletonList(key), comment);
}
Map<String, CommentNode> children = node.getChildren();
if (children != null) {
CommentedConfig config = getRaw(Collections.singletonList(key));
config.putAllComments(children);
}
}
}
/**
* Puts the comments in the given config to this config. Existing comments are replaced, missing
* comments are created.
*
* @param commentedConfig the config to copy its comments
*/
default void putAllComments(UnmodifiableCommentedConfig commentedConfig) {
for (UnmodifiableCommentedConfig.Entry entry : commentedConfig.entrySet()) {
String key = entry.getKey();
String comment = entry.getComment();
if (comment != null) {
setComment(Collections.singletonList(key), comment);
}
Object value = entry.getValue();
if (value instanceof UnmodifiableCommentedConfig) {
CommentedConfig config = getRaw(Collections.singletonList(key));
config.putAllComments((UnmodifiableCommentedConfig)value);
}
}
}
@Override
default UnmodifiableCommentedConfig unmodifiable() {
return new UnmodifiableCommentedConfig() {
@Override
public <T> T getRaw(List<String> path) {
return CommentedConfig.this.getRaw(path);
}
@Override
public String getComment(List<String> path) {
return CommentedConfig.this.getComment(path);
}
@Override
public boolean contains(List<String> path) {
return CommentedConfig.this.contains(path);
}
@Override
public boolean containsComment(List<String> path) {
return CommentedConfig.this.containsComment(path);
}
@Override
public int size() {
return CommentedConfig.this.size();
}
@Override
public Map<String, Object> valueMap() {
return Collections.unmodifiableMap(CommentedConfig.this.valueMap());
}
@Override
public Map<String, String> commentMap() {
return Collections.unmodifiableMap(CommentedConfig.this.commentMap());
}
@Override
public Map<String, CommentNode> getComments() {
return CommentedConfig.this.getComments();
}
@Override
public Set<? extends Entry> entrySet() {
return CommentedConfig.this.entrySet();
}
@Override
public ConfigFormat<?> configFormat() {
return CommentedConfig.this.configFormat();
}
};
}
default CommentedConfig checked() {
return new CheckedCommentedConfig(this);
}
/**
* Returns a Map view of the config's comments. Any change to the map is reflected in the
* config and vice-versa.
* <p>
* The comment map contains only the comments of the direct elements of the configuration, not
* the comments of their sub-elements.
*/
@Override
Map<String, String> commentMap();
@Override
Set<? extends Entry> entrySet();
/**
* A modifiable commented config entry.
*/
interface Entry extends Config.Entry, UnmodifiableCommentedConfig.Entry {
/**
* Sets the entry's comment.
*
* @param comment the comment to set, may contain several lines.
* @return the previous comment, or {@code null} if none.
*/
String setComment(String comment);
/**
* Removes the entry's comment.
*
* @return the previous comment, or {@code null} if none.
*/
String removeComment();
}
@Override
CommentedConfig createSubConfig();
/**
* Creates a CommentedConfig of the given format.
*
* @param format the config's format
* @return a new empty config
*/
static CommentedConfig of(ConfigFormat<? extends CommentedConfig> format) {
return new SimpleCommentedConfig(format, false);
}
/**
* Creates a Config backed by a certain kind of map, given by a supplier.
*
* @param mapCreator a supplier which will be called to create all backing maps for this config (including sub-configs)
* @param format the config's format
* @return a new empty config
*/
static CommentedConfig of(Supplier<Map<String, Object>> mapCreator, ConfigFormat<? extends CommentedConfig> format) {
return new SimpleCommentedConfig(mapCreator, format);
}
/**
* Creates a thread-safe CommentedConfig of the given format.
*
* @param format the config's format
* @return a new empty, thread-safe config
*/
static CommentedConfig ofConcurrent(ConfigFormat<? extends CommentedConfig> format) {
return new SimpleCommentedConfig(format, false);
}
/**
* Creates a CommentedConfig with format {@link InMemoryCommentedFormat#defaultInstance()}.
*
* @return a new empty config
*/
static CommentedConfig inMemory() {
return InMemoryCommentedFormat.defaultInstance().createConfig();
}
/**
* Creates a CommentedConfig with format {@link InMemoryFormat#defaultInstance()}.
*
* @return a new empty config
*/
static CommentedConfig inMemoryConcurrent() {
return InMemoryCommentedFormat.defaultInstance().createConcurrentConfig();
}
/**
* Creates a CommentedConfig backed by a Map. Any change to the map is reflected in the config
* and vice-versa.
*
* @param map the Map to use
* @param format the config's format
* @return a new config backed by the map
*/
static CommentedConfig wrap(Map<String, Object> map, ConfigFormat<?> format) {
return new SimpleCommentedConfig(map, format);
}
/**
* Creates a new CommentedConfig with the content of the given config. The returned config will
* have the same format as the copied config.
*
* @param config the config to copy
* @return a copy of the config
*/
static CommentedConfig copy(UnmodifiableConfig config) {
return new SimpleCommentedConfig(config, config.configFormat(), false);
}
/**
* Creates a new CommentedConfig with the content of the given config. The returned config will
* have the same format as the copied config, and be backed by the given supplier.
*
* @see #of(Supplier, ConfigFormat)
*
* @param config the config to copy
* @param mapCreator a supplier which will be called to create all backing maps for this config (including sub-configs)
* @return a copy of the config
*/
static CommentedConfig copy(UnmodifiableConfig config, Supplier<Map<String, Object>> mapCreator) {
return new SimpleCommentedConfig(config, mapCreator, config.configFormat());
}
/**
* Creates a new CommentedConfig with the content of the given config.
*
* @param config the config to copy
* @param format the config's format
* @return a copy of the config
*/
static CommentedConfig copy(UnmodifiableConfig config, ConfigFormat<?> format) {
return new SimpleCommentedConfig(config, format, false);
}
/**
* Creates a new CommentedConfig with the content of the given config. The returned config will
* be backed by the given map supplier.
*
* @see #of(Supplier, ConfigFormat)
*
* @param config the config to copy
* @param mapCreator a supplier which will be called to create all backing maps for this config (including sub-configs)
* @param format the config's format
* @return a copy of the config
*/
static CommentedConfig copy(UnmodifiableConfig config, Supplier<Map<String, Object>> mapCreator, ConfigFormat<?> format) {
return new SimpleCommentedConfig(config, mapCreator, format);
}
/**
* Creates a new CommentedConfig with the content of the given config. The returned config will
* have the same format as the copied config.
*
* @param config the config to copy
* @return a copy of the config
*/
static CommentedConfig copy(UnmodifiableCommentedConfig config) {
return new SimpleCommentedConfig(config, config.configFormat(), false);
}
/**
* Creates a new CommentedConfig with the content of the given config. The returned config will
* have the same format as the copied config.
*
* @param config the config to copy
* @param mapCreator a supplier which will be called to create all backing maps for this config (including sub-configs)
* @return a copy of the config
*/
static CommentedConfig copy(UnmodifiableCommentedConfig config, Supplier<Map<String, Object>> mapCreator) {
return new SimpleCommentedConfig(config, mapCreator, config.configFormat());
}
/**
* Creates a new CommentedConfig with the content of the given config.
*
* @param config the config to copy
* @param format the config's format
* @return a copy of the config
*/
static CommentedConfig copy(UnmodifiableCommentedConfig config, ConfigFormat<?> format) {
return new SimpleCommentedConfig(config, format, false);
}
/**
* Creates a new CommentedConfig with the content of the given config. The returned config will
* be backed by the given map supplier.
*
* @see #of(Supplier, ConfigFormat)
*
* @param config the config to copy
* @param mapCreator a supplier which will be called to create all backing maps for this config (including sub-configs)
* @param format the config's format
* @return a copy of the config
*/
static CommentedConfig copy(UnmodifiableCommentedConfig config, Supplier<Map<String, Object>> mapCreator, ConfigFormat<? extends CommentedConfig> format) {
return new SimpleCommentedConfig(config, mapCreator, format);
}
/**
* Creates a new CommentedConfig with the content of the given config. The returned config will
* have the same format as the copied config.
*
* @param config the config to copy
* @return a thread-safe copy of the config
*/
static CommentedConfig concurrentCopy(UnmodifiableConfig config) {
return new SimpleCommentedConfig(config, config.configFormat(), true);
}
/**
* Creates a new CommentedConfig with the content of the given config.
*
* @param config the config to copy
* @param format the config's format
* @return a thread-safe copy of the config
*/
static CommentedConfig concurrentCopy(UnmodifiableConfig config, ConfigFormat<?> format) {
return new SimpleCommentedConfig(config, format, true);
}
/**
* Creates a new CommentedConfig with the content of the given config. The returned config will
* have the same format as the copied config.
*
* @param config the config to copy
* @return a thread-safe copy of the config
*/
static CommentedConfig concurrentCopy(UnmodifiableCommentedConfig config) {
return new SimpleCommentedConfig(config, config.configFormat(), true);
}
/**
* Creates a new CommentedConfig with the content of the given config.
*
* @param config the config to copy
* @param format the config's format
* @return a thread-safe copy of the config
*/
static CommentedConfig concurrentCopy(UnmodifiableCommentedConfig config,
ConfigFormat<?> format) {
return new SimpleCommentedConfig(config, format, true);
}
/**
* If the specified config is an instance of CommentedConfig, returns it. Else, returns a
* "fake" CommentedConfig instance with the same values (ie the valueMaps are equal) as the
* config. This fake CommentedConfig doesn't actually store nor process comments, it just
* provides the methods of CommentedConfig.
*
* @param config the config
* @return a CommentedConfig instance backed by the specified config
*/
static CommentedConfig fake(Config config) {
if (config instanceof CommentedConfig) {
return (CommentedConfig)config;
}
return new FakeCommentedConfig(config);
}
}
这是一个来自 NightConfig 库的核心接口,定义了支持注释的可修改配置的功能。下面详细解释其作用、用法和各个部分:
接口概述
是一个配置接口,扩展了
CommentedConfig 和
UnmodifiableCommentedConfig,提供了读写配置值和注释的能力。
Config
核心功能
1. 注释管理
// 设置注释
String oldComment = config.setComment("database.host", "数据库服务器地址");
String oldComment2 = config.setComment(Arrays.asList("database", "port"), "数据库端口");
// 移除注释
String removed = config.removeComment("database.host");
// 清空所有注释
config.clearComments();
2. 批量操作注释
// 从Map复制注释
Map<String, CommentNode> comments = new HashMap<>();
config.putAllComments(comments);
// 从其他配置复制注释
CommentedConfig otherConfig = CommentedConfig.inMemory();
config.putAllComments(otherConfig);
3. 创建不可修改视图
UnmodifiableCommentedConfig unmodifiable = config.unmodifiable();
// unmodifiable 是只读的,任何修改操作都会抛出异常
4. 创建子配置
CommentedConfig subConfig = config.createSubConfig();
// 用于创建嵌套的配置结构
静态工厂方法
创建新配置
// 创建内存中的配置
CommentedConfig config1 = CommentedConfig.inMemory();
// 创建线程安全的内存配置
CommentedConfig config2 = CommentedConfig.inMemoryConcurrent();
// 使用特定格式创建配置
CommentedConfig config3 = CommentedConfig.of(InMemoryCommentedFormat.defaultInstance());
复制配置
// 复制普通配置(不带注释)
CommentedConfig copy1 = CommentedConfig.copy(existingConfig);
// 复制带注释的配置
CommentedConfig copy2 = CommentedConfig.copy(commentedConfig);
// 创建线程安全的副本
CommentedConfig concurrentCopy = CommentedConfig.concurrentCopy(config);
包装现有Map
Map<String, Object> map = new HashMap<>();
CommentedConfig wrapped = CommentedConfig.wrap(map, InMemoryFormat.defaultInstance());
// wrapped 配置与原始Map同步更新
创建伪配置
CommentedConfig fake = CommentedConfig.fake(regularConfig);
// 对于非注释配置,提供CommentedConfig接口但不实际存储注释
配置条目接口
表示单个配置条目:
CommentedConfig.Entry
CommentedConfig.Entry entry = // 获取条目
String oldComment = entry.setComment("新的注释");
String removed = entry.removeComment();
主要特性
路径支持: 使用点分隔字符串或字符串列表指定嵌套路径注释继承: 支持嵌套配置的注释管理线程安全: 提供并发安全的配置实例不可修改视图: 提供只读的配置视图灵活创建: 多种工厂方法满足不同需求
使用示例
// 创建配置
CommentedConfig config = CommentedConfig.inMemory();
// 设置值和注释
config.set("database.host", "localhost");
config.setComment("database.host", "数据库服务器地址");
config.set("database.port", 3306);
config.setComment("database.port", "数据库连接端口");
// 获取注释
String comment = config.getComment("database.host");
// 创建不可修改副本
UnmodifiableCommentedConfig readOnly = config.unmodifiable();
这个接口是 NightConfig 库中处理带注释配置文件的核心组件,常用于应用程序配置管理、游戏设置、插件配置等场景。
1. 注释(Comment)在这里的含义
在 NightConfig 中,”注释”指的是配置文件中的注释文本,而不是 Java 代码注释。
Java 代码注释:
// 这是Java代码注释,编译时会被忽略
config.setValue("key", "value");
配置注释(NightConfig 中的概念):
# 这是配置文件中的注释
# 用户可以看到这些说明文字
database {
# 数据库连接地址
host = "localhost"
# 数据库端口号
port = 3306
}
管理的正是配置文件中的这些说明性文字。
CommentedConfig
2. 内存配置的持久化
内存中的配置是可以保存到本地的,只是需要显式调用保存方法:
CommentedConfig config = CommentedConfig.inMemory();
// 在内存中设置配置值和注释
config.set("app.name", "MyApp");
config.setComment("app.name", "应用程序名称");
config.set("app.version", "1.0.0");
config.setComment("app.version", "应用程序版本");
// 保存到文件
ConfigFormat<CommentedConfig> format = TomlFormat.instance();
try {
// 将内存配置写入文件
format.writer().write(config, new File("config.toml"));
} catch (IOException e) {
e.printStackTrace();
}
// 从文件加载配置
CommentedConfig loadedConfig = format.reader().read(new File("config.toml"));
3. 不同配置格式的注释支持
不同的文件格式对注释的支持不同:
| 格式 | 注释符号 | 是否支持注释 |
|---|---|---|
| TOML | |
✅ 完全支持 |
| JSON | 原生不支持 | ⚠️ 有限支持 |
| YAML | |
✅ 完全支持 |
| HOCON | 或 |
✅ 完全支持 |
| XML | |
✅ 支持 |
4. 实际使用示例
// 创建内存配置
CommentedConfig config = CommentedConfig.inMemory();
// 添加配置值和注释
config.set("server.host", "127.0.0.1");
config.setComment("server.host", "服务器IP地址");
config.set("server.port", 8080);
config.setComment("server.port", "服务器监听端口");
config.set("database.url", "jdbc:mysql://localhost:3306/mydb");
config.setComment("database.url", "数据库连接URL");
// 保存为TOML文件
TomlFormat tomlFormat = TomlFormat.instance();
tomlFormat.writer().write(config, Paths.get("config.toml"));
生成的 文件:
config.toml
# 服务器IP地址
host = "127.0.0.1"
# 服务器监听端口
port = 8080
[database]
# 数据库连接URL
url = "jdbc:mysql://localhost:3306/mydb"
总结
配置注释:指的是配置文件中给人看的说明文字,不是 Java 代码注释内存配置:可以随时保存到本地文件,也可以从文件加载持久化:通过 的读写器实现内存与文件的转换用途:让生成的配置文件包含有用的说明信息,提高可读性
ConfigFormat
这种设计让开发者可以在代码中管理配置文件的注释,确保配置文件的文档完整性。
这是 NightConfig 库中的核心 接口,让我们详细分析它与之前
Config 的联系和区别:
CommentedConfig
核心关系
继承关系: →
CommentedConfig →
Config
UnmodifiableConfig
也就是说:
扩展了
CommentedConfig 的功能
Config 是基础配置接口
Config 在
CommentedConfig 基础上增加了注释功能
Config
主要区别对比
| 特性 | Config | CommentedConfig |
|---|---|---|
| 注释支持 | ❌ 不支持 | ✅ 完全支持 |
| 核心功能 | 键值对管理 | 键值对 + 注释管理 |
| 接口方法 | 值操作相关 | 值操作 + 注释操作 |
| Entry接口 | 只能修改值 | 可以修改值和注释 |
Config 接口的核心功能
1. 基础值操作
// 设置值(覆盖已存在的)
<T> T set(List<String> path, Object value);
// 添加值(仅当不存在时)
boolean add(List<String> path, Object value);
// 移除值
<T> T remove(List<String> path);
// 清空所有值
void clear();
2. 批量操作
// 添加所有(不覆盖)
config.addAll(otherConfig);
// 放入所有(覆盖)
config.putAll(otherConfig);
// 移除所有
config.removeAll(otherConfig);
3. 视图和包装
// 创建不可修改视图
UnmodifiableConfig unmodifiable = config.unmodifiable();
// 创建类型检查视图
Config checked = config.checked();
// 包装现有Map
Map<String, Object> map = new HashMap<>();
Config wrapped = Config.wrap(map, format);
与 CommentedConfig 的具体差异
1. 方法差异
Config 独有的方法:
boolean add(List<String> path, Object value); // 条件添加
void addAll(UnmodifiableConfig config); // 非覆盖式添加
void removeAll(UnmodifiableConfig config); // 批量移除
CommentedConfig 额外的方法:
String setComment(List<String> path, String comment); // 设置注释
String removeComment(List<String> path); // 移除注释
void clearComments(); // 清空注释
2. Entry 接口差异
Config.Entry:
interface Entry {
<T> T setValue(Object value); // 只能设置值
}
CommentedConfig.Entry:
interface Entry {
<T> T setValue(Object value); // 设置值
String setComment(String comment); // 设置注释
String removeComment(); // 移除注释
}
3. 工厂方法差异
Config 的创建方法:
Config config1 = Config.inMemory(); // 基础内存配置
Config config2 = Config.inMemoryUniversal(); // 支持所有类型
Config config3 = Config.inMemoryConcurrent(); // 线程安全
CommentedConfig 的创建方法:
CommentedConfig config1 = CommentedConfig.inMemory(); // 带注释的内存配置
CommentedConfig config2 = CommentedConfig.inMemoryConcurrent(); // 线程安全带注释
新增的重要特性
1. 顺序控制
// 检查是否保持插入顺序
boolean preservesOrder = Config.isInsertionOrderPreserved();
// 设置是否保持插入顺序
Config.setInsertionOrderPreserved(true);
// 获取符合要求的Map供应商
Supplier<Map<String, Object>> mapSupplier =
Config.getDefaultMapCreator(false, true); // 非并发,保持顺序
2. Scala 便利方法
// 为Scala语言提供的别名方法
config.update("path", value); // 等同于 set("path", value)
3. 更灵活的Map支持
// 使用自定义Map供应商
Supplier<Map<String, Object>> mapSupplier = LinkedHashMap::new;
Config config = Config.of(mapSupplier, format); // 使用LinkedHashMap保持顺序
使用示例对比
使用 Config(无注释)
Config config = Config.inMemory();
// 只能操作值
config.set("database.host", "localhost");
config.set("database.port", 3306);
String host = config.get("database.host"); // 只能获取值
使用 CommentedConfig(带注释)
CommentedConfig config = CommentedConfig.inMemory();
// 可以同时操作值和注释
config.set("database.host", "localhost");
config.setComment("database.host", "数据库服务器地址");
config.set("database.port", 3306);
config.setComment("database.port", "数据库端口");
String host = config.get("database.host"); // 获取值
String comment = config.getComment("database.host"); // 获取注释
设计模式体现
接口隔离原则: 专注于值管理,
Config 扩展注释功能装饰器模式:
CommentedConfig 和
unmodifiable() 方法提供装饰视图工厂模式:静态工厂方法创建不同特性的配置实例适配器模式:
checked() 方法适配现有Map
wrap()
总结
Config 是基础配置接口,提供核心的键值对管理功能CommentedConfig 是增强接口,在 Config 基础上添加完整的注释管理Config 更轻量,适合不需要注释的场景CommentedConfig 功能更全面,适合需要文档化配置的场景两者共享相同的值操作逻辑,具有良好的向后兼容性
这种分层设计让用户可以根据需要选择合适的功能级别,避免不必要的功能开销。
















暂无评论内容