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