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, null);
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, null);
411 sb.append(Chars.DQUOTE);
412 }
413 }
414
415 protected void asJson(final StringBuilder sb) {
416 sb.append('{');
417 for (int i = 0; i < data.size(); i++) {
418 if (i > 0) {
419 sb.append(", ");
420 }
421 sb.append(Chars.DQUOTE);
422 int start = sb.length();
423 sb.append(data.getKeyAt(i));
424 StringBuilders.escapeJson(sb, start);
425 sb.append(Chars.DQUOTE).append(':').append(Chars.DQUOTE);
426 start = sb.length();
427 ParameterFormatter.recursiveDeepToString(data.getValueAt(i), sb, null);
428 StringBuilders.escapeJson(sb, start);
429 sb.append(Chars.DQUOTE);
430 }
431 sb.append('}');
432 }
433
434 protected void asJavaUnquoted(final StringBuilder sb) {
435 asJava(sb, false);
436 }
437
438 protected void asJava(final StringBuilder sb) {
439 asJava(sb, true);
440 }
441
442 private void asJava(final StringBuilder sb, boolean quoted) {
443 sb.append('{');
444 for (int i = 0; i < data.size(); i++) {
445 if (i > 0) {
446 sb.append(", ");
447 }
448 sb.append(data.getKeyAt(i)).append(Chars.EQ);
449 if (quoted) {
450 sb.append(Chars.DQUOTE);
451 }
452 ParameterFormatter.recursiveDeepToString(data.getValueAt(i), sb, null);
453 if (quoted) {
454 sb.append(Chars.DQUOTE);
455 }
456 }
457 sb.append('}');
458 }
459
460
461
462
463
464
465 @SuppressWarnings("unchecked")
466 public M newInstance(final Map<String, V> map) {
467 return (M) new MapMessage<>(map);
468 }
469
470 @Override
471 public String toString() {
472 return asString();
473 }
474
475 @Override
476 public void formatTo(final StringBuilder buffer) {
477 format((MapFormat) null, buffer);
478 }
479
480 @Override
481 public void formatTo(final String[] formats, final StringBuilder buffer) {
482 format(getFormat(formats), buffer);
483 }
484
485 @Override
486 public boolean equals(final Object o) {
487 if (this == o) {
488 return true;
489 }
490 if (o == null || this.getClass() != o.getClass()) {
491 return false;
492 }
493
494 final MapMessage<?, ?> that = (MapMessage<?, ?>) o;
495
496 return this.data.equals(that.data);
497 }
498
499 @Override
500 public int hashCode() {
501 return data.hashCode();
502 }
503
504
505
506
507
508
509 @Override
510 public Throwable getThrowable() {
511 return null;
512 }
513
514
515
516
517
518
519 protected void validate(final String key, final boolean value) {
520
521 }
522
523
524
525
526
527
528 protected void validate(final String key, final byte value) {
529
530 }
531
532
533
534
535
536
537 protected void validate(final String key, final char value) {
538
539 }
540
541
542
543
544
545
546 protected void validate(final String key, final double value) {
547
548 }
549
550
551
552
553
554
555 protected void validate(final String key, final float value) {
556
557 }
558
559
560
561
562
563
564 protected void validate(final String key, final int value) {
565
566 }
567
568
569
570
571
572
573 protected void validate(final String key, final long value) {
574
575 }
576
577
578
579
580
581
582 protected void validate(final String key, final Object value) {
583
584 }
585
586
587
588
589
590
591 protected void validate(final String key, final short value) {
592
593 }
594
595
596
597
598
599
600 protected void validate(final String key, final String value) {
601
602 }
603
604
605
606
607
608
609
610
611 protected String toKey(final String candidateKey) {
612 return candidateKey;
613 }
614
615
616
617
618
619
620
621
622 @SuppressWarnings("unchecked")
623 public M with(final String candidateKey, final boolean 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 byte 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 @SuppressWarnings("unchecked")
653 public M with(final String candidateKey, final char value) {
654 final String key = toKey(candidateKey);
655 validate(key, value);
656 data.putValue(key, value);
657 return (M) this;
658 }
659
660
661
662
663
664
665
666
667
668 @SuppressWarnings("unchecked")
669 public M with(final String candidateKey, final double 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 float 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 int 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 long 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 Object 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
743 @SuppressWarnings("unchecked")
744 public M with(final String candidateKey, final short value) {
745 final String key = toKey(candidateKey);
746 validate(key, value);
747 data.putValue(key, value);
748 return (M) this;
749 }
750
751
752
753
754
755
756
757 @SuppressWarnings("unchecked")
758 public M with(final String candidateKey, final String value) {
759 final String key = toKey(candidateKey);
760 put(key, value);
761 return (M) this;
762 }
763
764 }