/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2015, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package ch.qos.logback.core.pattern.parser; import java.util.HashMap; import java.util.List; import java.util.Map; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.pattern.Converter; import ch.qos.logback.core.pattern.FormatInfo; import ch.qos.logback.core.pattern.IdentityCompositeConverter; import ch.qos.logback.core.pattern.ReplacingCompositeConverter; import ch.qos.logback.core.pattern.util.IEscapeUtil; import ch.qos.logback.core.pattern.util.RegularEscapeUtil; import ch.qos.logback.core.spi.ContextAwareBase; import ch.qos.logback.core.spi.ScanException; // ~=lamda // E = TE|T // Left factorization // E = T(E|~) // Eopt = E|~ // replace E|~ with Eopt in E // E = TEopt // T = LITERAL | '%' C | '%' FORMAT_MODIFIER C // C = SIMPLE_KEYWORD OPTION | COMPOSITE_KEYWORD COMPOSITE // OPTION = {...} | ~ // COMPOSITE = E ')' OPTION public class Parser<E> extends ContextAwareBase { public final static String MISSING_RIGHT_PARENTHESIS = CoreConstants.CODES_URL + "#missingRightParenthesis"; public final static Map<String, String> DEFAULT_COMPOSITE_CONVERTER_MAP = new HashMap<String, String>(); public final static String REPLACE_CONVERTER_WORD = "replace"; static { DEFAULT_COMPOSITE_CONVERTER_MAP.put(Token.BARE_COMPOSITE_KEYWORD_TOKEN.getValue().toString(), IdentityCompositeConverter.class.getName()); DEFAULT_COMPOSITE_CONVERTER_MAP.put(REPLACE_CONVERTER_WORD, ReplacingCompositeConverter.class.getName()); } final List tokenList; int pointer = 0; Parser(TokenStream ts) throws ScanException { this.tokenList = ts.tokenize(); } public Parser(String pattern) throws ScanException { this(pattern, new RegularEscapeUtil()); } public Parser(String pattern, IEscapeUtil escapeUtil) throws ScanException { try { TokenStream ts = new TokenStream(pattern, escapeUtil); this.tokenList = ts.tokenize(); } catch (IllegalArgumentException npe) { throw new ScanException("Failed to initialize Parser", npe); } } /** * When the parsing step is done, the Node list can be transformed into a * converter chain. * * @param top * @param converterMap * @return * @throws ScanException */ public Converter<E> compile(final Node top, Map converterMap) { Compiler<E> compiler = new Compiler<E>(top, converterMap); compiler.setContext(context); // compiler.setStatusManager(statusManager); return compiler.compile(); } public Node parse() throws ScanException { return E(); } // E = TEopt Node E() throws ScanException { Node t = T(); if (t == null) { return null; } Node eOpt = Eopt(); if (eOpt != null) { t.setNext(eOpt); } return t; } // Eopt = E|~ Node Eopt() throws ScanException { // System.out.println("in Eopt()"); Token next = getCurentToken(); // System.out.println("Current token is " + next); if (next == null) { return null; } else { return E(); } } // T = LITERAL | '%' C | '%' FORMAT_MODIFIER C Node T() throws ScanException { Token t = getCurentToken(); expectNotNull(t, "a LITERAL or '%'"); switch (t.getType()) { case Token.LITERAL: advanceTokenPointer(); return new Node(Node.LITERAL, t.getValue()); case Token.PERCENT: advanceTokenPointer(); // System.out.println("% token found"); FormatInfo fi; Token u = getCurentToken(); FormattingNode c; expectNotNull(u, "a FORMAT_MODIFIER, SIMPLE_KEYWORD or COMPOUND_KEYWORD"); if (u.getType() == Token.FORMAT_MODIFIER) { fi = FormatInfo.valueOf((String) u.getValue()); advanceTokenPointer(); c = C(); c.setFormatInfo(fi); } else { c = C(); } return c; default: return null; } } FormattingNode C() throws ScanException { Token t = getCurentToken(); // System.out.println("in C()"); // System.out.println("Current token is " + t); expectNotNull(t, "a LEFT_PARENTHESIS or KEYWORD"); int type = t.getType(); switch (type) { case Token.SIMPLE_KEYWORD: return SINGLE(); case Token.COMPOSITE_KEYWORD: advanceTokenPointer(); return COMPOSITE(t.getValue().toString()); default: throw new IllegalStateException("Unexpected token " + t); } } FormattingNode SINGLE() throws ScanException { // System.out.println("in SINGLE()"); Token t = getNextToken(); // System.out.println("==" + t); SimpleKeywordNode keywordNode = new SimpleKeywordNode(t.getValue()); Token ot = getCurentToken(); if (ot != null && ot.getType() == Token.OPTION) { List<String> optionList = (List<String>) ot.getValue(); keywordNode.setOptions(optionList); advanceTokenPointer(); } return keywordNode; } FormattingNode COMPOSITE(String keyword) throws ScanException { CompositeNode compositeNode = new CompositeNode(keyword); Node childNode = E(); compositeNode.setChildNode(childNode); Token t = getNextToken(); if (t == null || t.getType() != Token.RIGHT_PARENTHESIS) { String msg = "Expecting RIGHT_PARENTHESIS token but got " + t; addError(msg); addError("See also " + MISSING_RIGHT_PARENTHESIS); throw new ScanException(msg); } Token ot = getCurentToken(); if (ot != null && ot.getType() == Token.OPTION) { List<String> optionList = (List<String>) ot.getValue(); compositeNode.setOptions(optionList); advanceTokenPointer(); } return compositeNode; } Token getNextToken() { if (pointer < tokenList.size()) { return (Token) tokenList.get(pointer++); } return null; } Token getCurentToken() { if (pointer < tokenList.size()) { return (Token) tokenList.get(pointer); } return null; } void advanceTokenPointer() { pointer++; } void expectNotNull(Token t, String expected) { if (t == null) { throw new IllegalStateException("All tokens consumed but was expecting " + expected); } } // public void setStatusManager(StatusManager statusManager) { // this.statusManager = statusManager; // } }