Created
December 13, 2020 17:51
-
-
Save Mr00Anderson/ab12d45c46b6e09eea61e98c731aad3b to your computer and use it in GitHub Desktop.
Sl4j String formatter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app.virtualhex.gdx.util; | |
/** | |
* Copyright (c) 2004-2011 QOS.ch | |
* All rights reserved. | |
* <p> | |
* Permission is hereby granted, free of charge, to any person obtaining | |
* a copy of this software and associated documentation files (the | |
* "Software"), to deal in the Software without restriction, including | |
* without limitation the rights to use, copy, modify, merge, publish, | |
* distribute, sublicense, and/or sell copies of the Software, and to | |
* permit persons to whom the Software is furnished to do so, subject to | |
* the following conditions: | |
* <p> | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* <p> | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* <p> | |
* <p> | |
* The ideas, code and implementations are NON-CONFIDENTIAL. | |
* <p> | |
* This class was taken from https://www.slf4j.org/ and adapted by | |
* Free Universe Games, LLC. | |
*/ | |
import java.util.HashMap; | |
import java.util.Map; | |
// contributors: lizongbo: proposed special treatment of array parameter values | |
// Joern Huxhorn: pointed out double[] omission, suggested deep array copy | |
/** | |
* Formats messages according to very simple substitution rules. Substitutions | |
* can be made 1, 2 or more arguments. | |
* | |
* <p> | |
* For example, | |
* | |
* <pre> | |
* MessageFormatter.format("Hi {}.", "there") | |
* </pre> | |
* | |
* will return the string "Hi there.". | |
* <p> | |
* The {} pair is called the <em>formatting anchor</em>. It serves to designate | |
* the location where arguments need to be substituted within the message | |
* pattern. | |
* <p> | |
* In case your message contains the '{' or the '}' character, you do not have | |
* to do anything special unless the '}' character immediately follows '{'. For | |
* example, | |
* | |
* <pre> | |
* MessageFormatter.format("Set {1,2,3} is not equal to {}.", "1,2"); | |
* </pre> | |
* | |
* will return the string "Set {1,2,3} is not equal to 1,2.". | |
* | |
* <p> | |
* If for whatever reason you need to place the string "{}" in the message | |
* without its <em>formatting anchor</em> meaning, then you need to escape the | |
* '{' character with '\', that is the backslash character. Only the '{' | |
* character should be escaped. There is no need to escape the '}' character. | |
* For example, | |
* | |
* <pre> | |
* MessageFormatter.format("Set \\{} is not equal to {}.", "1,2"); | |
* </pre> | |
* | |
* will return the string "Set {} is not equal to 1,2.". | |
* | |
* <p> | |
* The escaping behavior just described can be overridden by escaping the escape | |
* character '\'. Calling | |
* | |
* <pre> | |
* MessageFormatter.format("File name is C:\\\\{}.", "file.zip"); | |
* </pre> | |
* | |
* will return the string "File name is C:\file.zip". | |
* | |
* <p> | |
* The formatting conventions are different than those of {@link java.text.MessageFormat} | |
* which ships with the Java platform. This is justified by the fact that | |
* SLF4J's implementation is 10 times faster than that of {@link java.text.MessageFormat}. | |
* This local performance difference is both measurable and significant in the | |
* larger context of the complete logging processing chain. | |
* | |
* <p> | |
* {@link #arrayFormat(String, Object[])} methods for more details. | |
* | |
* @author Ceki Gülcü | |
* @author Joern Huxhorn | |
*/ | |
public final class StringFormatter { | |
static final char DELIM_START = '{'; | |
static final char DELIM_STOP = '}'; | |
static final String DELIM_STR = "{}"; | |
private static final char ESCAPE_CHAR = '\\'; | |
/** | |
* | |
* Performs a two argument substitution for the 'messagePattern' passed as | |
* parameter. | |
* <p> | |
* For example, | |
* | |
* <pre> | |
* MessageFormatter.format("Hi {}. My name is {}.", "Alice", "Bob"); | |
* </pre> | |
* | |
* will return the string "Hi Alice. My name is Bob.". | |
* | |
* @param messagePattern | |
* The message pattern which will be parsed and formatted | |
* @return The formatted message | |
*/ | |
final public static String format(final String messagePattern, Object... args) { | |
return arrayFormat(messagePattern, args).getMessage(); | |
} | |
static final Throwable getThrowableCandidate(Object[] argArray) { | |
if (argArray == null || argArray.length == 0) { | |
return null; | |
} | |
final Object lastEntry = argArray[argArray.length - 1]; | |
if (lastEntry instanceof Throwable) { | |
return (Throwable) lastEntry; | |
} | |
return null; | |
} | |
final public static StringFormattingTuple arrayFormat(final String messagePattern, final Object[] argArray) { | |
Throwable throwableCandidate = getThrowableCandidate(argArray); | |
Object[] args = argArray; | |
if (throwableCandidate != null) { | |
args = trimmedCopy(argArray); | |
} | |
return arrayFormat(messagePattern, args, throwableCandidate); | |
} | |
private static Object[] trimmedCopy(Object[] argArray) { | |
if (argArray == null || argArray.length == 0) { | |
throw new IllegalStateException("non-sensical empty or null argument array"); | |
} | |
final int trimemdLen = argArray.length - 1; | |
Object[] trimmed = new Object[trimemdLen]; | |
System.arraycopy(argArray, 0, trimmed, 0, trimemdLen); | |
return trimmed; | |
} | |
final public static StringFormattingTuple arrayFormat(final String messagePattern, final Object[] argArray, Throwable throwable) { | |
if (messagePattern == null) { | |
return new StringFormattingTuple(null, argArray, throwable); | |
} | |
if (argArray == null) { | |
return new StringFormattingTuple(messagePattern); | |
} | |
int i = 0; | |
int j; | |
// use string builder for better multicore performance | |
StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50); | |
int L; | |
for (L = 0; L < argArray.length; L++) { | |
j = messagePattern.indexOf(DELIM_STR, i); | |
if (j == -1) { | |
// no more variables | |
if (i == 0) { // this is a simple string | |
return new StringFormattingTuple(messagePattern, argArray, throwable); | |
} else { // add the tail string which contains no variables and return | |
// the result. | |
sbuf.append(messagePattern, i, messagePattern.length()); | |
return new StringFormattingTuple(sbuf.toString(), argArray, throwable); | |
} | |
} else { | |
if (isEscapedDelimeter(messagePattern, j)) { | |
if (!isDoubleEscaped(messagePattern, j)) { | |
L--; // DELIM_START was escaped, thus should not be incremented | |
sbuf.append(messagePattern, i, j - 1); | |
sbuf.append(DELIM_START); | |
i = j + 1; | |
} else { | |
// The escape character preceding the delimiter start is | |
// itself escaped: "abc x:\\{}" | |
// we have to consume one backward slash | |
sbuf.append(messagePattern, i, j - 1); | |
deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>()); | |
i = j + 2; | |
} | |
} else { | |
// normal case | |
sbuf.append(messagePattern, i, j); | |
deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>()); | |
i = j + 2; | |
} | |
} | |
} | |
// append the characters following the last {} pair. | |
sbuf.append(messagePattern, i, messagePattern.length()); | |
return new StringFormattingTuple(sbuf.toString(), argArray, throwable); | |
} | |
final static boolean isEscapedDelimeter(String messagePattern, int delimeterStartIndex) { | |
if (delimeterStartIndex == 0) { | |
return false; | |
} | |
char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1); | |
if (potentialEscape == ESCAPE_CHAR) { | |
return true; | |
} else { | |
return false; | |
} | |
} | |
final static boolean isDoubleEscaped(String messagePattern, int delimeterStartIndex) { | |
if (delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) { | |
return true; | |
} else { | |
return false; | |
} | |
} | |
// special treatment of array values was suggested by 'lizongbo' | |
private static void deeplyAppendParameter(StringBuilder sbuf, Object o, Map<Object[], Object> seenMap) { | |
if (o == null) { | |
sbuf.append("null"); | |
return; | |
} | |
if (!o.getClass().isArray()) { | |
safeObjectAppend(sbuf, o); | |
} else { | |
// check for primitive array types because they | |
// unfortunately cannot be cast to Object[] | |
if (o instanceof boolean[]) { | |
booleanArrayAppend(sbuf, (boolean[]) o); | |
} else if (o instanceof byte[]) { | |
byteArrayAppend(sbuf, (byte[]) o); | |
} else if (o instanceof char[]) { | |
charArrayAppend(sbuf, (char[]) o); | |
} else if (o instanceof short[]) { | |
shortArrayAppend(sbuf, (short[]) o); | |
} else if (o instanceof int[]) { | |
intArrayAppend(sbuf, (int[]) o); | |
} else if (o instanceof long[]) { | |
longArrayAppend(sbuf, (long[]) o); | |
} else if (o instanceof float[]) { | |
floatArrayAppend(sbuf, (float[]) o); | |
} else if (o instanceof double[]) { | |
doubleArrayAppend(sbuf, (double[]) o); | |
} else { | |
objectArrayAppend(sbuf, (Object[]) o, seenMap); | |
} | |
} | |
} | |
private static void safeObjectAppend(StringBuilder sbuf, Object o) { | |
try { | |
String oAsString = o.toString(); | |
sbuf.append(oAsString); | |
} catch (Throwable t) { | |
sbuf.append("[FAILED toString()]"); | |
} | |
} | |
private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Map<Object[], Object> seenMap) { | |
sbuf.append('['); | |
if (!seenMap.containsKey(a)) { | |
seenMap.put(a, null); | |
final int len = a.length; | |
for (int i = 0; i < len; i++) { | |
deeplyAppendParameter(sbuf, a[i], seenMap); | |
if (i != len - 1) | |
sbuf.append(", "); | |
} | |
// allow repeats in siblings | |
seenMap.remove(a); | |
} else { | |
sbuf.append("..."); | |
} | |
sbuf.append(']'); | |
} | |
private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) { | |
sbuf.append('['); | |
final int len = a.length; | |
for (int i = 0; i < len; i++) { | |
sbuf.append(a[i]); | |
if (i != len - 1) | |
sbuf.append(", "); | |
} | |
sbuf.append(']'); | |
} | |
private static void byteArrayAppend(StringBuilder sbuf, byte[] a) { | |
sbuf.append('['); | |
final int len = a.length; | |
for (int i = 0; i < len; i++) { | |
sbuf.append(a[i]); | |
if (i != len - 1) | |
sbuf.append(", "); | |
} | |
sbuf.append(']'); | |
} | |
private static void charArrayAppend(StringBuilder sbuf, char[] a) { | |
sbuf.append('['); | |
final int len = a.length; | |
for (int i = 0; i < len; i++) { | |
sbuf.append(a[i]); | |
if (i != len - 1) | |
sbuf.append(", "); | |
} | |
sbuf.append(']'); | |
} | |
private static void shortArrayAppend(StringBuilder sbuf, short[] a) { | |
sbuf.append('['); | |
final int len = a.length; | |
for (int i = 0; i < len; i++) { | |
sbuf.append(a[i]); | |
if (i != len - 1) | |
sbuf.append(", "); | |
} | |
sbuf.append(']'); | |
} | |
private static void intArrayAppend(StringBuilder sbuf, int[] a) { | |
sbuf.append('['); | |
final int len = a.length; | |
for (int i = 0; i < len; i++) { | |
sbuf.append(a[i]); | |
if (i != len - 1) | |
sbuf.append(", "); | |
} | |
sbuf.append(']'); | |
} | |
private static void longArrayAppend(StringBuilder sbuf, long[] a) { | |
sbuf.append('['); | |
final int len = a.length; | |
for (int i = 0; i < len; i++) { | |
sbuf.append(a[i]); | |
if (i != len - 1) | |
sbuf.append(", "); | |
} | |
sbuf.append(']'); | |
} | |
private static void floatArrayAppend(StringBuilder sbuf, float[] a) { | |
sbuf.append('['); | |
final int len = a.length; | |
for (int i = 0; i < len; i++) { | |
sbuf.append(a[i]); | |
if (i != len - 1) | |
sbuf.append(", "); | |
} | |
sbuf.append(']'); | |
} | |
private static void doubleArrayAppend(StringBuilder sbuf, double[] a) { | |
sbuf.append('['); | |
final int len = a.length; | |
for (int i = 0; i < len; i++) { | |
sbuf.append(a[i]); | |
if (i != len - 1) | |
sbuf.append(", "); | |
} | |
sbuf.append(']'); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app.virtualhex.gdx.util; | |
/** | |
* Copyright (c) 2004-2011 QOS.ch | |
* All rights reserved. | |
* <p> | |
* Permission is hereby granted, free of charge, to any person obtaining | |
* a copy of this software and associated documentation files (the | |
* "Software"), to deal in the Software without restriction, including | |
* without limitation the rights to use, copy, modify, merge, publish, | |
* distribute, sublicense, and/or sell copies of the Software, and to | |
* permit persons to whom the Software is furnished to do so, subject to | |
* the following conditions: | |
* <p> | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* <p> | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* <p> | |
* <p> | |
* The ideas, code and implementations are NON-CONFIDENTIAL. | |
* <p> | |
* This class was taken from https://www.slf4j.org/ and adapted by | |
* Free Universe Games, LLC. | |
*/ | |
/** | |
* Holds the results of formatting done by {@link StringFormatter}. | |
* | |
* | |
* @author Joern Huxhorn | |
*/ | |
public class StringFormattingTuple { | |
static public StringFormattingTuple NULL = new StringFormattingTuple(null); | |
private String message; | |
private Throwable throwable; | |
private Object[] argArray; | |
public StringFormattingTuple(String message) { | |
this(message, null, null); | |
} | |
public StringFormattingTuple(String message, Object[] argArray, Throwable throwable) { | |
this.message = message; | |
this.throwable = throwable; | |
this.argArray = argArray; | |
} | |
public String getMessage() { | |
return message; | |
} | |
public Object[] getArgArray() { | |
return argArray; | |
} | |
public Throwable getThrowable() { | |
return throwable; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment