001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.subst;
015
016import ch.qos.logback.core.CoreConstants;
017import ch.qos.logback.core.spi.ScanException;
018
019import java.util.List;
020
021// E = TE|T
022//   = T(E|~)
023// E = TEopt where Eopt = E|~
024// T = LITERAL | { C } |'${' V '}'
025// C = E|E :- E
026//   = E(':-'E|~)
027// V = E|E :- E
028//   = E(':-'E|~)
029
030// new definition
031
032// V = E | E :- Eopt
033//   = E (~| :- Eopt)
034
035/**
036 * Parse a token list returning a node chain.
037 *
038 * @author Ceki Gulcu
039 */
040public class Parser {
041    
042    static final public String EXPECTING_DATA_AFTER_LEFT_ACCOLADE = "Expecting at least a literal between left accolade and ':-'";
043    
044    final List<Token> tokenList;
045    int pointer = 0;
046
047    public Parser(List<Token> tokenList) {
048        this.tokenList = tokenList;
049    }
050
051    public Node parse() throws ScanException {
052        if (tokenList == null || tokenList.isEmpty())
053            return null;
054        return E();
055    }
056
057    private Node E() throws ScanException {
058        Node t = T();
059        if (t == null) {
060            return null;
061        }
062        Node eOpt = Eopt();
063        if (eOpt != null) {
064            t.append(eOpt);
065        }
066        return t;
067    }
068
069    // Eopt = E|~
070    private Node Eopt() throws ScanException {
071        Token next = peekAtCurentToken();
072        if (next == null) {
073            return null;
074        } else {
075            return E();
076        }
077    }
078
079    // T = LITERAL | '${' V '}'
080    private Node T() throws ScanException {
081        Token t = peekAtCurentToken();
082        if(t == null) {
083            return null;    
084        }
085        switch (t.type) {
086        case LITERAL:
087            advanceTokenPointer();
088            return makeNewLiteralNode(t.payload);
089        case CURLY_LEFT:
090            advanceTokenPointer();
091            Node innerNode = C();
092            Token right = peekAtCurentToken();
093            expectCurlyRight(right);
094            advanceTokenPointer();
095            Node curlyLeft = makeNewLiteralNode(CoreConstants.LEFT_ACCOLADE);
096            curlyLeft.append(innerNode);
097            curlyLeft.append(makeNewLiteralNode(CoreConstants.RIGHT_ACCOLADE));
098            return curlyLeft;
099        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}