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.util.AbstractMap;
20 import java.util.Collections;
21 import java.util.Map;
22 import java.util.TreeMap;
23
24 import org.apache.logging.log4j.util.BiConsumer;
25 import org.apache.logging.log4j.util.Chars;
26 import org.apache.logging.log4j.util.EnglishEnums;
27 import org.apache.logging.log4j.util.IndexedReadOnlyStringMap;
28 import org.apache.logging.log4j.util.IndexedStringMap;
29 import org.apache.logging.log4j.util.MultiFormatStringBuilderFormattable;
30 import org.apache.logging.log4j.util.PerformanceSensitive;
31 import org.apache.logging.log4j.util.ReadOnlyStringMap;
32 import org.apache.logging.log4j.util.SortedArrayStringMap;
33 import org.apache.logging.log4j.util.StringBuilders;
34 import org.apache.logging.log4j.util.Strings;
35 import org.apache.logging.log4j.util.TriConsumer;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 @PerformanceSensitive("allocation")
52 @AsynchronouslyFormattable
53 public class MapMessage<M extends MapMessage<M, V>, V> implements MultiFormatStringBuilderFormattable {
54
55 private static final long serialVersionUID = -5031471831131487120L;
56
57
58
59
60 public enum MapFormat {
61
62
63 XML,
64
65
66 JSON,
67
68
69 JAVA,
70
71
72
73
74
75
76 JAVA_UNQUOTED;
77
78
79
80
81
82
83
84 public static MapFormat lookupIgnoreCase(final String format) {
85 return XML.name().equalsIgnoreCase(format) ? XML
86 : JSON.name().equalsIgnoreCase(format) ? JSON
87 : JAVA.name().equalsIgnoreCase(format) ? JAVA
88 : JAVA_UNQUOTED.name().equalsIgnoreCase(format) ? JAVA_UNQUOTED
89 : null;
90 }
91
92
93
94
95
96
97 public static String[] names() {
98 return new String[] {XML.name(), JSON.name(), JAVA.name(), JAVA_UNQUOTED.name()};
99 }
100 }
101
102 private final IndexedStringMap data;
103
104
105
106
107 public MapMessage() {
108 this.data = new SortedArrayStringMap();
109 }
110
111
112
113
114
115
116 public MapMessage(final int initialCapacity) {
117 this.data = new SortedArrayStringMap(initialCapacity);
118 }
119
120
121
122
123
124 public MapMessage(final Map<String, V> map) {
125 this.data = new SortedArrayStringMap(map);
126 }
127
128 @Override
129 public String[] getFormats() {
130 return MapFormat.names();
131 }
132
133
134
135
136
137 @Override
138 public Object[] getParameters() {
139 final Object[] result = new Object[data.size()];
140 for (int i = 0; i < data.size(); i++) {
141 result[i] = data.getValueAt(i);
142 }
143 return result;
144 }
145
146
147
148
149
150 @Override
151 public String getFormat() {
152 return Strings.EMPTY;
153 }
154
155
156
157
158
159 @SuppressWarnings("unchecked")
160 public Map<String, V> getData() {
161 final TreeMap<String, V> result = new TreeMap<>();
162 for (int i = 0; i < data.size(); i++) {
163
164 result.put(data.getKeyAt(i), (V) data.getValueAt(i));
165 }
166 return Collections.unmodifiableMap(result);
167 }
168
169
170
171
172
173 public IndexedReadOnlyStringMap getIndexedReadOnlyStringMap() {
174 return data;
175 }
176
177
178
179
180 public void clear() {
181 data.clear();
182 }
183
184
185
186
187
188
189
190
191 public boolean containsKey(final String key) {
192 return data.containsKey(key);
193 }
194
195
196
197
198
199
200 public void put(final String candidateKey, final String value) {
201 if (value == null) {
202 throw new IllegalArgumentException("No value provided for key " + candidateKey);
203 }
204 final String key = toKey(candidateKey);
205 validate(key, value);
206 data.putValue(key, value);
207 }
208
209
210
211
212
213 public void putAll(final Map<String, String> map) {
214 for (final Map.Entry<String, String> entry : map.entrySet()) {
215 data.putValue(entry.getKey(), entry.getValue());
216 }
217 }
218
219
220
221
222
223
224 public String get(final String key) {
225 final Object result = data.getValue(key);
226 return ParameterFormatter.deepToString(result);
227 }
228
229
230
231
232
233
234 public String remove(final String key) {
235 final String result = get(key);
236 data.remove(key);
237 return result;
238 }
239
240
241
242
243
244
245 public String asString() {
246 return format((MapFormat) null, new StringBuilder()).toString();
247 }
248
249
250
251
252
253
254
255 public String asString(final String format) {
256 try {
257 return format(EnglishEnums.valueOf(MapFormat.class, format), new StringBuilder()).toString();
258 } catch (final IllegalArgumentException ex) {
259 return asString();
260 }
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281 public <CV> void forEach(final BiConsumer<String, ? super CV> action) {
282 data.forEach(action);
283 }
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310 public <CV, S> void forEach(final TriConsumer<String, ? super CV, S> action, final S state) {
311 data.forEach(action, state);
312 }
313
314
315
316
317
318
319
320 private StringBuilder format(final MapFormat format, final StringBuilder sb) {
321 if (format == null) {
322 appendMap(sb);
323 } else {
324 switch (format) {
325 case XML : {
326 asXml(sb);
327 break;
328 }
329 case JSON : {
330 asJson(sb);
331 break;
332 }
333 case JAVA : {
334 asJava(sb);
335 break;
336 }
337 case JAVA_UNQUOTED:
338 asJavaUnquoted(sb);
339 break;
340 default : {
341 appendMap(sb);
342 }
343 }
344 }
345 return sb;
346 }
347
348
349
350
351
352
353 public void asXml(final StringBuilder sb) {
354 sb.append("<Map>\n");
355 for (int i = 0; i < data.size(); i++) {
356 sb.append(" <Entry key=\"")
357 .append(data.getKeyAt(i))
358 .append("\">");
359 final int size = sb.length();
360 ParameterFormatter.recursiveDeepToString(data.getValueAt(i), sb);
361 StringBuilders.escapeXml(sb, size);
362 sb.append("</Entry>\n");
363 }
364 sb.append("</Map>");
365 }
366
367
368
369
370
371 @Override
372 public String getFormattedMessage() {
373 return asString();
374 }
375
376
377
378
379
380
381
382
383
384
385
386 @Override
387 public String getFormattedMessage(final String[] formats) {
388 return format(getFormat(formats), new StringBuilder()).toString();
389 }
390
391 private MapFormat getFormat(final String[] formats) {
392 if (formats == null || formats.length == 0) {
393 return null;
394 }
395 for (int i = 0; i < formats.length; i++) {
396 final MapFormat mapFormat = MapFormat.lookupIgnoreCase(formats[i]);
397 if (mapFormat != null) {
398 return mapFormat;
399 }
400 }
401 return null;
402 }
403
404 protected void appendMap(final StringBuilder sb) {
405 for (int i = 0; i < data.size(); i++) {
406 if (i > 0) {
407 sb.append(' ');
408 }
409 sb.append(data.getKeyAt(i)).append(Chars.EQ).append(Chars.DQUOTE);
410 ParameterFormatter.recursiveDeepToString(data.getValueAt(i), sb);
411 sb.append(Chars.DQUOTE);
412 }
413 }
414
415 protected void asJson(final StringBuilder sb) {
416 MapMessageJsonFormatter.format(sb, data);
417 }
418
419 protected void asJavaUnquoted(final StringBuilder sb) {
420 asJava(sb, false);
421 }
422
423 protected void asJava(final StringBuilder sb) {
424 asJava(sb, true);
425 }
426
427 private void asJava(final StringBuilder sb, boolean quoted) {
428 sb.append('{');
429 for (int i = 0; i < data.size(); i++) {
430 if (i > 0) {
431 sb.append(", ");
432 }
433 sb.append(data.getKeyAt(i)).append(Chars.EQ);
434 if (quoted) {
435 sb.append(Chars.DQUOTE);
436 }
437 ParameterFormatter.recursiveDeepToString(data.getValueAt(i), sb);
438 if (quoted) {
439 sb.append(Chars.DQUOTE);
440 }
441 }
442 sb.append('}');
443 }
444
445
446
447
448
449
450 @SuppressWarnings("unchecked")
451 public M newInstance(final Map<String, V> map) {
452 return (M) new MapMessage<>(map);
453 }
454
455 @Override
456 public String toString() {
457 return asString();
458 }
459
460 @Override
461 public void formatTo(final StringBuilder buffer) {
462 format((MapFormat) null, buffer);
463 }
464
465 @Override
466 public void formatTo(final String[] formats, final StringBuilder buffer) {
467 format(getFormat(formats), buffer);
468 }
469
470 @Override
471 public boolean equals(final Object o) {
472 if (this == o) {
473 return true;
474 }
475 if (o == null || this.getClass() != o.getClass()) {
476 return false;
477 }
478
479 final MapMessage<?, ?> that = (MapMessage<?, ?>) o;
480
481 return this.data.equals(that.data);
482 }
483
484 @Override
485 public int hashCode() {
486 return data.hashCode();
487 }
488
489
490
491
492
493
494 @Override
495 public Throwable getThrowable() {
496 return null;
497 }
498
499
500
501
502
503
504 protected void validate(final String key, final boolean value) {
505
506 }
507
508
509
510
511
512
513 protected void validate(final String key, final byte value) {
514
515 }
516
517
518
519
520
521
522 protected void validate(final String key, final char value) {
523
524 }
525
526
527
528
529
530
531 protected void validate(final String key, final double value) {
532
533 }
534
535
536
537
538
539
540 protected void validate(final String key, final float value) {
541
542 }
543
544
545
546
547
548
549 protected void validate(final String key, final int value) {
550
551 }
552
553
554
555
556
557
558 protected void validate(final String key, final long value) {
559
560 }
561
562
563
564
565
566
567 protected void validate(final String key, final Object value) {
568
569 }
570
571
572
573
574
575
576 protected void validate(final String key, final short value) {
577
578 }
579
580
581
582
583
584
585 protected void validate(final String key, final String value) {
586
587 }
588
589
590
591
592
593
594
595
596 protected String toKey(final String candidateKey) {
597 return candidateKey;
598 }
599
600
601
602
603
604
605
606
607 @SuppressWarnings("unchecked")
608 public M with(final String candidateKey, final boolean value) {
609 final String key = toKey(candidateKey);
610 validate(key, value);
611 data.putValue(key, value);
612 return (M) this;
613 }
614
615
616
617
618
619
620
621
622 @SuppressWarnings("unchecked")
623 public M with(final String candidateKey, final byte value) {
624 final String key = toKey(candidateKey);
625 validate(key, value);
626 data.putValue(key, value);
627 return (M) this;
628 }
629
630
631
632
633
634
635
636
637 @SuppressWarnings("unchecked")
638 public M with(final String candidateKey, final char value) {
639 final String key = toKey(candidateKey);
640 validate(key, value);
641 data.putValue(key, value);
642 return (M) this;
643 }
644
645
646
647
648
649
650
651
652
653 @SuppressWarnings("unchecked")
654 public M with(final String candidateKey, final double value) {
655 final String key = toKey(candidateKey);
656 validate(key, value);
657 data.putValue(key, value);
658 return (M) this;
659 }
660
661
662
663
664
665
666
667
668 @SuppressWarnings("unchecked")
669 public M with(final String candidateKey, final float value) {
670 final String key = toKey(candidateKey);
671 validate(key, value);
672 data.putValue(key, value);
673 return (M) this;
674 }
675
676
677
678
679
680
681
682
683 @SuppressWarnings("unchecked")
684 public M with(final String candidateKey, final int value) {
685 final String key = toKey(candidateKey);
686 validate(key, value);
687 data.putValue(key, value);
688 return (M) this;
689 }
690
691
692
693
694
695
696
697
698 @SuppressWarnings("unchecked")
699 public M with(final String candidateKey, final long value) {
700 final String key = toKey(candidateKey);
701 validate(key, value);
702 data.putValue(key, value);
703 return (M) this;
704 }
705
706
707
708
709
710
711
712
713 @SuppressWarnings("unchecked")
714 public M with(final String candidateKey, final Object value) {
715 final String key = toKey(candidateKey);
716 validate(key, value);
717 data.putValue(key, value);
718 return (M) this;
719 }
720
721
722
723
724
725
726
727
728 @SuppressWarnings("unchecked")
729 public M with(final String candidateKey, final short value) {
730 final String key = toKey(candidateKey);
731 validate(key, value);
732 data.putValue(key, value);
733 return (M) this;
734 }
735
736
737
738
739
740
741
742 @SuppressWarnings("unchecked")
743 public M with(final String candidateKey, final String value) {
744 final String key = toKey(candidateKey);
745 put(key, value);
746 return (M) this;
747 }
748
749 }