mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-01-23 16:41:22 +01:00
New Config code (Untested)
This commit is contained in:
parent
557fa83177
commit
2fcb9590e5
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Risto Lahtela
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.djrapitops.plan.system.settings.config;
|
||||
|
||||
import com.djrapitops.plugin.utilities.Verify;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Configuration utility for storing settings in a .yml file.
|
||||
* <p>
|
||||
* Based on
|
||||
* https://github.com/Rsl1122/Abstract-Plugin-Framework/blob/72e221d3571ef200727713d10d3684c51e9f469d/AbstractPluginFramework/api/src/main/java/com/djrapitops/plugin/config/Config.java
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public class Config extends ConfigNode {
|
||||
|
||||
private final Path configFilePath;
|
||||
|
||||
public Config(File configFile) {
|
||||
super("", null, null);
|
||||
File folder = configFile.getParentFile();
|
||||
this.configFilePath = configFile.toPath();
|
||||
|
||||
try {
|
||||
Verify.isTrue(folder.exists() || folder.mkdirs(), () ->
|
||||
new FileNotFoundException("Folders could not be created for config file " + configFile.getAbsolutePath()));
|
||||
Verify.isTrue(configFile.exists() || configFile.createNewFile(), () ->
|
||||
new FileNotFoundException("Could not create file: " + configFile.getAbsolutePath()));
|
||||
read();
|
||||
save();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public Config(File configFile, ConfigNode defaults) {
|
||||
this(configFile);
|
||||
copyMissing(defaults);
|
||||
try {
|
||||
save();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
Config() {
|
||||
super("", null, null);
|
||||
configFilePath = null;
|
||||
}
|
||||
|
||||
public void read() throws IOException {
|
||||
copyMissing(new ConfigReader(Files.newInputStream(configFilePath)).read());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws IOException {
|
||||
new ConfigWriter(configFilePath).write(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Risto Lahtela
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.djrapitops.plan.system.settings.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents a single node in a configuration file
|
||||
* <p>
|
||||
* Based on
|
||||
* https://github.com/Rsl1122/Abstract-Plugin-Framework/blob/72e221d3571ef200727713d10d3684c51e9f469d/AbstractPluginFramework/api/src/main/java/com/djrapitops/plugin/config/ConfigNode.java
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public class ConfigNode {
|
||||
|
||||
protected final String key;
|
||||
protected ConfigNode parent;
|
||||
|
||||
protected List<String> nodeOrder;
|
||||
protected Map<String, ConfigNode> childNodes;
|
||||
protected List<String> comment;
|
||||
|
||||
protected String value;
|
||||
|
||||
public ConfigNode(String key, ConfigNode parent, String value) {
|
||||
this.key = key;
|
||||
this.parent = parent;
|
||||
this.value = value;
|
||||
|
||||
nodeOrder = new ArrayList<>();
|
||||
childNodes = new HashMap<>();
|
||||
comment = new ArrayList<>();
|
||||
}
|
||||
|
||||
protected void updateParent(ConfigNode newParent) {
|
||||
parent = newParent;
|
||||
}
|
||||
|
||||
public Optional<ConfigNode> getNode(String path) {
|
||||
String[] parts = path.split("\\.", 2);
|
||||
String key = parts[0];
|
||||
String leftover = parts[1];
|
||||
|
||||
if (leftover.isEmpty()) {
|
||||
return Optional.ofNullable(childNodes.get(key));
|
||||
} else {
|
||||
return getNode(key).flatMap(child -> child.getNode(leftover));
|
||||
}
|
||||
}
|
||||
|
||||
protected void addNode(String path) {
|
||||
ConfigNode newParent = this;
|
||||
if (!path.isEmpty()) {
|
||||
String[] parts = path.split("\\.", 2);
|
||||
String key = parts[0];
|
||||
String leftover = parts[1];
|
||||
|
||||
if (!childNodes.containsKey(key)) {
|
||||
addChild(new ConfigNode(key, newParent, null));
|
||||
}
|
||||
ConfigNode child = childNodes.get(key);
|
||||
child.addNode(leftover);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a node at a certain path.
|
||||
*
|
||||
* @param path Path to the node that is up for removal.
|
||||
* @return {@code true} if the node was present and is now removed. {@code false} if the path did not have a node.
|
||||
*/
|
||||
public boolean removeNode(String path) {
|
||||
Optional<ConfigNode> node = getNode(path);
|
||||
node.ifPresent(ConfigNode::remove);
|
||||
return node.isPresent();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
parent.childNodes.remove(key);
|
||||
parent.nodeOrder.remove(key);
|
||||
updateParent(null);
|
||||
}
|
||||
|
||||
protected void addChild(ConfigNode child) {
|
||||
getNode(child.key).ifPresent(ConfigNode::remove);
|
||||
childNodes.put(child.key, child);
|
||||
nodeOrder.add(child.key);
|
||||
child.updateParent(this);
|
||||
}
|
||||
|
||||
protected void removeChild(ConfigNode child) {
|
||||
removeNode(child.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a node from old path to new path.
|
||||
*
|
||||
* @param oldPath Old path of the node.
|
||||
* @param newPath New path of the node.
|
||||
* @return {@code true} if the move was successful. {@code false} if the new node is not present
|
||||
*/
|
||||
public boolean moveChild(String oldPath, String newPath) {
|
||||
Optional<ConfigNode> found = getNode(oldPath);
|
||||
if (!found.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
addNode(newPath);
|
||||
|
||||
ConfigNode moveFrom = found.get();
|
||||
ConfigNode moveTo = getNode(newPath).orElseThrow(() -> new IllegalStateException("Config node was not added properly: " + newPath));
|
||||
ConfigNode oldParent = moveFrom.parent;
|
||||
ConfigNode newParent = moveTo.parent;
|
||||
oldParent.removeChild(moveFrom);
|
||||
newParent.addChild(moveTo);
|
||||
|
||||
return getNode(newPath).isPresent();
|
||||
}
|
||||
|
||||
public String getKey(boolean deep) {
|
||||
if (deep && parent != null) {
|
||||
String deepKey = parent.getKey(true) + "." + key;
|
||||
if (deepKey.startsWith(".")) {
|
||||
return deepKey.substring(1);
|
||||
}
|
||||
return deepKey;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
public void sort() {
|
||||
Collections.sort(nodeOrder);
|
||||
}
|
||||
|
||||
public boolean reorder(List<String> newOrder) {
|
||||
if (nodeOrder.containsAll(newOrder)) {
|
||||
nodeOrder = newOrder;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the root node and save.
|
||||
*
|
||||
* @throws IOException If the save can not be performed.
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
ConfigNode root = this.parent;
|
||||
while (root.parent != null) {
|
||||
root = root.parent;
|
||||
}
|
||||
root.save();
|
||||
}
|
||||
|
||||
public <T> void set(String path, T value) {
|
||||
addNode(path);
|
||||
ConfigNode node = getNode(path).orElseThrow(() -> new IllegalStateException("Config node was not added properly: " + path));
|
||||
node.set(value);
|
||||
}
|
||||
|
||||
public <T> void set(T value) {
|
||||
if (value instanceof ConfigNode) {
|
||||
addChild(((ConfigNode) value));
|
||||
} else {
|
||||
ConfigValueParser<T> parser = ConfigValueParser.getParserFor(value.getClass());
|
||||
this.value = parser.decompose(value);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(List<String> comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public List<String> getStringList() {
|
||||
return new ConfigValueParser.StringListParser().compose(value);
|
||||
}
|
||||
|
||||
public Integer getInteger() {
|
||||
return new ConfigValueParser.IntegerParser().compose(value);
|
||||
}
|
||||
|
||||
public Long getLong() {
|
||||
return new ConfigValueParser.LongParser().compose(value);
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return new ConfigValueParser.StringParser().compose(value);
|
||||
}
|
||||
|
||||
public boolean isTrue() {
|
||||
return new ConfigValueParser.BooleanParser().compose(value);
|
||||
}
|
||||
|
||||
public List<String> getStringList(String path) {
|
||||
return getNode(path).map(ConfigNode::getStringList).orElse(new ArrayList<>());
|
||||
}
|
||||
|
||||
public Integer getInteger(String path) {
|
||||
return getNode(path).map(ConfigNode::getInteger).orElse(null);
|
||||
}
|
||||
|
||||
public Long getLong(String path) {
|
||||
return getNode(path).map(ConfigNode::getLong).orElse(null);
|
||||
}
|
||||
|
||||
public String getString(String path) {
|
||||
return getNode(path).map(ConfigNode::getString).orElse(null);
|
||||
}
|
||||
|
||||
public boolean isTrue(String path) {
|
||||
return getNode(path).map(ConfigNode::isTrue).orElse(false);
|
||||
}
|
||||
|
||||
public void copyMissing(ConfigNode from) {
|
||||
if (comment.size() < from.comment.size()) {
|
||||
comment = from.comment;
|
||||
}
|
||||
|
||||
if (value == null && from.value != null) {
|
||||
value = from.value;
|
||||
}
|
||||
|
||||
for (String key : from.nodeOrder) {
|
||||
ConfigNode newChild = from.childNodes.get(key);
|
||||
|
||||
if (childNodes.containsKey(key)) {
|
||||
ConfigNode oldChild = childNodes.get(key);
|
||||
oldChild.copyMissing(newChild);
|
||||
} else {
|
||||
addChild(newChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int getNodeDepth() {
|
||||
return parent != null ? parent.getNodeDepth() + 1 : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.system.settings.config;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Reader for parsing {@link Config} out of file-lines.
|
||||
* <p>
|
||||
* ConfigReader can read a single file at a time, so it is NOT thread safe.
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public class ConfigReader implements Closeable {
|
||||
|
||||
private static final ConfigValueParser.StringParser STRING_PARSER = new ConfigValueParser.StringParser();
|
||||
private final InputStream in;
|
||||
private final Scanner scanner;
|
||||
private ConfigNode previousNode;
|
||||
private ConfigNode parent;
|
||||
|
||||
public ConfigReader(InputStream in) {
|
||||
this.in = in;
|
||||
this.scanner = new Scanner(in);
|
||||
}
|
||||
|
||||
public Config read() {
|
||||
Config config = new Config();
|
||||
|
||||
previousNode = config;
|
||||
parent = config;
|
||||
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = readNewLine();
|
||||
// Determine where the node belongs
|
||||
parent = findParent(previousNode.getNodeDepth(), findCurrentDepth(line));
|
||||
previousNode = parseNode(line.trim());
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private String readNewLine() {
|
||||
String line = scanner.nextLine();
|
||||
|
||||
// Removing any dangling comments
|
||||
int danglingComment = line.indexOf(" #");
|
||||
if (danglingComment != -1) {
|
||||
line = line.substring(0, danglingComment);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
private ConfigNode parseNode(String line) {
|
||||
if (line.startsWith("#")) {
|
||||
return handleCommentLine(line);
|
||||
}
|
||||
|
||||
String[] keyAndValue = line.split(":", 2);
|
||||
if (keyAndValue.length <= 1) {
|
||||
return handleMultiline(line);
|
||||
}
|
||||
String key = keyAndValue[0].trim();
|
||||
String value = keyAndValue[1].trim();
|
||||
return handleNewNode(key, value);
|
||||
}
|
||||
|
||||
private ConfigNode handleMultiline(String line) {
|
||||
if (line.startsWith("- ")) {
|
||||
return handleListCase(line);
|
||||
} else {
|
||||
return handleMultilineString(line);
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigNode handleCommentLine(String line) {
|
||||
previousNode.comment.add(line.substring(1).trim());
|
||||
return previousNode;
|
||||
}
|
||||
|
||||
private ConfigNode handleMultilineString(String line) {
|
||||
if (previousNode.value == null) {
|
||||
previousNode.value = "";
|
||||
}
|
||||
// Append the new line to the end of the value.
|
||||
previousNode.value += STRING_PARSER.compose(line.substring(2).trim());
|
||||
return previousNode;
|
||||
}
|
||||
|
||||
private ConfigNode handleNewNode(String key, String value) {
|
||||
ConfigNode newNode = new ConfigNode(key, parent, STRING_PARSER.compose(value));
|
||||
parent.addChild(newNode);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
private ConfigNode handleListCase(String line) {
|
||||
if (previousNode.value == null) {
|
||||
previousNode.value = "";
|
||||
}
|
||||
// Append list item to the value.
|
||||
previousNode.value += "\n- " + STRING_PARSER.compose(line.substring(2).trim());
|
||||
return previousNode;
|
||||
}
|
||||
|
||||
private ConfigNode findParent(int previousDepth, int currentDepth) {
|
||||
if (previousDepth < currentDepth) {
|
||||
return previousNode;
|
||||
} else if (previousDepth > currentDepth) {
|
||||
// Prevents incorrect indent in the case:
|
||||
// 1:
|
||||
// 2:
|
||||
// 3:
|
||||
// 1:
|
||||
int helperDepth = previousDepth;
|
||||
ConfigNode foundParent = parent;
|
||||
while (helperDepth > currentDepth) {
|
||||
helperDepth = parent.getNodeDepth();
|
||||
foundParent = foundParent.parent; // Moves one level up the tree
|
||||
}
|
||||
return foundParent;
|
||||
} else {
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
|
||||
private int findCurrentDepth(String line) {
|
||||
int indent = readIndent(line);
|
||||
int depth;
|
||||
if (indent % 4 == 0) {
|
||||
depth = indent / 4;
|
||||
} else {
|
||||
depth = (indent - (indent % 4)) / 4;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
private int readIndent(String line) {
|
||||
int indentation = 0;
|
||||
for (char c : line.toCharArray()) {
|
||||
if (c == ' ') {
|
||||
indentation++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return indentation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
scanner.close();
|
||||
in.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.system.settings.config;
|
||||
|
||||
import com.djrapitops.plugin.utilities.Verify;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utilities for parsing config values.
|
||||
*
|
||||
* @param <T> Type of the object being parsed.
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public interface ConfigValueParser<T> {
|
||||
|
||||
static ConfigValueParser getParserFor(Class type) {
|
||||
if (String.class.isAssignableFrom(type)) {
|
||||
return new StringParser();
|
||||
} else if (List.class.isAssignableFrom(type)) {
|
||||
return new StringListParser();
|
||||
} else if (Boolean.class.isAssignableFrom(type)) {
|
||||
return new BooleanParser();
|
||||
} else if (Long.class.isAssignableFrom(type)) {
|
||||
return new LongParser();
|
||||
} else if (Integer.class.isAssignableFrom(type)) {
|
||||
return new IntegerParser();
|
||||
}
|
||||
return new StringParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a String value in the config into the appropriate object.
|
||||
*
|
||||
* @param fromValue String value in the config.
|
||||
* @return Config value or null if it could not be parsed.
|
||||
*/
|
||||
T compose(String fromValue);
|
||||
|
||||
/**
|
||||
* Parse an object into a String value to save in the config.
|
||||
*
|
||||
* @param ofValue Value to save, not null.
|
||||
* @return String format to save in the config.
|
||||
* @throws IllegalArgumentException If null value is given.
|
||||
*/
|
||||
String decompose(T ofValue);
|
||||
|
||||
class StringParser implements ConfigValueParser<String> {
|
||||
@Override
|
||||
public String compose(String fromValue) {
|
||||
String parsed = fromValue;
|
||||
boolean surroundedWithSingleQuotes = parsed.startsWith("'") && parsed.endsWith("'");
|
||||
boolean surroundedWithDoubleQuotes = parsed.startsWith("\"") && parsed.endsWith("\"");
|
||||
if (surroundedWithSingleQuotes || surroundedWithDoubleQuotes) {
|
||||
parsed = parsed.substring(1, parsed.length() - 1);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decompose(String ofValue) {
|
||||
Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving"));
|
||||
|
||||
boolean surroundedWithSingleQuotes = ofValue.startsWith("'") && ofValue.endsWith("'");
|
||||
if (surroundedWithSingleQuotes) {
|
||||
return "\"" + ofValue + "\"";
|
||||
}
|
||||
boolean surroundedWithDoubleQuotes = ofValue.startsWith("\"") && ofValue.endsWith("\"");
|
||||
if (surroundedWithDoubleQuotes) {
|
||||
return "'" + ofValue + "'";
|
||||
}
|
||||
return ofValue;
|
||||
}
|
||||
}
|
||||
|
||||
class IntegerParser implements ConfigValueParser<Integer> {
|
||||
@Override
|
||||
public Integer compose(String fromValue) {
|
||||
if (NumberUtils.isParsable(fromValue)) {
|
||||
return NumberUtils.createInteger(fromValue);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decompose(Integer ofValue) {
|
||||
Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving"));
|
||||
return Integer.toString(ofValue);
|
||||
}
|
||||
}
|
||||
|
||||
class LongParser implements ConfigValueParser<Long> {
|
||||
@Override
|
||||
public Long compose(String fromValue) {
|
||||
try {
|
||||
return Long.parseLong(fromValue);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decompose(Long ofValue) {
|
||||
Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving"));
|
||||
return Long.toString(ofValue);
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanParser implements ConfigValueParser<Boolean> {
|
||||
@Override
|
||||
public Boolean compose(String fromValue) {
|
||||
return Boolean.valueOf(fromValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decompose(Boolean ofValue) {
|
||||
Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving"));
|
||||
return Boolean.toString(ofValue);
|
||||
}
|
||||
}
|
||||
|
||||
class StringListParser implements ConfigValueParser<List<String>> {
|
||||
private static final StringParser STRING_PARSER = new StringParser();
|
||||
|
||||
@Override
|
||||
public List<String> compose(String fromValue) {
|
||||
List<String> values = new ArrayList<>();
|
||||
for (String line : fromValue.split("\\n")) {
|
||||
// Removes '- ' in front of the value.
|
||||
line = line.substring(2).trim();
|
||||
// Handle quotes around the string
|
||||
line = STRING_PARSER.compose(line);
|
||||
if (!line.isEmpty()) {
|
||||
values.add(line);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decompose(List<String> ofValue) {
|
||||
Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving"));
|
||||
|
||||
StringBuilder decomposedString = new StringBuilder();
|
||||
for (String value : ofValue) {
|
||||
decomposedString.append("\n- ").append(STRING_PARSER.decompose(value));
|
||||
}
|
||||
return decomposedString.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.system.settings.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Writer for parsing {@link Config} into file-lines.
|
||||
* <p>
|
||||
* ConfigReader can write a single file at a time, so it is NOT thread safe.
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public class ConfigWriter {
|
||||
|
||||
private final Path outputPath;
|
||||
private int indent;
|
||||
|
||||
public ConfigWriter(Path outputPath) {
|
||||
this.outputPath = outputPath;
|
||||
}
|
||||
|
||||
public void write(ConfigNode writing) throws IOException {
|
||||
ConfigNode storedParent = writing.parent;
|
||||
writing.updateParent(null);
|
||||
|
||||
Files.write(outputPath, parseLines(writing), StandardCharsets.UTF_8);
|
||||
|
||||
writing.updateParent(storedParent);
|
||||
}
|
||||
|
||||
private List<String> parseLines(ConfigNode writing) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
|
||||
indent = writing.getNodeDepth() * 4;
|
||||
|
||||
addComment(writing.comment, lines);
|
||||
addValue(writing.key, writing.value, lines);
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
private void addValue(String key, String value, Collection<String> lines) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
if (value.contains("\n")) {
|
||||
addListValue(key, value.split("\\n"), lines);
|
||||
} else {
|
||||
addNormalValue(key, value, lines);
|
||||
}
|
||||
}
|
||||
|
||||
private void addNormalValue(String key, String value, Collection<String> lines) {
|
||||
StringBuilder lineBuilder = indentedBuilder().append(key).append(": ").append(value);
|
||||
lines.add(lineBuilder.toString());
|
||||
}
|
||||
|
||||
private void addListValue(String key, String[] listItems, Collection<String> lines) {
|
||||
addNormalValue(key, "", lines);
|
||||
for (String listItem : listItems) {
|
||||
listItem = listItem.trim();
|
||||
if (listItem.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
StringBuilder lineBuilder = indentedBuilder().append(listItem);
|
||||
lines.add(lineBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void addComment(Iterable<String> comments, Collection<String> lines) {
|
||||
for (String comment : comments) {
|
||||
StringBuilder lineBuilder = indentedBuilder().append("# ").append(comment);
|
||||
lines.add(lineBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private StringBuilder indentedBuilder() {
|
||||
StringBuilder lineBuilder = new StringBuilder();
|
||||
indent(indent, lineBuilder);
|
||||
return lineBuilder;
|
||||
}
|
||||
|
||||
private void indent(int indent, StringBuilder lineBuilder) {
|
||||
for (int i = 0; i < indent; i++) {
|
||||
lineBuilder.append(' ');
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user