package tools.messages; import ch.jalu.configme.SettingsManager; import ch.jalu.configme.configurationdata.ConfigurationData; import ch.jalu.configme.properties.Property; import ch.jalu.configme.resource.PropertyResource; import ch.jalu.configme.resource.YamlFileResource; import fr.xephi.authme.message.updater.MessageUpdater; import fr.xephi.authme.message.updater.MessageUpdater.MigraterYamlFileResource; import org.bukkit.configuration.file.FileConfiguration; import tools.utils.FileIoUtils; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Writes to a messages file, adding comments with the default file's message where * entries are missing. *

* This writer writes to the file twice: once with ConfigMe to ensure a proper order * of the properties and comments, and a second time to add any custom comments that * were at the top of the file and to separate comments by new lines (which ConfigMe * currently doesn't support). */ public final class MessagesFileWriter { /** Marker used inside a text to signal that it should be a comment later on. */ private static final String COMMENT_MARKER = "::COMMENT::"; private static final Pattern SPACES_BEFORE_TEXT_PATTERN = Pattern.compile("(\\s+)\\w.*"); /** The messages file to update. */ private final File file; /** Messages from the default file. */ private final FileConfiguration defaultFile; private MessagesFileWriter(File file, FileConfiguration defaultFile) { this.file = file; this.defaultFile = defaultFile; } public static void writeToFileWithCommentsFromDefault(File file, FileConfiguration configuration) { new MessagesFileWriter(file, configuration).performWrite(); } private void performWrite() { // Store initial comments so we can add them back later List initialComments = getInitialUserComments(); // Create property resource with new defaults, save with ConfigMe for proper sections & comments PropertyResource resource = createPropertyResourceWithCommentEntries(); new SettingsManager(resource, null, MessageUpdater.getConfigurationData()).save(); // Go through the newly saved file and replace texts with comment marker to actual YAML comments // and add initial comments back to the file rewriteToFileWithComments(initialComments); } /** * @return any custom comments at the top of the file, for later usage */ private List getInitialUserComments() { final List initialComments = new ArrayList<>(); final String firstCommentByConfigMe = getFirstCommentByConfigMe(); for (String line : FileIoUtils.readLinesFromFile(file.toPath())) { if (line.isEmpty() || line.startsWith("#") && !line.equals(firstCommentByConfigMe)) { initialComments.add(line); } else { break; } } // Small fix: so we can keep running this writer and get the same result, we need to make sure that any ending // empty lines are removed for (int i = initialComments.size() - 1; i >= 0; --i) { if (initialComments.get(i).isEmpty()) { initialComments.remove(i); } else { break; } } return initialComments; } /** * @return the first comment generated by ConfigMe (comment of the first root path) */ private static String getFirstCommentByConfigMe() { ConfigurationData configurationData = MessageUpdater.getConfigurationData(); String firstRootPath = configurationData.getProperties().get(0).getPath().split("\\.")[0]; return "# " + configurationData.getCommentsForSection(firstRootPath)[0]; } /** * @return generated {@link PropertyResource} with missing entries taken from the default file and marked * with the {@link #COMMENT_MARKER} */ private PropertyResource createPropertyResourceWithCommentEntries() { YamlFileResource resource = new MigraterYamlFileResource(file); for (Property property : MessageUpdater.getConfigurationData().getProperties()) { String text = resource.getString(property.getPath()); if (text == null) { resource.setValue(property.getPath(), COMMENT_MARKER + defaultFile.getString(property.getPath())); } } return resource; } /** * Writes to the file again, adding the provided initial comments at the top of the file and converting * any entries marked with {@link #COMMENT_MARKER} to YAML comments. * * @param initialComments the comments at the top of the file to add back */ private void rewriteToFileWithComments(List initialComments) { List newLines = new ArrayList<>(initialComments); for (String line : FileIoUtils.readLinesFromFile(file.toPath())) { if (line.contains(COMMENT_MARKER)) { String lineAsYamlComment = convertLineWithCommentMarkerToYamlComment(line); newLines.add(lineAsYamlComment); } else if (line.startsWith("#") && !newLines.isEmpty()) { // ConfigMe doesn't support empty line between comments, so here we check if we have a comment that // isn't at the very top and sneak in an empty line if so. newLines.add(""); newLines.add(line); } else if (!line.isEmpty()) { // ConfigMe adds an empty line at the beginning, so check here that we don't include any empty lines... newLines.add(line); } } newLines.add(""); // Makes sure file ends with new line FileIoUtils.writeToFile(file.toPath(), String.join("\n", newLines)); } private static String convertLineWithCommentMarkerToYamlComment(String line) { Matcher matcher = SPACES_BEFORE_TEXT_PATTERN.matcher(line); if (matcher.matches()) { String spacesBefore = matcher.group(1); return spacesBefore + "# TODO " + line.replace(COMMENT_MARKER, "").trim(); } else { throw new IllegalStateException("Space-counting pattern unexpectedly did not match on line '" + line + "'"); } } }