1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package org.slf4j.helpers;
26
27 import java.text.MessageFormat;
28 import java.util.HashMap;
29 import java.util.Map;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 final public class MessageFormatter {
99 static final char DELIM_START = '{';
100 static final char DELIM_STOP = '}';
101 static final String DELIM_STR = "{}";
102 private static final char ESCAPE_CHAR = '\\';
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 final public static FormattingTuple format(String messagePattern, Object arg) {
124 return arrayFormat(messagePattern, new Object[] { arg });
125 }
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 final public static FormattingTuple format(final String messagePattern, Object arg1, Object arg2) {
151 return arrayFormat(messagePattern, new Object[] { arg1, arg2 });
152 }
153
154 static final Throwable getThrowableCandidate(Object[] argArray) {
155 if (argArray == null || argArray.length == 0) {
156 return null;
157 }
158
159 final Object lastEntry = argArray[argArray.length - 1];
160 if (lastEntry instanceof Throwable) {
161 return (Throwable) lastEntry;
162 }
163 return null;
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178 final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray) {
179
180 Throwable throwableCandidate = getThrowableCandidate(argArray);
181
182 if (messagePattern == null) {
183 return new FormattingTuple(null, argArray, throwableCandidate);
184 }
185
186 if (argArray == null) {
187 return new FormattingTuple(messagePattern);
188 }
189
190 int i = 0;
191 int j;
192
193 StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
194
195 int L;
196 for (L = 0; L < argArray.length; L++) {
197
198 j = messagePattern.indexOf(DELIM_STR, i);
199
200 if (j == -1) {
201
202 if (i == 0) {
203 return new FormattingTuple(messagePattern, argArray, throwableCandidate);
204 } else {
205
206 sbuf.append(messagePattern.substring(i, messagePattern.length()));
207 return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate);
208 }
209 } else {
210 if (isEscapedDelimeter(messagePattern, j)) {
211 if (!isDoubleEscaped(messagePattern, j)) {
212 L--;
213 sbuf.append(messagePattern.substring(i, j - 1));
214 sbuf.append(DELIM_START);
215 i = j + 1;
216 } else {
217
218
219
220 sbuf.append(messagePattern.substring(i, j - 1));
221 deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>());
222 i = j + 2;
223 }
224 } else {
225
226 sbuf.append(messagePattern.substring(i, j));
227 deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>());
228 i = j + 2;
229 }
230 }
231 }
232
233 sbuf.append(messagePattern.substring(i, messagePattern.length()));
234 if (L < argArray.length - 1) {
235 return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate);
236 } else {
237 return new FormattingTuple(sbuf.toString(), argArray, null);
238 }
239 }
240
241 final static boolean isEscapedDelimeter(String messagePattern, int delimeterStartIndex) {
242
243 if (delimeterStartIndex == 0) {
244 return false;
245 }
246 char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1);
247 if (potentialEscape == ESCAPE_CHAR) {
248 return true;
249 } else {
250 return false;
251 }
252 }
253
254 final static boolean isDoubleEscaped(String messagePattern, int delimeterStartIndex) {
255 if (delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) {
256 return true;
257 } else {
258 return false;
259 }
260 }
261
262
263 private static void deeplyAppendParameter(StringBuilder sbuf, Object o, Map<Object[], Object> seenMap) {
264 if (o == null) {
265 sbuf.append("null");
266 return;
267 }
268 if (!o.getClass().isArray()) {
269 safeObjectAppend(sbuf, o);
270 } else {
271
272
273 if (o instanceof boolean[]) {
274 booleanArrayAppend(sbuf, (boolean[]) o);
275 } else if (o instanceof byte[]) {
276 byteArrayAppend(sbuf, (byte[]) o);
277 } else if (o instanceof char[]) {
278 charArrayAppend(sbuf, (char[]) o);
279 } else if (o instanceof short[]) {
280 shortArrayAppend(sbuf, (short[]) o);
281 } else if (o instanceof int[]) {
282 intArrayAppend(sbuf, (int[]) o);
283 } else if (o instanceof long[]) {
284 longArrayAppend(sbuf, (long[]) o);
285 } else if (o instanceof float[]) {
286 floatArrayAppend(sbuf, (float[]) o);
287 } else if (o instanceof double[]) {
288 doubleArrayAppend(sbuf, (double[]) o);
289 } else {
290 objectArrayAppend(sbuf, (Object[]) o, seenMap);
291 }
292 }
293 }
294
295 private static void safeObjectAppend(StringBuilder sbuf, Object o) {
296 try {
297 String oAsString = o.toString();
298 sbuf.append(oAsString);
299 } catch (Throwable t) {
300 System.err.println("SLF4J: Failed toString() invocation on an object of type [" + o.getClass().getName() + "]");
301 t.printStackTrace();
302 sbuf.append("[FAILED toString()]");
303 }
304
305 }
306
307 private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Map<Object[], Object> seenMap) {
308 sbuf.append('[');
309 if (!seenMap.containsKey(a)) {
310 seenMap.put(a, null);
311 final int len = a.length;
312 for (int i = 0; i < len; i++) {
313 deeplyAppendParameter(sbuf, a[i], seenMap);
314 if (i != len - 1)
315 sbuf.append(", ");
316 }
317
318 seenMap.remove(a);
319 } else {
320 sbuf.append("...");
321 }
322 sbuf.append(']');
323 }
324
325 private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) {
326 sbuf.append('[');
327 final int len = a.length;
328 for (int i = 0; i < len; i++) {
329 sbuf.append(a[i]);
330 if (i != len - 1)
331 sbuf.append(", ");
332 }
333 sbuf.append(']');
334 }
335
336 private static void byteArrayAppend(StringBuilder sbuf, byte[] a) {
337 sbuf.append('[');
338 final int len = a.length;
339 for (int i = 0; i < len; i++) {
340 sbuf.append(a[i]);
341 if (i != len - 1)
342 sbuf.append(", ");
343 }
344 sbuf.append(']');
345 }
346
347 private static void charArrayAppend(StringBuilder sbuf, char[] a) {
348 sbuf.append('[');
349 final int len = a.length;
350 for (int i = 0; i < len; i++) {
351 sbuf.append(a[i]);
352 if (i != len - 1)
353 sbuf.append(", ");
354 }
355 sbuf.append(']');
356 }
357
358 private static void shortArrayAppend(StringBuilder sbuf, short[] a) {
359 sbuf.append('[');
360 final int len = a.length;
361 for (int i = 0; i < len; i++) {
362 sbuf.append(a[i]);
363 if (i != len - 1)
364 sbuf.append(", ");
365 }
366 sbuf.append(']');
367 }
368
369 private static void intArrayAppend(StringBuilder sbuf, int[] a) {
370 sbuf.append('[');
371 final int len = a.length;
372 for (int i = 0; i < len; i++) {
373 sbuf.append(a[i]);
374 if (i != len - 1)
375 sbuf.append(", ");
376 }
377 sbuf.append(']');
378 }
379
380 private static void longArrayAppend(StringBuilder sbuf, long[] a) {
381 sbuf.append('[');
382 final int len = a.length;
383 for (int i = 0; i < len; i++) {
384 sbuf.append(a[i]);
385 if (i != len - 1)
386 sbuf.append(", ");
387 }
388 sbuf.append(']');
389 }
390
391 private static void floatArrayAppend(StringBuilder sbuf, float[] a) {
392 sbuf.append('[');
393 final int len = a.length;
394 for (int i = 0; i < len; i++) {
395 sbuf.append(a[i]);
396 if (i != len - 1)
397 sbuf.append(", ");
398 }
399 sbuf.append(']');
400 }
401
402 private static void doubleArrayAppend(StringBuilder sbuf, double[] a) {
403 sbuf.append('[');
404 final int len = a.length;
405 for (int i = 0; i < len; i++) {
406 sbuf.append(a[i]);
407 if (i != len - 1)
408 sbuf.append(", ");
409 }
410 sbuf.append(']');
411 }
412 }