Fix infinite replacement bug
- At one spot the replacement Limit object was not passed correctly, which could cause infinite replacements crashing the server - Improve handling of reaching the limit, throw exception instead of checking in all places. - Move logging class to library itself, so that InteractiveMessenger can also handle logging
This commit is contained in:
1 parent
b6066cc4e4
commit
678cf1030c
9 files changed
+379
-118
No files matched your search
@@ -0,0 +1,139 @@
|
||||
package me.wiefferink.interactivemessenger;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
public class Log {
|
||||
|
||||
private static Logger logger;
|
||||
|
||||
/**
|
||||
* Set the logger
|
||||
* @param newLogger Logger to use
|
||||
*/
|
||||
public static void setLogger(Logger newLogger) {
|
||||
logger = newLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an information message to the console
|
||||
* @param message The message to print
|
||||
*/
|
||||
public static void info(Object... message) {
|
||||
String combined = StringUtils.join(message, " ");
|
||||
if(logger != null) {
|
||||
logger.info(combined);
|
||||
} else {
|
||||
System.out.println(combined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a warning to the console
|
||||
* @param message The message to print
|
||||
*/
|
||||
public static void warn(Object... message) {
|
||||
String combined = StringUtils.join(message, " ");
|
||||
if(logger != null) {
|
||||
logger.warning(combined);
|
||||
} else {
|
||||
System.out.println("Warn: " + combined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an error to the console
|
||||
* @param message The message to print
|
||||
*/
|
||||
public static void error(Object... message) {
|
||||
String combined = StringUtils.join(message, " ");
|
||||
if(logger != null) {
|
||||
logger.severe(combined);
|
||||
} else {
|
||||
System.err.println(combined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indent a message
|
||||
* @param depth Number of indents to add, one indent is two spaces
|
||||
* @param message Message to indent
|
||||
* @return Original message with requested number of indents added
|
||||
*/
|
||||
public static Object[] indent(int depth, Object... message) {
|
||||
String[] lines = StringUtils.join(message, " ").split("\\r?\\n");
|
||||
for(int i = 0; i < lines.length; i++) {
|
||||
lines[i] = StringUtils.leftPad("", depth * 2, " ") + lines[i];
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a string indented with a certain number of spaces
|
||||
* @param indent The number of indents (each indent is 2 spaces)
|
||||
* @param message Message to print (will be split on newline to apply indentation)
|
||||
*/
|
||||
public static void infoIndent(int indent, Object... message) {
|
||||
for(Object line : indent(indent, message)) {
|
||||
info(line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a list of strings to the output with indentation
|
||||
* @param indent The number of indents (each indent is 2 spaces)
|
||||
* @param lines The string to print (will be split on newline to apply indentation)
|
||||
*/
|
||||
public static void infoIndent(int indent, Collection<String> lines) {
|
||||
for(String part : lines) {
|
||||
infoIndent(indent, part);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a string indented with a certain number of spaces
|
||||
* @param indent The number of indents (each indent is 2 spaces)
|
||||
* @param message Message to print (will be split on newline to apply indentation)
|
||||
*/
|
||||
public static void warnIndent(int indent, Object... message) {
|
||||
for(Object line : indent(indent, message)) {
|
||||
warn(line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a list of strings to the output with indentation
|
||||
* @param indent The number of indents (each indent is 2 spaces)
|
||||
* @param lines The string to print (will be split on newline to apply indentation)
|
||||
*/
|
||||
public static void warnIndent(int indent, Collection<String> lines) {
|
||||
for(String part : lines) {
|
||||
warnIndent(indent, part);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a string indented with a certain number of spaces
|
||||
* @param indent The number of indents (each indent is 2 spaces)
|
||||
* @param message Message to print (will be split on newline to apply indentation)
|
||||
*/
|
||||
public static void errorIndent(int indent, Object... message) {
|
||||
for(Object line : indent(indent, message)) {
|
||||
error(line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a list of strings to the output with indentation
|
||||
* @param indent The number of indents (each indent is 2 spaces)
|
||||
* @param lines The string to print (will be split on newline to apply indentation)
|
||||
*/
|
||||
public static void errorIndent(int indent, Collection<String> lines) {
|
||||
for(String part : lines) {
|
||||
errorIndent(indent, part);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,21 @@ import me.wiefferink.interactivemessenger.generators.ConsoleGenerator;
|
||||
import me.wiefferink.interactivemessenger.message.InteractiveMessage;
|
||||
import me.wiefferink.interactivemessenger.message.InteractiveMessagePart;
|
||||
import me.wiefferink.interactivemessenger.message.TextMessagePart;
|
||||
import me.wiefferink.interactivemessenger.message.enums.*;
|
||||
import me.wiefferink.interactivemessenger.message.enums.Click;
|
||||
import me.wiefferink.interactivemessenger.message.enums.Color;
|
||||
import me.wiefferink.interactivemessenger.message.enums.Control;
|
||||
import me.wiefferink.interactivemessenger.message.enums.Format;
|
||||
import me.wiefferink.interactivemessenger.message.enums.Hover;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -352,7 +363,7 @@ public class YamlParser {
|
||||
* @param line The line to check
|
||||
* @return true if the line is interactive, false when it is a text line
|
||||
*/
|
||||
private static boolean isTaggedInteractive(String line) {
|
||||
public static boolean isTaggedInteractive(String line) {
|
||||
return getInteractiveTag(line) != null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.wiefferink.interactivemessenger.processing;
|
||||
|
||||
import me.wiefferink.interactivemessenger.Log;
|
||||
import me.wiefferink.interactivemessenger.generators.ConsoleGenerator;
|
||||
import me.wiefferink.interactivemessenger.generators.TellrawGenerator;
|
||||
import me.wiefferink.interactivemessenger.parsers.YamlParser;
|
||||
@@ -78,7 +79,7 @@ public class Message {
|
||||
*/
|
||||
public static void init(MessageProvider provider, Logger theLogger) {
|
||||
messageProvider = provider;
|
||||
logger = theLogger;
|
||||
Log.setLogger(logger);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,11 +154,9 @@ public class Message {
|
||||
* Get the message with all replacements done
|
||||
* @param limit the limit to hold to
|
||||
* @return Message as a list
|
||||
* @throws ReplacementLimitReachedException when the limit is reached
|
||||
*/
|
||||
private List<String> get(Limit limit) {
|
||||
if(limit.reached()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
private List<String> get(Limit limit) throws ReplacementLimitReachedException {
|
||||
doReplacements(limit);
|
||||
return message;
|
||||
}
|
||||
@@ -171,6 +170,16 @@ public class Message {
|
||||
return StringUtils.join(message, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message with all replacements done
|
||||
* @return Message as a string
|
||||
* @throws ReplacementLimitReachedException when the limit is reached
|
||||
*/
|
||||
public String getSingle(Limit limit) throws ReplacementLimitReachedException {
|
||||
doReplacements(limit);
|
||||
return StringUtils.join(message, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw message without replacing anything
|
||||
* @return The message
|
||||
@@ -336,7 +345,7 @@ public class Message {
|
||||
List<String> jsonMessages = TellrawGenerator.generate(YamlParser.parse(message));
|
||||
for(String jsonMessage : jsonMessages) {
|
||||
if(jsonMessage.length() > MAXIMUMJSONLENGTH) {
|
||||
error("Message with key", key, "could not be send, results in a JSON string that is too big to send to the client, start of the message:", getMessageStart(this, 200));
|
||||
Log.error("Message with key", key, "could not be send, results in a JSON string that is too big to send to the client, start of the message:", getMessageStart(this, 200));
|
||||
return this;
|
||||
}
|
||||
result &= Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw "+((Player)target).getName()+" "+jsonMessage);
|
||||
@@ -345,7 +354,7 @@ public class Message {
|
||||
fancyWorks = result;
|
||||
} catch(Exception e) {
|
||||
fancyWorks = false;
|
||||
error("Sending fancy message did not work, falling back to plain messages. Message key:", key, ", error:", ExceptionUtils.getStackTrace(e));
|
||||
Log.error("Sending fancy message did not work, falling back to plain messages. Message key:", key, ", error:", ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
if(sendPlain) { // Fancy messages disabled or broken
|
||||
@@ -368,10 +377,10 @@ public class Message {
|
||||
((BufferedWriter)target).write(ChatColor.stripColor(plainMessage));
|
||||
((BufferedWriter)target).newLine();
|
||||
} catch(IOException e) {
|
||||
warn("Exception while writing to BufferedWriter:", ExceptionUtils.getStackTrace(e));
|
||||
Log.warn("Exception while writing to BufferedWriter:", ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
} else {
|
||||
warn("Could not send message (key: "+key+") because the target ("+target.getClass().getName()+") is not recognized, message: "+plainMessage);
|
||||
Log.warn("Could not send message (key: " + key + ") because the target (" + target.getClass().getName() + ") is not recognized, message: " + plainMessage);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
@@ -401,7 +410,7 @@ public class Message {
|
||||
private Message setMessageFromKey(String key) {
|
||||
this.key = key;
|
||||
if(messageProvider == null) {
|
||||
error("Tried to get message with key", key+", but there is no MessageProvider!");
|
||||
Log.error("Tried to get message with key", key + ", but there is no MessageProvider!");
|
||||
} else {
|
||||
this.setMessage(messageProvider.getMessage(key));
|
||||
}
|
||||
@@ -425,20 +434,22 @@ public class Message {
|
||||
*/
|
||||
public Message doReplacements() {
|
||||
Limit limit = new Limit(REPLACEMENTLIMIT, this);
|
||||
doReplacements(limit);
|
||||
|
||||
try {
|
||||
doReplacements(limit);
|
||||
} catch(ReplacementLimitReachedException e) {
|
||||
// Limit should have logged the error to the console already
|
||||
}
|
||||
|
||||
//depthPrint(limit, "Replacing took", System.currentTimeMillis()-limit.started, "milliseconds", this);
|
||||
return this;
|
||||
}
|
||||
|
||||
private Message doReplacements(Limit limit) {
|
||||
if(limit.reached()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
private Message doReplacements(Limit limit) throws ReplacementLimitReachedException {
|
||||
limit.depth++;
|
||||
|
||||
//depthPrint(limit, ">>> doReplacements:", message, limit);
|
||||
// Replace LANGUAGE_VARIABLE_PATTERN until they are all gone, or when the limit is reached
|
||||
// Replace LANGUAGE_VARIABLE_PATTERN until they are all gone, nothing changes anymore or when the limit is reached
|
||||
try {
|
||||
List<String> outerOriginal;
|
||||
// Repeat replacements for if language replacements introduced new LANGUAGE_VARIABLE_PATTERN
|
||||
@@ -446,17 +457,11 @@ public class Message {
|
||||
do {
|
||||
outerOriginal = new ArrayList<>(message);
|
||||
List<String> innerOriginal;
|
||||
limit.left--;
|
||||
if(limit.reached()) {
|
||||
break;
|
||||
}
|
||||
limit.decrease();
|
||||
|
||||
// Do argument replacements
|
||||
do {
|
||||
innerOriginal = new ArrayList<>(message);
|
||||
if(limit.reached()) {
|
||||
break;
|
||||
}
|
||||
replaceArgumentVariables(limit);
|
||||
} while(!message.equals(innerOriginal));
|
||||
|
||||
@@ -464,9 +469,6 @@ public class Message {
|
||||
if(doLanguageReplacements) {
|
||||
do {
|
||||
innerOriginal = new ArrayList<>(message);
|
||||
if(limit.reached()) {
|
||||
break;
|
||||
}
|
||||
replaceLanguageVariables(limit);
|
||||
} while(!message.equals(innerOriginal));
|
||||
}
|
||||
@@ -476,12 +478,14 @@ public class Message {
|
||||
|
||||
// Increase limit by one to compensate for the last round where no replacements have been done
|
||||
if(!limit.reached() && fullRounds >= 1) {
|
||||
limit.left++;
|
||||
limit.increase();
|
||||
}
|
||||
} catch(StackOverflowError e) {
|
||||
limit.left = 0;
|
||||
limit.notified = true;
|
||||
error("Too many recursive replacements for message with key: "+limit.message.key+" (probably includes itself as replacement), start of the message: "+getMessageStart(limit.message, 200));
|
||||
Log.error("Too many recursive replacements for message with key: " + limit.message.key + " (probably includes itself as replacement), start of the message: " + getMessageStart(limit.message, 200));
|
||||
// Trigger exception
|
||||
limit.decrease();
|
||||
}
|
||||
limit.depth--;
|
||||
return this;
|
||||
@@ -492,11 +496,12 @@ public class Message {
|
||||
* The arguments to apply as replacements:
|
||||
* - If it is a GeneralRegion the replacements of the region will be applied
|
||||
* - Else the parameter will replace its number surrounded with VARIABLE_START and VARIABLE_END
|
||||
* @throws ReplacementLimitReachedException when the limit is reached
|
||||
*/
|
||||
private void replaceArgumentVariables(Limit limit) {
|
||||
private void replaceArgumentVariables(Limit limit) throws ReplacementLimitReachedException {
|
||||
limit.depth++;
|
||||
//depthPrint(limit, ">>> replaceArgumentVariables:", message, limit);
|
||||
if(message == null || message.size() == 0 || replacements == null || limit.reached()) {
|
||||
if(message == null || message.size() == 0 || replacements == null) {
|
||||
//depthPrint(limit, "quick return");
|
||||
limit.depth--;
|
||||
return;
|
||||
@@ -563,17 +568,13 @@ public class Message {
|
||||
|
||||
// Insert inline
|
||||
if(mParam.inline) {
|
||||
message.set(i, insert(line, mParam.getSingle(), matcher.start(), matcher.end()));
|
||||
message.set(i, insert(line, mParam.getSingle(limit), matcher.start(), matcher.end()));
|
||||
}
|
||||
|
||||
// Insert as message
|
||||
else {
|
||||
List<String> insertMessage = ((Message)param).get(limit);
|
||||
//depthPrint(limit, "insert message resolved:", ((Message)param).message);
|
||||
if(limit.reached()) {
|
||||
limit.depth--;
|
||||
return;
|
||||
}
|
||||
YamlParser.insertMessage(message, insertMessage, i, matcher.start(), matcher.end());
|
||||
// Skip to end of insert
|
||||
i = message.size()-startDiff;
|
||||
@@ -618,11 +619,12 @@ public class Message {
|
||||
|
||||
/**
|
||||
* Replace all language LANGUAGE_VARIABLE_PATTERN in a message
|
||||
* @throws ReplacementLimitReachedException when the limit is reached
|
||||
*/
|
||||
private void replaceLanguageVariables(Limit limit) {
|
||||
private void replaceLanguageVariables(Limit limit) throws ReplacementLimitReachedException {
|
||||
limit.depth++;
|
||||
//depthPrint(limit, ">>> replaceLanguageVariables:", message, limit);
|
||||
if(message == null || message.size() == 0 || limit.reached()) {
|
||||
if(message == null || message.size() == 0) {
|
||||
//depthPrint(limit, "quick return");
|
||||
limit.depth--;
|
||||
return;
|
||||
@@ -663,10 +665,6 @@ public class Message {
|
||||
// Insert message
|
||||
int startDiff = message.size()-i;
|
||||
List<String> insertMessage = insert.get(limit);
|
||||
if(limit.reached()) {
|
||||
limit.depth--;
|
||||
return;
|
||||
}
|
||||
YamlParser.insertMessage(message, insertMessage, i, matcher.start(), matcher.end());
|
||||
// Skip to end of insert
|
||||
i = message.size()-startDiff;
|
||||
@@ -704,22 +702,51 @@ public class Message {
|
||||
this.started = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease the limit
|
||||
* @throws ReplacementLimitReachedException when the limit hits zero
|
||||
*/
|
||||
public void decrease() throws ReplacementLimitReachedException {
|
||||
this.left--;
|
||||
if(left <= 0) {
|
||||
if(!notified) {
|
||||
notified = true;
|
||||
Log.error("Reached replacement limit, probably has replacements loops, problematic message key: " + message.key + ", first characters of the message: " + getMessageStart(message, 200));
|
||||
}
|
||||
throw new ReplacementLimitReachedException(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the limit
|
||||
*/
|
||||
public void increase() {
|
||||
this.left++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the limit is reached
|
||||
* @return true if the limit is reached, otherwise false
|
||||
*/
|
||||
public boolean reached() {
|
||||
boolean reached = left <= 0;
|
||||
if(reached && !notified) {
|
||||
notified = true;
|
||||
error("Reached replacement limit, probably has replacements loops, problematic message key: "+message.key+", first characters of the message: "+getMessageStart(message, 200));
|
||||
}
|
||||
return reached;
|
||||
return left <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Limit(left: "+left+", notified: "+notified+", depth: "+depth+", message.key: "+message.key+")";
|
||||
return "Limit(left: "+left+", notified: "+notified+", depth: "+depth+ ", message.key: " + message.key + ")";
|
||||
}
|
||||
}
|
||||
|
||||
public class ReplacementLimitReachedException extends Exception {
|
||||
private Limit limit;
|
||||
|
||||
public ReplacementLimitReachedException(Limit limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public Limit getLimit() {
|
||||
return limit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,43 +771,6 @@ public class Message {
|
||||
* @param message The message to print indented
|
||||
*/
|
||||
private static void depthPrint(Limit limit, Object... message) {
|
||||
String indent = "";
|
||||
for(int i = 0; i < limit.depth; i++) {
|
||||
indent += " ";
|
||||
}
|
||||
Object[] iMessage = new Object[message.length+1];
|
||||
iMessage[0] = indent;
|
||||
System.arraycopy(message, 0, iMessage, 1, message.length);
|
||||
info(iMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print information to the console
|
||||
* @param message The message to print
|
||||
*/
|
||||
public static void info(Object... message) {
|
||||
if(logger != null) {
|
||||
logger.info(StringUtils.join(message, " "));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a warning to the console
|
||||
* @param message The message to print
|
||||
*/
|
||||
public static void warn(Object... message) {
|
||||
if(logger != null) {
|
||||
logger.warning(StringUtils.join(message, " "));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an error to the console
|
||||
* @param message The messagfe to print
|
||||
*/
|
||||
public static void error(Object... message) {
|
||||
if(logger != null) {
|
||||
logger.severe(StringUtils.join(message, " "));
|
||||
}
|
||||
Log.infoIndent(limit.depth, message);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,27 @@
|
||||
package me.wiefferink.interactivemessenger.source;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import me.wiefferink.interactivemessenger.Log;
|
||||
import me.wiefferink.interactivemessenger.processing.Message;
|
||||
import me.wiefferink.interactivemessenger.translation.Transifex;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@@ -73,7 +85,7 @@ public class LanguageManager implements MessageProvider {
|
||||
File langFolder;
|
||||
if(!languageFolder.exists()) {
|
||||
if(!languageFolder.mkdirs()) {
|
||||
Message.warn("Could not create language directory: "+languageFolder.getAbsolutePath());
|
||||
Log.warn("Could not create language directory: " + languageFolder.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -102,14 +114,14 @@ public class LanguageManager implements MessageProvider {
|
||||
output.write(bytes, 0, read);
|
||||
}
|
||||
} catch(IOException e) {
|
||||
Message.warn("Something went wrong saving a default language file: "+targetFile.getAbsolutePath());
|
||||
Log.warn("Something went wrong saving a default language file: " + targetFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(URISyntaxException e) {
|
||||
Message.error("Failed to find location of jar file:", ExceptionUtils.getStackTrace(e));
|
||||
Log.error("Failed to find location of jar file:", ExceptionUtils.getStackTrace(e));
|
||||
} catch(IOException e) {
|
||||
Message.error("Failed to read zip file:", ExceptionUtils.getStackTrace(e));
|
||||
Log.error("Failed to read zip file:", ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +152,7 @@ public class LanguageManager implements MessageProvider {
|
||||
// Detect empty language files, happens when the YAML parsers prints an exception (it does return an empty YamlConfiguration though)
|
||||
YamlConfiguration ymlFile = YamlConfiguration.loadConfiguration(reader);
|
||||
if(ymlFile.getKeys(false).isEmpty()) {
|
||||
Message.warn("Language file "+key+".yml has zero messages.");
|
||||
Log.warn("Language file " + key + ".yml has zero messages.");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -157,13 +169,13 @@ public class LanguageManager implements MessageProvider {
|
||||
convertFromTransifex = true;
|
||||
}
|
||||
} catch(IOException e) {
|
||||
Message.warn("Could not load language file: "+file.getAbsolutePath());
|
||||
Log.warn("Could not load language file: " + file.getAbsolutePath());
|
||||
}
|
||||
|
||||
// Do conversion (after block above closed the reader)
|
||||
if(convertFromTransifex) {
|
||||
if(!Transifex.convertFrom(file)) {
|
||||
Message.warn("Failed to convert "+file.getName()+" from the Transifex layout to the AreaShop layout, check the errors above");
|
||||
Log.warn("Failed to convert " + file.getName() + " from the Transifex layout to the AreaShop layout, check the errors above");
|
||||
}
|
||||
return loadLanguage(key, false);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
package me.wiefferink.interactivemessenger.translation;
|
||||
|
||||
import me.wiefferink.interactivemessenger.processing.Message;
|
||||
import me.wiefferink.interactivemessenger.Log;
|
||||
import me.wiefferink.interactivemessenger.parsers.YamlParser;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Convert to and from a format suitable for Transifex (online collaborative translation platform)
|
||||
@@ -39,12 +50,12 @@ public class Transifex {
|
||||
warn("Could not convert Transifix file, it does not exist");
|
||||
return false;
|
||||
}
|
||||
Message.info("Transifex conversion of", inputFile.getAbsolutePath());
|
||||
Log.info("Transifex conversion of", inputFile.getAbsolutePath());
|
||||
|
||||
File newFile = new File(inputFile.getAbsolutePath()+".new");
|
||||
try(
|
||||
BufferedReader reader = new BufferedReader(new FileReader(inputFile));
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(newFile))
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), StandardCharsets.UTF_8));
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), StandardCharsets.UTF_8))
|
||||
) {
|
||||
// Create target file
|
||||
newFile.createNewFile();
|
||||
@@ -133,7 +144,97 @@ public class Transifex {
|
||||
oldFile.renameTo(inputFile); // Try to recover original file
|
||||
return false;
|
||||
}
|
||||
Message.info("Converted "+inputFile.getName()+" from the Transifex layout to the AreaShop layout, original saved in "+oldFile.getName());
|
||||
Log.info("Converted " + inputFile.getName() + " from the Transifex layout to the AreaShop layout, original saved in " + oldFile.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert source language to format compatible for upload to transifex
|
||||
* @param inputFile File to read the language file from
|
||||
* @param outputFile File to output converted language file to
|
||||
* @param languageCode Languagecode as used on Transifex
|
||||
*/
|
||||
public static boolean exportTo(File inputFile, File outputFile, String languageCode) {
|
||||
try(
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), StandardCharsets.UTF_8));
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8))
|
||||
) {
|
||||
// Header indicating the contained language to Transifex
|
||||
writer.write("\"" + languageCode + "\":");
|
||||
writer.newLine();
|
||||
|
||||
// Add all lines
|
||||
String next = reader.readLine();
|
||||
List<String> message = new ArrayList<>();
|
||||
int lineCount = 0;
|
||||
while(next != null) {
|
||||
lineCount++;
|
||||
String line = next;
|
||||
next = reader.readLine();
|
||||
// Strip UTF-8 BOM marker
|
||||
if(lineCount == 1 && line.length() > 0 && line.charAt(0) == 0xFEFF) {
|
||||
line = line.substring(1);
|
||||
}
|
||||
|
||||
String trimmed = line.trim();
|
||||
// Ignore comments
|
||||
if(line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore empty lines
|
||||
if(line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Part of a list of strings (an InteractiveMessage)
|
||||
if(trimmed.startsWith("- ")) {
|
||||
// Trim the list identifier
|
||||
trimmed = trimmed.substring(2);
|
||||
|
||||
// Trim quotes from the message
|
||||
if(trimmed.length() > 0 && trimmed.charAt(0) == '"' && trimmed.charAt(trimmed.length() - 1) == '"') {
|
||||
trimmed = trimmed.substring(1, trimmed.length() - 1);
|
||||
}
|
||||
|
||||
// Replace spaces in front of special lines by tabs (Transifex ignores spaces, while tabs show nicely, helping with easy translation)
|
||||
if(YamlParser.isTaggedInteractive(trimmed)) {
|
||||
trimmed = "\\t" + trimmed.replaceAll("^\\s+", "");
|
||||
}
|
||||
message.add(trimmed);
|
||||
} else {
|
||||
// Last message complete, write it to the result
|
||||
if(message.size() > 0) {
|
||||
String result = message.remove(0);
|
||||
// Trim the " at the end
|
||||
if(result.endsWith("\"")) {
|
||||
result = result.substring(0, result.length() - 1);
|
||||
} else {
|
||||
result += " \"";
|
||||
}
|
||||
|
||||
for(String messagePart : message) {
|
||||
// Add newlines between parts
|
||||
if(!result.endsWith("\"")) {
|
||||
result += "\\n";
|
||||
}
|
||||
result += messagePart;
|
||||
}
|
||||
|
||||
writer.write(" " + result + "\"");
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
// Start next message
|
||||
message.clear();
|
||||
message.add(trimmed);
|
||||
}
|
||||
}
|
||||
} catch(IOException e) {
|
||||
warn("Reformatting from '" + inputFile.getAbsolutePath() + "' to '" + outputFile.getAbsolutePath() + "' failed:", ExceptionUtils.getStackTrace(e));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -142,6 +243,6 @@ public class Transifex {
|
||||
* @param message The message to print
|
||||
*/
|
||||
private static void warn(String... message) {
|
||||
Message.warn("[Transifex language file conversion]", StringUtils.join(message, " "));
|
||||
Log.warn("[Transifex language file conversion]", StringUtils.join(message, " "));
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import me.wiefferink.interactivemessenger.Log;
|
||||
import me.wiefferink.interactivemessenger.generators.TellrawGenerator;
|
||||
import me.wiefferink.interactivemessenger.message.InteractiveMessage;
|
||||
import me.wiefferink.interactivemessenger.parsers.YamlParser;
|
||||
import me.wiefferink.interactivemessenger.testing.Log;
|
||||
import me.wiefferink.interactivemessenger.testing.parsergenerator.ParserGeneratorTestInput;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -92,7 +92,7 @@ public class TestCase implements Comparable<TestCase> {
|
||||
tellRawJsonOutput = gson.fromJson(jsonMessage, JsonElement.class);
|
||||
testRun.console("Message structure:", parsedMessage);
|
||||
testRun.console("Output line", line+":");
|
||||
Log.printIndented(2, gson.toJson(tellRawJsonOutput));
|
||||
Log.infoIndent(2, gson.toJson(tellRawJsonOutput));
|
||||
result &= Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw "+testRun.getTester().getName()+" "+jsonMessage);
|
||||
} catch(JsonSyntaxException e) {
|
||||
result = false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.wiefferink.interactivemessenger.testing.ingame;
|
||||
|
||||
import me.wiefferink.interactivemessenger.testing.Log;
|
||||
import me.wiefferink.interactivemessenger.Log;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -9,14 +9,22 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
+6
-6
@@ -5,11 +5,11 @@ import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import junit.framework.TestCase;
|
||||
import me.wiefferink.interactivemessenger.Log;
|
||||
import me.wiefferink.interactivemessenger.generators.ConsoleGenerator;
|
||||
import me.wiefferink.interactivemessenger.generators.TellrawGenerator;
|
||||
import me.wiefferink.interactivemessenger.message.InteractiveMessage;
|
||||
import me.wiefferink.interactivemessenger.parsers.YamlParser;
|
||||
import me.wiefferink.interactivemessenger.testing.Log;
|
||||
import me.wiefferink.interactivemessenger.testing.RunTests;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
@@ -57,11 +57,11 @@ public class ParserGeneratorTestCase extends TestCase {
|
||||
}
|
||||
|
||||
Log.info(" Input:");
|
||||
Log.printIndented(2, data.input);
|
||||
Log.infoIndent(2, data.input);
|
||||
Log.info(" Expected ConsoleGenerator output:");
|
||||
Log.printIndented(2, data.expectedConsoleOutput);
|
||||
Log.infoIndent(2, data.expectedConsoleOutput);
|
||||
Log.info(" Expected TellrawGenerator output:");
|
||||
Log.printIndented(2, data.expectedTellrawOutput);
|
||||
Log.infoIndent(2, data.expectedTellrawOutput);
|
||||
|
||||
// Parse into InteractiveMessage
|
||||
InteractiveMessage parsedMessage = YamlParser.parse(data.input);
|
||||
@@ -70,7 +70,7 @@ public class ParserGeneratorTestCase extends TestCase {
|
||||
// Generate result of ConsoleGenerator
|
||||
String actualConsoleOutput = ConsoleGenerator.generate(parsedMessage);
|
||||
Log.info(" Generated by ConsoleGenerator:");
|
||||
Log.printIndented(2, actualConsoleOutput);
|
||||
Log.infoIndent(2, actualConsoleOutput);
|
||||
|
||||
// Generate result of TellrawGenerator
|
||||
List<String> actualTellrawOutputList = TellrawGenerator.generate(parsedMessage);
|
||||
@@ -85,7 +85,7 @@ public class ParserGeneratorTestCase extends TestCase {
|
||||
return;
|
||||
}
|
||||
Log.info(" Generated by TellrawGenerator:");
|
||||
Log.printIndented(2, gson.toJson(actualTellrawOutputJson));
|
||||
Log.infoIndent(2, gson.toJson(actualTellrawOutputJson));
|
||||
|
||||
// Parse expected output into Json
|
||||
JsonElement expectedTellrawOutputJson;
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
package me.wiefferink.interactivemessenger.testing.parsergenerator;
|
||||
|
||||
import junit.framework.TestSuite;
|
||||
import me.wiefferink.interactivemessenger.testing.Log;
|
||||
import me.wiefferink.interactivemessenger.Log;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
Reference in new issue
Block a user