src/test/java/tools/messages/MessagesFileWriter.java
package tools.messages;
import ch.jalu.configme.configurationdata.ConfigurationData;
import ch.jalu.configme.properties.Property;
import ch.jalu.configme.resource.PropertyReader;
import ch.jalu.configme.resource.YamlFileResource;
import fr.xephi.authme.message.updater.MessageKeyConfigurationData;
import fr.xephi.authme.message.updater.MessageUpdater;
import fr.xephi.authme.message.updater.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.
* <p>
* 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() {
// Initialize ConfigMe classes
MessageKeyConfigurationData configurationData = MessageUpdater.createConfigurationData();
YamlFileResource resource = new MigraterYamlFileResource(file);
PropertyReader reader = resource.createReader();
configurationData.initializeValues(reader);
// Store initial comments so we can add them back later
List<String> initialComments = getInitialUserComments(configurationData);
// Create property resource with new defaults, save with ConfigMe for proper sections & comments
addMissingMessagesWithCommentMarker(reader, configurationData);
resource.exportProperties(configurationData);
// 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);
}
/**
* Returns the comments at the top which are custom to the file.
*
* @param configurationData the configuration data
* @return any custom comments at the top of the file, for later usage
*/
private List<String> getInitialUserComments(ConfigurationData configurationData) {
final List<String> initialComments = new ArrayList<>();
final String firstCommentByConfigMe = getFirstCommentByConfigMe(configurationData);
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;
}
/**
* Returns the first comment generated by ConfigMe (comment of the first root path).
*
* @param configurationData the configuration data
* @return first comment which is generated by ConfigMe
*/
private static String getFirstCommentByConfigMe(ConfigurationData configurationData) {
String firstRootPath = configurationData.getProperties().get(0).getPath().split("\\.")[0];
return "# " + configurationData.getCommentsForSection(firstRootPath).get(0);
}
/**
* Adds a text with a {@link #COMMENT_MARKER} for all properties which are not yet present in the reader.
*
* @param reader the property reader
* @param configurationData the configuration data
*/
private void addMissingMessagesWithCommentMarker(PropertyReader reader,
MessageKeyConfigurationData configurationData) {
for (Property<String> property : configurationData.getAllMessageProperties()) {
String text = reader.getString(property.getPath());
if (text == null) {
configurationData.setValue(property, COMMENT_MARKER + defaultFile.getString(property.getPath()));
}
}
}
/**
* 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<String> initialComments) {
List<String> 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 + "'");
}
}
}