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}