View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.pattern.parser;
15  
16  import java.util.HashMap;
17  import java.util.List;
18  import java.util.Map;
19  
20  import ch.qos.logback.core.CoreConstants;
21  import ch.qos.logback.core.pattern.Converter;
22  import ch.qos.logback.core.pattern.FormatInfo;
23  import ch.qos.logback.core.pattern.IdentityCompositeConverter;
24  import ch.qos.logback.core.pattern.ReplacingCompositeConverter;
25  import ch.qos.logback.core.pattern.util.IEscapeUtil;
26  import ch.qos.logback.core.pattern.util.RegularEscapeUtil;
27  import ch.qos.logback.core.spi.ContextAwareBase;
28  import ch.qos.logback.core.spi.ScanException;
29  
30  // ~=lamda
31  // E = TE|T
32  
33  // Left factorization
34  // E = T(E|~)
35  // Eopt = E|~
36  // replace E|~ with Eopt in E
37  // E = TEopt
38  
39  // T = LITERAL | '%' C | '%' FORMAT_MODIFIER C
40  // C = SIMPLE_KEYWORD OPTION | COMPOSITE_KEYWORD COMPOSITE
41  // OPTION = {...} | ~
42  // COMPOSITE = E ')' OPTION
43  
44  public class Parser<E> extends ContextAwareBase {
45  
46      public final static String MISSING_RIGHT_PARENTHESIS = CoreConstants.CODES_URL + "#missingRightParenthesis";
47      public final static Map<String, String> DEFAULT_COMPOSITE_CONVERTER_MAP = new HashMap<String, String>();
48      public final static String REPLACE_CONVERTER_WORD = "replace";
49      static {
50          DEFAULT_COMPOSITE_CONVERTER_MAP.put(Token.BARE_COMPOSITE_KEYWORD_TOKEN.getValue().toString(),
51                  IdentityCompositeConverter.class.getName());
52          DEFAULT_COMPOSITE_CONVERTER_MAP.put(REPLACE_CONVERTER_WORD, ReplacingCompositeConverter.class.getName());
53      }
54  
55      final List<Token> tokenList;
56      int pointer = 0;
57  
58      Parser(TokenStream ts) throws ScanException {
59          this.tokenList = ts.tokenize();
60      }
61  
62      public Parser(String pattern) throws ScanException {
63          this(pattern, new RegularEscapeUtil());
64      }
65  
66      public Parser(String pattern, IEscapeUtil escapeUtil) throws ScanException {
67          try {
68              TokenStream ts = new TokenStream(pattern, escapeUtil);
69              this.tokenList = ts.tokenize();
70          } catch (IllegalArgumentException npe) {
71              throw new ScanException("Failed to initialize Parser", npe);
72          }
73      }
74  
75      /**
76       * When the parsing step is done, the Node list can be transformed into a
77       * converter chain.
78       *
79       * @param top
80       * @param converterMap
81       * @return
82       */
83      public Converter<E> compile(final Node top, Map<String, String> converterMap) {
84          Compiler<E> compiler = new Compiler<E>(top, converterMap);
85          compiler.setContext(context);
86          // compiler.setStatusManager(statusManager);
87          return compiler.compile();
88      }
89  
90      public Node parse() throws ScanException {
91          return E();
92      }
93  
94      // E = TEopt
95      Node E() throws ScanException {
96          Node t = T();
97          if (t == null) {
98              return null;
99          }
100         Node eOpt = Eopt();
101         if (eOpt != null) {
102             t.setNext(eOpt);
103         }
104         return t;
105     }
106 
107     // Eopt = E|~
108     Node Eopt() throws ScanException {
109         // System.out.println("in Eopt()");
110         Token next = getCurentToken();
111         // System.out.println("Current token is " + next);
112         if (next == null) {
113             return null;
114         } else {
115             return E();
116         }
117     }
118 
119     // T = LITERAL | '%' C | '%' FORMAT_MODIFIER C
120     Node T() throws ScanException {
121         Token t = getCurentToken();
122         expectNotNull(t, "a LITERAL or '%'");
123 
124         switch (t.getType()) {
125         case Token.LITERAL:
126             advanceTokenPointer();
127             return new Node(Node.LITERAL, t.getValue());
128         case Token.PERCENT:
129             advanceTokenPointer();
130             // System.out.println("% token found");
131             FormatInfo fi;
132             Token u = getCurentToken();
133             FormattingNode c;
134             expectNotNull(u, "a FORMAT_MODIFIER, SIMPLE_KEYWORD or COMPOUND_KEYWORD");
135             if (u.getType() == Token.FORMAT_MODIFIER) {
136                 fi = FormatInfo.valueOf((String) u.getValue());
137                 advanceTokenPointer();
138                 c = C();
139                 c.setFormatInfo(fi);
140             } else {
141                 c = C();
142             }
143             return c;
144 
145         default:
146             return null;
147 
148         }
149 
150     }
151 
152     FormattingNode C() throws ScanException {
153         Token t = getCurentToken();
154         // System.out.println("in C()");
155         // System.out.println("Current token is " + t);
156         expectNotNull(t, "a LEFT_PARENTHESIS or KEYWORD");
157         int type = t.getType();
158         switch (type) {
159         case Token.SIMPLE_KEYWORD:
160             return SINGLE();
161         case Token.COMPOSITE_KEYWORD:
162             advanceTokenPointer();
163             return COMPOSITE(t.getValue().toString());
164         default:
165             throw new IllegalStateException("Unexpected token " + t);
166         }
167     }
168 
169     FormattingNode SINGLE() throws ScanException {
170         // System.out.println("in SINGLE()");
171         Token t = getNextToken();
172         // System.out.println("==" + t);
173         SimpleKeywordNode keywordNode = new SimpleKeywordNode(t.getValue());
174 
175         Token ot = getCurentToken();
176         if (ot != null && ot.getType() == Token.OPTION) {
177             List<String> optionList = ot.getOptionsList();
178             keywordNode.setOptions(optionList);
179             advanceTokenPointer();
180         }
181         return keywordNode;
182     }
183 
184     FormattingNode COMPOSITE(String keyword) throws ScanException {
185         CompositeNode compositeNode = new CompositeNode(keyword);
186 
187         Node childNode = E();
188         compositeNode.setChildNode(childNode);
189 
190         Token t = getNextToken();
191 
192         if (t == null || t.getType() != Token.RIGHT_PARENTHESIS) {
193             String msg = "Expecting RIGHT_PARENTHESIS token but got " + t;
194             addError(msg);
195             addError("See also " + MISSING_RIGHT_PARENTHESIS);
196             throw new ScanException(msg);
197         }
198         Token ot = getCurentToken();
199         if (ot != null && ot.getType() == Token.OPTION) {
200             List<String> optionList = ot.getOptionsList();
201             compositeNode.setOptions(optionList);
202             advanceTokenPointer();
203         }
204         return compositeNode;
205     }
206 
207     Token getNextToken() {
208         if (pointer < tokenList.size()) {
209             return (Token) tokenList.get(pointer++);
210         }
211         return null;
212     }
213 
214     Token getCurentToken() {
215         if (pointer < tokenList.size()) {
216             return (Token) tokenList.get(pointer);
217         }
218         return null;
219     }
220 
221     void advanceTokenPointer() {
222         pointer++;
223     }
224 
225     void expectNotNull(Token t, String expected) {
226         if (t == null) {
227             throw new IllegalStateException("All tokens consumed but was expecting " + expected);
228         }
229     }
230 
231     // public void setStatusManager(StatusManager statusManager) {
232     // this.statusManager = statusManager;
233     // }
234 }