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; 019import java.util.function.Supplier; 020 021import ch.qos.logback.core.CoreConstants; 022import ch.qos.logback.core.pattern.*; 023import ch.qos.logback.core.pattern.util.IEscapeUtil; 024import ch.qos.logback.core.pattern.util.RegularEscapeUtil; 025import ch.qos.logback.core.spi.ContextAwareBase; 026import ch.qos.logback.core.spi.ScanException; 027 028// ~=lambda 029// E = TE|T 030 031// Left factorization 032// E = T(E|~) 033// Eopt = E|~ 034// replace E|~ with Eopt in E 035// E = TEopt 036 037// T = LITERAL | '%' C | '%' FORMAT_MODIFIER C 038// C = SIMPLE_KEYWORD OPTION | COMPOSITE_KEYWORD COMPOSITE 039// OPTION = {...} | ~ 040// COMPOSITE = E ')' OPTION 041 042public class Parser<E> extends ContextAwareBase { 043 044 public final static String MISSING_RIGHT_PARENTHESIS = CoreConstants.CODES_URL + "#missingRightParenthesis"; 045 public final static Map<String, Supplier<DynamicConverter>> DEFAULT_COMPOSITE_CONVERTER_MAP = new HashMap<>(); 046 public final static String REPLACE_CONVERTER_WORD = "replace"; 047 static { 048 DEFAULT_COMPOSITE_CONVERTER_MAP.put(Token.BARE_COMPOSITE_KEYWORD_TOKEN.getValue().toString(), 049 IdentityCompositeConverter::new); 050 DEFAULT_COMPOSITE_CONVERTER_MAP.put(REPLACE_CONVERTER_WORD, ReplacingCompositeConverter::new); 051 } 052 053 final List<Token> tokenList; 054 int pointer = 0; 055 056 Parser(TokenStream ts) throws ScanException { 057 this.tokenList = ts.tokenize(); 058 } 059 060 public Parser(String pattern) throws ScanException { 061 this(pattern, new RegularEscapeUtil()); 062 } 063 064 public Parser(String pattern, IEscapeUtil escapeUtil) throws ScanException { 065 try { 066 TokenStream ts = new TokenStream(pattern, escapeUtil); 067 this.tokenList = ts.tokenize(); 068 } catch (IllegalArgumentException npe) { 069 throw new ScanException("Failed to initialize Parser", npe); 070 } 071 } 072 073 /** 074 * When the parsing step is done, the Node list can be transformed into a 075 * converter chain. 076 * 077 * @param top 078 * @param converterMap 079 * @return 080 */ 081 public Converter<E> compile(final Node top, Map<String, Supplier<DynamicConverter>> converterMap) { 082 Compiler<E> compiler = new Compiler<E>(top, converterMap); 083 compiler.setContext(context); 084 // compiler.setStatusManager(statusManager); 085 return compiler.compile(); 086 } 087 088 public Node parse() throws ScanException { 089 return E(); 090 } 091 092 // E = TEopt 093 Node E() throws ScanException { 094 Node t = T(); 095 if (t == null) { 096 return null; 097 } 098 Node eOpt = Eopt(); 099 if (eOpt != null) { 100 t.setNext(eOpt); 101 } 102 return t; 103 } 104 105 // Eopt = E|~ 106 Node Eopt() throws ScanException { 107 // System.out.println("in Eopt()"); 108 Token next = getCurentToken(); 109 // System.out.println("Current token is " + next); 110 if (next == null) { 111 return null; 112 } else { 113 return E(); 114 } 115 } 116 117 // T = LITERAL | '%' C | '%' FORMAT_MODIFIER C 118 Node T() throws ScanException { 119 Token t = getCurentToken(); 120 expectNotNull(t, "a LITERAL or '%'"); 121 122 switch (t.getType()) { 123 case Token.LITERAL: 124 advanceTokenPointer(); 125 return new Node(Node.LITERAL, t.getValue()); 126 case Token.PERCENT: 127 advanceTokenPointer(); 128 // System.out.println("% token found"); 129 FormatInfo fi; 130 Token u = getCurentToken(); 131 FormattingNode c; 132 expectNotNull(u, "a FORMAT_MODIFIER, SIMPLE_KEYWORD or COMPOUND_KEYWORD"); 133 if (u.getType() == Token.FORMAT_MODIFIER) { 134 fi = FormatInfo.valueOf((String) u.getValue()); 135 advanceTokenPointer(); 136 c = C(); 137 c.setFormatInfo(fi); 138 } else { 139 c = C(); 140 } 141 return c; 142 143 default: 144 return null; 145 146 } 147 148 } 149 150 FormattingNode C() throws ScanException { 151 Token t = getCurentToken(); 152 // System.out.println("in C()"); 153 // System.out.println("Current token is " + t); 154 expectNotNull(t, "a LEFT_PARENTHESIS or KEYWORD"); 155 int type = t.getType(); 156 switch (type) { 157 case Token.SIMPLE_KEYWORD: 158 return SINGLE(); 159 case Token.COMPOSITE_KEYWORD: 160 advanceTokenPointer(); 161 return COMPOSITE(t.getValue().toString()); 162 default: 163 throw new IllegalStateException("Unexpected token " + t); 164 } 165 } 166 167 FormattingNode SINGLE() throws ScanException { 168 // System.out.println("in SINGLE()"); 169 Token t = getNextToken(); 170 // System.out.println("==" + t); 171 SimpleKeywordNode keywordNode = new SimpleKeywordNode(t.getValue()); 172 173 Token ot = getCurentToken(); 174 if (ot != null && ot.getType() == Token.OPTION) { 175 List<String> optionList = ot.getOptionsList(); 176 keywordNode.setOptions(optionList); 177 advanceTokenPointer(); 178 } 179 return keywordNode; 180 } 181 182 FormattingNode COMPOSITE(String keyword) throws ScanException { 183 CompositeNode compositeNode = new CompositeNode(keyword); 184 185 Node childNode = E(); 186 compositeNode.setChildNode(childNode); 187 188 Token t = getNextToken(); 189 190 if (t == null || t.getType() != Token.RIGHT_PARENTHESIS) { 191 String msg = "Expecting RIGHT_PARENTHESIS token but got " + t; 192 addError(msg); 193 addError("See also " + MISSING_RIGHT_PARENTHESIS); 194 throw new ScanException(msg); 195 } 196 Token ot = getCurentToken(); 197 if (ot != null && ot.getType() == Token.OPTION) { 198 List<String> optionList = ot.getOptionsList(); 199 compositeNode.setOptions(optionList); 200 advanceTokenPointer(); 201 } 202 return compositeNode; 203 } 204 205 Token getNextToken() { 206 if (pointer < tokenList.size()) { 207 return (Token) tokenList.get(pointer++); 208 } 209 return null; 210 } 211 212 Token getCurentToken() { 213 if (pointer < tokenList.size()) { 214 return (Token) tokenList.get(pointer); 215 } 216 return null; 217 } 218 219 void advanceTokenPointer() { 220 pointer++; 221 } 222 223 void expectNotNull(Token t, String expected) { 224 if (t == null) { 225 throw new IllegalStateException("All tokens consumed but was expecting " + expected); 226 } 227 } 228 229 // public void setStatusManager(StatusManager statusManager) { 230 // this.statusManager = statusManager; 231 // } 232}