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.pattern.parser;
015
016import java.util.HashMap;
017import java.util.List;
018import java.util.Map;
019
020import ch.qos.logback.core.CoreConstants;
021import ch.qos.logback.core.pattern.Converter;
022import ch.qos.logback.core.pattern.FormatInfo;
023import ch.qos.logback.core.pattern.IdentityCompositeConverter;
024import ch.qos.logback.core.pattern.ReplacingCompositeConverter;
025import ch.qos.logback.core.pattern.util.IEscapeUtil;
026import ch.qos.logback.core.pattern.util.RegularEscapeUtil;
027import ch.qos.logback.core.spi.ContextAwareBase;
028import ch.qos.logback.core.spi.ScanException;
029
030// ~=lambda
031// E = TE|T
032
033// Left factorization
034// E = T(E|~)
035// Eopt = E|~
036// replace E|~ with Eopt in E
037// E = TEopt
038
039// T = LITERAL | '%' C | '%' FORMAT_MODIFIER C
040// C = SIMPLE_KEYWORD OPTION | COMPOSITE_KEYWORD COMPOSITE
041// OPTION = {...} | ~
042// COMPOSITE = E ')' OPTION
043
044public class Parser<E> extends ContextAwareBase {
045
046    public final static String MISSING_RIGHT_PARENTHESIS = CoreConstants.CODES_URL + "#missingRightParenthesis";
047    public final static Map<String, String> DEFAULT_COMPOSITE_CONVERTER_MAP = new HashMap<String, String>();
048    public final static String REPLACE_CONVERTER_WORD = "replace";
049    static {
050        DEFAULT_COMPOSITE_CONVERTER_MAP.put(Token.BARE_COMPOSITE_KEYWORD_TOKEN.getValue().toString(),
051                IdentityCompositeConverter.class.getName());
052        DEFAULT_COMPOSITE_CONVERTER_MAP.put(REPLACE_CONVERTER_WORD, ReplacingCompositeConverter.class.getName());
053    }
054
055    final List<Token> tokenList;
056    int pointer = 0;
057
058    Parser(TokenStream ts) throws ScanException {
059        this.tokenList = ts.tokenize();
060    }
061
062    public Parser(String pattern) throws ScanException {
063        this(pattern, new RegularEscapeUtil());
064    }
065
066    public Parser(String pattern, IEscapeUtil escapeUtil) throws ScanException {
067        try {
068            TokenStream ts = new TokenStream(pattern, escapeUtil);
069            this.tokenList = ts.tokenize();
070        } catch (IllegalArgumentException npe) {
071            throw new ScanException("Failed to initialize Parser", npe);
072        }
073    }
074
075    /**
076     * When the parsing step is done, the Node list can be transformed into a
077     * converter chain.
078     *
079     * @param top
080     * @param converterMap
081     * @return
082     */
083    public Converter<E> compile(final Node top, Map<String, String> converterMap) {
084        Compiler<E> compiler = new Compiler<E>(top, converterMap);
085        compiler.setContext(context);
086        // compiler.setStatusManager(statusManager);
087        return compiler.compile();
088    }
089
090    public Node parse() throws ScanException {
091        return E();
092    }
093
094    // E = TEopt
095    Node E() throws ScanException {
096        Node t = T();
097        if (t == null) {
098            return null;
099        }
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}