All source projects
MrUltimateLibrary Source
Standalone source project / 68 files
MrUltimateLibrary is the backbone of all my plugins; it offers features that facilitate plugin creation, maintenance, and updates.
java
MrUltimateLibrary/src/main/java/eu/mrneznamy/mrultimatelibrary/config/MrLibYamlUpdater.java
JAVA · Read-only source
package eu.mrneznamy.mrultimatelibrary.config;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility for modifying YAML files (renaming, setting, adding keys)
* while perfectly preserving comments (# popisky), blank lines, and original formatting.
*/
public class MrLibYamlUpdater {
private static final Pattern KEY_PATTERN = Pattern.compile("^(\\s*)([\"']?)([a-zA-Z0-9_.-]+)\\2\\s*:(.*)$");
/**
* Renames a key path (e.g. "Section.OldKey") to a new key name ("NewKey")
* while retaining the original value, spacing, and trailing comments.
*/
public static boolean renameKey(File file, String oldPath, String newKeyName) {
if (file == null || !file.exists()) return false;
try {
List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
List<String> newLines = new ArrayList<>();
List<PathSegment> pathStack = new ArrayList<>();
boolean modified = false;
for (String line : lines) {
String trimmed = line.trim();
if (trimmed.isEmpty() || trimmed.startsWith("#")) {
newLines.add(line);
continue;
}
Matcher matcher = KEY_PATTERN.matcher(line);
if (matcher.find()) {
int indent = matcher.group(1).length();
String key = matcher.group(3);
String rest = matcher.group(4);
pathStack.removeIf(segment -> segment.indent >= indent);
pathStack.add(new PathSegment(key, indent));
String currentPath = getFullPath(pathStack);
if (currentPath.equals(oldPath)) {
String quotes = matcher.group(2);
String renamedLine = matcher.group(1) + quotes + newKeyName + quotes + ":" + rest;
newLines.add(renamedLine);
modified = true;
pathStack.get(pathStack.size() - 1).key = newKeyName;
continue;
}
}
newLines.add(line);
}
if (modified) {
Files.write(file.toPath(), newLines, StandardCharsets.UTF_8);
return true;
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* Updates or sets the value of an existing key path (e.g. "Section.Key")
* while preserving the original spacing and trailing comments.
*/
public static boolean setKeyValue(File file, String targetPath, Object value) {
if (file == null || !file.exists()) return false;
try {
List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
List<String> newLines = new ArrayList<>();
List<PathSegment> pathStack = new ArrayList<>();
boolean modified = false;
String formattedValue = formatValue(value);
for (String line : lines) {
String trimmed = line.trim();
if (trimmed.isEmpty() || trimmed.startsWith("#")) {
newLines.add(line);
continue;
}
Matcher matcher = KEY_PATTERN.matcher(line);
if (matcher.find()) {
int indent = matcher.group(1).length();
String key = matcher.group(3);
String rest = matcher.group(4);
pathStack.removeIf(segment -> segment.indent >= indent);
pathStack.add(new PathSegment(key, indent));
String currentPath = getFullPath(pathStack);
if (currentPath.equals(targetPath)) {
String comment = "";
int hashIndex = rest.indexOf('#');
if (hashIndex != -1) {
comment = " " + rest.substring(hashIndex);
}
String updatedLine = matcher.group(1) + matcher.group(2) + key + matcher.group(2) + ": " + formattedValue + comment;
newLines.add(updatedLine);
modified = true;
continue;
}
}
newLines.add(line);
}
if (modified) {
Files.write(file.toPath(), newLines, StandardCharsets.UTF_8);
return true;
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* Inserts a new key-value pair under a specific parent section path
* using the correct section indentation, while leaving everything else intact.
*/
public static boolean addKey(File file, String parentPath, String newKey, Object value) {
if (file == null || !file.exists()) return false;
try {
List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
List<String> newLines = new ArrayList<>();
List<PathSegment> pathStack = new ArrayList<>();
int insertIndex = -1;
int parentIndent = -1;
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
String trimmed = line.trim();
if (trimmed.isEmpty() || trimmed.startsWith("#")) {
continue;
}
Matcher matcher = KEY_PATTERN.matcher(line);
if (matcher.find()) {
int indent = matcher.group(1).length();
String key = matcher.group(3);
pathStack.removeIf(segment -> segment.indent >= indent);
pathStack.add(new PathSegment(key, indent));
String currentPath = getFullPath(pathStack);
if (currentPath.equals(parentPath)) {
parentIndent = indent;
insertIndex = i;
}
}
}
if (insertIndex != -1) {
int endOfSection = insertIndex + 1;
for (int i = insertIndex + 1; i < lines.size(); i++) {
String line = lines.get(i);
String trimmed = line.trim();
if (trimmed.isEmpty() || trimmed.startsWith("#")) {
continue;
}
Matcher matcher = KEY_PATTERN.matcher(line);
if (matcher.find()) {
int indent = matcher.group(1).length();
if (indent <= parentIndent) {
break;
}
endOfSection = i + 1;
}
}
boolean exists = false;
pathStack.clear();
for (int i = insertIndex + 1; i < endOfSection; i++) {
String line = lines.get(i);
Matcher matcher = KEY_PATTERN.matcher(line);
if (matcher.find()) {
int indent = matcher.group(1).length();
String key = matcher.group(3);
pathStack.removeIf(segment -> segment.indent >= indent);
pathStack.add(new PathSegment(key, indent));
String fullPath = parentPath + "." + getFullPath(pathStack);
if (fullPath.equals(parentPath + "." + newKey)) {
exists = true;
break;
}
}
}
if (!exists) {
StringBuilder indentStr = new StringBuilder();
for (int k = 0; k < parentIndent + 2; k++) {
indentStr.append(" ");
}
String newLineStr = indentStr.toString() + newKey + ": " + formatValue(value);
for (int i = 0; i < lines.size(); i++) {
newLines.add(lines.get(i));
if (i == endOfSection - 1) {
newLines.add(newLineStr);
}
}
Files.write(file.toPath(), newLines, StandardCharsets.UTF_8);
return true;
}
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
private static String getFullPath(List<PathSegment> pathStack) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < pathStack.size(); i++) {
sb.append(pathStack.get(i).key);
if (i < pathStack.size() - 1) {
sb.append(".");
}
}
return sb.toString();
}
private static String formatValue(Object value) {
if (value == null) return "";
if (value instanceof String) {
String str = (String) value;
if (str.contains(" ") || str.contains("&") || str.contains(":") || str.contains("#")) {
if (!str.startsWith("\"") && !str.startsWith("'")) {
return "\"" + str.replace("\"", "\\\"") + "\"";
}
}
return str;
}
if (value instanceof List) {
List<?> list = (List<?>) value;
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < list.size(); i++) {
sb.append(formatValue(list.get(i)));
if (i < list.size() - 1) sb.append(", ");
}
sb.append("]");
return sb.toString();
}
return value.toString();
}
private static class PathSegment {
String key;
int indent;
PathSegment(String key, int indent) {
this.key = key;
this.indent = indent;
}
}
}
269 linesGNU GENERAL PUBLIC LICENSE · Version 3, 29 June 2007 · UTF-8 · Read only