This commit is contained in:
Roman Zhuravlev 2023-07-16 20:06:05 +05:00
commit e443249166
29 changed files with 2887 additions and 0 deletions

20
io/pom.xml Normal file
View file

@ -0,0 +1,20 @@
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>parent</artifactId>
<groupId>org.zhdev.varioutil</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>io</artifactId>
<dependencies>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,41 @@
package org.zhdev.config;
import java.io.*;
public interface Config extends ConfigSection {
String DEFAULT_KEY = "config.yml";
String getKey();
String[] getHeaderComments();
String[] getEndComments();
void setHeaderComments(String... headerComments);
void setEndComments(String... endComments);
void load(Reader reader);
void load(InputStream stream) throws IOException, ConfigException;
void load(File file) throws IOException, ConfigException;
File load(String path) throws ConfigException;
File load() throws ConfigException;
void save(Writer writer);
void save(OutputStream stream) throws IOException, ConfigException;
void save(File file) throws IOException, ConfigException;
File save(String path) throws ConfigException;
File save() throws ConfigException;
File saveIfEmpty(String path) throws ConfigException;
File saveIfEmpty() throws ConfigException;
}

View file

@ -0,0 +1,18 @@
package org.zhdev.config;
public class ConfigException extends RuntimeException {
public ConfigException() {
}
public ConfigException(Throwable cause) {
super(cause);
}
public ConfigException(String s) {
super(s);
}
public ConfigException(String message, Throwable cause) {
super(message, cause);
}
}

View file

@ -0,0 +1,123 @@
package org.zhdev.config;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public interface ConfigSection extends Iterable<String> {
String[] getBlockComments(String key);
String[] getInlineComments(String key);
void setBlockComments(String key, String... blockComments);
void setInlineComments(String key, String... inlineComments);
void removeBlockComments(String key);
void removeInlineComments(String key);
Map<String, Object> toMap();
<T> T get(String key);
<T> T get(String key, T fallback);
<T> T get(String key, Function<String, T> fallback);
<T> T getOrSet(String key, T fallback, String... blockComments);
<T> T getOrSet(String key, Function<String, T> fallback, String... blockComments);
ConfigSection getSection(String key, String... names);
ConfigSection getOrCreateSection(String key, String... names);
Byte getByte(String key);
Byte getByte(String key, Byte fallback);
Byte getByte(String key, Function<String, Byte> fallback);
Byte getOrSetByte(String key, Byte fallback, String... blockComments);
Byte getOrSetByte(String key, Function<String, Byte> fallback, String... blockComments);
Short getShort(String key);
Short getShort(String key, Short fallback);
Short getShort(String key, Function<String, Short> fallback);
Short getOrSetShort(String key, Short fallback, String... blockComments);
Short getOrSetShort(String key, Function<String, Short> fallback, String... blockComments);
Integer getInteger(String key);
Integer getInteger(String key, Integer fallback);
Integer getInteger(String key, Function<String, Integer> fallback);
Integer getOrSetInteger(String key, Integer fallback, String... blockComments);
Integer getOrSetInteger(String key, Function<String, Integer> fallback, String... blockComments);
Long getLong(String key);
Long getLong(String key, Long fallback);
Long getLong(String key, Function<String, Long> fallback);
Long getOrSetLong(String key, Long fallback, String... blockComments);
Long getOrSetLong(String key, Function<String, Long> fallback, String... blockComments);
Float getFloat(String key);
Float getFloat(String key, Float fallback);
Float getFloat(String key, Function<String, Float> fallback);
Float getOrSetFloat(String key, Float fallback, String... blockComments);
Float getOrSetFloat(String key, Function<String, Float> fallback, String... blockComments);
Double getDouble(String key);
Double getDouble(String key, Double fallback);
Double getDouble(String key, Function<String, Double> fallback);
Double getOrSetDouble(String key, Double fallback, String... blockComments);
Double getOrSetDouble(String key, Function<String, Double> fallback, String... blockComments);
String getString(String key);
String getString(String key, String fallback);
String getString(String key, Function<String, String> fallback);
String getOrSetString(String key, String fallback, String... blockComments);
String getOrSetString(String key, Function<String, String> fallback, String... blockComments);
List<?> getList(String key);
List<?> getList(String key, List<?> fallback);
List<?> getList(String key, Function<String, List<?>> fallback);
List<?> getOrSetList(String key, List<?> fallback, String... blockComments);
List<?> getOrSetList(String key, Function<String, List<?>> fallback, String... blockComments);
<T> T set(String key, T object, String... blockComments);
<T> T set(String key, T object);
<T> T remove(String key);
void clear();
}

