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/**
031 * Parse a token list returning a node chain.
032 *
033 * @author Ceki Gulcu
034 */
035public class Parser {
036    
037    static final public String EXPECTING_DATA_AFTER_LEFT_ACCOLADE = "Expecting at least a literal between left accolade and ':-'";
038    
039    final List<Token> tokenList;
040    int pointer = 0;
041
042    public Parser(List<Token> tokenList) {
043        this.tokenList = tokenList;
044    }
045
046    public Node parse() throws ScanException {
047        if (tokenList == null || tokenList.isEmpty())
048            return null;
049        return E();
050    }
051
052    private Node E() throws ScanException {
053        Node t = T();
054        if (t == null) {
055            return null;
056        }
057        Node eOpt = Eopt();
058        if (eOpt != null) {
059            t.append(eOpt);
060        }
061        return t;
062    }
063
064    // Eopt = E|~
065    private Node Eopt() throws ScanException {
066        Token next = peekAtCurentToken();
067        if (next == null) {
068            return null;
069        } else {
070            return E();
071        }
072    }
073
074    // T = LITERAL | '${' V '}'
075    private Node T() throws ScanException {
076        Token t = peekAtCurentToken();
077        if(t == null) {
078            return null;    
079        }
080        switch (t.type) {
081        case LITERAL:
082            advanceTokenPointer();
083            return makeNewLiteralNode(t.payload);
084        case CURLY_LEFT:
085            advanceTokenPointer();
086            Node innerNode = C();
087            Token right = peekAtCurentToken();
088            expectCurlyRight(right);
089            advanceTokenPointer();
090            Node curlyLeft = makeNewLiteralNode(CoreConstants.LEFT_ACCOLADE);
091            curlyLeft.append(innerNode);
092            curlyLeft.append(makeNewLiteralNode(CoreConstants.RIGHT_ACCOLADE));
093            return curlyLeft;
094        case START:
095            advanceTokenPointer();
096            Node v = V();
097            Token w = peekAtCurentToken();
098            expectCurlyRight(w);
099            advanceTokenPointer();
100            return v;
101        default:
102            return null;
103        }
104    }
105
106    private Node makeNewLiteralNode(String s) {
107        return new Node(Node.Type.LITERAL, s);
108    }
109
110    // V = E(':='E|~)
111    private Node V() throws ScanException {
112        Node e = E();
113        Node variable = new Node(Node.Type.VARIABLE, e);
114        Token t = peekAtCurentToken();
115        if (isDefaultToken(t)) {
116            advanceTokenPointer();
117            Node def = E();
118            variable.defaultPart = def;
119        }
120        return variable;
121    }
122
123    
124    // C = E(':='E|~)
125    private Node C() throws ScanException {
126        Node e0 = E();
127        Token t = peekAtCurentToken();
128        if (isDefaultToken(t)) {
129            advanceTokenPointer();
130            Node literal = makeNewLiteralNode(CoreConstants.DEFAULT_VALUE_SEPARATOR);
131            if(e0 == null) {
132              throw new ScanException(EXPECTING_DATA_AFTER_LEFT_ACCOLADE);
133            }
134            e0.append(literal);
135            Node e1 = E();
136            e0.append(e1);
137        }
138        return e0;
139    }
140
141    private boolean isDefaultToken(Token t) {
142        return t != null && t.type == Token.Type.DEFAULT;
143    }
144
145    void advanceTokenPointer() {
146        pointer++;
147    }
148
149    void expectNotNull(Token t, String expected) {
150        if (t == null) {
151            throw new IllegalArgumentException("All tokens consumed but was expecting \"" + expected + "\"");
152        }
153    }
154
155    void expectCurlyRight(Token t) throws ScanException {
156        expectNotNull(t, "}");
157        if (t.type != Token.Type.CURLY_RIGHT) {
158            throw new ScanException("Expecting }");
159        }
160    }
161
162    Token peekAtCurentToken() {
163        if (pointer < tokenList.size()) {
164            return tokenList.get(pointer);
165        }
166        return null;
167    }
168
169}