All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.hutool.setting.Setting Maven / Gradle / Ivy

There is a newer version: 5.8.33
Show newest version
package cn.hutool.setting;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.io.resource.FileResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.io.resource.UrlResource;
import cn.hutool.core.io.watch.SimpleWatcher;
import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.WatchUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.StaticLog;
import cn.hutool.setting.dialect.Props;

import java.io.File;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.Consumer;

/**
 * 设置工具类。 用于支持设置(配置)文件
* BasicSetting用于替换Properties类,提供功能更加强大的配置文件,同时对Properties文件向下兼容 * *
 *  1、支持变量,默认变量命名为 ${变量名},变量只能识别读入行的变量,例如第6行的变量在第三行无法读取
 *  2、支持分组,分组为中括号括起来的内容,中括号以下的行都为此分组的内容,无分组相当于空字符分组,若某个key是name,加上分组后的键相当于group.name
 *  3、注释以#开头,但是空行和不带“=”的行也会被跳过,但是建议加#
 *  4、store方法不会保存注释内容,慎重使用
 * 
* * @author looly */ public class Setting extends AbsSetting implements Map { private static final long serialVersionUID = 3618305164959883393L; /** * 默认字符集 */ public static final Charset DEFAULT_CHARSET = CharsetUtil.CHARSET_UTF_8; /** * 默认配置文件扩展名 */ public static final String EXT_NAME = "setting"; /** * 构建一个空的Setting,用于手动加入参数 * * @return Setting * @since 5.4.3 */ public static Setting create() { return new Setting(); } /** * 附带分组的键值对存储 */ private final GroupedMap groupedMap = new GroupedMap(); /** * 本设置对象的字符集 */ protected Charset charset; /** * 是否使用变量 */ protected boolean isUseVariable; /** * 设定文件的资源 */ protected Resource resource; private SettingLoader settingLoader; private WatchMonitor watchMonitor; // ------------------------------------------------------------------------------------- Constructor start /** * 空构造 */ public Setting() { this.charset = DEFAULT_CHARSET; } /** * 构造 * * @param path 相对路径或绝对路径 */ public Setting(String path) { this(path, false); } /** * 构造 * * @param path 相对路径或绝对路径 * @param isUseVariable 是否使用变量 */ public Setting(String path, boolean isUseVariable) { this(path, DEFAULT_CHARSET, isUseVariable); } /** * 构造,使用相对于Class文件根目录的相对路径 * * @param path 相对路径或绝对路径 * @param charset 字符集 * @param isUseVariable 是否使用变量 */ public Setting(String path, Charset charset, boolean isUseVariable) { Assert.notBlank(path, "Blank setting path !"); this.init(ResourceUtil.getResourceObj(path), charset, isUseVariable); } /** * 构造 * * @param configFile 配置文件对象 * @param charset 字符集 * @param isUseVariable 是否使用变量 */ public Setting(File configFile, Charset charset, boolean isUseVariable) { Assert.notNull(configFile, "Null setting file define!"); this.init(new FileResource(configFile), charset, isUseVariable); } /** * 构造,相对于classes读取文件 * * @param path 相对ClassPath路径或绝对路径 * @param clazz 基准类 * @param charset 字符集 * @param isUseVariable 是否使用变量 */ public Setting(String path, Class clazz, Charset charset, boolean isUseVariable) { Assert.notBlank(path, "Blank setting path !"); this.init(new ClassPathResource(path, clazz), charset, isUseVariable); } /** * 构造 * * @param url 设定文件的URL * @param charset 字符集 * @param isUseVariable 是否使用变量 */ public Setting(URL url, Charset charset, boolean isUseVariable) { Assert.notNull(url, "Null setting url define!"); this.init(new UrlResource(url), charset, isUseVariable); } /** * 构造 * * @param resource Setting的Resource * @param charset 字符集 * @param isUseVariable 是否使用变量 * @since 5.4.4 */ public Setting(Resource resource, Charset charset, boolean isUseVariable) { this.init(resource, charset, isUseVariable); } // ------------------------------------------------------------------------------------- Constructor end /** * 初始化设定文件 * * @param resource {@link Resource} * @param charset 字符集 * @param isUseVariable 是否使用变量 * @return 成功初始化与否 */ public boolean init(Resource resource, Charset charset, boolean isUseVariable) { Assert.notNull(resource, "Setting resource must be not null!"); this.resource = resource; this.charset = charset; this.isUseVariable = isUseVariable; return load(); } /** * 重新加载配置文件 * * @return 是否加载成功 */ synchronized public boolean load() { if (null == this.settingLoader) { settingLoader = new SettingLoader(this.groupedMap, this.charset, this.isUseVariable); } return settingLoader.load(this.resource); } /** * 在配置文件变更时自动加载 * * @param autoReload 是否自动加载 */ public void autoLoad(boolean autoReload) { autoLoad(autoReload, null); } /** * 在配置文件变更时自动加载 * * @param callback 加载完成回调 * @param autoReload 是否自动加载 */ public void autoLoad(boolean autoReload, Consumer callback) { if (autoReload) { Assert.notNull(this.resource, "Setting resource must be not null !"); if (null != this.watchMonitor) { // 先关闭之前的监听 this.watchMonitor.close(); } this.watchMonitor = WatchUtil.createModify(resource.getUrl(), new SimpleWatcher() { @Override public void onModify(WatchEvent event, Path currentPath) { boolean success = load(); // 如果有回调,加载完毕则执行回调 if (callback != null) { callback.accept(success); } } }); this.watchMonitor.start(); StaticLog.debug("Auto load for [{}] listenning...", this.resource.getUrl()); } else { IoUtil.close(this.watchMonitor); this.watchMonitor = null; } } /** * 获得设定文件的URL * * @return 获得设定文件的路径 * @since 5.4.3 */ public URL getSettingUrl() { return (null == this.resource) ? null : this.resource.getUrl(); } /** * 获得设定文件的路径 * * @return 获得设定文件的路径 */ public String getSettingPath() { final URL settingUrl = getSettingUrl(); return (null == settingUrl) ? null : settingUrl.getPath(); } /** * 键值总数 * * @return 键值总数 */ @Override public int size() { return this.groupedMap.size(); } @Override public String getByGroup(String key, String group) { return this.groupedMap.get(group, key); } /** * 获取并删除键值对,当指定键对应值非空时,返回并删除这个值,后边的键对应的值不再查找 * * @param keys 键列表,常用于别名 * @return 值 * @since 3.1.2 */ public Object getAndRemove(String... keys) { Object value = null; for (String key : keys) { value = remove(key); if (null != value) { break; } } return value; } /** * 获取并删除键值对,当指定键对应值非空时,返回并删除这个值,后边的键对应的值不再查找 * * @param keys 键列表,常用于别名 * @return 字符串值 * @since 3.1.2 */ public String getAndRemoveStr(String... keys) { String value = null; for (String key : keys) { value = remove(key); if (null != value) { break; } } return value; } /** * 获得指定分组的所有键值对,此方法获取的是原始键值对,获取的键值对可以被修改 * * @param group 分组 * @return map */ public Map getMap(String group) { final LinkedHashMap map = this.groupedMap.get(group); return (null != map) ? map : new LinkedHashMap<>(0); } /** * 获取group分组下所有配置键值对,组成新的Setting * * @param group 分组 * @return Setting */ public Setting getSetting(String group) { final Setting setting = new Setting(); setting.putAll(this.getMap(group)); return setting; } /** * 获取group分组下所有配置键值对,组成新的{@link Properties} * * @param group 分组 * @return Properties对象 */ public Properties getProperties(String group) { final Properties properties = new Properties(); properties.putAll(getMap(group)); return properties; } /** * 获取group分组下所有配置键值对,组成新的{@link Props} * * @param group 分组 * @return Props对象 * @since 4.1.21 */ public Props getProps(String group) { final Props props = new Props(); props.putAll(getMap(group)); return props; } // --------------------------------------------------------------------------------- Functions /** * 持久化当前设置,会覆盖掉之前的设置
* 持久化不会保留之前的分组,注意如果配置文件在jar内部或者在exe中,此方法会报错。 * * @since 5.4.3 */ public void store() { final URL resourceUrl = getSettingUrl(); Assert.notNull(resourceUrl, "Setting path must be not null !"); store(FileUtil.file(resourceUrl)); } /** * 持久化当前设置,会覆盖掉之前的设置
* 持久化不会保留之前的分组 * * @param absolutePath 设置文件的绝对路径 */ public void store(String absolutePath) { store(FileUtil.touch(absolutePath)); } /** * 持久化当前设置,会覆盖掉之前的设置
* 持久化不会保留之前的分组 * * @param file 设置文件 * @since 5.4.3 */ public void store(File file) { if (null == this.settingLoader) { settingLoader = new SettingLoader(this.groupedMap, this.charset, this.isUseVariable); } settingLoader.store(file); } /** * 转换为Properties对象,原分组变为前缀 * * @return Properties对象 */ public Properties toProperties() { final Properties properties = new Properties(); String group; for (Entry> groupEntry : this.groupedMap.entrySet()) { group = groupEntry.getKey(); for (Entry entry : groupEntry.getValue().entrySet()) { properties.setProperty(StrUtil.isEmpty(group) ? entry.getKey() : group + CharUtil.DOT + entry.getKey(), entry.getValue()); } } return properties; } /** * 获取GroupedMap * * @return GroupedMap * @since 4.0.12 */ public GroupedMap getGroupedMap() { return this.groupedMap; } /** * 获取所有分组 * * @return 获得所有分组名 */ public List getGroups() { return CollUtil.newArrayList(this.groupedMap.keySet()); } /** * 设置变量的正则
* 正则只能有一个group表示变量本身,剩余为字符 例如 \$\{(name)\}表示${name}变量名为name的一个变量表示 * * @param regex 正则 * @return this */ public Setting setVarRegex(String regex) { if (null == this.settingLoader) { throw new NullPointerException("SettingLoader is null !"); } this.settingLoader.setVarRegex(regex); return this; } /** * 自定义字符编码 * * @param charset 字符编码 * @return this * @since 4.6.2 */ public Setting setCharset(Charset charset) { this.charset = charset; return this; } // ------------------------------------------------- Map interface with group /** * 某个分组对应的键值对是否为空 * * @param group 分组 * @return 是否为空 */ public boolean isEmpty(String group) { return this.groupedMap.isEmpty(group); } /** * 指定分组中是否包含指定key * * @param group 分组 * @param key 键 * @return 是否包含key */ public boolean containsKey(String group, String key) { return this.groupedMap.containsKey(group, key); } /** * 指定分组中是否包含指定值 * * @param group 分组 * @param value 值 * @return 是否包含值 */ public boolean containsValue(String group, String value) { return this.groupedMap.containsValue(group, value); } /** * 获取分组对应的值,如果分组不存在或者值不存在则返回null * * @param group 分组 * @param key 键 * @return 值,如果分组不存在或者值不存在则返回null */ public String get(String group, String key) { return this.groupedMap.get(group, key); } /** * 将键值对加入到对应分组中 * * @param key 键 * @param group 分组 * @param value 值 * @return 此key之前存在的值,如果没有返回null */ public String putByGroup(String key, String group, String value) { return this.groupedMap.put(group, key, value); } /** * 从指定分组中删除指定值 * * @param group 分组 * @param key 键 * @return 被删除的值,如果值不存在,返回null */ public String remove(String group, Object key) { return this.groupedMap.remove(group, Convert.toStr(key)); } /** * 加入多个键值对到某个分组下 * * @param group 分组 * @param m 键值对 * @return this */ public Setting putAll(String group, Map m) { this.groupedMap.putAll(group, m); return this; } /** * 添加一个Stting到主配置中 * * @param setting Setting配置 * @return this * @since 5.2.4 */ public Setting addSetting(Setting setting) { for (Entry> e : setting.getGroupedMap().entrySet()) { this.putAll(e.getKey(), e.getValue()); } return this; } /** * 清除指定分组下的所有键值对 * * @param group 分组 * @return this */ public Setting clear(String group) { this.groupedMap.clear(group); return this; } /** * 指定分组所有键的Set * * @param group 分组 * @return 键Set */ public Set keySet(String group) { return this.groupedMap.keySet(group); } /** * 指定分组下所有值 * * @param group 分组 * @return 值 */ public Collection values(String group) { return this.groupedMap.values(group); } /** * 指定分组下所有键值对 * * @param group 分组 * @return 键值对 */ public Set> entrySet(String group) { return this.groupedMap.entrySet(group); } /** * 设置值 * * @param key 键 * @param value 值 * @return this * @since 3.3.1 */ public Setting set(String key, String value) { this.put(key, value); return this; } /** * 将键值对加入到对应分组中
* 此方法用于与getXXX统一参数顺序 * * @param key 键 * @param group 分组 * @param value 值 * @return 此key之前存在的值,如果没有返回null * @since 5.5.7 */ public Setting setByGroup(String key, String group, String value) { this.putByGroup(key, group, value); return this; } // ------------------------------------------------- Override Map interface @Override public boolean isEmpty() { return this.groupedMap.isEmpty(); } /** * 默认分组(空分组)中是否包含指定key对应的值 * * @param key 键 * @return 默认分组中是否包含指定key对应的值 */ @Override public boolean containsKey(Object key) { return this.groupedMap.containsKey(DEFAULT_GROUP, Convert.toStr(key)); } /** * 默认分组(空分组)中是否包含指定值 * * @param value 值 * @return 默认分组中是否包含指定值 */ @Override public boolean containsValue(Object value) { return this.groupedMap.containsValue(DEFAULT_GROUP, Convert.toStr(value)); } /** * 获取默认分组(空分组)中指定key对应的值 * * @param key 键 * @return 默认分组(空分组)中指定key对应的值 */ @Override public String get(Object key) { return this.groupedMap.get(DEFAULT_GROUP, Convert.toStr(key)); } /** * 将指定键值对加入到默认分组(空分组)中 * * @param key 键 * @param value 值 * @return 加入的值 */ @Override public String put(String key, String value) { return this.groupedMap.put(DEFAULT_GROUP, key, value); } /** * 移除默认分组(空分组)中指定值 * * @param key 键 * @return 移除的值 */ @Override public String remove(Object key) { return this.groupedMap.remove(DEFAULT_GROUP, Convert.toStr(key)); } /** * 将键值对Map加入默认分组(空分组)中 * * @param m Map */ @SuppressWarnings("NullableProblems") @Override public void putAll(Map m) { this.groupedMap.putAll(DEFAULT_GROUP, m); } /** * 清空默认分组(空分组)中的所有键值对 */ @Override public void clear() { this.groupedMap.clear(DEFAULT_GROUP); } /** * 获取默认分组(空分组)中的所有键列表 * * @return 默认分组(空分组)中的所有键列表 */ @SuppressWarnings("NullableProblems") @Override public Set keySet() { return this.groupedMap.keySet(DEFAULT_GROUP); } /** * 获取默认分组(空分组)中的所有值列表 * * @return 默认分组(空分组)中的所有值列表 */ @SuppressWarnings("NullableProblems") @Override public Collection values() { return this.groupedMap.values(DEFAULT_GROUP); } /** * 获取默认分组(空分组)中的所有键值对列表 * * @return 默认分组(空分组)中的所有键值对列表 */ @SuppressWarnings("NullableProblems") @Override public Set> entrySet() { return this.groupedMap.entrySet(DEFAULT_GROUP); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((charset == null) ? 0 : charset.hashCode()); result = prime * result + groupedMap.hashCode(); result = prime * result + (isUseVariable ? 1231 : 1237); result = prime * result + ((this.resource == null) ? 0 : this.resource.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Setting other = (Setting) obj; if (charset == null) { if (other.charset != null) { return false; } } else if (false == charset.equals(other.charset)) { return false; } if (false == groupedMap.equals(other.groupedMap)) { return false; } if (isUseVariable != other.isUseVariable) { return false; } if (this.resource == null) { return other.resource == null; } else { return resource.equals(other.resource); } } @Override public String toString() { return groupedMap.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy