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 + "'");
}
}
}