View file

@ -0,0 +1,19 @@
package org.zhdev.config;
class ConfigSectionNode {
Object value;
String[] blockComments;
String[] inlineComments;
ConfigSectionNode(Object value, String[] blockComments) {
this.value = value;
this.blockComments = blockComments;
}
ConfigSectionNode(Object value) {
this.value = value;
}
ConfigSectionNode() {
}
}

View file

@ -0,0 +1,469 @@
package org.zhdev.config;
import java.util.*;
import java.util.function.Function;
public class MapConfigSection implements ConfigSection {
protected final LinkedHashMap<String, ConfigSectionNode> map;
protected MapConfigSection(LinkedHashMap<String, ConfigSectionNode> map) {;
this.map = map;
}
protected MapConfigSection() {
this(new LinkedHashMap<>());
}
public String[] getBlockComments(String key) {
ConfigSectionNode node = map.get(key);
return node == null ? null : node.blockComments;
}
public String[] getInlineComments(String key) {
ConfigSectionNode node = map.get(key);
return node == null ? null : node.inlineComments;
}
public void setBlockComments(String key, String... blockComments) {
ConfigSectionNode node = map.get(key);
if (node == null) {
node = new ConfigSectionNode();
map.put(key, node);
}
node.blockComments = blockComments;
}
public void setInlineComments(String key, String... inlineComments) {
ConfigSectionNode node = map.get(key);
if (node == null) {
node = new ConfigSectionNode();
map.put(key, node);
}
node.inlineComments = inlineComments;
}
public void removeBlockComments(String key) {
ConfigSectionNode node = map.get(key);
if (node != null) {
node.blockComments = null;
}
}
public void removeInlineComments(String key) {
ConfigSectionNode node = map.get(key);
if (node != null) {
node.inlineComments = null;
}
}
@Override
public Map<String, Object> toMap() {
Map<String, Object> map = new LinkedHashMap<>(this.map.size());
for (Map.Entry<String, ConfigSectionNode> entry : this.map.entrySet()) {
Object obj = entry.getValue().value;
if (obj instanceof MapConfigSection) {
obj = ((MapConfigSection) obj).toMap();
}
map.put(entry.getKey(), obj);
}
return map;
}
@SuppressWarnings("unchecked")
public <T> T get(String key) {
ConfigSectionNode node = map.get(key);
return node == null ? null : (T) node.value;
}
@SuppressWarnings("unchecked")
public <T> T get(String key, T fallback) {
ConfigSectionNode node = map.get(key);
return node == null ? fallback : (T) node.value;
}
public <T> T get(String key, Function<String, T> fallback) {
T object = get(key);
return object == null ? fallback.apply(key) : object;
}
public <T> T getOrSet(String key, T fallback, String... blockComments) {
T object = get(key);
if (object == null) {
map.put(key, new ConfigSectionNode(fallback, blockComments));
return fallback;
}
return object;
}
public <T> T getOrSet(String key, Function<String, T> fallback, String... blockComments) {
T object = get(key);
if (object == null) {
object = fallback.apply(key);
map.put(key, new ConfigSectionNode(object, blockComments));
}
return object;
}
public MapConfigSection getSection(String key, String... names) {
Object object = get(key);
if (object instanceof MapConfigSection) {
MapConfigSection section = (MapConfigSection) object;
for (String n : names) {
section = section.getSection(n);
if (section == null) return null;
}
return section;
}
return null;
}
public MapConfigSection getOrCreateSection(String key, String... names) {
MapConfigSection section;
Object object = get(key);
if (object instanceof MapConfigSection) {
section = (MapConfigSection) object;
} else {
section = new MapConfigSection();
map.put(key, new ConfigSectionNode(section));
}
for (String n : names) section = section.getOrCreateSection(n);
return section;
}
public Byte getByte(String key) {
Object object = get(key);
if (object instanceof Byte) {
return (Byte) object;
} else if (object instanceof Number) {
Number number = (Number) object;
return number.byteValue();
}
return null;
}
public Byte getByte(String key, Byte fallback) {
Byte object = getByte(key);
return object == null ? fallback : object;
}
public Byte getByte(String key, Function<String, Byte> fallback) {
Byte object = getByte(key);
return object == null ? fallback.apply(key) : object;
}
public Byte getOrSetByte(String key, Byte fallback, String... blockComments) {
Byte object = getByte(key);
if (object == null) {
map.put(key, new ConfigSectionNode(fallback, blockComments));
return fallback;
}
return object;
}
public Byte getOrSetByte(String key, Function<String, Byte> fallback, String... blockComments) {
Byte object = getByte(key);
if (object == null) {
object = fallback.apply(key);
map.put(key, new ConfigSectionNode(object, blockComments));
}
return object;
}
public Short getShort(String key) {
Object object = get(key);
if (object instanceof Short) {
return (Short) object;
} else if (object instanceof Number) {
Number number = (Number) object;
return number.shortValue();
}
return null;
}
public Short getShort(String key, Short fallback) {
Short object = getShort(key);
return object == null ? fallback : object;
}
public Short getShort(String key, Function<String, Short> fallback) {
Short object = getShort(key);
return object == null ? fallback.apply(key) : object;
}
public Short getOrSetShort(String key, Short fallback, String... blockComments) {
Short object = getShort(key);
if (object == null) {
map.put(key, new ConfigSectionNode(fallback, blockComments));
return fallback;
}
return object;
}
public Short getOrSetShort(String key, Function<String, Short> fallback, String... blockComments) {
Short object = getShort(key);
if (object == null) {
object = fallback.apply(key);
map.put(key, new ConfigSectionNode(object, blockComments));
}
return object;
}
public Integer getInteger(String key) {
Object object = get(key);
if (object instanceof Integer) {
return (Integer) object;
} else if (object instanceof Number) {
Number number = (Number) object;
return number.intValue();
}
return null;
}
public Integer getInteger(String key, Integer fallback) {
Integer object = getInteger(key);
return object == null ? fallback : object;
}
public Integer getInteger(String key, Function<String, Integer> fallback) {
Integer object = getInteger(key);
return object == null ? fallback.apply(key) : object;
}
public Integer getOrSetInteger(String key, Integer fallback, String... blockComments) {
Integer object = getInteger(key);
if (object == null) {
map.put(key, new ConfigSectionNode(fallback, blockComments));
return fallback;
}
return object;
}
public Integer getOrSetInteger(String key, Function<String, Integer> fallback, String... blockComments) {
Integer object = getInteger(key);
if (object == null) {
object = fallback.apply(key);
map.put(key, new ConfigSectionNode(object, blockComments));
}
return object;
}
public Long getLong(String key) {
Object object = get(key);
if (object instanceof Long) {
return (Long) object;
} else if (object instanceof Number) {
Number number = (Number) object;
return number.longValue();
}
return null;
}
public Long getLong(String key, Long fallback) {
Long object = getLong(key);
return object == null ? fallback : object;
}
public Long getLong(String key, Function<String, Long> fallback) {
Long object = getLong(key);
return object == null ? fallback.apply(key) : object;
}
public Long getOrSetLong(String key, Long fallback, String... blockComments) {
Long object = getLong(key);
if (object == null) {
map.put(key, new ConfigSectionNode(fallback, blockComments));
return fallback;
}
return object;
}
public Long getOrSetLong(String key, Function<String, Long> fallback, String... blockComments) {
Long object = getLong(key);
if (object == null) {
object = fallback.apply(key);
map.put(key, new ConfigSectionNode(object, blockComments));
}
return object;
}
public Float getFloat(String key) {
Object object = get(key);
if (object instanceof Float) {
return (Float) object;
} else if (object instanceof Number) {
Number number = (Number) object;
return number.floatValue();
}
return null;
}
public Float getFloat(String key, Float fallback) {
Float object = getFloat(key);
return object == null ? fallback : object;
}
public Float getFloat(String key, Function<String, Float> fallback) {
Float object = getFloat(key);
return object == null ? fallback.apply(key) : object;
}
public Float getOrSetFloat(String key, Float fallback, String... blockComments) {
Float object = getFloat(key);
if (object == null) {
map.put(key, new ConfigSectionNode(fallback, blockComments));
return fallback;
}
return object;
}
public Float getOrSetFloat(String key, Function<String, Float> fallback, String... blockComments) {
Float object = getFloat(key);
if (object == null) {
object = fallback.apply(key);
map.put(key, new ConfigSectionNode(object, blockComments));
}
return object;
}
public Double getDouble(String key) {
Object object = get(key);
if (object instanceof Double) {
return (Double) object;
} else if (object instanceof Number) {
Number number = (Number) object;
return number.doubleValue();
}
return null;
}
public Double getDouble(String key, Double fallback) {
Double object = getDouble(key);
return object == null ? fallback : object;
}
public Double getDouble(String key, Function<String, Double> fallback) {
Double object = getDouble(key);
return object == null ? fallback.apply(key) : object;
}
public Double getOrSetDouble(String key, Double fallback, String... blockComments) {
Double object = getDouble(key);
if (object == null) {
map.put(key, new ConfigSectionNode(fallback, blockComments));
return fallback;
}
return object;
}
public Double getOrSetDouble(String key, Function<String, Double> fallback, String... blockComments) {
Double object = getDouble(key);
if (object == null) {
object = fallback.apply(key);
map.put(key, new ConfigSectionNode(object, blockComments));
}
return object;
}
public String getString(String key) {
Object object = get(key);
return object == null ? null : object.toString();
}
public String getString(String key, String fallback) {
String object = getString(key);
return object == null ? fallback : object;
}
public String getString(String key, Function<String, String> fallback) {
String object = getString(key);
return object == null ? fallback.apply(key) : object;
}
public String getOrSetString(String key, String fallback, String... blockComments) {
String object = getString(key);
if (object == null) {
map.put(key, new ConfigSectionNode(fallback, blockComments));
return fallback;
}
return object;
}
public String getOrSetString(String key, Function<String, String> fallback, String... blockComments) {
String object = getString(key);
if (object == null) {
object = fallback.apply(key);
map.put(key, new ConfigSectionNode(object, blockComments));
}
return object;
}
public List<?> getList(String key) {
Object object = get(key);
return object instanceof List ? (List<?>) object : null;
}
public List<?> getList(String key, List<?> fallback) {
List<?> object = getList(key);
return object == null ? fallback : object;
}
public List<?> getList(String key, Function<String, List<?>> fallback) {
List<?> object = getList(key);
return object == null ? fallback.apply(key) : object;
}
public List<?> getOrSetList(String key, List<?> fallback, String... blockComments) {
List<?> object = getList(key);
if (object == null) {
map.put(key, new ConfigSectionNode(fallback, blockComments));
return fallback;
}
return object;
}
public List<?> getOrSetList(String key, Function<String, List<?>> fallback, String... blockComments) {
List<?> object = getList(key);
if (object == null) {
object = fallback.apply(key);
map.put(key, new ConfigSectionNode(fallback, blockComments));
}
return object;
}
@SuppressWarnings("unchecked")
public <T> T set(String key, T value, String... blockComments) {
ConfigSectionNode node = map.get(key);
if (node == null) {
return (T) map.put(key, new ConfigSectionNode(value, blockComments));
}
Object oldValue = node.value;
node.value = value;
node.blockComments = blockComments;
return (T) oldValue;
}
@SuppressWarnings("unchecked")
public <T> T set(String key, T value) {
ConfigSectionNode node = map.get(key);
if (node == null) {
return (T) map.put(key, new ConfigSectionNode(value));
}
Object oldValue = node.value;
node.value = value;
return (T) oldValue;
}
@SuppressWarnings("unchecked")
public <T> T remove(String key) {
return (T) map.remove(key);
}
public void clear() {
map.clear();
}
@Override
public Iterator<String> iterator() {
return map.keySet().iterator();
}
}

