Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
682b2b78d6 | ||
|
|
e7749258ca | ||
|
|
7aed184e8c | ||
|
|
b5b7e9c1e5 | ||
|
|
c9a90bb352 | ||
|
|
90fa17d54d | ||
|
|
255c1e4f60 | ||
|
|
ddfef8ed50 | ||
|
|
083da2228c | ||
|
|
73172b9b1a | ||
|
|
08ceec2c8e | ||
|
|
288c9a43e0 | ||
|
|
5c42aa1cbe | ||
|
|
b00e8fc946 | ||
|
|
fa8476db0b | ||
|
|
e8174c87a0 | ||
|
|
678cf1030c | ||
|
|
b6066cc4e4 |
No files matched your search
@@ -0,0 +1,17 @@
|
||||
name: Build plugin
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build with Maven
|
||||
run: mvn package --file pom.xml --update-snapshots --threads 8C -Dsources
|
||||
@@ -29,7 +29,7 @@ A format for text in config files to make use of the fancy Minecraft chat featur
|
||||
<dependency>
|
||||
<groupId>me.wiefferink</groupId>
|
||||
<artifactId>interactivemessenger</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>me.wiefferink</groupId>
|
||||
<artifactId>interactivemessenger</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.1-SNAPSHOT</version>
|
||||
<name>InteractiveMessenger</name>
|
||||
|
||||
<properties>
|
||||
@@ -32,7 +32,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.0</version>
|
||||
<version>2.8.9</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.0</version>
|
||||
<version>2.8.9</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package me.wiefferink.interactivemessenger.processing;
|
||||
|
||||
import me.wiefferink.interactivemessenger.Log;
|
||||
|
||||
/**
|
||||
* Class to store a limit
|
||||
*/
|
||||
public class Limit {
|
||||
public int left;
|
||||
public int depth;
|
||||
public boolean notified = false;
|
||||
public Message message;
|
||||
public long started;
|
||||
|
||||
/**
|
||||
* Set the initial limit
|
||||
* @param count The limit to use
|
||||
* @param message The message this limit is started for
|
||||
*/
|
||||
public Limit(int count, Message message) {
|
||||
this.left = count;
|
||||
this.depth = 0;
|
||||
this.message = 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.getKey() + ", first characters of the message: " + 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() {
|
||||
return left <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Limit(left: " + left + ", notified: " + notified + ", depth: " + depth + ", message.key: " + message.getKey() + ")";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -25,14 +26,13 @@ public class Message {
|
||||
// CONFIGURATION
|
||||
private static boolean useInteractiveMessages = true;
|
||||
private static boolean useColorsInConsole = false;
|
||||
private static Logger logger = null;
|
||||
private static MessageProvider messageProvider = null;
|
||||
|
||||
// Define the symbols used for variables
|
||||
public static final String VARIABLE_START = "%";
|
||||
public static final String VARIABLE_END = "%";
|
||||
public static final String LANGUAGE_KEY_PREFIX = "lang:";
|
||||
public static final Pattern VARIABLE_PATTERN = Pattern.compile(Pattern.quote(VARIABLE_START) + "[a-zA-Z]+?" + Pattern.quote(VARIABLE_END));
|
||||
public static final Pattern VARIABLE_PATTERN = Pattern.compile(Pattern.quote(VARIABLE_START) + "(?<name>[a-zA-Z]+)" + Pattern.quote(VARIABLE_END));
|
||||
public static final Pattern LANGUAGE_VARIABLE_PATTERN = Pattern.compile(
|
||||
Pattern.quote(VARIABLE_START) +
|
||||
Pattern.quote(LANGUAGE_KEY_PREFIX) + "[a-zA-Z-]+?" + // Language key
|
||||
@@ -58,7 +58,7 @@ public class Message {
|
||||
// Language variable used to insert a prefix
|
||||
public static final String CHATLANGUAGEVARIABLE = "prefix";
|
||||
// Maximum number of replacement rounds (the replaced value can have variables again)
|
||||
public static final int REPLACEMENTLIMIT = 100;
|
||||
public static final int REPLACEMENTLIMIT = 200;
|
||||
// Limit of the client is 32767 for the complete message
|
||||
public static final int MAXIMUMJSONLENGTH = 30000;
|
||||
// If sending a fancy message does not work we disable it for this run of the server
|
||||
@@ -74,11 +74,11 @@ public class Message {
|
||||
/**
|
||||
* Initialize the Message class
|
||||
* @param provider The provider to use for getting messages based on keys
|
||||
* @param theLogger The logger to use for logging warning and error messages
|
||||
* @param logger The logger to use for logging warning and error messages
|
||||
*/
|
||||
public static void init(MessageProvider provider, Logger theLogger) {
|
||||
public static void init(MessageProvider provider, Logger logger) {
|
||||
messageProvider = provider;
|
||||
logger = theLogger;
|
||||
Log.setLogger(logger);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,11 +153,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 +169,17 @@ public class Message {
|
||||
return StringUtils.join(message, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message with all replacements done
|
||||
* @param limit Limit to use while processing
|
||||
* @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
|
||||
@@ -202,7 +211,7 @@ public class Message {
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
for(String part : message) {
|
||||
if(!part.isEmpty()) {
|
||||
if(part != null && !part.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -210,12 +219,12 @@ public class Message {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the default prefix to the message
|
||||
* Add the default prefix to the message (if the message is not empty)
|
||||
* @param doIt true if the prefix should be added, otherwise false
|
||||
* @return this
|
||||
*/
|
||||
public Message prefix(boolean doIt) {
|
||||
if(doIt) {
|
||||
if(doIt && !isEmpty()) {
|
||||
message.add(0, VARIABLE_START + LANGUAGE_KEY_PREFIX + CHATLANGUAGEVARIABLE + VARIABLE_END);
|
||||
}
|
||||
return this;
|
||||
@@ -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,15 +377,22 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key that has been used to initialize this message (if any)
|
||||
* @return Key used to create this message, or null if none
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
@@ -401,7 +417,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));
|
||||
}
|
||||
@@ -415,7 +431,9 @@ public class Message {
|
||||
*/
|
||||
private Message setMessage(String message) {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add(message);
|
||||
if(message != null) {
|
||||
list.add(message);
|
||||
}
|
||||
return this.setMessage(list);
|
||||
}
|
||||
|
||||
@@ -425,63 +443,62 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply replacements to the message using a certain Limit (intended for internal and testing use)
|
||||
* @param limit Limit to apply
|
||||
* @return this
|
||||
* @throws ReplacementLimitReachedException When the limit has been hit before replacement is finished
|
||||
*/
|
||||
public 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
|
||||
int fullRounds = 0;
|
||||
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;
|
||||
}
|
||||
limit.decrease();
|
||||
replaceArgumentVariables(limit);
|
||||
} while(!message.equals(innerOriginal));
|
||||
limit.increase(); // Compensate last round that did nothing
|
||||
|
||||
// Do language replacements
|
||||
if(doLanguageReplacements) {
|
||||
do {
|
||||
innerOriginal = new ArrayList<>(message);
|
||||
if(limit.reached()) {
|
||||
break;
|
||||
}
|
||||
limit.decrease();
|
||||
replaceLanguageVariables(limit);
|
||||
} while(!message.equals(innerOriginal));
|
||||
}
|
||||
|
||||
fullRounds++;
|
||||
limit.increase(); // Compensate last round that did nothing
|
||||
} while(!message.equals(outerOriginal));
|
||||
limit.increase(); // Compensate last round that did nothing
|
||||
|
||||
// Increase limit by one to compensate for the last round where no replacements have been done
|
||||
if(!limit.reached() && fullRounds >= 1) {
|
||||
limit.left++;
|
||||
}
|
||||
} 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 +509,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;
|
||||
@@ -506,90 +524,78 @@ public class Message {
|
||||
int number = 0;
|
||||
for(Object param : replacements) {
|
||||
String line = message.get(i);
|
||||
if(param != null) {
|
||||
if(param instanceof ReplacementProvider) {
|
||||
// Find the first non-escaped named variable
|
||||
Matcher matcher = VARIABLE_PATTERN.matcher(line);
|
||||
int startAt = 0;
|
||||
while(matcher.find()) {
|
||||
// Check for escaping
|
||||
int beforeAt = matcher.start()-1;
|
||||
if(beforeAt >= 0 && line.charAt(beforeAt) == YamlParser.ESCAPE_CHAR) {
|
||||
//depthPrint(limit, "skipping named variable:", matcher.group(), limit);
|
||||
continue;
|
||||
}
|
||||
//depthPrint(limit, "replacing named variable:", matcher.group());
|
||||
|
||||
// Insert replacement provided by the ReplacementProvider
|
||||
Object replacement = ((ReplacementProvider)param).provideReplacement(matcher.group().substring(1, matcher.group().length()-1));
|
||||
if(replacement != null) {
|
||||
String result = "";
|
||||
// Prefix
|
||||
if(matcher.start() > 0) {
|
||||
result += line.substring(0, matcher.start());
|
||||
}
|
||||
// Replacement
|
||||
String add = replacement.toString();
|
||||
result += add;
|
||||
// Suffix
|
||||
if(matcher.end() < line.length()) {
|
||||
result += line.substring(matcher.end());
|
||||
}
|
||||
// Skip null, but increase index
|
||||
if(param == null) {
|
||||
Log.warn("null replacement for message", limit.message, "at index", number);
|
||||
|
||||
message.set(i, result);
|
||||
line = result;
|
||||
int matcherStart = matcher.start();
|
||||
matcher = VARIABLE_PATTERN.matcher(line);
|
||||
matcher.region(matcherStart+add.length(), line.length());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Find first non-escaped numbered variable
|
||||
Matcher matcher = getIndexPattern(number).matcher(line);
|
||||
while(matcher.find()) {
|
||||
// Check for escaping
|
||||
int beforeAt = matcher.start()-1;
|
||||
if(beforeAt >= 0 && line.charAt(beforeAt) == YamlParser.ESCAPE_CHAR) {
|
||||
//depthPrint(limit, "skipping indexed variable:", matcher.group(), limit);
|
||||
continue;
|
||||
}
|
||||
//depthPrint(limit, "replacing indexed variable:", matcher.group());
|
||||
// Set param to an empty string to ensure the variable will be removed from the message (preventing loops)
|
||||
param = "";
|
||||
}
|
||||
|
||||
// Insert another Message
|
||||
if(param instanceof Message) {
|
||||
int startDiff = message.size()-i;
|
||||
//depthPrint(limit, "insert message raw:", ((Message)param).message);
|
||||
Message mParam = (Message)param;
|
||||
// Match indexed or named variables
|
||||
Matcher matcher;
|
||||
boolean named;
|
||||
if(param instanceof ReplacementProvider) {
|
||||
matcher = VARIABLE_PATTERN.matcher(line);
|
||||
named = true;
|
||||
} else {
|
||||
matcher = getIndexPattern(number).matcher(line);
|
||||
named = false;
|
||||
}
|
||||
|
||||
// Insert inline
|
||||
if(mParam.inline) {
|
||||
message.set(i, insert(line, mParam.getSingle(), matcher.start(), matcher.end()));
|
||||
}
|
||||
// Find an replace
|
||||
while(matcher.find()) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert a simple string
|
||||
else {
|
||||
// Insert it inline, assuming this might be user input, therefore escaping it
|
||||
//depthPrint(limit, "insert string:", param.toString());
|
||||
message.set(i, insert(line, YamlParser.escape(param.toString()), matcher.start(), matcher.end()));
|
||||
}
|
||||
break; // Maximum of one replacement
|
||||
}
|
||||
number++;
|
||||
// Skip escaped matches
|
||||
int beforeAt = matcher.start() - 1;
|
||||
if(beforeAt >= 0 && line.charAt(beforeAt) == YamlParser.ESCAPE_CHAR) {
|
||||
//depthPrint(limit, "skipping indexed variable:", matcher.group(), limit);
|
||||
continue;
|
||||
}
|
||||
//depthPrint(limit, "replacing indexed variable:", matcher.group());
|
||||
|
||||
Object toInsert = param;
|
||||
if(named) {
|
||||
toInsert = ((ReplacementProvider) param).provideReplacement(matcher.group("name"));
|
||||
// Skip if null
|
||||
if(toInsert == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert another Message
|
||||
if(toInsert instanceof Message) {
|
||||
int startDiff = message.size() - i;
|
||||
//depthPrint(limit, "insert message raw:", ((Message)toInsert).message);
|
||||
Message toInsertMessage = (Message) toInsert;
|
||||
|
||||
// Insert inline
|
||||
if(toInsertMessage.inline) {
|
||||
message.set(i, insert(line, toInsertMessage.getSingle(limit), matcher.start(), matcher.end()));
|
||||
}
|
||||
|
||||
// Insert as message
|
||||
else {
|
||||
List<String> insertMessage = toInsertMessage.get(limit);
|
||||
//depthPrint(limit, "insert message resolved:", ((Message)toInsert).message);
|
||||
YamlParser.insertMessage(message, insertMessage, i, matcher.start(), matcher.end());
|
||||
// Skip to end of insert
|
||||
i = Math.max(0, message.size() - startDiff);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert a simple string
|
||||
else {
|
||||
// Insert it inline, assuming this might be user input, therefore escaping it
|
||||
//depthPrint(limit, "insert string:", toInsert.toString());
|
||||
message.set(i, insert(line, YamlParser.escape(toInsert.toString()), matcher.start(), matcher.end()));
|
||||
}
|
||||
break; // Maximum of one replacement`
|
||||
}
|
||||
|
||||
if(!named) {
|
||||
number++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -618,11 +624,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 +670,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;
|
||||
@@ -681,56 +684,13 @@ public class Message {
|
||||
return "Message(key:"+key+", message:"+message+")";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class to store a limit
|
||||
*/
|
||||
private class Limit {
|
||||
public int left;
|
||||
public int depth;
|
||||
public boolean notified = false;
|
||||
public Message message;
|
||||
public long started;
|
||||
|
||||
/**
|
||||
* Set the initial limit
|
||||
* @param count The limit to use
|
||||
* @param message The message this limit is started for
|
||||
*/
|
||||
public Limit(int count, Message message) {
|
||||
this.left = count;
|
||||
this.depth = 0;
|
||||
this.message = message;
|
||||
this.started = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Limit(left: "+left+", notified: "+notified+", depth: "+depth+", message.key: "+message.key+")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a start of the message with a maximum length
|
||||
* @param message The message
|
||||
* @param maximumLength The maximum length to return
|
||||
* @return The start of the message with at most maximumLength characters
|
||||
*/
|
||||
private static String getMessageStart(Message message, int maximumLength) {
|
||||
public static String getMessageStart(Message message, int maximumLength) {
|
||||
String messageStart = "";
|
||||
for(int i = 0; i < message.getRaw().size() && messageStart.length() < maximumLength; i++) {
|
||||
messageStart += message.getRaw().get(i).substring(0, Math.min(maximumLength, message.getRaw().get(i).length()));
|
||||
@@ -744,43 +704,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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package me.wiefferink.interactivemessenger.processing;
|
||||
|
||||
public class Replacement {
|
||||
|
||||
public static ReplacementProvider name(final String name, final Object replacement) {
|
||||
return new ReplacementProvider() {
|
||||
@Override
|
||||
public Object provideReplacement(String variable) {
|
||||
if(variable.equalsIgnoreCase(name)) {
|
||||
return replacement;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ReplacementProvider prefix(final String prefix, final ReplacementProvider provider) {
|
||||
return new ReplacementProvider() {
|
||||
@Override
|
||||
public Object provideReplacement(String variable) {
|
||||
if(variable.startsWith(prefix)) {
|
||||
return provider.provideReplacement(variable.substring(prefix.length()));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package me.wiefferink.interactivemessenger.processing;
|
||||
|
||||
public class ReplacementLimitReachedException extends Exception {
|
||||
private Limit limit;
|
||||
|
||||
public ReplacementLimitReachedException(Limit limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public Limit getLimit() {
|
||||
return limit;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@ public interface ReplacementProvider {
|
||||
/**
|
||||
* Get the replacement for a variable
|
||||
* @param variable The variable to replace
|
||||
* @return The replacement for the variable, or null if empty
|
||||
* @return The replacement for the variable, or null if it has no replacement for it
|
||||
*/
|
||||
Object provideReplacement(String variable);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -60,6 +72,7 @@ public class LanguageManager implements MessageProvider {
|
||||
message = defaultLanguage.get(key);
|
||||
}
|
||||
if(message == null) {
|
||||
Log.warn("Did not find message '" + key + "' in the current or default language");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return new ArrayList<>(message);
|
||||
@@ -73,7 +86,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 +115,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 +153,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 +170,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,98 @@ 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
|
||||
* @return true if it worked orrectly, otherwise false
|
||||
*/
|
||||
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 +244,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, " "));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package me.wiefferink.interactivemessenger.source;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import me.wiefferink.interactivemessenger.Log;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class YAMLMessageProvider implements MessageProvider {
|
||||
private Map<String, List<String>> messages;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param file File to read the messages from
|
||||
*/
|
||||
public YAMLMessageProvider(File file) {
|
||||
messages = loadLanguage(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message for a certain key (result can be modified)
|
||||
* @param key The key of the message to get
|
||||
* @return The message as a list of strings
|
||||
*/
|
||||
@Override
|
||||
public List<String> getMessage(String key) {
|
||||
List<String> message = messages.get(key);
|
||||
if(message == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return new ArrayList<>(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the specified language
|
||||
* @param file YAML rile to load from
|
||||
* @return Map with the messages loaded from the file
|
||||
*/
|
||||
private Map<String, List<String>> loadLanguage(File file) {
|
||||
HashMap<String, List<String>> result = new HashMap<>();
|
||||
|
||||
// Load the language file
|
||||
try(
|
||||
InputStreamReader reader = new InputStreamReader(new FileInputStream(file), Charsets.UTF_8)
|
||||
) {
|
||||
// Detect staticEmpty language files, happens when the YAML parsers prints an exception (it does return an staticEmpty YamlConfiguration though)
|
||||
YamlConfiguration ymlFile = YamlConfiguration.loadConfiguration(reader);
|
||||
if(ymlFile.getKeys(false).isEmpty()) {
|
||||
Log.warn("Language file has zero messages:", file.getAbsolutePath());
|
||||
return result;
|
||||
}
|
||||
|
||||
// Retrieve the messages from the YAML file and create the result
|
||||
for(String messageKey : ymlFile.getKeys(false)) {
|
||||
if(ymlFile.isList(messageKey)) {
|
||||
result.put(messageKey, new ArrayList<>(ymlFile.getStringList(messageKey)));
|
||||
} else {
|
||||
result.put(messageKey, new ArrayList<>(Collections.singletonList(ymlFile.getString(messageKey))));
|
||||
}
|
||||
}
|
||||
} catch(IOException e) {
|
||||
Log.warn("Could not load language file: " + file.getAbsolutePath());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package me.wiefferink.interactivemessenger.testing;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class Log {
|
||||
|
||||
/**
|
||||
* Print an information message to the console
|
||||
* @param message The message to print
|
||||
*/
|
||||
public static void info(Object... message) {
|
||||
System.out.println(StringUtils.join(message, " "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an error to the console
|
||||
* @param message The message to print
|
||||
*/
|
||||
public static void error(Object... message) {
|
||||
System.err.println(StringUtils.join(message, " "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a string indented with a certain number of spaces
|
||||
* @param indent The number of indents (each indent is 2 spaces)
|
||||
* @param print The string to print (will be split on newline to apply indentation)
|
||||
*/
|
||||
public static void printIndented(int indent, String print) {
|
||||
String indentation = "";
|
||||
for(int i = 0; i < indent; i++) {
|
||||
indentation += " ";
|
||||
}
|
||||
String[] parts = print.split("\\r?\\n");
|
||||
for(String part : parts) {
|
||||
info(indentation+part);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a list of strings to the output with indentation
|
||||
* @param indent The number of indents (each indent is 2 spaces)
|
||||
* @param print The string to print (will be split on newline to apply indentation)
|
||||
*/
|
||||
public static void printIndented(int indent, List<String> print) {
|
||||
for(String part : print) {
|
||||
printIndented(indent, part);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
package me.wiefferink.interactivemessenger.testing.unit;
|
||||
|
||||
import me.wiefferink.interactivemessenger.processing.Limit;
|
||||
import me.wiefferink.interactivemessenger.processing.Message;
|
||||
import me.wiefferink.interactivemessenger.processing.ReplacementLimitReachedException;
|
||||
import me.wiefferink.interactivemessenger.source.MessageProvider;
|
||||
import me.wiefferink.interactivemessenger.source.YAMLMessageProvider;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.Timeout;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
public class TestMessage {
|
||||
|
||||
// Max 10 seconds per method, to catch infinite recursion failures
|
||||
@Rule
|
||||
public Timeout globalTimeout = new Timeout(10 * 1000);
|
||||
|
||||
@BeforeClass
|
||||
public static void initMessage() {
|
||||
URL resource = TestMessage.class.getClassLoader().getResource("processing/messages.yml");
|
||||
assertNotNull("Resource file messages.yml not found in the jar", resource);
|
||||
MessageProvider provider = new YAMLMessageProvider(new File(resource.getFile()));
|
||||
Message.init(provider, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticEmpty() {
|
||||
Message message = Message.empty();
|
||||
assertTrue(message.isEmpty());
|
||||
assertTrue(message.get().isEmpty());
|
||||
assertTrue(message.getPlain().isEmpty());
|
||||
assertTrue(message.getSingle().isEmpty());
|
||||
assertTrue(message.getSingleRaw().isEmpty());
|
||||
assertTrue(message.getRaw().isEmpty());
|
||||
}
|
||||
|
||||
private void testFromString(String message, String... expected) {
|
||||
assertEquals(Arrays.asList(expected), Message.fromString(message).getRaw());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticFromString() {
|
||||
testFromString(null);
|
||||
testFromString("", "");
|
||||
testFromString("a", "a");
|
||||
testFromString("abc", "abc");
|
||||
}
|
||||
|
||||
private void testFromList(List<String> message, String... expected) {
|
||||
assertEquals(Arrays.asList(expected), Message.fromList(message).getRaw());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticFromList() {
|
||||
testFromList(null);
|
||||
testFromList(Collections.<String>emptyList());
|
||||
testFromList(Collections.singletonList("a"), "a");
|
||||
testFromList(Arrays.asList("", ""), "", "");
|
||||
testFromList(Arrays.asList("abc", "def"), "abc", "def");
|
||||
testFromList(Arrays.asList("abc", "", "def"), "abc", "", "def");
|
||||
}
|
||||
|
||||
private void testFromKey(String key, String... expected) {
|
||||
assertEquals(Arrays.asList(expected), Message.fromKey(key).getRaw());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticFromKey() {
|
||||
testFromKey(null);
|
||||
testFromKey("doestNotExist");
|
||||
testFromKey("single", "Hello world!");
|
||||
testFromKey("multiple", "Hello ", "world!");
|
||||
|
||||
testFromKey("multilineWithNewlines1", "Hello world!\nSecond line\n");
|
||||
testFromKey("multilineWithNewlines2", "Hello world!\nSecond line");
|
||||
testFromKey("multilineWithNewlines3", "Hello world!\nSecond line\n\n");
|
||||
|
||||
testFromKey("multilineWithoutNewlines1", "Hello world! More text\n");
|
||||
testFromKey("multilineWithoutNewlines2", "Hello world! More text");
|
||||
testFromKey("multilineWithoutNewlines3", "Hello world! More text\n\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isEmpty() {
|
||||
assertTrue(Message.fromString(null).isEmpty());
|
||||
assertTrue(Message.fromString("").isEmpty());
|
||||
assertFalse(Message.fromString("a").isEmpty());
|
||||
|
||||
assertTrue(Message.fromList(new ArrayList<String>()).isEmpty());
|
||||
assertTrue(Message.fromList(Collections.singletonList("")).isEmpty());
|
||||
assertFalse(Message.fromList(Collections.singletonList("a")).isEmpty());
|
||||
assertTrue(Message.fromList(Arrays.asList("", "")).isEmpty());
|
||||
assertFalse(Message.fromList(Arrays.asList("a", "")).isEmpty());
|
||||
assertFalse(Message.fromList(Arrays.asList("", "a")).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefix() {
|
||||
assertTrue(Message.empty().prefix().isEmpty());
|
||||
assertTrue(Message.empty().prefix(false).isEmpty());
|
||||
assertTrue(Message.empty().prefix(true).isEmpty());
|
||||
assertTrue(Message.fromString("abc").prefix().getSingleRaw().length() > 3);
|
||||
assertTrue(Message.fromString("abc").prefix().getSingleRaw().endsWith("abc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void append() {
|
||||
// Append a String
|
||||
assertEquals(Collections.singletonList("abc"), Message.empty().append("abc").getRaw());
|
||||
assertEquals(Arrays.asList("abc", "def"), Message.empty().append("abc").append("def").getRaw());
|
||||
|
||||
// Append a List
|
||||
assertEquals(Collections.emptyList(), Message.empty().append(Collections.<String>emptyList()).getRaw());
|
||||
assertEquals(Collections.singletonList("abc"), Message.empty().append(Collections.singletonList("abc")).getRaw());
|
||||
assertEquals(Arrays.asList("a", "bc"), Message.empty().append(Arrays.asList("a", "bc")).getRaw());
|
||||
assertEquals(Arrays.asList("a", "bc", "de", "f"), Message.empty().append(Arrays.asList("a", "bc")).append(Arrays.asList("de", "f")).getRaw());
|
||||
|
||||
// Append a Message
|
||||
// TODO test if appended message has replacement already done? (they should be)
|
||||
assertEquals(Collections.emptyList(), Message.empty().append(Message.empty()).getRaw());
|
||||
assertEquals(Collections.singletonList("abc"), Message.empty().append(Message.fromString("abc")).getRaw());
|
||||
assertEquals(Arrays.asList("a", "bc"), Message.empty().append(Message.fromList(Arrays.asList("a", "bc"))).getRaw());
|
||||
assertEquals(Arrays.asList("a", "bc", "de", "f"), Message.empty().append(Message.fromList(Arrays.asList("a", "bc"))).append(Message.fromList(Arrays.asList("de", "f"))).getRaw());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prepend() {
|
||||
// Append a String
|
||||
assertEquals(Collections.singletonList("abc"), Message.empty().prepend("abc").getRaw());
|
||||
assertEquals(Arrays.asList("def", "abc"), Message.empty().prepend("abc").prepend("def").getRaw());
|
||||
|
||||
// Append a List
|
||||
assertEquals(Collections.emptyList(), Message.empty().prepend(Collections.<String>emptyList()).getRaw());
|
||||
assertEquals(Collections.singletonList("abc"), Message.empty().prepend(Collections.singletonList("abc")).getRaw());
|
||||
assertEquals(Arrays.asList("a", "bc"), Message.empty().prepend(Arrays.asList("a", "bc")).getRaw());
|
||||
assertEquals(Arrays.asList("de", "f", "a", "bc"), Message.empty().prepend(Arrays.asList("a", "bc")).prepend(Arrays.asList("de", "f")).getRaw());
|
||||
|
||||
// Append a Message
|
||||
// TODO test if prepended message has replacement already done? (they should be)
|
||||
assertEquals(Collections.emptyList(), Message.empty().prepend(Message.empty()).getRaw());
|
||||
assertEquals(Collections.singletonList("abc"), Message.empty().prepend(Message.fromString("abc")).getRaw());
|
||||
assertEquals(Arrays.asList("a", "bc"), Message.empty().prepend(Message.fromList(Arrays.asList("a", "bc"))).getRaw());
|
||||
assertEquals(Arrays.asList("de", "f", "a", "bc"), Message.empty().prepend(Message.fromList(Arrays.asList("a", "bc"))).prepend(Message.fromList(Arrays.asList("de", "f"))).getRaw());
|
||||
}
|
||||
|
||||
private boolean reachesLimit(Message message) {
|
||||
Limit limit = new Limit(100, message);
|
||||
try {
|
||||
message.doReplacements(limit);
|
||||
System.out.println("reachesLimit: " + message.getKey() + " used " + (100 - limit.left));
|
||||
return false;
|
||||
} catch(ReplacementLimitReachedException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replacementsLimit() {
|
||||
// Language replacement
|
||||
assertFalse(reachesLimit(Message.fromKey("limit-no")));
|
||||
|
||||
// Direct recursion
|
||||
assertTrue(reachesLimit(Message.fromKey("limit-leftRecursive")));
|
||||
assertTrue(reachesLimit(Message.fromKey("limit-rightRecursive")));
|
||||
|
||||
// Indirect recursion
|
||||
assertTrue(reachesLimit(Message.fromKey("limit-leftIndirectRecursive")));
|
||||
assertTrue(reachesLimit(Message.fromKey("limit-rightIndirectRecursive")));
|
||||
|
||||
// Argument recursion
|
||||
assertTrue(reachesLimit(Message.fromKey("limit-arg").replacements(Message.fromKey("limit-arg"))));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package me.wiefferink.interactivemessenger.testing.unit;
|
||||
|
||||
import me.wiefferink.interactivemessenger.source.YAMLMessageProvider;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
|
||||
public class TestYAMLMessageProvider {
|
||||
|
||||
private static YAMLMessageProvider provider;
|
||||
|
||||
@BeforeClass
|
||||
public static void initMessage() {
|
||||
URL resource = TestYAMLMessageProvider.class.getClassLoader().getResource("processing/messages.yml");
|
||||
assertNotNull("Resource file messages.yml not found in the jar", resource);
|
||||
provider = new YAMLMessageProvider(new File(resource.getFile()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the result of a MessageProvider with an expected list of values
|
||||
* @param message Key of the message to test
|
||||
* @param expected List of values that are expected
|
||||
*/
|
||||
private void test(String message, String... expected) {
|
||||
assertEquals(Arrays.asList(expected), provider.getMessage(message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simple() {
|
||||
test("single", "Hello world!");
|
||||
test("multiple", "Hello ", "world!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multilineWithNewliens() {
|
||||
test("multilineWithNewlines1", "Hello world!\nSecond line\n");
|
||||
test("multilineWithNewlines2", "Hello world!\nSecond line");
|
||||
test("multilineWithNewlines3", "Hello world!\nSecond line\n\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multilineWithoutNewliens() {
|
||||
test("multilineWithoutNewlines1", "Hello world! More text\n");
|
||||
test("multilineWithoutNewlines2", "Hello world! More text");
|
||||
test("multilineWithoutNewlines3", "Hello world! More text\n\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
########## MessageProvider
|
||||
# Simple
|
||||
single: "Hello world!"
|
||||
multiple:
|
||||
- "Hello "
|
||||
- "world!"
|
||||
# With newline, remove trailing blank lines, keep newline at the end
|
||||
multilineWithNewlines1: |
|
||||
Hello world!
|
||||
Second line
|
||||
|
||||
# With newline, remove trailing blank lines, remove newline at the end
|
||||
multilineWithNewlines2: |-
|
||||
Hello world!
|
||||
Second line
|
||||
|
||||
# With newline, with trailing blank lines, with newline at the end
|
||||
multilineWithNewlines3: |+
|
||||
Hello world!
|
||||
Second line
|
||||
|
||||
# Without newline, remove trailing blank lines, keep newline at the end
|
||||
multilineWithoutNewlines1: >
|
||||
Hello world!
|
||||
More text
|
||||
|
||||
# Without newline, remove trailing blank lines, remove newline at the end
|
||||
multilineWithoutNewlines2: >-
|
||||
Hello world!
|
||||
More text
|
||||
|
||||
# Without newline, with trailing blank lines, with newline at the end
|
||||
multilineWithoutNewlines3: >+
|
||||
Hello world!
|
||||
More text
|
||||
|
||||
# Empty line above is important
|
||||
|
||||
########## Message
|
||||
limit-hello: "hello"
|
||||
limit-world: "world"
|
||||
limit-no: "A message with an insertion: %lang:limit-hello% %lang:limit-world%"
|
||||
limit-leftRecursive: "%lang:limit-leftRecursive% Hello world!"
|
||||
limit-rightRecursive: "Hello world! %lang:limit-rightRecursive%"
|
||||
limit-leftIndirectRecursive: "%lang:limit-leftIndirectRecursiveLoop% Hello world!"
|
||||
limit-rightIndirectRecursive: "Hello world! %lang:limit-rightIndirectRecursiveLoop%"
|
||||
limit-leftIndirectRecursiveLoop: "%lang:limit-leftIndirectRecursive% Hello world!"
|
||||
limit-rightIndirectRecursiveLoop: "Hello world! %lang:limit-rightIndirectRecursive%"
|
||||
limit-arg: "argument: %0%"
|
||||
Reference in new issue
Block a user