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
155 static final Throwable getThrowableCandidate(Object[] argArray) {
156 if (argArray == null || argArray.length == 0) {
157 return null;
158 }
159
160 final Object lastEntry = argArray[argArray.length - 1];
161 if (lastEntry instanceof Throwable) {
162 return (Throwable) lastEntry;
163 }
164 return null;
165 }
166
167 final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray) {
168 Throwable throwableCandidate = getThrowableCandidate(argArray);
169 Object[] args = argArray;
170 if (throwableCandidate != null) {
171 args = trimmedCopy(argArray);
172 }
173 return arrayFormat(messagePattern, args, throwableCandidate);
174 }
175
176 private static Object[] trimmedCopy(Object[] argArray) {
177 if (argArray == null || argArray.length == 0) {
178 throw new IllegalStateException("non-sensical empty or null argument array");
179 }
180 final int trimemdLen = argArray.length - 1;
181 Object[] trimmed = new Object[trimemdLen];
182 System.arraycopy(argArray, 0, trimmed, 0, trimemdLen);
183 return trimmed;
184 }
185
186 final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray, Throwable throwable) {
187
188 if (messagePattern == null) {
189 return new FormattingTuple(null, argArray, throwable);
190 }
191
192 if (argArray == null) {
193 return new FormattingTuple(messagePattern);
194 }
195
196 int i = 0;
197 int j;
198
199 StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
200
201 int L;
202 for (L = 0; L < argArray.length; L++) {
203
204 j = messagePattern.indexOf(DELIM_STR, i);
205
206 if (j == -1) {
207
208 if (i == 0) {
209 return new FormattingTuple(messagePattern, argArray, throwable);
210 } else {
211
212 sbuf.append(messagePattern, i, messagePattern.length());
213 return new FormattingTuple(sbuf.toString(), argArray, throwable);
214 }
215 } else {
216 if (isEscapedDelimeter(messagePattern, j)) {
217 if (!isDoubleEscaped(messagePattern, j)) {
218 L--;
219 sbuf.append(messagePattern, i, j - 1);
220 sbuf.append(DELIM_START);
221 i = j + 1;
222 } else {
223
224
225
226 sbuf.append(messagePattern, i, j - 1);
227 deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>());
228 i = j + 2;
229 }
230 } else {
231
232 sbuf.append(messagePattern, i, j);
233 deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>());
234 i = j + 2;
235 }
236 }
237 }
238
239 sbuf.append(messagePattern, i, messagePattern.length());
240 return new FormattingTuple(sbuf.toString(), argArray, throwable);
241 }
242
243 final static boolean isEscapedDelimeter(String messagePattern, int delimeterStartIndex) {
244
245 if (delimeterStartIndex == 0) {
246 return false;
247 }
248 char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1);
249 if (potentialEscape == ESCAPE_CHAR) {
250 return true;
251 } else {
252 return false;
253 }
254 }
255
256 final static boolean isDoubleEscaped(String messagePattern, int delimeterStartIndex) {
257 if (delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) {
258 return true;
259 } else {
260 return false;
261 }
262 }
263
264
265 private static void deeplyAppendParameter(StringBuilder sbuf, Object o, Map<Object[], Object> seenMap) {
266 if (o == null) {
267 sbuf.append("null");
268 return;
269 }
270 if (!o.getClass().isArray()) {
271 safeObjectAppend(sbuf, o);
272 } else {
273
274
275 if (o instanceof boolean[]) {
276 booleanArrayAppend(sbuf, (boolean[]) o);
277 } else if (o instanceof byte[]) {
278 byteArrayAppend(sbuf, (byte[]) o);
279 } else if (o instanceof char[]) {
280 charArrayAppend(sbuf, (char[]) o);
281 } else if (o instanceof short[]) {
282 shortArrayAppend(sbuf, (short[]) o);
283 } else if (o instanceof int[]) {
284 intArrayAppend(sbuf, (int[]) o);
285 } else if (o instanceof long[]) {
286 longArrayAppend(sbuf, (long[]) o);
287 } else if (o instanceof float[]) {
288 floatArrayAppend(sbuf, (float[]) o);
289 } else if (o instanceof double[]) {
290 doubleArrayAppend(sbuf, (double[]) o);
291 } else {
292 objectArrayAppend(sbuf, (Object[]) o, seenMap);
293 }
294 }
295 }
296
297 private static void safeObjectAppend(StringBuilder sbuf, Object o) {
298 try {
299 String oAsString = o.toString();
300 sbuf.append(oAsString);
301 } catch (Throwable t) {
302 Util.report("SLF4J: Failed toString() invocation on an object of type [" + o.getClass().getName() + "]", t);
303 sbuf.append("[FAILED toString()]");
304 }
305
306 }
307
308 private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Map<Object[], Object> seenMap) {
309 sbuf.append('[');
310 if (!seenMap.containsKey(a)) {
311 seenMap.put(a, null);
312 final int len = a.length;
313 for (int i = 0; i < len; i++) {
314 deeplyAppendParameter(sbuf, a[i], seenMap);
315 if (i != len - 1)
316 sbuf.append(", ");
317 }
318
319 seenMap.remove(a);
320 } else {
321 sbuf.append("...");
322 }
323 sbuf.append(']');
324 }
325
326 private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) {
327 sbuf.append('[');
328 final int len = a.length;
329 for (int i = 0; i < len; i++) {
330 sbuf.append(a[i]);
331 if (i != len - 1)
332 sbuf.append(", ");
333 }
334 sbuf.append(']');
335 }
336
337 private static void byteArrayAppend(StringBuilder sbuf, byte[] a) {
338 sbuf.append('[');
339 final int len = a.length;
340 for (int i = 0; i < len; i++) {
341 sbuf.append(a[i]);
342 if (i != len - 1)
343 sbuf.append(", ");
344 }
345 sbuf.append(']');
346 }
347
348 private static void charArrayAppend(StringBuilder sbuf, char[] a) {
349 sbuf.append('[');
350 final int len = a.length;
351 for (int i = 0; i < len; i++) {
352 sbuf.append(a[i]);
353 if (i != len - 1)
354 sbuf.append(", ");
355 }
356 sbuf.append(']');
357 }
358
359 private static void shortArrayAppend(StringBuilder sbuf, short[] a) {
360 sbuf.append('[');
361 final int len = a.length;
362 for (int i = 0; i < len; i++) {
363 sbuf.append(a[i]);
364 if (i != len - 1)
365 sbuf.append(", ");
366 }
367 sbuf.append(']');
368 }
369
370 private static void intArrayAppend(StringBuilder sbuf, int[] a) {
371 sbuf.append('[');
372 final int len = a.length;
373 for (int i = 0; i < len; i++) {
374 sbuf.append(a[i]);
375 if (i != len - 1)
376 sbuf.append(", ");
377 }
378 sbuf.append(']');
379 }
380
381 private static void longArrayAppend(StringBuilder sbuf, long[] a) {
382 sbuf.append('[');
383 final int len = a.length;
384 for (int i = 0; i < len; i++) {
385 sbuf.append(a[i]);
386 if (i != len - 1)
387 sbuf.append(", ");
388 }
389 sbuf.append(']');
390 }
391
392 private static void floatArrayAppend(StringBuilder sbuf, float[] a) {
393 sbuf.append('[');
394 final int len = a.length;
395 for (int i = 0; i < len; i++) {
396 sbuf.append(a[i]);
397 if (i != len - 1)
398 sbuf.append(", ");
399 }
400 sbuf.append(']');
401 }
402
403 private static void doubleArrayAppend(StringBuilder sbuf, double[] a) {
404 sbuf.append('[');
405 final int len = a.length;
406 for (int i = 0; i < len; i++) {
407 sbuf.append(a[i]);
408 if (i != len - 1)
409 sbuf.append(", ");
410 }
411 sbuf.append(']');
412 }
413
414 }