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}