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(), IdentityCompositeConverter.class.getName());
51          DEFAULT_COMPOSITE_CONVERTER_MAP.put(REPLACE_CONVERTER_WORD, ReplacingCompositeConverter.class.getName());
52      }
53  
54      final List<Token> tokenList;
55      int pointer = 0;
56  
57      Parser(TokenStream ts) throws ScanException {
58          this.tokenList = ts.tokenize();
59      }
60  
61      public Parser(String pattern) throws ScanException {
62          this(pattern, new RegularEscapeUtil());
63      }
64  
65      public Parser(String pattern, IEscapeUtil escapeUtil) throws ScanException {
66          try {
67              TokenStream ts = new TokenStream(pattern, escapeUtil);
68              this.tokenList = ts.tokenize();
69          } catch (IllegalArgumentException npe) {
70              throw new ScanException("Failed to initialize Parser", npe);
71          }
72      }
73  
74      /**
75       * When the parsing step is done, the Node list can be transformed into a
76       * converter chain.
77       *
78       * @param top
79       * @param converterMap
80       * @return
81       */
82      public Converter<E> compile(final Node top, Map<String, String> converterMap) {
83          Compiler<E> compiler = new Compiler<E>(top, converterMap);
84          compiler.setContext(context);
85          // compiler.setStatusManager(statusManager);
86          return compiler.compile();
87      }
88  
89      public Node parse() throws ScanException {
90          return E();
91      }
92  
93      // E = TEopt
94      Node E() throws ScanException {
95          Node t = T();
96          if (t == null) {
97              return null;
98          }
99          Node eOpt = Eopt();
100         if (eOpt != null) {
101             t.setNext(eOpt);
102         }
103         return t;
104     }
105 
106     // Eopt = E|~
107     Node Eopt() throws ScanException {
108         // System.out.println("in Eopt()");
109         Token next = getCurentToken();
110         // System.out.println("Current token is " + next);
111         if (next == null) {
112             return null;
113         } else {
114             return E();
115         }
116     }
117 
118     // T = LITERAL | '%' C | '%' FORMAT_MODIFIER C
119     Node T() throws ScanException {
120         Token t = getCurentToken();
121         expectNotNull(t, "a LITERAL or '%'");
122 
123         switch (t.getType()) {
124         case Token.LITERAL:
125             advanceTokenPointer();
126             return new Node(Node.LITERAL, t.getValue());
127         case Token.PERCENT:
128             advanceTokenPointer();
129             // System.out.println("% token found");
130             FormatInfo fi;
131             Token u = getCurentToken();
132             FormattingNode c;
133             expectNotNull(u, "a FORMAT_MODIFIER, SIMPLE_KEYWORD or COMPOUND_KEYWORD");
134             if (u.getType() == Token.FORMAT_MODIFIER) {
135                 fi = FormatInfo.valueOf((String) u.getValue());
136                 advanceTokenPointer();
137                 c = C();
138                 c.setFormatInfo(fi);
139             } else {
140                 c = C();
141             }
142             return c;
143 
144         default:
145             return null;
146 
147         }
148 
149     }
150 
151     FormattingNode C() throws ScanException {
152         Token t = getCurentToken();
153         // System.out.println("in C()");
154         // System.out.println("Current token is " + t);
155         expectNotNull(t, "a LEFT_PARENTHESIS or KEYWORD");
156         int type = t.getType();
157         switch (type) {
158         case Token.SIMPLE_KEYWORD:
159             return SINGLE();
160         case Token.COMPOSITE_KEYWORD:
161             advanceTokenPointer();
162             return COMPOSITE(t.getValue().toString());
163         default:
164             throw new IllegalStateException("Unexpected token " + t);
165         }
166     }
167 
168     FormattingNode SINGLE() throws ScanException {
169         // System.out.println("in SINGLE()");
170         Token t = getNextToken();
171         // System.out.println("==" + t);
172         SimpleKeywordNode keywordNode = new SimpleKeywordNode(t.getValue());
173 
174         Token ot = getCurentToken();
175         if (ot != null && ot.getType() == Token.OPTION) {
176             List<String> optionList = ot.getOptionsList();
177             keywordNode.setOptions(optionList);
178             advanceTokenPointer();
179         }
180         return keywordNode;
181     }
182 
183     FormattingNode COMPOSITE(String keyword) throws ScanException {
184         CompositeNode compositeNode = new CompositeNode(keyword);
185 
186         Node childNode = E();
187         compositeNode.setChildNode(childNode);
188 
189         Token t = getNextToken();
190 
191         if (t == null || t.getType() != Token.RIGHT_PARENTHESIS) {
192             String msg = "Expecting RIGHT_PARENTHESIS token but got " + t;
193             addError(msg);
194             addError("See also " + MISSING_RIGHT_PARENTHESIS);
195             throw new ScanException(msg);
196         }
197         Token ot = getCurentToken();
198         if (ot != null && ot.getType() == Token.OPTION) {
199             List<String> optionList = ot.getOptionsList();
200             compositeNode.setOptions(optionList);
201             advanceTokenPointer();
202         }
203         return compositeNode;
204     }
205 
206     Token getNextToken() {
207         if (pointer < tokenList.size()) {
208             return (Token) tokenList.get(pointer++);
209         }
210         return null;
211     }
212 
213     Token getCurentToken() {
214         if (pointer < tokenList.size()) {
215             return (Token) tokenList.get(pointer);
216         }
217         return null;
218     }
219 
220     void advanceTokenPointer() {
221         pointer++;
222     }
223 
224     void expectNotNull(Token t, String expected) {
225         if (t == null) {
226             throw new IllegalStateException("All tokens consumed but was expecting " + expected);
227         }
228     }
229 
230     // public void setStatusManager(StatusManager statusManager) {
231     // this.statusManager = statusManager;
232     // }
233 }