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.subst;
15  
16  import ch.qos.logback.core.CoreConstants;
17  import ch.qos.logback.core.spi.ScanException;
18  
19  import java.util.List;
20  
21  // E = TE|T
22  //   = T(E|~)
23  // E = TEopt where Eopt = E|~
24  // T = LITERAL | { C } |'${' V '}'
25  // C = E|E :- E
26  //   = E(':-'E|~)
27  // V = E|E :- E
28  //   = E(':-'E|~)
29  
30  // new definition
31  
32  // V = E | E :- Eopt
33  //   = E (~| :- Eopt)
34  
35  /**
36   * Parse a token list returning a node chain.
37   *
38   * @author Ceki Gulcu
39   */
40  public class Parser {
41      
42      static final public String EXPECTING_DATA_AFTER_LEFT_ACCOLADE = "Expecting at least a literal between left accolade and ':-'";
43      
44      final List<Token> tokenList;
45      int pointer = 0;
46  
47      public Parser(List<Token> tokenList) {
48          this.tokenList = tokenList;
49      }
50  
51      public Node parse() throws ScanException {
52          if (tokenList == null || tokenList.isEmpty())
53              return null;
54          return E();
55      }
56  
57      private Node E() throws ScanException {
58          Node t = T();
59          if (t == null) {
60              return null;
61          }
62          Node eOpt = Eopt();
63          if (eOpt != null) {
64              t.append(eOpt);
65          }
66          return t;
67      }
68  
69      // Eopt = E|~
70      private Node Eopt() throws ScanException {
71          Token next = peekAtCurentToken();
72          if (next == null) {
73              return null;
74          } else {
75              return E();
76          }
77      }
78  
79      // T = LITERAL | '${' V '}'
80      private Node T() throws ScanException {
81          Token t = peekAtCurentToken();
82          if(t == null) {
83              return null;    
84          }
85          switch (t.type) {
86          case LITERAL:
87              advanceTokenPointer();
88              return makeNewLiteralNode(t.payload);
89          case CURLY_LEFT:
90              advanceTokenPointer();
91              Node innerNode = C();
92              Token right = peekAtCurentToken();
93              expectCurlyRight(right);
94              advanceTokenPointer();
95              Node curlyLeft = makeNewLiteralNode(CoreConstants.LEFT_ACCOLADE);
96              curlyLeft.append(innerNode);
97              curlyLeft.append(makeNewLiteralNode(CoreConstants.RIGHT_ACCOLADE));
98              return curlyLeft;
99          case START:
100             advanceTokenPointer();
101             Node v = V();
102             Token w = peekAtCurentToken();
103             expectCurlyRight(w);
104             advanceTokenPointer();
105             return v;
106         default:
107             return null;
108         }
109     }
110 
111     private Node makeNewLiteralNode(String s) {
112         return new Node(Node.Type.LITERAL, s);
113     }
114 
115     // V = E (~| :- Eopt)
116     private Node V() throws ScanException {
117         Node e = E();
118         Node variable = new Node(Node.Type.VARIABLE, e);
119         Token t = peekAtCurentToken();
120         if (isDefaultToken(t)) {
121             advanceTokenPointer();
122             Node def = Eopt();
123             if(def != null) {
124                 variable.defaultPart = def;
125             } else {
126                 variable.defaultPart = makeNewLiteralNode(CoreConstants.EMPTY_STRING);
127             }
128         }
129         return variable;
130     }
131 
132     
133     // C = E(':-'E|~)
134     private Node C() throws ScanException {
135         Node e0 = E();
136         Token t = peekAtCurentToken();
137         if (isDefaultToken(t)) {
138             advanceTokenPointer();
139             Node literal = makeNewLiteralNode(CoreConstants.DEFAULT_VALUE_SEPARATOR);
140             if(e0 == null) {
141               throw new ScanException(EXPECTING_DATA_AFTER_LEFT_ACCOLADE);
142             }
143             e0.append(literal);
144             Node e1 = E();
145             e0.append(e1);
146         }
147         return e0;
148     }
149 
150     private boolean isDefaultToken(Token t) {
151         return t != null && t.type == Token.Type.DEFAULT;
152     }
153 
154     void advanceTokenPointer() {
155         pointer++;
156     }
157 
158     void expectNotNull(Token t, String expected) {
159         if (t == null) {
160             throw new IllegalArgumentException("All tokens consumed but was expecting \"" + expected + "\"");
161         }
162     }
163 
164     void expectCurlyRight(Token t) throws ScanException {
165         expectNotNull(t, "}");
166         if (t.type != Token.Type.CURLY_RIGHT) {
167             throw new ScanException("Expecting }");
168         }
169     }
170 
171     Token peekAtCurentToken() {
172         if (pointer < tokenList.size()) {
173             return tokenList.get(pointer);
174         }
175         return null;
176     }
177 
178 }