View file

@ -0,0 +1,258 @@
package org.zhdev.config;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.comments.CommentLine;
import org.yaml.snakeyaml.comments.CommentType;
import org.yaml.snakeyaml.nodes.*;
import org.yaml.snakeyaml.parser.ParserException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class YamlConfig extends MapConfigSection implements Config {
protected static final YamlConfigConstructor CONSTRUCTOR;
protected static final YamlConfigRepresenter REPRESENTER;
protected static final Yaml YAML;
protected final String key;
protected String[] headerComments;
protected String[] endComments;
public YamlConfig(String key) {
super(null);
this.key = key;
}
public YamlConfig() {
this(DEFAULT_KEY);
}
@Override
public String getKey() {
return key;
}
@Override
public String[] getHeaderComments() {
return headerComments;
}
@Override
public String[] getEndComments() {
return endComments;
}
@Override
public void setHeaderComments(String... headerComments) {
this.headerComments = headerComments;
}
@Override
public void setEndComments(String... endComments) {
this.endComments = endComments;
}
private String[] createComments(List<CommentLine> lines) {
String[] comments = new String[lines.size()];
for (int i = 0; i < lines.size(); i++) {
CommentLine line = lines.get(i);
comments[i] = line.getValue().trim();
}
return comments;
}
protected Object constructHandle(Node keyNode, Node valueNode, String key, Object value) {
if (valueNode instanceof MappingNode) {
MapConfigSection childSection = new MapConfigSection();
nodesToSections(childSection, (MappingNode) valueNode);
value = childSection;
}
return value;
}
private void nodesToSections(MapConfigSection section, MappingNode node) {
for (NodeTuple tuple : node.getValue()) {
Node keyNode = tuple.getKeyNode();
Node valueNode = tuple.getValueNode();
String key = String.valueOf(CONSTRUCTOR.constructObject(keyNode));
Object value = constructHandle(keyNode, valueNode, key, CONSTRUCTOR.constructObject(valueNode));
ConfigSectionNode sectionNode = new ConfigSectionNode(value);
if (keyNode.getBlockComments() != null && keyNode.getBlockComments().size() > 0) {
sectionNode.blockComments = createComments(keyNode.getBlockComments());
}
if (keyNode.getInLineComments() != null && keyNode.getInLineComments().size() > 0) {
sectionNode.inlineComments = createComments((valueNode instanceof MappingNode || valueNode instanceof SequenceNode ? keyNode : valueNode).getInLineComments());
}
section.map.put(key, sectionNode);
}
}
@Override
public void load(Reader reader) throws ConfigException {
try {
MappingNode node = (MappingNode) YAML.compose(reader);
if (node == null) return;
if (node.getBlockComments() != null && node.getBlockComments().size() > 0) {
headerComments = createComments(node.getBlockComments());
}
if (node.getEndComments() != null && node.getEndComments().size() > 0) {
endComments = createComments(node.getEndComments());
}
nodesToSections(this, node);
} catch (ParserException e) {
throw new ConfigException(e);
}
}
@Override
public void load(InputStream stream) {
load(new InputStreamReader(stream, StandardCharsets.UTF_8));
}
@Override
public void load(File file) throws IOException, ConfigException {
load(Files.newInputStream(file.toPath()));
}
@Override
public File load(String path) throws ConfigException {
File file = new File(path);
try {
if (file.exists()) load(file);
} catch (IOException e) {
throw new ConfigException(e);
}
return file;
}
@Override
public File load() throws ConfigException {
return load(key);
}
private List<CommentLine> createCommentLines(String[] comments, CommentType commentType) {
List<CommentLine> lines = new ArrayList<>(comments.length);
for (String comment : comments) {
lines.add(new CommentLine(null, null, comment.isEmpty() ? comment : " " + comment, commentType));
}
return lines;
}
protected Node representHandle(String key, Object value) {
Node valueNode;
if (value instanceof MapConfigSection) {
valueNode = sectionsToNodes((MapConfigSection) value);
} else {
valueNode = REPRESENTER.represent(value);
}
return valueNode;
}
private MappingNode sectionsToNodes(MapConfigSection section) {
List<NodeTuple> tuples = new ArrayList<>();
for (Map.Entry<String, ConfigSectionNode> entry : section.map.entrySet()) {
String key = entry.getKey();
ConfigSectionNode sectionNode = entry.getValue();
Object value = sectionNode.value;
Node keyNode = REPRESENTER.represent(key);
Node valueNode = representHandle(key, value);
keyNode.setBlockComments(sectionNode.blockComments == null ? null : createCommentLines(sectionNode.blockComments, CommentType.BLOCK));
(valueNode instanceof MappingNode || valueNode instanceof SequenceNode ? keyNode : valueNode).setInLineComments(sectionNode.inlineComments == null ? null : createCommentLines(sectionNode.inlineComments, CommentType.IN_LINE));
tuples.add(new NodeTuple(keyNode, valueNode));
}
return new MappingNode(Tag.MAP, tuples, DumperOptions.FlowStyle.BLOCK);
}
@Override
public void save(Writer writer) {
MappingNode node = sectionsToNodes(this);
node.setBlockComments(headerComments == null ? null : createCommentLines(headerComments, CommentType.BLOCK));
node.setEndComments(endComments == null ? null : createCommentLines(endComments, CommentType.BLOCK));
YAML.serialize(node, writer);
}
@Override
public void save(OutputStream stream) {
save(new OutputStreamWriter(stream, StandardCharsets.UTF_8));
}
@Override
public void save(File file) throws IOException {
save(Files.newOutputStream(file.toPath()));
}
@Override
public File save(String path) throws ConfigException {
File file = new File(path);
if (!file.exists()) {
File parent = file.getParentFile();
if (parent != null) parent.mkdirs();
try {
file.createNewFile();
save(file);
} catch (IOException e) {
throw new ConfigException(e);
}
}
return file;
}
@Override
public File save() throws ConfigException {
return save(key);
}
@Override
public File saveIfEmpty(String path) throws ConfigException {
File file = new File(path);
if (file.length() == 0) {
File parent = file.getParentFile();
if (parent != null) parent.mkdirs();
try {
file.createNewFile();
save(file);
} catch (IOException e) {
throw new ConfigException(e);
}
}
return file;
}
@Override
public File saveIfEmpty() throws ConfigException {
return saveIfEmpty(key);
}
static {
LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setProcessComments(true);
DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setProcessComments(true);
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
dumperOptions.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);
YamlConfigRepresenter representer = new YamlConfigRepresenter(dumperOptions);
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
representer.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);
REPRESENTER = representer;
YAML = new Yaml(CONSTRUCTOR = new YamlConfigConstructor(loaderOptions), representer, dumperOptions, loaderOptions);
}
}

View file

@ -0,0 +1,16 @@
package org.zhdev.config;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.nodes.Node;
public class YamlConfigConstructor extends SafeConstructor {
public YamlConfigConstructor(LoaderOptions loaderOptions) {
super(loaderOptions);
}
@Override
protected Object constructObject(Node node) {
return super.constructObject(node);
}
}

View file

@ -0,0 +1,10 @@
package org.zhdev.config;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.representer.Representer;
public class YamlConfigRepresenter extends Representer {
public YamlConfigRepresenter(DumperOptions options) {
super(options);
}
}