JAL-3032 adds Java 8 functionality (1/2)
[jalview.git] / srcjar2 / org / apache / log4j / helpers / PatternParser.java
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  * 
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  * 
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package org.apache.log4j.helpers;
18
19 import org.apache.log4j.Layout;
20 import org.apache.log4j.spi.LoggingEvent;
21 import org.apache.log4j.spi.LocationInfo;
22 import java.text.DateFormat;
23 import java.text.SimpleDateFormat;
24 import java.util.Date;
25 import java.util.Map;
26 import java.util.Arrays;
27
28 // Contributors:   Nelson Minar <(nelson@monkey.org>
29 //                 Igor E. Poteryaev <jah@mail.ru>
30 //                 Reinhard Deschler <reinhard.deschler@web.de>
31
32 /**
33    Most of the work of the {@link org.apache.log4j.PatternLayout} class
34    is delegated to the PatternParser class.
35
36    <p>It is this class that parses conversion patterns and creates
37    a chained list of {@link OptionConverter OptionConverters}.
38
39    @author <a href=mailto:"cakalijp@Maritz.com">James P. Cakalic</a>
40    @author Ceki G&uuml;lc&uuml;
41    @author Anders Kristensen
42
43    @since 0.8.2
44 */
45 public class PatternParser {
46
47   private static final char ESCAPE_CHAR = '%';
48
49   private static final int LITERAL_STATE = 0;
50   private static final int CONVERTER_STATE = 1;
51   private static final int DOT_STATE = 3;
52   private static final int MIN_STATE = 4;
53   private static final int MAX_STATE = 5;
54
55   static final int FULL_LOCATION_CONVERTER = 1000;
56   static final int METHOD_LOCATION_CONVERTER = 1001;
57   static final int CLASS_LOCATION_CONVERTER = 1002;
58   static final int LINE_LOCATION_CONVERTER = 1003;
59   static final int FILE_LOCATION_CONVERTER = 1004;
60
61   static final int RELATIVE_TIME_CONVERTER = 2000;
62   static final int THREAD_CONVERTER = 2001;
63   static final int LEVEL_CONVERTER = 2002;
64   static final int NDC_CONVERTER = 2003;
65   static final int MESSAGE_CONVERTER = 2004;
66
67   int state;
68   protected StringBuffer currentLiteral = new StringBuffer(32);
69   protected int patternLength;
70   protected int i;
71   PatternConverter head;
72   PatternConverter tail;
73   protected FormattingInfo formattingInfo = new FormattingInfo();
74   protected String pattern;
75
76   public
77   PatternParser(String pattern) {
78     this.pattern = pattern;
79     patternLength =  pattern.length();
80     state = LITERAL_STATE;
81   }
82
83   private
84   void  addToList(PatternConverter pc) {
85     if(head == null) {
86       head = tail = pc;
87     } else {
88       tail.next = pc;
89       tail = pc;
90     }
91   }
92
93   protected
94   String extractOption() {
95     if((i < patternLength) && (pattern.charAt(i) == '{')) {
96       int end = pattern.indexOf('}', i);
97       if (end > i) {
98         String r = pattern.substring(i + 1, end);
99         i = end+1;
100         return r;
101       }
102     }
103     return null;
104   }
105
106
107   /**
108      The option is expected to be in decimal and positive. In case of
109      error, zero is returned.  */
110   protected
111   int extractPrecisionOption() {
112     String opt = extractOption();
113     int r = 0;
114     if(opt != null) {
115       try {
116         r = Integer.parseInt(opt);
117         if(r <= 0) {
118             LogLog.error(
119                 "Precision option (" + opt + ") isn't a positive integer.");
120             r = 0;
121         }
122       }
123       catch (NumberFormatException e) {
124         LogLog.error("Category option \""+opt+"\" not a decimal integer.", e);
125       }
126     }
127     return r;
128   }
129
130   public
131   PatternConverter parse() {
132     char c;
133     i = 0;
134     while(i < patternLength) {
135       c = pattern.charAt(i++);
136       switch(state) {
137       case LITERAL_STATE:
138         // In literal state, the last char is always a literal.
139         if(i == patternLength) {
140           currentLiteral.append(c);
141           continue;
142         }
143         if(c == ESCAPE_CHAR) {
144           // peek at the next char.
145           switch(pattern.charAt(i)) {
146           case ESCAPE_CHAR:
147             currentLiteral.append(c);
148             i++; // move pointer
149             break;
150           case 'n':
151             currentLiteral.append(Layout.LINE_SEP);
152             i++; // move pointer
153             break;
154           default:
155             if(currentLiteral.length() != 0) {
156               addToList(new LiteralPatternConverter(
157                                                   currentLiteral.toString()));
158               //LogLog.debug("Parsed LITERAL converter: \""
159               //           +currentLiteral+"\".");
160             }
161             currentLiteral.setLength(0);
162             currentLiteral.append(c); // append %
163             state = CONVERTER_STATE;
164             formattingInfo.reset();
165           }
166         }
167         else {
168           currentLiteral.append(c);
169         }
170         break;
171       case CONVERTER_STATE:
172         currentLiteral.append(c);
173         switch(c) {
174         case '-':
175           formattingInfo.leftAlign = true;
176           break;
177         case '.':
178           state = DOT_STATE;
179           break;
180         default:
181           if(c >= '0' && c <= '9') {
182             formattingInfo.min = c - '0';
183             state = MIN_STATE;
184           } else {
185         finalizeConverter(c);
186     }
187         } // switch
188         break;
189       case MIN_STATE:
190         currentLiteral.append(c);
191         if(c >= '0' && c <= '9') {
192         formattingInfo.min = formattingInfo.min*10 + (c - '0');
193     } else if(c == '.') {
194         state = DOT_STATE;
195     } else {
196           finalizeConverter(c);
197         }
198         break;
199       case DOT_STATE:
200         currentLiteral.append(c);
201         if(c >= '0' && c <= '9') {
202           formattingInfo.max = c - '0';
203            state = MAX_STATE;
204         }
205         else {
206           LogLog.error("Error occured in position "+i
207                      +".\n Was expecting digit, instead got char \""+c+"\".");
208           state = LITERAL_STATE;
209         }
210         break;
211       case MAX_STATE:
212         currentLiteral.append(c);
213         if(c >= '0' && c <= '9') {
214         formattingInfo.max = formattingInfo.max*10 + (c - '0');
215     } else {
216           finalizeConverter(c);
217           state = LITERAL_STATE;
218         }
219         break;
220       } // switch
221     } // while
222     if(currentLiteral.length() != 0) {
223       addToList(new LiteralPatternConverter(currentLiteral.toString()));
224       //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
225     }
226     return head;
227   }
228
229   protected
230   void finalizeConverter(char c) {
231     PatternConverter pc = null;
232     switch(c) {
233     case 'c':
234       pc = new CategoryPatternConverter(formattingInfo,
235                                         extractPrecisionOption());
236       //LogLog.debug("CATEGORY converter.");
237       //formattingInfo.dump();
238       currentLiteral.setLength(0);
239       break;
240     case 'C':
241       pc = new ClassNamePatternConverter(formattingInfo,
242                                          extractPrecisionOption());
243       //LogLog.debug("CLASS_NAME converter.");
244       //formattingInfo.dump();
245       currentLiteral.setLength(0);
246       break;
247     case 'd':
248       String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
249       DateFormat df;
250       String dOpt = extractOption();
251       if(dOpt != null) {
252         dateFormatStr = dOpt;
253     }
254
255       if(dateFormatStr.equalsIgnoreCase(
256                                     AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT)) {
257         df = new  ISO8601DateFormat();
258     } else if(dateFormatStr.equalsIgnoreCase(
259                                    AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT)) {
260         df = new AbsoluteTimeDateFormat();
261     } else if(dateFormatStr.equalsIgnoreCase(
262                               AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT)) {
263         df = new DateTimeDateFormat();
264     } else {
265         try {
266           df = new SimpleDateFormat(dateFormatStr);
267         }
268         catch (IllegalArgumentException e) {
269           LogLog.error("Could not instantiate SimpleDateFormat with " +
270                        dateFormatStr, e);
271           df = (DateFormat) OptionConverter.instantiateByClassName(
272                                    "org.apache.log4j.helpers.ISO8601DateFormat",
273                                    DateFormat.class, null);
274         }
275       }
276       pc = new DatePatternConverter(formattingInfo, df);
277       //LogLog.debug("DATE converter {"+dateFormatStr+"}.");
278       //formattingInfo.dump();
279       currentLiteral.setLength(0);
280       break;
281     case 'F':
282       pc = new LocationPatternConverter(formattingInfo,
283                                         FILE_LOCATION_CONVERTER);
284       //LogLog.debug("File name converter.");
285       //formattingInfo.dump();
286       currentLiteral.setLength(0);
287       break;
288     case 'l':
289       pc = new LocationPatternConverter(formattingInfo,
290                                         FULL_LOCATION_CONVERTER);
291       //LogLog.debug("Location converter.");
292       //formattingInfo.dump();
293       currentLiteral.setLength(0);
294       break;
295     case 'L':
296       pc = new LocationPatternConverter(formattingInfo,
297                                         LINE_LOCATION_CONVERTER);
298       //LogLog.debug("LINE NUMBER converter.");
299       //formattingInfo.dump();
300       currentLiteral.setLength(0);
301       break;
302     case 'm':
303       pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
304       //LogLog.debug("MESSAGE converter.");
305       //formattingInfo.dump();
306       currentLiteral.setLength(0);
307       break;
308     case 'M':
309       pc = new LocationPatternConverter(formattingInfo,
310                                         METHOD_LOCATION_CONVERTER);
311       //LogLog.debug("METHOD converter.");
312       //formattingInfo.dump();
313       currentLiteral.setLength(0);
314       break;
315     case 'p':
316       pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
317       //LogLog.debug("LEVEL converter.");
318       //formattingInfo.dump();
319       currentLiteral.setLength(0);
320       break;
321     case 'r':
322       pc = new BasicPatternConverter(formattingInfo,
323                                          RELATIVE_TIME_CONVERTER);
324       //LogLog.debug("RELATIVE time converter.");
325       //formattingInfo.dump();
326       currentLiteral.setLength(0);
327       break;
328     case 't':
329       pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
330       //LogLog.debug("THREAD converter.");
331       //formattingInfo.dump();
332       currentLiteral.setLength(0);
333       break;
334       /*case 'u':
335       if(i < patternLength) {
336         char cNext = pattern.charAt(i);
337         if(cNext >= '0' && cNext <= '9') {
338           pc = new UserFieldPatternConverter(formattingInfo, cNext - '0');
339           LogLog.debug("USER converter ["+cNext+"].");
340           formattingInfo.dump();
341           currentLiteral.setLength(0);
342           i++;
343         }
344         else
345           LogLog.error("Unexpected char" +cNext+" at position "+i);
346       }
347       break;*/
348     case 'x':
349       pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
350       //LogLog.debug("NDC converter.");
351       currentLiteral.setLength(0);
352       break;
353     case 'X':
354       String xOpt = extractOption();
355       pc = new MDCPatternConverter(formattingInfo, xOpt);
356       currentLiteral.setLength(0);
357       break;
358     default:
359       LogLog.error("Unexpected char [" +c+"] at position "+i
360                    +" in conversion patterrn.");
361       pc = new LiteralPatternConverter(currentLiteral.toString());
362       currentLiteral.setLength(0);
363     }
364
365     addConverter(pc);
366   }
367
368   protected
369   void addConverter(PatternConverter pc) {
370     currentLiteral.setLength(0);
371     // Add the pattern converter to the list.
372     addToList(pc);
373     // Next pattern is assumed to be a literal.
374     state = LITERAL_STATE;
375     // Reset formatting info
376     formattingInfo.reset();
377   }
378
379   // ---------------------------------------------------------------------
380   //                      PatternConverters
381   // ---------------------------------------------------------------------
382
383   private static class BasicPatternConverter extends PatternConverter {
384     int type;
385
386     BasicPatternConverter(FormattingInfo formattingInfo, int type) {
387       super(formattingInfo);
388       this.type = type;
389     }
390
391     public
392     String convert(LoggingEvent event) {
393       switch(type) {
394       case RELATIVE_TIME_CONVERTER:
395         return (Long.toString(event.timeStamp - LoggingEvent.getStartTime()));
396       case THREAD_CONVERTER:
397         return event.getThreadName();
398       case LEVEL_CONVERTER:
399         return event.getLevel().toString();
400       case NDC_CONVERTER:
401         return event.getNDC();
402       case MESSAGE_CONVERTER: {
403         return event.getRenderedMessage();
404       }
405       default: return null;
406       }
407     }
408   }
409
410   private static class LiteralPatternConverter extends PatternConverter {
411     private String literal;
412
413     LiteralPatternConverter(String value) {
414       literal = value;
415     }
416
417     public
418     final
419     void format(StringBuffer sbuf, LoggingEvent event) {
420       sbuf.append(literal);
421     }
422
423     public
424     String convert(LoggingEvent event) {
425       return literal;
426     }
427   }
428
429   private static class DatePatternConverter extends PatternConverter {
430     private DateFormat df;
431     private Date date;
432
433     DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
434       super(formattingInfo);
435       date = new Date();
436       this.df = df;
437     }
438
439     public
440     String convert(LoggingEvent event) {
441       date.setTime(event.timeStamp);
442       String converted = null;
443       try {
444         converted = df.format(date);
445       }
446       catch (Exception ex) {
447         LogLog.error("Error occured while converting date.", ex);
448       }
449       return converted;
450     }
451   }
452
453   private static class MDCPatternConverter extends PatternConverter {
454     private String key;
455
456     MDCPatternConverter(FormattingInfo formattingInfo, String key) {
457       super(formattingInfo);
458       this.key = key;
459     }
460
461     public
462     String convert(LoggingEvent event) {
463       if (key == null) {
464           StringBuffer buf = new StringBuffer("{");
465           Map properties = event.getProperties();
466           if (properties.size() > 0) {
467             Object[] keys = properties.keySet().toArray();
468             Arrays.sort(keys);
469             for (int i = 0; i < keys.length; i++) {
470                 buf.append('{');
471                 buf.append(keys[i]);
472                 buf.append(',');
473                 buf.append(properties.get(keys[i]));
474                 buf.append('}');
475             }
476           }
477           buf.append('}');
478           return buf.toString();
479       } else {
480         Object val = event.getMDC(key);
481         if(val == null) {
482                 return null;
483         } else {
484                 return val.toString();
485         }
486       }
487     }
488   }
489
490
491   private class LocationPatternConverter extends PatternConverter {
492     int type;
493
494     LocationPatternConverter(FormattingInfo formattingInfo, int type) {
495       super(formattingInfo);
496       this.type = type;
497     }
498
499     public
500     String convert(LoggingEvent event) {
501       LocationInfo locationInfo = event.getLocationInformation();
502       switch(type) {
503       case FULL_LOCATION_CONVERTER:
504         return locationInfo.fullInfo;
505       case METHOD_LOCATION_CONVERTER:
506         return locationInfo.getMethodName();
507       case LINE_LOCATION_CONVERTER:
508         return locationInfo.getLineNumber();
509       case FILE_LOCATION_CONVERTER:
510         return locationInfo.getFileName();
511       default: return null;
512       }
513     }
514   }
515
516   private static abstract class NamedPatternConverter extends PatternConverter {
517     int precision;
518
519     NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
520       super(formattingInfo);
521       this.precision =  precision;
522     }
523
524     abstract
525     String getFullyQualifiedName(LoggingEvent event);
526
527     public
528     String convert(LoggingEvent event) {
529       String n = getFullyQualifiedName(event);
530       if(precision <= 0) {
531         return n;
532     } else {
533         int len = n.length();
534
535         // We substract 1 from 'len' when assigning to 'end' to avoid out of
536         // bounds exception in return r.substring(end+1, len). This can happen if
537         // precision is 1 and the category name ends with a dot.
538         int end = len -1 ;
539         for(int i = precision; i > 0; i--) {
540           end = n.lastIndexOf('.', end-1);
541           if(end == -1) {
542         return n;
543     }
544         }
545         return n.substring(end+1, len);
546       }
547     }
548   }
549
550   private class ClassNamePatternConverter extends NamedPatternConverter {
551
552     ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
553       super(formattingInfo, precision);
554     }
555
556     String getFullyQualifiedName(LoggingEvent event) {
557       return event.getLocationInformation().getClassName();
558     }
559   }
560
561   private class CategoryPatternConverter extends NamedPatternConverter {
562
563     CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
564       super(formattingInfo, precision);
565     }
566
567     String getFullyQualifiedName(LoggingEvent event) {
568       return event.getLoggerName();
569     }
570   }
571 }
572