1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.logging.log4j.message;
19
20 import java.util.Map;
21
22 import org.apache.logging.log4j.util.EnglishEnums;
23
24
25
26
27
28
29
30
31
32
33
34
35 public class StructuredDataMessage extends MapMessage {
36
37 private static final long serialVersionUID = 1703221292892071920L;
38 private static final int MAX_LENGTH = 32;
39 private static final int HASHVAL = 31;
40
41 private StructuredDataId id;
42
43 private String message;
44
45 private String type;
46
47
48
49
50 public enum Format {
51
52 XML,
53
54 FULL
55 }
56
57
58
59
60
61
62
63 public StructuredDataMessage(final String id, final String msg, final String type) {
64 this.id = new StructuredDataId(id, null, null);
65 this.message = msg;
66 this.type = type;
67 }
68
69
70
71
72
73
74
75
76 public StructuredDataMessage(final String id, final String msg, final String type,
77 final Map<String, String> data) {
78 super(data);
79 this.id = new StructuredDataId(id, null, null);
80 this.message = msg;
81 this.type = type;
82 }
83
84
85
86
87
88
89
90 public StructuredDataMessage(final StructuredDataId id, final String msg, final String type) {
91 this.id = id;
92 this.message = msg;
93 this.type = type;
94 }
95
96
97
98
99
100
101
102
103
104 public StructuredDataMessage(final StructuredDataId id, final String msg, final String type,
105 final Map<String, String> data) {
106 super(data);
107 this.id = id;
108 this.message = msg;
109 this.type = type;
110 }
111
112
113
114
115
116
117
118 private StructuredDataMessage(final StructuredDataMessage msg, final Map<String, String> map) {
119 super(map);
120 this.id = msg.id;
121 this.message = msg.message;
122 this.type = msg.type;
123 }
124
125
126
127
128
129 protected StructuredDataMessage() {
130
131 }
132
133
134
135
136
137
138
139 @Override
140 public StructuredDataMessage with(final String key, final String value) {
141 put(key, value);
142 return this;
143 }
144
145
146
147
148
149 @Override
150 public String[] getFormats() {
151 final String[] formats = new String[Format.values().length];
152 int i = 0;
153 for (final Format format : Format.values()) {
154 formats[i++] = format.name();
155 }
156 return formats;
157 }
158
159
160
161
162
163 public StructuredDataId getId() {
164 return id;
165 }
166
167
168
169
170
171 protected void setId(final String id) {
172 this.id = new StructuredDataId(id, null, null);
173 }
174
175
176
177
178
179 protected void setId(final StructuredDataId id) {
180 this.id = id;
181 }
182
183
184
185
186
187 public String getType() {
188 return type;
189 }
190
191 protected void setType(final String type) {
192 if (type.length() > MAX_LENGTH) {
193 throw new IllegalArgumentException("structured data type exceeds maximum length of 32 characters: " + type);
194 }
195 this.type = type;
196 }
197
198
199
200
201
202 @Override
203 public String getFormat() {
204 return message;
205 }
206
207 protected void setMessageFormat(final String msg) {
208 this.message = msg;
209 }
210
211
212 @Override
213 protected void validate(final String key, final String value) {
214 validateKey(key);
215 }
216
217 private void validateKey(final String key) {
218 if (key.length() > MAX_LENGTH) {
219 throw new IllegalArgumentException("Structured data keys are limited to 32 characters. key: " + key);
220 }
221 final char[] chars = key.toCharArray();
222 for (final char c : chars) {
223 if (c < '!' || c > '~' || c == '=' || c == ']' || c == '"') {
224 throw new IllegalArgumentException("Structured data keys must contain printable US ASCII characters" +
225 "and may not contain a space, =, ], or \"");
226 }
227 }
228 }
229
230
231
232
233
234
235 @Override
236 public String asString() {
237 return asString(Format.FULL, null);
238 }
239
240
241
242
243
244
245
246
247 @Override
248 public String asString(final String format) {
249 try {
250 return asString(EnglishEnums.valueOf(Format.class, format), null);
251 } catch (final IllegalArgumentException ex) {
252 return asString();
253 }
254 }
255
256
257
258
259
260
261
262
263
264
265 public final String asString(final Format format, final StructuredDataId structuredDataId) {
266 final StringBuilder sb = new StringBuilder();
267 final boolean full = Format.FULL.equals(format);
268 if (full) {
269 final String myType = getType();
270 if (myType == null) {
271 return sb.toString();
272 }
273 sb.append(getType()).append(' ');
274 }
275 StructuredDataId sdId = getId();
276 if (sdId != null) {
277 sdId = sdId.makeId(structuredDataId);
278 } else {
279 sdId = structuredDataId;
280 }
281 if (sdId == null || sdId.getName() == null) {
282 return sb.toString();
283 }
284 sb.append('[');
285 sb.append(sdId);
286 sb.append(' ');
287 appendMap(sb);
288 sb.append(']');
289 if (full) {
290 final String msg = getFormat();
291 if (msg != null) {
292 sb.append(' ').append(msg);
293 }
294 }
295 return sb.toString();
296 }
297
298
299
300
301
302 @Override
303 public String getFormattedMessage() {
304 return asString(Format.FULL, null);
305 }
306
307
308
309
310
311
312
313
314
315
316 @Override
317 public String getFormattedMessage(final String[] formats) {
318 if (formats != null && formats.length > 0) {
319 for (final String format : formats) {
320 if (Format.XML.name().equalsIgnoreCase(format)) {
321 return asXml();
322 } else if (Format.FULL.name().equalsIgnoreCase(format)) {
323 return asString(Format.FULL, null);
324 }
325 }
326 return asString(null, null);
327 }
328 return asString(Format.FULL, null);
329 }
330
331 private String asXml() {
332 final StringBuilder sb = new StringBuilder();
333 final StructuredDataId sdId = getId();
334 if (sdId == null || sdId.getName() == null || type == null) {
335 return sb.toString();
336 }
337 sb.append("<StructuredData>\n");
338 sb.append("<type>").append(type).append("</type>\n");
339 sb.append("<id>").append(sdId).append("</id>\n");
340 super.asXml(sb);
341 sb.append("</StructuredData>\n");
342 return sb.toString();
343 }
344
345 @Override
346 public String toString() {
347 return asString(null, null);
348 }
349
350
351 @Override
352 public MapMessage newInstance(final Map<String, String> map) {
353 return new StructuredDataMessage(this, map);
354 }
355
356 @Override
357 public boolean equals(final Object o) {
358 if (this == o) {
359 return true;
360 }
361 if (o == null || getClass() != o.getClass()) {
362 return false;
363 }
364
365 final StructuredDataMessage that = (StructuredDataMessage) o;
366
367 if (!super.equals(o)) {
368 return false;
369 }
370 if (type != null ? !type.equals(that.type) : that.type != null) {
371 return false;
372 }
373 if (id != null ? !id.equals(that.id) : that.id != null) {
374 return false;
375 }
376 if (message != null ? !message.equals(that.message) : that.message != null) {
377 return false;
378 }
379
380 return true;
381 }
382
383 @Override
384 public int hashCode() {
385 int result = super.hashCode();
386 result = HASHVAL * result + (type != null ? type.hashCode() : 0);
387 result = HASHVAL * result + (id != null ? id.hashCode() : 0);
388 result = HASHVAL * result + (message != null ? message.hashCode() : 0);
389 return result;
390 }
391 }