MessageFormatter improvements
Motivation: `FormattingTuple.getArgArray()` is never used. In the `MessageFormatter` it is possible to make some improvements, e.g. replace `StringBuffer` with `StringBuilder`, avoid redundant allocations, etc. Modifications: - Remove `argArray` field from the `FormattingTuple`. - In `MessageFormatter`: - replace `StringBuffer` with `StringBuilder`, - replace `HashMap` with `HashSet` and make it lazy initialized. - avoid redundant allocations (`substring()`, etc.) - use appropriate StringBuilder's methods for the some `Number` values. - Porting unit tests from `slf4j`. Result: Less GC load on logging with internal `MessageFormatter`.
This commit is contained in:
parent
ba5d1880bc
commit
d768c5e628
@ -42,46 +42,20 @@ package io.netty.util.internal.logging;
|
|||||||
/**
|
/**
|
||||||
* Holds the results of formatting done by {@link MessageFormatter}.
|
* Holds the results of formatting done by {@link MessageFormatter}.
|
||||||
*/
|
*/
|
||||||
class FormattingTuple {
|
final class FormattingTuple {
|
||||||
|
|
||||||
static final FormattingTuple NULL = new FormattingTuple(null);
|
|
||||||
|
|
||||||
private final String message;
|
private final String message;
|
||||||
private final Throwable throwable;
|
private final Throwable throwable;
|
||||||
private final Object[] argArray;
|
|
||||||
|
|
||||||
FormattingTuple(String message) {
|
FormattingTuple(String message, Throwable throwable) {
|
||||||
this(message, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
FormattingTuple(String message, Object[] argArray, Throwable throwable) {
|
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.throwable = throwable;
|
this.throwable = throwable;
|
||||||
if (throwable == null) {
|
|
||||||
this.argArray = argArray;
|
|
||||||
} else {
|
|
||||||
this.argArray = trimmedCopy(argArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Object[] trimmedCopy(Object[] argArray) {
|
|
||||||
if (argArray == null || argArray.length == 0) {
|
|
||||||
throw new IllegalStateException("nonsensical empty or null argument array");
|
|
||||||
}
|
|
||||||
final int trimmedLen = argArray.length - 1;
|
|
||||||
Object[] trimmed = new Object[trimmedLen];
|
|
||||||
System.arraycopy(argArray, 0, trimmed, 0, trimmedLen);
|
|
||||||
return trimmed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] getArgArray() {
|
|
||||||
return argArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Throwable getThrowable() {
|
public Throwable getThrowable() {
|
||||||
return throwable;
|
return throwable;
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,8 @@
|
|||||||
package io.netty.util.internal.logging;
|
package io.netty.util.internal.logging;
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Set;
|
||||||
|
|
||||||
// contributors: lizongbo: proposed special treatment of array parameter values
|
// contributors: lizongbo: proposed special treatment of array parameter values
|
||||||
// Joern Huxhorn: pointed out double[] omission, suggested deep array copy
|
// Joern Huxhorn: pointed out double[] omission, suggested deep array copy
|
||||||
@ -109,9 +109,7 @@ import java.util.Map;
|
|||||||
* {@link #arrayFormat(String, Object[])} methods for more details.
|
* {@link #arrayFormat(String, Object[])} methods for more details.
|
||||||
*/
|
*/
|
||||||
final class MessageFormatter {
|
final class MessageFormatter {
|
||||||
static final char DELIM_START = '{';
|
private static final String DELIM_STR = "{}";
|
||||||
static final char DELIM_STOP = '}';
|
|
||||||
static final String DELIM_STR = "{}";
|
|
||||||
private static final char ESCAPE_CHAR = '\\';
|
private static final char ESCAPE_CHAR = '\\';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,18 +157,6 @@ final class MessageFormatter {
|
|||||||
return arrayFormat(messagePattern, new Object[]{argA, argB});
|
return arrayFormat(messagePattern, new Object[]{argA, argB});
|
||||||
}
|
}
|
||||||
|
|
||||||
static 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same principle as the {@link #format(String, Object)} and
|
* Same principle as the {@link #format(String, Object)} and
|
||||||
* {@link #format(String, Object, Object)} methods except that any number of
|
* {@link #format(String, Object, Object)} methods except that any number of
|
||||||
@ -183,118 +169,109 @@ final class MessageFormatter {
|
|||||||
*/
|
*/
|
||||||
static FormattingTuple arrayFormat(final String messagePattern,
|
static FormattingTuple arrayFormat(final String messagePattern,
|
||||||
final Object[] argArray) {
|
final Object[] argArray) {
|
||||||
|
if (argArray == null || argArray.length == 0) {
|
||||||
|
return new FormattingTuple(messagePattern, null);
|
||||||
|
}
|
||||||
|
|
||||||
Throwable throwableCandidate = getThrowableCandidate(argArray);
|
int lastArrIdx = argArray.length - 1;
|
||||||
|
Object lastEntry = argArray[lastArrIdx];
|
||||||
|
Throwable throwable = lastEntry instanceof Throwable? (Throwable) lastEntry : null;
|
||||||
|
|
||||||
if (messagePattern == null) {
|
if (messagePattern == null) {
|
||||||
return new FormattingTuple(null, argArray, throwableCandidate);
|
return new FormattingTuple(null, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argArray == null) {
|
int j = messagePattern.indexOf(DELIM_STR);
|
||||||
return new FormattingTuple(messagePattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
int j;
|
|
||||||
StringBuffer sbuf = new StringBuffer(messagePattern.length() + 50);
|
|
||||||
|
|
||||||
int L;
|
|
||||||
for (L = 0; L < argArray.length; L++) {
|
|
||||||
|
|
||||||
j = messagePattern.indexOf(DELIM_STR, i);
|
|
||||||
|
|
||||||
if (j == -1) {
|
if (j == -1) {
|
||||||
// no more variables
|
// this is a simple string
|
||||||
if (i == 0) { // this is a simple string
|
return new FormattingTuple(messagePattern, throwable);
|
||||||
return new FormattingTuple(messagePattern, argArray,
|
|
||||||
throwableCandidate);
|
|
||||||
} else { // add the tail string which contains no variables and return
|
|
||||||
// the result.
|
|
||||||
sbuf.append(messagePattern.substring(i, messagePattern.length()));
|
|
||||||
return new FormattingTuple(sbuf.toString(), argArray,
|
|
||||||
throwableCandidate);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (isEscapedDelimeter(messagePattern, j)) {
|
StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
|
||||||
if (!isDoubleEscaped(messagePattern, j)) {
|
int i = 0;
|
||||||
L--; // DELIM_START was escaped, thus should not be incremented
|
int L = 0;
|
||||||
sbuf.append(messagePattern.substring(i, j - 1));
|
do {
|
||||||
sbuf.append(DELIM_START);
|
boolean notEscaped = j == 0 || messagePattern.charAt(j - 1) != ESCAPE_CHAR;
|
||||||
i = j + 1;
|
if (notEscaped) {
|
||||||
} else {
|
|
||||||
// The escape character preceding the delimiter start is
|
|
||||||
// itself escaped: "abc x:\\{}"
|
|
||||||
// we have to consume one backward slash
|
|
||||||
sbuf.append(messagePattern.substring(i, j - 1));
|
|
||||||
deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Void>());
|
|
||||||
i = j + 2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// normal case
|
// normal case
|
||||||
sbuf.append(messagePattern.substring(i, j));
|
sbuf.append(messagePattern, i, j);
|
||||||
deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Void>());
|
|
||||||
i = j + 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// append the characters following the last {} pair.
|
|
||||||
sbuf.append(messagePattern.substring(i, messagePattern.length()));
|
|
||||||
if (L < argArray.length - 1) {
|
|
||||||
return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate);
|
|
||||||
} else {
|
} else {
|
||||||
return new FormattingTuple(sbuf.toString(), argArray, null);
|
sbuf.append(messagePattern, i, j - 1);
|
||||||
}
|
// check that escape char is not is escaped: "abc x:\\{}"
|
||||||
|
notEscaped = j >= 2 && messagePattern.charAt(j - 2) == ESCAPE_CHAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isEscapedDelimeter(String messagePattern,
|
i = j + 2;
|
||||||
int delimeterStartIndex) {
|
if (notEscaped) {
|
||||||
|
deeplyAppendParameter(sbuf, argArray[L], null);
|
||||||
if (delimeterStartIndex == 0) {
|
L++;
|
||||||
return false;
|
if (L > lastArrIdx) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return messagePattern.charAt(delimeterStartIndex - 1) == ESCAPE_CHAR;
|
} else {
|
||||||
|
sbuf.append(DELIM_STR);
|
||||||
}
|
}
|
||||||
|
j = messagePattern.indexOf(DELIM_STR, i);
|
||||||
|
} while (j != -1);
|
||||||
|
|
||||||
static boolean isDoubleEscaped(String messagePattern,
|
// append the characters following the last {} pair.
|
||||||
int delimeterStartIndex) {
|
sbuf.append(messagePattern, i, messagePattern.length());
|
||||||
return delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR;
|
return new FormattingTuple(sbuf.toString(), L <= lastArrIdx? throwable : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// special treatment of array values was suggested by 'lizongbo'
|
// special treatment of array values was suggested by 'lizongbo'
|
||||||
private static void deeplyAppendParameter(StringBuffer sbuf, Object o,
|
private static void deeplyAppendParameter(StringBuilder sbuf, Object o,
|
||||||
Map<Object[], Void> seenMap) {
|
Set<Object[]> seenSet) {
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
sbuf.append("null");
|
sbuf.append("null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!o.getClass().isArray()) {
|
Class<?> objClass = o.getClass();
|
||||||
|
if (!objClass.isArray()) {
|
||||||
|
if (Number.class.isAssignableFrom(objClass)) {
|
||||||
|
// Prevent String instantiation for some number types
|
||||||
|
if (objClass == Long.class) {
|
||||||
|
sbuf.append(((Long) o).longValue());
|
||||||
|
} else if (objClass == Integer.class || objClass == Short.class || objClass == Byte.class) {
|
||||||
|
sbuf.append(((Number) o).intValue());
|
||||||
|
} else if (objClass == Double.class) {
|
||||||
|
sbuf.append(((Double) o).doubleValue());
|
||||||
|
} else if (objClass == Float.class) {
|
||||||
|
sbuf.append(((Float) o).floatValue());
|
||||||
|
} else {
|
||||||
safeObjectAppend(sbuf, o);
|
safeObjectAppend(sbuf, o);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
safeObjectAppend(sbuf, o);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// check for primitive array types because they
|
// check for primitive array types because they
|
||||||
// unfortunately cannot be cast to Object[]
|
// unfortunately cannot be cast to Object[]
|
||||||
if (o instanceof boolean[]) {
|
sbuf.append('[');
|
||||||
|
if (objClass == boolean[].class) {
|
||||||
booleanArrayAppend(sbuf, (boolean[]) o);
|
booleanArrayAppend(sbuf, (boolean[]) o);
|
||||||
} else if (o instanceof byte[]) {
|
} else if (objClass == byte[].class) {
|
||||||
byteArrayAppend(sbuf, (byte[]) o);
|
byteArrayAppend(sbuf, (byte[]) o);
|
||||||
} else if (o instanceof char[]) {
|
} else if (objClass == char[].class) {
|
||||||
charArrayAppend(sbuf, (char[]) o);
|
charArrayAppend(sbuf, (char[]) o);
|
||||||
} else if (o instanceof short[]) {
|
} else if (objClass == short[].class) {
|
||||||
shortArrayAppend(sbuf, (short[]) o);
|
shortArrayAppend(sbuf, (short[]) o);
|
||||||
} else if (o instanceof int[]) {
|
} else if (objClass == int[].class) {
|
||||||
intArrayAppend(sbuf, (int[]) o);
|
intArrayAppend(sbuf, (int[]) o);
|
||||||
} else if (o instanceof long[]) {
|
} else if (objClass == long[].class) {
|
||||||
longArrayAppend(sbuf, (long[]) o);
|
longArrayAppend(sbuf, (long[]) o);
|
||||||
} else if (o instanceof float[]) {
|
} else if (objClass == float[].class) {
|
||||||
floatArrayAppend(sbuf, (float[]) o);
|
floatArrayAppend(sbuf, (float[]) o);
|
||||||
} else if (o instanceof double[]) {
|
} else if (objClass == double[].class) {
|
||||||
doubleArrayAppend(sbuf, (double[]) o);
|
doubleArrayAppend(sbuf, (double[]) o);
|
||||||
} else {
|
} else {
|
||||||
objectArrayAppend(sbuf, (Object[]) o, seenMap);
|
objectArrayAppend(sbuf, (Object[]) o, seenSet);
|
||||||
}
|
}
|
||||||
|
sbuf.append(']');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void safeObjectAppend(StringBuffer sbuf, Object o) {
|
private static void safeObjectAppend(StringBuilder sbuf, Object o) {
|
||||||
try {
|
try {
|
||||||
String oAsString = o.toString();
|
String oAsString = o.toString();
|
||||||
sbuf.append(oAsString);
|
sbuf.append(oAsString);
|
||||||
@ -307,121 +284,113 @@ final class MessageFormatter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void objectArrayAppend(StringBuffer sbuf, Object[] a,
|
private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Set<Object[]> seenSet) {
|
||||||
Map<Object[], Void> seenMap) {
|
if (a.length == 0) {
|
||||||
sbuf.append('[');
|
return;
|
||||||
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(", ");
|
|
||||||
}
|
}
|
||||||
|
if (seenSet == null) {
|
||||||
|
seenSet = new HashSet<Object[]>(a.length);
|
||||||
|
}
|
||||||
|
if (seenSet.add(a)) {
|
||||||
|
deeplyAppendParameter(sbuf, a[0], seenSet);
|
||||||
|
for (int i = 1; i < a.length; i++) {
|
||||||
|
sbuf.append(", ");
|
||||||
|
deeplyAppendParameter(sbuf, a[i], seenSet);
|
||||||
}
|
}
|
||||||
// allow repeats in siblings
|
// allow repeats in siblings
|
||||||
seenMap.remove(a);
|
seenSet.remove(a);
|
||||||
} else {
|
} else {
|
||||||
sbuf.append("...");
|
sbuf.append("...");
|
||||||
}
|
}
|
||||||
sbuf.append(']');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void booleanArrayAppend(StringBuffer sbuf, boolean[] a) {
|
private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) {
|
||||||
sbuf.append('[');
|
if (a.length == 0) {
|
||||||
final int len = a.length;
|
return;
|
||||||
for (int i = 0; i < len; i++) {
|
}
|
||||||
sbuf.append(a[i]);
|
sbuf.append(a[0]);
|
||||||
if (i != len - 1) {
|
for (int i = 1; i < a.length; i++) {
|
||||||
sbuf.append(", ");
|
sbuf.append(", ");
|
||||||
|
sbuf.append(a[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sbuf.append(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void byteArrayAppend(StringBuffer sbuf, byte[] a) {
|
private static void byteArrayAppend(StringBuilder sbuf, byte[] a) {
|
||||||
sbuf.append('[');
|
if (a.length == 0) {
|
||||||
final int len = a.length;
|
return;
|
||||||
for (int i = 0; i < len; i++) {
|
}
|
||||||
sbuf.append(a[i]);
|
sbuf.append(a[0]);
|
||||||
if (i != len - 1) {
|
for (int i = 1; i < a.length; i++) {
|
||||||
sbuf.append(", ");
|
sbuf.append(", ");
|
||||||
|
sbuf.append(a[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sbuf.append(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void charArrayAppend(StringBuffer sbuf, char[] a) {
|
private static void charArrayAppend(StringBuilder sbuf, char[] a) {
|
||||||
sbuf.append('[');
|
if (a.length == 0) {
|
||||||
final int len = a.length;
|
return;
|
||||||
for (int i = 0; i < len; i++) {
|
}
|
||||||
sbuf.append(a[i]);
|
sbuf.append(a[0]);
|
||||||
if (i != len - 1) {
|
for (int i = 1; i < a.length; i++) {
|
||||||
sbuf.append(", ");
|
sbuf.append(", ");
|
||||||
|
sbuf.append(a[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sbuf.append(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void shortArrayAppend(StringBuffer sbuf, short[] a) {
|
private static void shortArrayAppend(StringBuilder sbuf, short[] a) {
|
||||||
sbuf.append('[');
|
if (a.length == 0) {
|
||||||
final int len = a.length;
|
return;
|
||||||
for (int i = 0; i < len; i++) {
|
}
|
||||||
sbuf.append(a[i]);
|
sbuf.append(a[0]);
|
||||||
if (i != len - 1) {
|
for (int i = 1; i < a.length; i++) {
|
||||||
sbuf.append(", ");
|
sbuf.append(", ");
|
||||||
|
sbuf.append(a[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sbuf.append(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void intArrayAppend(StringBuffer sbuf, int[] a) {
|
private static void intArrayAppend(StringBuilder sbuf, int[] a) {
|
||||||
sbuf.append('[');
|
if (a.length == 0) {
|
||||||
final int len = a.length;
|
return;
|
||||||
for (int i = 0; i < len; i++) {
|
}
|
||||||
sbuf.append(a[i]);
|
sbuf.append(a[0]);
|
||||||
if (i != len - 1) {
|
for (int i = 1; i < a.length; i++) {
|
||||||
sbuf.append(", ");
|
sbuf.append(", ");
|
||||||
|
sbuf.append(a[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sbuf.append(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void longArrayAppend(StringBuffer sbuf, long[] a) {
|
private static void longArrayAppend(StringBuilder sbuf, long[] a) {
|
||||||
sbuf.append('[');
|
if (a.length == 0) {
|
||||||
final int len = a.length;
|
return;
|
||||||
for (int i = 0; i < len; i++) {
|
}
|
||||||
sbuf.append(a[i]);
|
sbuf.append(a[0]);
|
||||||
if (i != len - 1) {
|
for (int i = 1; i < a.length; i++) {
|
||||||
sbuf.append(", ");
|
sbuf.append(", ");
|
||||||
|
sbuf.append(a[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sbuf.append(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void floatArrayAppend(StringBuffer sbuf, float[] a) {
|
private static void floatArrayAppend(StringBuilder sbuf, float[] a) {
|
||||||
sbuf.append('[');
|
if (a.length == 0) {
|
||||||
final int len = a.length;
|
return;
|
||||||
for (int i = 0; i < len; i++) {
|
}
|
||||||
sbuf.append(a[i]);
|
sbuf.append(a[0]);
|
||||||
if (i != len - 1) {
|
for (int i = 1; i < a.length; i++) {
|
||||||
sbuf.append(", ");
|
sbuf.append(", ");
|
||||||
|
sbuf.append(a[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sbuf.append(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void doubleArrayAppend(StringBuffer sbuf, double[] a) {
|
private static void doubleArrayAppend(StringBuilder sbuf, double[] a) {
|
||||||
sbuf.append('[');
|
if (a.length == 0) {
|
||||||
final int len = a.length;
|
return;
|
||||||
for (int i = 0; i < len; i++) {
|
}
|
||||||
sbuf.append(a[i]);
|
sbuf.append(a[0]);
|
||||||
if (i != len - 1) {
|
for (int i = 1; i < a.length; i++) {
|
||||||
sbuf.append(", ");
|
sbuf.append(", ");
|
||||||
|
sbuf.append(a[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sbuf.append(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
private MessageFormatter() {
|
private MessageFormatter() {
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package io.netty.util.internal.logging;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class MessageFormatterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNull() {
|
||||||
|
String result = MessageFormatter.format(null, 1).getMessage();
|
||||||
|
assertNull(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nullParametersShouldBeHandledWithoutBarfing() {
|
||||||
|
String result = MessageFormatter.format("Value is {}.", null).getMessage();
|
||||||
|
assertEquals("Value is null.", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Val1 is {}, val2 is {}.", null, null).getMessage();
|
||||||
|
assertEquals("Val1 is null, val2 is null.", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Val1 is {}, val2 is {}.", 1, null).getMessage();
|
||||||
|
assertEquals("Val1 is 1, val2 is null.", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Val1 is {}, val2 is {}.", null, 2).getMessage();
|
||||||
|
assertEquals("Val1 is null, val2 is 2.", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat(
|
||||||
|
"Val1 is {}, val2 is {}, val3 is {}", new Integer[] { null, null, null }).getMessage();
|
||||||
|
assertEquals("Val1 is null, val2 is null, val3 is null", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat(
|
||||||
|
"Val1 is {}, val2 is {}, val3 is {}", new Integer[] { null, 2, 3 }).getMessage();
|
||||||
|
assertEquals("Val1 is null, val2 is 2, val3 is 3", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat(
|
||||||
|
"Val1 is {}, val2 is {}, val3 is {}", new Integer[] { null, null, 3 }).getMessage();
|
||||||
|
assertEquals("Val1 is null, val2 is null, val3 is 3", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verifyOneParameterIsHandledCorrectly() {
|
||||||
|
String result = MessageFormatter.format("Value is {}.", 3).getMessage();
|
||||||
|
assertEquals("Value is 3.", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Value is {", 3).getMessage();
|
||||||
|
assertEquals("Value is {", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("{} is larger than 2.", 3).getMessage();
|
||||||
|
assertEquals("3 is larger than 2.", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("No subst", 3).getMessage();
|
||||||
|
assertEquals("No subst", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Incorrect {subst", 3).getMessage();
|
||||||
|
assertEquals("Incorrect {subst", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Value is {bla} {}", 3).getMessage();
|
||||||
|
assertEquals("Value is {bla} 3", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Escaped \\{} subst", 3).getMessage();
|
||||||
|
assertEquals("Escaped {} subst", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("{Escaped", 3).getMessage();
|
||||||
|
assertEquals("{Escaped", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("\\{}Escaped", 3).getMessage();
|
||||||
|
assertEquals("{}Escaped", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("File name is {{}}.", "App folder.zip").getMessage();
|
||||||
|
assertEquals("File name is {App folder.zip}.", result);
|
||||||
|
|
||||||
|
// escaping the escape character
|
||||||
|
result = MessageFormatter.format("File name is C:\\\\{}.", "App folder.zip").getMessage();
|
||||||
|
assertEquals("File name is C:\\App folder.zip.", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTwoParameters() {
|
||||||
|
String result = MessageFormatter.format("Value {} is smaller than {}.", 1, 2).getMessage();
|
||||||
|
assertEquals("Value 1 is smaller than 2.", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Value {} is smaller than {}", 1, 2).getMessage();
|
||||||
|
assertEquals("Value 1 is smaller than 2", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("{}{}", 1, 2).getMessage();
|
||||||
|
assertEquals("12", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Val1={}, Val2={", 1, 2).getMessage();
|
||||||
|
assertEquals("Val1=1, Val2={", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Value {} is smaller than \\{}", 1, 2).getMessage();
|
||||||
|
assertEquals("Value 1 is smaller than {}", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Value {} is smaller than \\{} tail", 1, 2).getMessage();
|
||||||
|
assertEquals("Value 1 is smaller than {} tail", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Value {} is smaller than \\{", 1, 2).getMessage();
|
||||||
|
assertEquals("Value 1 is smaller than \\{", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Value {} is smaller than {tail", 1, 2).getMessage();
|
||||||
|
assertEquals("Value 1 is smaller than {tail", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.format("Value \\{} is smaller than {}", 1, 2).getMessage();
|
||||||
|
assertEquals("Value {} is smaller than 1", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExceptionIn_toString() {
|
||||||
|
Object o = new Object() {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
throw new IllegalStateException("a");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
String result = MessageFormatter.format("Troublesome object {}", o).getMessage();
|
||||||
|
assertEquals("Troublesome object [FAILED toString()]", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNullArray() {
|
||||||
|
String msg0 = "msg0";
|
||||||
|
String msg1 = "msg1 {}";
|
||||||
|
String msg2 = "msg2 {} {}";
|
||||||
|
String msg3 = "msg3 {} {} {}";
|
||||||
|
|
||||||
|
Object[] args = null;
|
||||||
|
|
||||||
|
String result = MessageFormatter.arrayFormat(msg0, args).getMessage();
|
||||||
|
assertEquals(msg0, result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat(msg1, args).getMessage();
|
||||||
|
assertEquals(msg1, result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat(msg2, args).getMessage();
|
||||||
|
assertEquals(msg2, result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat(msg3, args).getMessage();
|
||||||
|
assertEquals(msg3, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests the case when the parameters are supplied in a single array
|
||||||
|
@Test
|
||||||
|
public void testArrayFormat() {
|
||||||
|
Integer[] ia0 = { 1, 2, 3 };
|
||||||
|
|
||||||
|
String result = MessageFormatter.arrayFormat("Value {} is smaller than {} and {}.", ia0).getMessage();
|
||||||
|
assertEquals("Value 1 is smaller than 2 and 3.", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat("{}{}{}", ia0).getMessage();
|
||||||
|
assertEquals("123", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat("Value {} is smaller than {}.", ia0).getMessage();
|
||||||
|
assertEquals("Value 1 is smaller than 2.", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat("Value {} is smaller than {}", ia0).getMessage();
|
||||||
|
assertEquals("Value 1 is smaller than 2", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat("Val={}, {, Val={}", ia0).getMessage();
|
||||||
|
assertEquals("Val=1, {, Val=2", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat("Val={}, {, Val={}", ia0).getMessage();
|
||||||
|
assertEquals("Val=1, {, Val=2", result);
|
||||||
|
|
||||||
|
result = MessageFormatter.arrayFormat("Val1={}, Val2={", ia0).getMessage();
|
||||||
|
assertEquals("Val1=1, Val2={", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testArrayValues() {
|
||||||
|
Integer[] p1 = { 2, 3 };
|
||||||
|
|
||||||
|
String result = MessageFormatter.format("{}{}", 1, p1).getMessage();
|
||||||
|
assertEquals("1[2, 3]", result);
|
||||||
|
|
||||||
|
// Integer[]
|
||||||
|
result = MessageFormatter.arrayFormat("{}{}", new Object[] { "a", p1 }).getMessage();
|
||||||
|
assertEquals("a[2, 3]", result);
|
||||||
|
|
||||||
|
// byte[]
|
||||||
|
result = MessageFormatter.arrayFormat("{}{}", new Object[] { "a", new byte[] { 1, 2 } }).getMessage();
|
||||||
|
assertEquals("a[1, 2]", result);
|
||||||
|
|
||||||
|
// int[]
|
||||||
|
result = MessageFormatter.arrayFormat("{}{}", new Object[] { "a", new int[] { 1, 2 } }).getMessage();
|
||||||
|
assertEquals("a[1, 2]", result);
|
||||||
|
|
||||||
|
// float[]
|
||||||
|
result = MessageFormatter.arrayFormat("{}{}", new Object[] { "a", new float[] { 1, 2 } }).getMessage();
|
||||||
|
assertEquals("a[1.0, 2.0]", result);
|
||||||
|
|
||||||
|
// double[]
|
||||||
|
result = MessageFormatter.arrayFormat("{}{}", new Object[] { "a", new double[] { 1, 2 } }).getMessage();
|
||||||
|
assertEquals("a[1.0, 2.0]", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiDimensionalArrayValues() {
|
||||||
|
Integer[] ia0 = { 1, 2, 3 };
|
||||||
|
Integer[] ia1 = { 10, 20, 30 };
|
||||||
|
|
||||||
|
Integer[][] multiIntegerA = { ia0, ia1 };
|
||||||
|
String result = MessageFormatter.arrayFormat("{}{}", new Object[] { "a", multiIntegerA }).getMessage();
|
||||||
|
assertEquals("a[[1, 2, 3], [10, 20, 30]]", result);
|
||||||
|
|
||||||
|
int[][] multiIntA = { { 1, 2 }, { 10, 20 } };
|
||||||
|
result = MessageFormatter.arrayFormat("{}{}", new Object[] { "a", multiIntA }).getMessage();
|
||||||
|
assertEquals("a[[1, 2], [10, 20]]", result);
|
||||||
|
|
||||||
|
float[][] multiFloatA = { { 1, 2 }, { 10, 20 } };
|
||||||
|
result = MessageFormatter.arrayFormat("{}{}", new Object[] { "a", multiFloatA }).getMessage();
|
||||||
|
assertEquals("a[[1.0, 2.0], [10.0, 20.0]]", result);
|
||||||
|
|
||||||
|
Object[][] multiOA = { ia0, ia1 };
|
||||||
|
result = MessageFormatter.arrayFormat("{}{}", new Object[] { "a", multiOA }).getMessage();
|
||||||
|
assertEquals("a[[1, 2, 3], [10, 20, 30]]", result);
|
||||||
|
|
||||||
|
Object[][][] _3DOA = { multiOA, multiOA };
|
||||||
|
result = MessageFormatter.arrayFormat("{}{}", new Object[] { "a", _3DOA }).getMessage();
|
||||||
|
assertEquals("a[[[1, 2, 3], [10, 20, 30]], [[1, 2, 3], [10, 20, 30]]]", result);
|
||||||
|
|
||||||
|
Byte[] ba0 = { 0, Byte.MAX_VALUE, Byte.MIN_VALUE };
|
||||||
|
Short[] sa0 = { 0, Short.MIN_VALUE, Short.MAX_VALUE };
|
||||||
|
result = MessageFormatter.arrayFormat("{}\\{}{}", new Object[] { new Object[] { ba0, sa0 }, ia1 }).getMessage();
|
||||||
|
assertEquals("[[0, 127, -128], [0, -32768, 32767]]{}[10, 20, 30]", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCyclicArrays() {
|
||||||
|
Object[] cyclicA = new Object[1];
|
||||||
|
cyclicA[0] = cyclicA;
|
||||||
|
assertEquals("[[...]]", MessageFormatter.arrayFormat("{}", cyclicA).getMessage());
|
||||||
|
|
||||||
|
Object[] a = new Object[2];
|
||||||
|
a[0] = 1;
|
||||||
|
Object[] c = { 3, a };
|
||||||
|
Object[] b = { 2, c };
|
||||||
|
a[1] = b;
|
||||||
|
assertEquals("1[2, [3, [1, [...]]]]",
|
||||||
|
MessageFormatter.arrayFormat("{}{}", a).getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testArrayThrowable() {
|
||||||
|
FormattingTuple ft;
|
||||||
|
Throwable t = new Throwable();
|
||||||
|
Object[] ia = { 1, 2, 3, t };
|
||||||
|
|
||||||
|
ft = MessageFormatter.arrayFormat("Value {} is smaller than {} and {}.", ia);
|
||||||
|
assertEquals("Value 1 is smaller than 2 and 3.", ft.getMessage());
|
||||||
|
assertEquals(t, ft.getThrowable());
|
||||||
|
|
||||||
|
ft = MessageFormatter.arrayFormat("{}{}{}", ia);
|
||||||
|
assertEquals("123", ft.getMessage());
|
||||||
|
assertEquals(t, ft.getThrowable());
|
||||||
|
|
||||||
|
ft = MessageFormatter.arrayFormat("Value {} is smaller than {}.", ia);
|
||||||
|
assertEquals("Value 1 is smaller than 2.", ft.getMessage());
|
||||||
|
assertEquals(t, ft.getThrowable());
|
||||||
|
|
||||||
|
ft = MessageFormatter.arrayFormat("Value {} is smaller than {}", ia);
|
||||||
|
assertEquals("Value 1 is smaller than 2", ft.getMessage());
|
||||||
|
assertEquals(t, ft.getThrowable());
|
||||||
|
|
||||||
|
ft = MessageFormatter.arrayFormat("Val={}, {, Val={}", ia);
|
||||||
|
assertEquals("Val=1, {, Val=2", ft.getMessage());
|
||||||
|
assertEquals(t, ft.getThrowable());
|
||||||
|
|
||||||
|
ft = MessageFormatter.arrayFormat("Val={}, \\{, Val={}", ia);
|
||||||
|
assertEquals("Val=1, \\{, Val=2", ft.getMessage());
|
||||||
|
assertEquals(t, ft.getThrowable());
|
||||||
|
|
||||||
|
ft = MessageFormatter.arrayFormat("Val1={}, Val2={", ia);
|
||||||
|
assertEquals("Val1=1, Val2={", ft.getMessage());
|
||||||
|
assertEquals(t, ft.getThrowable());
|
||||||
|
|
||||||
|
ft = MessageFormatter.arrayFormat("Value {} is smaller than {} and {}.", ia);
|
||||||
|
assertEquals("Value 1 is smaller than 2 and 3.", ft.getMessage());
|
||||||
|
assertEquals(t, ft.getThrowable());
|
||||||
|
|
||||||
|
ft = MessageFormatter.arrayFormat("{}{}{}{}", ia);
|
||||||
|
assertEquals("123java.lang.Throwable", ft.getMessage());
|
||||||
|
assertNull(ft.getThrowable());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user