1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.message;
18
19 import java.io.Serializable;
20 import java.text.SimpleDateFormat;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Date;
24 import java.util.HashSet;
25 import java.util.Map;
26 import java.util.Set;
27
28
29
30
31
32
33
34
35
36 public class ParameterizedMessage implements Message, Serializable {
37
38
39
40
41 public static final String RECURSION_PREFIX = "[...";
42
43
44
45 public static final String RECURSION_SUFFIX = "...]";
46
47
48
49
50 public static final String ERROR_PREFIX = "[!!!";
51
52
53
54 public static final String ERROR_SEPARATOR = "=>";
55
56
57
58 public static final String ERROR_MSG_SEPARATOR = ":";
59
60
61
62 public static final String ERROR_SUFFIX = "!!!]";
63
64 private static final long serialVersionUID = -665975803997290697L;
65
66 private static final int HASHVAL = 31;
67
68 private static final char DELIM_START = '{';
69 private static final char DELIM_STOP = '}';
70 private static final char ESCAPE_CHAR = '\\';
71
72 private String messagePattern;
73 private String[] stringArgs;
74 private transient Object[] argArray;
75 private transient String formattedMessage;
76 private transient Throwable throwable;
77
78
79
80
81 public ParameterizedMessage() {
82 this(null, null, null);
83 }
84
85
86
87
88
89
90
91
92 public ParameterizedMessage(String messagePattern, String[] stringArgs, Throwable throwable) {
93 this.messagePattern = messagePattern;
94 this.stringArgs = stringArgs;
95 this.throwable = throwable;
96 }
97
98 public ParameterizedMessage(String messagePattern, Object[] arguments, Throwable throwable) {
99 this.messagePattern = messagePattern;
100 this.throwable = throwable;
101 if (arguments != null) {
102 parseArguments(arguments);
103 }
104 }
105
106
107
108
109
110
111
112
113
114
115
116
117
118 public ParameterizedMessage(String messagePattern, Object[] arguments) {
119 this.messagePattern = messagePattern;
120 if (arguments == null) {
121 return;
122 }
123 parseArguments(arguments);
124 }
125
126
127
128
129
130
131 public ParameterizedMessage(String messagePattern, Object arg) {
132 this(messagePattern, new Object[]{arg});
133 }
134
135
136
137
138
139
140
141 public ParameterizedMessage(String messagePattern, Object arg1, Object arg2) {
142 this(messagePattern, new Object[]{arg1, arg2});
143 }
144
145 private void parseArguments(Object[] arguments) {
146 int argsCount = countArgumentPlaceholders(messagePattern);
147 int resultArgCount = arguments.length;
148 if (argsCount < arguments.length) {
149 if (throwable == null && arguments[arguments.length - 1] instanceof Throwable) {
150 throwable = (Throwable) arguments[arguments.length - 1];
151 resultArgCount--;
152 }
153 }
154 argArray = new Object[resultArgCount];
155 for (int i = 0; i < resultArgCount; ++i) {
156 argArray[i] = arguments[i];
157 }
158
159 if (argsCount == 1 && throwable == null && arguments.length > 1) {
160
161 stringArgs = new String[1];
162 stringArgs[0] = deepToString(arguments);
163 } else {
164 stringArgs = new String[resultArgCount];
165 for (int i = 0; i < stringArgs.length; i++) {
166 stringArgs[i] = deepToString(arguments[i]);
167 }
168 }
169 }
170
171
172
173
174
175 public String getFormattedMessage() {
176 if (formattedMessage == null) {
177 formattedMessage = formatMessage(messagePattern, stringArgs);
178 }
179 return formattedMessage;
180 }
181
182
183
184
185
186 public String getMessageFormat() {
187 return messagePattern;
188 }
189
190
191
192
193
194 public void setMessageFormat(String messagePattern) {
195 this.messagePattern = messagePattern;
196 this.formattedMessage = null;
197 }
198
199
200
201
202
203 public Object[] getParameters() {
204 if (argArray != null) {
205 return argArray;
206 }
207 return stringArgs;
208 }
209
210
211
212
213
214 public void setParameters(String[] parameters) {
215 this.stringArgs = parameters;
216 this.formattedMessage = null;
217 }
218
219
220
221
222
223 public void setParameters(Object[] parameters) {
224 parseArguments(parameters);
225 this.formattedMessage = null;
226 }
227
228
229
230
231
232 public void setThrowable(Throwable throwable) {
233 this.throwable = throwable;
234 }
235
236
237
238
239
240
241
242
243
244
245 public Throwable getThrowable() {
246 return throwable;
247 }
248
249 protected String formatMessage(String msgPattern, String[] sArgs) {
250 return format(msgPattern, sArgs);
251 }
252
253 public boolean equals(Object o) {
254 if (this == o) {
255 return true;
256 }
257 if (o == null || getClass() != o.getClass()) {
258 return false;
259 }
260
261 ParameterizedMessage that = (ParameterizedMessage) o;
262
263 if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) {
264 return false;
265 }
266 if (!Arrays.equals(stringArgs, that.stringArgs)) {
267 return false;
268 }
269
270
271 return true;
272 }
273
274 public int hashCode() {
275 int result = messagePattern != null ? messagePattern.hashCode() : 0;
276 result = HASHVAL * result + (stringArgs != null ? Arrays.hashCode(stringArgs) : 0);
277 return result;
278 }
279
280
281
282
283
284
285
286
287 public static String format(String messagePattern, Object[] arguments) {
288 if (messagePattern == null || arguments == null || arguments.length == 0) {
289 return messagePattern;
290 }
291
292 StringBuilder result = new StringBuilder();
293 int escapeCounter = 0;
294 int currentArgument = 0;
295 for (int i = 0; i < messagePattern.length(); i++) {
296 char curChar = messagePattern.charAt(i);
297 if (curChar == ESCAPE_CHAR) {
298 escapeCounter++;
299 } else {
300 if (curChar == DELIM_START) {
301 if (i < messagePattern.length() - 1) {
302 if (messagePattern.charAt(i + 1) == DELIM_STOP) {
303
304 int escapedEscapes = escapeCounter / 2;
305 for (int j = 0; j < escapedEscapes; j++) {
306 result.append(ESCAPE_CHAR);
307 }
308
309 if (escapeCounter % 2 == 1) {
310
311
312 result.append(DELIM_START);
313 result.append(DELIM_STOP);
314 } else {
315
316 if (currentArgument < arguments.length) {
317 result.append(arguments[currentArgument]);
318 } else {
319 result.append(DELIM_START).append(DELIM_STOP);
320 }
321 currentArgument++;
322 }
323 i++;
324 escapeCounter = 0;
325 continue;
326 }
327 }
328 }
329
330
331 if (escapeCounter > 0) {
332 for (int j = 0; j < escapeCounter; j++) {
333 result.append(ESCAPE_CHAR);
334 }
335 escapeCounter = 0;
336 }
337 result.append(curChar);
338 }
339 }
340 return result.toString();
341 }
342
343
344
345
346
347
348
349 public static int countArgumentPlaceholders(String messagePattern) {
350 if (messagePattern == null) {
351 return 0;
352 }
353
354 int delim = messagePattern.indexOf(DELIM_START);
355
356 if (delim == -1) {
357
358 return 0;
359 }
360 int result = 0;
361 boolean isEscaped = false;
362 for (int i = 0; i < messagePattern.length(); i++) {
363 char curChar = messagePattern.charAt(i);
364 if (curChar == ESCAPE_CHAR) {
365 isEscaped = !isEscaped;
366 } else if (curChar == DELIM_START) {
367 if (!isEscaped) {
368 if (i < messagePattern.length() - 1) {
369 if (messagePattern.charAt(i + 1) == DELIM_STOP) {
370 result++;
371 i++;
372 }
373 }
374 }
375 isEscaped = false;
376 } else {
377 isEscaped = false;
378 }
379 }
380 return result;
381 }
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399 public static String deepToString(Object o) {
400 if (o == null) {
401 return null;
402 }
403 if (o instanceof String) {
404 return (String) o;
405 }
406 StringBuilder str = new StringBuilder();
407 Set<String> dejaVu = new HashSet<String>();
408 recursiveDeepToString(o, str, dejaVu);
409 return str.toString();
410 }
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433 private static void recursiveDeepToString(Object o, StringBuilder str, Set<String> dejaVu) {
434 if (o == null) {
435 str.append("null");
436 return;
437 }
438 if (o instanceof String) {
439 str.append(o);
440 return;
441 }
442
443 Class oClass = o.getClass();
444 if (oClass.isArray()) {
445 if (oClass == byte[].class) {
446 str.append(Arrays.toString((byte[]) o));
447 } else if (oClass == short[].class) {
448 str.append(Arrays.toString((short[]) o));
449 } else if (oClass == int[].class) {
450 str.append(Arrays.toString((int[]) o));
451 } else if (oClass == long[].class) {
452 str.append(Arrays.toString((long[]) o));
453 } else if (oClass == float[].class) {
454 str.append(Arrays.toString((float[]) o));
455 } else if (oClass == double[].class) {
456 str.append(Arrays.toString((double[]) o));
457 } else if (oClass == boolean[].class) {
458 str.append(Arrays.toString((boolean[]) o));
459 } else if (oClass == char[].class) {
460 str.append(Arrays.toString((char[]) o));
461 } else {
462
463 String id = identityToString(o);
464 if (dejaVu.contains(id)) {
465 str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
466 } else {
467 dejaVu.add(id);
468 Object[] oArray = (Object[]) o;
469 str.append("[");
470 boolean first = true;
471 for (Object current : oArray) {
472 if (first) {
473 first = false;
474 } else {
475 str.append(", ");
476 }
477 recursiveDeepToString(current, str, new HashSet<String>(dejaVu));
478 }
479 str.append("]");
480 }
481
482 }
483 } else if (o instanceof Map) {
484
485 String id = identityToString(o);
486 if (dejaVu.contains(id)) {
487 str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
488 } else {
489 dejaVu.add(id);
490 Map oMap = (Map) o;
491 str.append("{");
492 boolean isFirst = true;
493 for (Object o1 : oMap.entrySet()) {
494 Map.Entry current = (Map.Entry) o1;
495 if (isFirst) {
496 isFirst = false;
497 } else {
498 str.append(", ");
499 }
500 Object key = current.getKey();
501 Object value = current.getValue();
502 recursiveDeepToString(key, str, new HashSet<String>(dejaVu));
503 str.append("=");
504 recursiveDeepToString(value, str, new HashSet<String>(dejaVu));
505 }
506 str.append("}");
507 }
508 } else if (o instanceof Collection) {
509
510 String id = identityToString(o);
511 if (dejaVu.contains(id)) {
512 str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
513 } else {
514 dejaVu.add(id);
515 Collection oCol = (Collection) o;
516 str.append("[");
517 boolean isFirst = true;
518 for (Object anOCol : oCol) {
519 if (isFirst) {
520 isFirst = false;
521 } else {
522 str.append(", ");
523 }
524 recursiveDeepToString(anOCol, str, new HashSet<String>(dejaVu));
525 }
526 str.append("]");
527 }
528 } else if (o instanceof Date) {
529 Date date = (Date) o;
530 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
531
532 str.append(format.format(date));
533 } else {
534
535 try {
536 str.append(o.toString());
537 } catch (Throwable t) {
538 str.append(ERROR_PREFIX);
539 str.append(identityToString(o));
540 str.append(ERROR_SEPARATOR);
541 String msg = t.getMessage();
542 String className = t.getClass().getName();
543 str.append(className);
544 if (!className.equals(msg)) {
545 str.append(ERROR_MSG_SEPARATOR);
546 str.append(msg);
547 }
548 str.append(ERROR_SUFFIX);
549 }
550 }
551 }
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571 public static String identityToString(Object obj) {
572 if (obj == null) {
573 return null;
574 }
575 return obj.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(obj));
576 }
577
578 public String toString() {
579 return "ParameterizedMessage[messagePattern=" + messagePattern + ", stringArgs=" +
580 Arrays.toString(stringArgs) + ", throwable=" + throwable + "]";
581 }
582 }