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}.
|
||||
*/
|
||||
class FormattingTuple {
|
||||
|
||||
static final FormattingTuple NULL = new FormattingTuple(null);
|
||||
final class FormattingTuple {
|
||||
|
||||
private final String message;
|
||||
private final Throwable throwable;
|
||||
private final Object[] argArray;
|
||||
|
||||
FormattingTuple(String message) {
|
||||
this(message, null, null);
|
||||
}
|
||||
|
||||
FormattingTuple(String message, Object[] argArray, Throwable throwable) {
|
||||
FormattingTuple(String message, Throwable throwable) {
|
||||
this.message = message;
|
||||
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() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Object[] getArgArray() {
|
||||
return argArray;
|
||||
}
|
||||
|
||||
public Throwable getThrowable() {
|
||||
return throwable;
|
||||
}
|
||||
|
@ -40,8 +40,8 @@
|
||||
package io.netty.util.internal.logging;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
// contributors: lizongbo: proposed special treatment of array parameter values
|
||||
// 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.
|
||||
*/
|
||||
final class MessageFormatter {
|
||||
static final char DELIM_START = '{';
|
||||
static final char DELIM_STOP = '}';
|
||||
static final String DELIM_STR = "{}";
|
||||
private static final String DELIM_STR = "{}";
|
||||
private static final char ESCAPE_CHAR = '\\';
|
||||
|
||||
/**
|
||||
@ -159,18 +157,6 @@ final class MessageFormatter {
|
||||
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
|
||||
* {@link #format(String, Object, Object)} methods except that any number of
|
||||
@ -183,118 +169,109 @@ final class MessageFormatter {
|
||||
*/
|
||||
static FormattingTuple arrayFormat(final String messagePattern,
|
||||
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) {
|
||||
return new FormattingTuple(null, argArray, throwableCandidate);
|
||||
return new FormattingTuple(null, throwable);
|
||||
}
|
||||
|
||||
if (argArray == null) {
|
||||
return new FormattingTuple(messagePattern);
|
||||
int j = messagePattern.indexOf(DELIM_STR);
|
||||
if (j == -1) {
|
||||
// this is a simple string
|
||||
return new FormattingTuple(messagePattern, throwable);
|
||||
}
|
||||
|
||||
StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
|
||||
int i = 0;
|
||||
int j;
|
||||
StringBuffer sbuf = new StringBuffer(messagePattern.length() + 50);
|
||||
int L = 0;
|
||||
do {
|
||||
boolean notEscaped = j == 0 || messagePattern.charAt(j - 1) != ESCAPE_CHAR;
|
||||
if (notEscaped) {
|
||||
// normal case
|
||||
sbuf.append(messagePattern, i, j);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
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 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);
|
||||
i = j + 2;
|
||||
if (notEscaped) {
|
||||
deeplyAppendParameter(sbuf, argArray[L], null);
|
||||
L++;
|
||||
if (L > lastArrIdx) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (isEscapedDelimeter(messagePattern, j)) {
|
||||
if (!isDoubleEscaped(messagePattern, j)) {
|
||||
L--; // DELIM_START was escaped, thus should not be incremented
|
||||
sbuf.append(messagePattern.substring(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.substring(i, j - 1));
|
||||
deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Void>());
|
||||
i = j + 2;
|
||||
}
|
||||
} else {
|
||||
// normal case
|
||||
sbuf.append(messagePattern.substring(i, j));
|
||||
deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Void>());
|
||||
i = j + 2;
|
||||
}
|
||||
sbuf.append(DELIM_STR);
|
||||
}
|
||||
}
|
||||
j = messagePattern.indexOf(DELIM_STR, i);
|
||||
} while (j != -1);
|
||||
|
||||
// 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 {
|
||||
return new FormattingTuple(sbuf.toString(), argArray, null);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isEscapedDelimeter(String messagePattern,
|
||||
int delimeterStartIndex) {
|
||||
|
||||
if (delimeterStartIndex == 0) {
|
||||
return false;
|
||||
}
|
||||
return messagePattern.charAt(delimeterStartIndex - 1) == ESCAPE_CHAR;
|
||||
}
|
||||
|
||||
static boolean isDoubleEscaped(String messagePattern,
|
||||
int delimeterStartIndex) {
|
||||
return delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR;
|
||||
sbuf.append(messagePattern, i, messagePattern.length());
|
||||
return new FormattingTuple(sbuf.toString(), L <= lastArrIdx? throwable : null);
|
||||
}
|
||||
|
||||
// special treatment of array values was suggested by 'lizongbo'
|
||||
private static void deeplyAppendParameter(StringBuffer sbuf, Object o,
|
||||
Map<Object[], Void> seenMap) {
|
||||
private static void deeplyAppendParameter(StringBuilder sbuf, Object o,
|
||||
Set<Object[]> seenSet) {
|
||||
if (o == null) {
|
||||
sbuf.append("null");
|
||||
return;
|
||||
}
|
||||
if (!o.getClass().isArray()) {
|
||||
safeObjectAppend(sbuf, o);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
safeObjectAppend(sbuf, o);
|
||||
}
|
||||
} else {
|
||||
// check for primitive array types because they
|
||||
// unfortunately cannot be cast to Object[]
|
||||
if (o instanceof boolean[]) {
|
||||
sbuf.append('[');
|
||||
if (objClass == boolean[].class) {
|
||||
booleanArrayAppend(sbuf, (boolean[]) o);
|
||||
} else if (o instanceof byte[]) {
|
||||
} else if (objClass == byte[].class) {
|
||||
byteArrayAppend(sbuf, (byte[]) o);
|
||||
} else if (o instanceof char[]) {
|
||||
} else if (objClass == char[].class) {
|
||||
charArrayAppend(sbuf, (char[]) o);
|
||||
} else if (o instanceof short[]) {
|
||||
} else if (objClass == short[].class) {
|
||||
shortArrayAppend(sbuf, (short[]) o);
|
||||
} else if (o instanceof int[]) {
|
||||
} else if (objClass == int[].class) {
|
||||
intArrayAppend(sbuf, (int[]) o);
|
||||
} else if (o instanceof long[]) {
|
||||
} else if (objClass == long[].class) {
|
||||
longArrayAppend(sbuf, (long[]) o);
|
||||
} else if (o instanceof float[]) {
|
||||
} else if (objClass == float[].class) {
|
||||
floatArrayAppend(sbuf, (float[]) o);
|
||||
} else if (o instanceof double[]) {
|
||||
} else if (objClass == double[].class) {
|
||||
doubleArrayAppend(sbuf, (double[]) o);
|
||||
} 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 {
|
||||
String oAsString = o.toString();
|
||||
sbuf.append(oAsString);
|
||||
@ -307,120 +284,112 @@ final class MessageFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
private static void objectArrayAppend(StringBuffer sbuf, Object[] a,
|
||||
Map<Object[], Void> 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(", ");
|
||||
}
|
||||
private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Set<Object[]> seenSet) {
|
||||
if (a.length == 0) {
|
||||
return;
|
||||
}
|
||||
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
|
||||
seenMap.remove(a);
|
||||
seenSet.remove(a);
|
||||
} else {
|
||||
sbuf.append("...");
|
||||
}
|
||||
sbuf.append(']');
|
||||
}
|
||||
|
||||
private static void booleanArrayAppend(StringBuffer 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(", ");
|
||||
}
|
||||
private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) {
|
||||
if (a.length == 0) {
|
||||
return;
|
||||
}
|
||||
sbuf.append(a[0]);
|
||||
for (int i = 1; i < a.length; i++) {
|
||||
sbuf.append(", ");
|
||||
sbuf.append(a[i]);
|
||||
}
|
||||
sbuf.append(']');
|
||||
}
|
||||
|
||||
private static void byteArrayAppend(StringBuffer 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(", ");
|
||||
}
|
||||
private static void byteArrayAppend(StringBuilder sbuf, byte[] a) {
|
||||
if (a.length == 0) {
|
||||
return;
|
||||
}
|
||||
sbuf.append(a[0]);
|
||||
for (int i = 1; i < a.length; i++) {
|
||||
sbuf.append(", ");
|
||||
sbuf.append(a[i]);
|
||||
}
|
||||
sbuf.append(']');
|
||||
}
|
||||
|
||||
private static void charArrayAppend(StringBuffer 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(", ");
|
||||
}
|
||||
private static void charArrayAppend(StringBuilder sbuf, char[] a) {
|
||||
if (a.length == 0) {
|
||||
return;
|
||||
}
|
||||
sbuf.append(a[0]);
|
||||
for (int i = 1; i < a.length; i++) {
|
||||
sbuf.append(", ");
|
||||
sbuf.append(a[i]);
|
||||
}
|
||||
sbuf.append(']');
|
||||
}
|
||||
|
||||
private static void shortArrayAppend(StringBuffer 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(", ");
|
||||
}
|
||||
private static void shortArrayAppend(StringBuilder sbuf, short[] a) {
|
||||
if (a.length == 0) {
|
||||
return;
|
||||
}
|
||||
sbuf.append(a[0]);
|
||||
for (int i = 1; i < a.length; i++) {
|
||||
sbuf.append(", ");
|
||||
sbuf.append(a[i]);
|
||||
}
|
||||
sbuf.append(']');
|
||||
}
|
||||
|
||||
private static void intArrayAppend(StringBuffer 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(", ");
|
||||
}
|
||||
private static void intArrayAppend(StringBuilder sbuf, int[] a) {
|
||||
if (a.length == 0) {
|
||||
return;
|
||||
}
|
||||
sbuf.append(a[0]);
|
||||
for (int i = 1; i < a.length; i++) {
|
||||
sbuf.append(", ");
|
||||
sbuf.append(a[i]);
|
||||
}
|
||||
sbuf.append(']');
|
||||
}
|
||||
|
||||
private static void longArrayAppend(StringBuffer 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(", ");
|
||||
}
|
||||
private static void longArrayAppend(StringBuilder sbuf, long[] a) {
|
||||
if (a.length == 0) {
|
||||
return;
|
||||
}
|
||||
sbuf.append(a[0]);
|
||||
for (int i = 1; i < a.length; i++) {
|
||||
sbuf.append(", ");
|
||||
sbuf.append(a[i]);
|
||||
}
|
||||
sbuf.append(']');
|
||||
}
|
||||
|
||||
private static void floatArrayAppend(StringBuffer 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(", ");
|
||||
}
|
||||
private static void floatArrayAppend(StringBuilder sbuf, float[] a) {
|
||||
if (a.length == 0) {
|
||||
return;
|
||||
}
|
||||
sbuf.append(a[0]);
|
||||
for (int i = 1; i < a.length; i++) {
|
||||
sbuf.append(", ");
|
||||
sbuf.append(a[i]);
|
||||
}
|
||||
sbuf.append(']');
|
||||
}
|
||||
|
||||
private static void doubleArrayAppend(StringBuffer 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(", ");
|
||||
}
|
||||
private static void doubleArrayAppend(StringBuilder sbuf, double[] a) {
|
||||
if (a.length == 0) {
|
||||
return;
|
||||
}
|
||||
sbuf.append(a[0]);
|
||||
for (int i = 1; i < a.length; i++) {
|
||||
sbuf.append(", ");
|
||||
sbuf.append(a[i]);
|
||||
}
|
||||
sbuf.append(']');
|
||||
}
|
||||
|
||||
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