NightConfig库


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()
方法提供装饰视图工厂模式:静态工厂方法创建不同特性的配置实例适配器模式
wrap()
方法适配现有Map

总结

Config 是基础配置接口,提供核心的键值对管理功能CommentedConfig 是增强接口,在 Config 基础上添加完整的注释管理Config 更轻量,适合不需要注释的场景CommentedConfig 功能更全面,适合需要文档化配置的场景两者共享相同的值操作逻辑,具有良好的向后兼容性

这种分层设计让用户可以根据需要选择合适的功能级别,避免不必要的功能开销。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
当美女好烦ib的头像 - 宋马
评论 抢沙发

请登录后发表评论

    暂无评论内容