1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.pattern;
19
20 import org.apache.log4j.helpers.Loader;
21 import org.apache.log4j.helpers.LogLog;
22
23 import java.lang.reflect.Method;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public final class PatternParser {
49
50
51
52 private static final char ESCAPE_CHAR = '%';
53
54
55
56
57 private static final int LITERAL_STATE = 0;
58
59
60
61
62 private static final int CONVERTER_STATE = 1;
63
64
65
66
67 private static final int DOT_STATE = 3;
68
69
70
71
72 private static final int MIN_STATE = 4;
73
74
75
76
77 private static final int MAX_STATE = 5;
78
79
80
81
82 private static final Map PATTERN_LAYOUT_RULES;
83
84
85
86
87 private static final Map FILENAME_PATTERN_RULES;
88
89 static {
90
91 Map rules = new HashMap(17);
92 rules.put("c", LoggerPatternConverter.class);
93 rules.put("logger", LoggerPatternConverter.class);
94
95 rules.put("C", ClassNamePatternConverter.class);
96 rules.put("class", ClassNamePatternConverter.class);
97
98 rules.put("d", DatePatternConverter.class);
99 rules.put("date", DatePatternConverter.class);
100
101 rules.put("F", FileLocationPatternConverter.class);
102 rules.put("file", FileLocationPatternConverter.class);
103
104 rules.put("l", FullLocationPatternConverter.class);
105
106 rules.put("L", LineLocationPatternConverter.class);
107 rules.put("line", LineLocationPatternConverter.class);
108
109 rules.put("m", MessagePatternConverter.class);
110 rules.put("message", MessagePatternConverter.class);
111
112 rules.put("n", LineSeparatorPatternConverter.class);
113
114 rules.put("M", MethodLocationPatternConverter.class);
115 rules.put("method", MethodLocationPatternConverter.class);
116
117 rules.put("p", LevelPatternConverter.class);
118 rules.put("level", LevelPatternConverter.class);
119
120 rules.put("r", RelativeTimePatternConverter.class);
121 rules.put("relative", RelativeTimePatternConverter.class);
122
123 rules.put("t", ThreadPatternConverter.class);
124 rules.put("thread", ThreadPatternConverter.class);
125
126 rules.put("x", NDCPatternConverter.class);
127 rules.put("ndc", NDCPatternConverter.class);
128
129 rules.put("X", PropertiesPatternConverter.class);
130 rules.put("properties", PropertiesPatternConverter.class);
131
132 rules.put("sn", SequenceNumberPatternConverter.class);
133 rules.put("sequenceNumber", SequenceNumberPatternConverter.class);
134
135 rules.put("throwable", ThrowableInformationPatternConverter.class);
136 PATTERN_LAYOUT_RULES = new ReadOnlyMap(rules);
137
138 Map fnameRules = new HashMap(4);
139 fnameRules.put("d", FileDatePatternConverter.class);
140 fnameRules.put("date", FileDatePatternConverter.class);
141 fnameRules.put("i", IntegerPatternConverter.class);
142 fnameRules.put("index", IntegerPatternConverter.class);
143
144 FILENAME_PATTERN_RULES = new ReadOnlyMap(fnameRules);
145 }
146
147
148
149
150 private PatternParser() {
151 }
152
153
154
155
156
157 public static Map getPatternLayoutRules() {
158 return PATTERN_LAYOUT_RULES;
159 }
160
161
162
163
164
165 public static Map getFileNamePatternRules() {
166 return FILENAME_PATTERN_RULES;
167 }
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184 private static int extractConverter(
185 char lastChar, final String pattern, int i, final StringBuffer convBuf,
186 final StringBuffer currentLiteral) {
187 convBuf.setLength(0);
188
189
190
191
192
193
194 if (!Character.isUnicodeIdentifierStart(lastChar)) {
195 return i;
196 }
197
198 convBuf.append(lastChar);
199
200 while (
201 (i < pattern.length())
202 && Character.isUnicodeIdentifierPart(pattern.charAt(i))) {
203 convBuf.append(pattern.charAt(i));
204 currentLiteral.append(pattern.charAt(i));
205
206
207 i++;
208 }
209
210 return i;
211 }
212
213
214
215
216
217
218
219
220 private static int extractOptions(String pattern, int i, List options) {
221 while ((i < pattern.length()) && (pattern.charAt(i) == '{')) {
222 int end = pattern.indexOf('}', i);
223
224 if (end == -1) {
225 break;
226 }
227
228 String r = pattern.substring(i + 1, end);
229 options.add(r);
230 i = end + 1;
231 }
232
233 return i;
234 }
235
236
237
238
239
240
241
242
243
244 public static void parse(
245 final String pattern, final List patternConverters,
246 final List formattingInfos, final Map converterRegistry, final Map rules) {
247 if (pattern == null) {
248 throw new NullPointerException("pattern");
249 }
250
251 StringBuffer currentLiteral = new StringBuffer(32);
252
253 int patternLength = pattern.length();
254 int state = LITERAL_STATE;
255 char c;
256 int i = 0;
257 FormattingInfo formattingInfo = FormattingInfo.getDefault();
258
259 while (i < patternLength) {
260 c = pattern.charAt(i++);
261
262 switch (state) {
263 case LITERAL_STATE:
264
265
266 if (i == patternLength) {
267 currentLiteral.append(c);
268
269 continue;
270 }
271
272 if (c == ESCAPE_CHAR) {
273
274 switch (pattern.charAt(i)) {
275 case ESCAPE_CHAR:
276 currentLiteral.append(c);
277 i++;
278
279 break;
280
281 default:
282
283 if (currentLiteral.length() != 0) {
284 patternConverters.add(
285 new LiteralPatternConverter(currentLiteral.toString()));
286 formattingInfos.add(FormattingInfo.getDefault());
287 }
288
289 currentLiteral.setLength(0);
290 currentLiteral.append(c);
291 state = CONVERTER_STATE;
292 formattingInfo = FormattingInfo.getDefault();
293 }
294 } else {
295 currentLiteral.append(c);
296 }
297
298 break;
299
300 case CONVERTER_STATE:
301 currentLiteral.append(c);
302
303 switch (c) {
304 case '-':
305 formattingInfo =
306 new FormattingInfo(
307 true, formattingInfo.getMinLength(),
308 formattingInfo.getMaxLength());
309
310 break;
311
312 case '.':
313 state = DOT_STATE;
314
315 break;
316
317 default:
318
319 if ((c >= '0') && (c <= '9')) {
320 formattingInfo =
321 new FormattingInfo(
322 formattingInfo.isLeftAligned(), c - '0',
323 formattingInfo.getMaxLength());
324 state = MIN_STATE;
325 } else {
326 i = finalizeConverter(
327 c, pattern, i, currentLiteral, formattingInfo,
328 converterRegistry, rules, patternConverters, formattingInfos);
329
330
331 state = LITERAL_STATE;
332 formattingInfo = FormattingInfo.getDefault();
333 currentLiteral.setLength(0);
334 }
335 }
336
337 break;
338
339 case MIN_STATE:
340 currentLiteral.append(c);
341
342 if ((c >= '0') && (c <= '9')) {
343 formattingInfo =
344 new FormattingInfo(
345 formattingInfo.isLeftAligned(),
346 (formattingInfo.getMinLength() * 10) + (c - '0'),
347 formattingInfo.getMaxLength());
348 } else if (c == '.') {
349 state = DOT_STATE;
350 } else {
351 i = finalizeConverter(
352 c, pattern, i, currentLiteral, formattingInfo,
353 converterRegistry, rules, patternConverters, formattingInfos);
354 state = LITERAL_STATE;
355 formattingInfo = FormattingInfo.getDefault();
356 currentLiteral.setLength(0);
357 }
358
359 break;
360
361 case DOT_STATE:
362 currentLiteral.append(c);
363
364 if ((c >= '0') && (c <= '9')) {
365 formattingInfo =
366 new FormattingInfo(
367 formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
368 c - '0');
369 state = MAX_STATE;
370 } else {
371 LogLog.error(
372 "Error occured in position " + i
373 + ".\n Was expecting digit, instead got char \"" + c + "\".");
374
375 state = LITERAL_STATE;
376 }
377
378 break;
379
380 case MAX_STATE:
381 currentLiteral.append(c);
382
383 if ((c >= '0') && (c <= '9')) {
384 formattingInfo =
385 new FormattingInfo(
386 formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
387 (formattingInfo.getMaxLength() * 10) + (c - '0'));
388 } else {
389 i = finalizeConverter(
390 c, pattern, i, currentLiteral, formattingInfo,
391 converterRegistry, rules, patternConverters, formattingInfos);
392 state = LITERAL_STATE;
393 formattingInfo = FormattingInfo.getDefault();
394 currentLiteral.setLength(0);
395 }
396
397 break;
398 }
399 }
400
401
402 if (currentLiteral.length() != 0) {
403 patternConverters.add(
404 new LiteralPatternConverter(currentLiteral.toString()));
405 formattingInfos.add(FormattingInfo.getDefault());
406 }
407 }
408
409
410
411
412
413
414
415
416
417
418
419
420
421 private static PatternConverter createConverter(
422 final String converterId, final StringBuffer currentLiteral,
423 final Map converterRegistry, final Map rules, final List options) {
424 String converterName = converterId;
425 Object converterObj = null;
426
427 for (int i = converterId.length(); (i > 0) && (converterObj == null);
428 i--) {
429 converterName = converterName.substring(0, i);
430
431 if (converterRegistry != null) {
432 converterObj = converterRegistry.get(converterName);
433 }
434
435 if ((converterObj == null) && (rules != null)) {
436 converterObj = rules.get(converterName);
437 }
438 }
439
440 if (converterObj == null) {
441 LogLog.error("Unrecognized format specifier [" + converterId + "]");
442
443 return null;
444 }
445
446 Class converterClass = null;
447
448 if (converterObj instanceof Class) {
449 converterClass = (Class) converterObj;
450 } else {
451 if (converterObj instanceof String) {
452 try {
453 converterClass = Loader.loadClass((String) converterObj);
454 } catch (ClassNotFoundException ex) {
455 LogLog.warn(
456 "Class for conversion pattern %" + converterName + " not found",
457 ex);
458
459 return null;
460 }
461 } else {
462 LogLog.warn(
463 "Bad map entry for conversion pattern %" + converterName + ".");
464
465 return null;
466 }
467 }
468
469 try {
470 Method factory =
471 converterClass.getMethod(
472 "newInstance",
473 new Class[] {
474 Class.forName("[Ljava.lang.String;")
475 });
476 String[] optionsArray = new String[options.size()];
477 optionsArray = (String[]) options.toArray(optionsArray);
478
479 Object newObj =
480 factory.invoke(null, new Object[] { optionsArray });
481
482 if (newObj instanceof PatternConverter) {
483 currentLiteral.delete(
484 0,
485 currentLiteral.length()
486 - (converterId.length() - converterName.length()));
487
488 return (PatternConverter) newObj;
489 } else {
490 LogLog.warn(
491 "Class " + converterClass.getName()
492 + " does not extend PatternConverter.");
493 }
494 } catch (Exception ex) {
495 LogLog.error("Error creating converter for " + converterId, ex);
496
497 try {
498
499
500 PatternConverter pc = (PatternConverter) converterClass.newInstance();
501 currentLiteral.delete(
502 0,
503 currentLiteral.length()
504 - (converterId.length() - converterName.length()));
505
506 return pc;
507 } catch (Exception ex2) {
508 LogLog.error("Error creating converter for " + converterId, ex2);
509 }
510 }
511
512 return null;
513 }
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529 private static int finalizeConverter(
530 char c, String pattern, int i,
531 final StringBuffer currentLiteral, final FormattingInfo formattingInfo,
532 final Map converterRegistry, final Map rules, final List patternConverters,
533 final List formattingInfos) {
534 StringBuffer convBuf = new StringBuffer();
535 i = extractConverter(c, pattern, i, convBuf, currentLiteral);
536
537 String converterId = convBuf.toString();
538
539 List options = new ArrayList();
540 i = extractOptions(pattern, i, options);
541
542 PatternConverter pc =
543 createConverter(
544 converterId, currentLiteral, converterRegistry, rules, options);
545
546 if (pc == null) {
547 StringBuffer msg;
548
549 if ((converterId == null) || (converterId.length() == 0)) {
550 msg =
551 new StringBuffer("Empty conversion specifier starting at position ");
552 } else {
553 msg = new StringBuffer("Unrecognized conversion specifier [");
554 msg.append(converterId);
555 msg.append("] starting at position ");
556 }
557
558 msg.append(Integer.toString(i));
559 msg.append(" in conversion pattern.");
560
561 LogLog.error(msg.toString());
562
563 patternConverters.add(
564 new LiteralPatternConverter(currentLiteral.toString()));
565 formattingInfos.add(FormattingInfo.getDefault());
566 } else {
567 patternConverters.add(pc);
568 formattingInfos.add(formattingInfo);
569
570 if (currentLiteral.length() > 0) {
571 patternConverters.add(
572 new LiteralPatternConverter(currentLiteral.toString()));
573 formattingInfos.add(FormattingInfo.getDefault());
574 }
575 }
576
577 currentLiteral.setLength(0);
578
579 return i;
580 }
581
582
583
584
585 private static class ReadOnlyMap implements Map {
586
587
588
589 private final Map map;
590
591
592
593
594
595 public ReadOnlyMap(Map src) {
596 map = src;
597 }
598
599
600
601
602 public void clear() {
603 throw new UnsupportedOperationException();
604 }
605
606
607
608
609 public boolean containsKey(Object key) {
610 return map.containsKey(key);
611 }
612
613
614
615
616 public boolean containsValue(Object value) {
617 return map.containsValue(value);
618 }
619
620
621
622
623 public Set entrySet() {
624 return map.entrySet();
625 }
626
627
628
629
630 public Object get(Object key) {
631 return map.get(key);
632 }
633
634
635
636
637 public boolean isEmpty() {
638 return map.isEmpty();
639 }
640
641
642
643
644 public Set keySet() {
645 return map.keySet();
646 }
647
648
649
650
651 public Object put(Object key, Object value) {
652 throw new UnsupportedOperationException();
653 }
654
655
656
657
658 public void putAll(Map t) {
659 throw new UnsupportedOperationException();
660 }
661
662
663
664
665 public Object remove(Object key) {
666 throw new UnsupportedOperationException();
667 }
668
669
670
671
672 public int size() {
673 return map.size();
674 }
675
676
677
678
679 public Collection values() {
680 return map.values();
681 }
682 }
683 }