1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core.pattern.parser;
15
16 import java.util.List;
17 import java.util.ArrayList;
18
19 import ch.qos.logback.core.CoreConstants;
20 import static ch.qos.logback.core.CoreConstants.CURLY_LEFT;
21 import static ch.qos.logback.core.CoreConstants.ESCAPE_CHAR;
22
23 import ch.qos.logback.core.pattern.util.IEscapeUtil;
24 import ch.qos.logback.core.pattern.util.RegularEscapeUtil;
25 import ch.qos.logback.core.pattern.util.RestrictedEscapeUtil;
26 import ch.qos.logback.core.spi.ScanException;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 class TokenStream {
49
50 enum TokenizerState {
51 LITERAL_STATE, FORMAT_MODIFIER_STATE, KEYWORD_STATE, OPTION_STATE, RIGHT_PARENTHESIS_STATE
52 }
53
54 final String pattern;
55 final int patternLength;
56 final IEscapeUtil escapeUtil;
57
58 final IEscapeUtil optionEscapeUtil = new RestrictedEscapeUtil();
59
60 TokenizerState state = TokenizerState.LITERAL_STATE;
61 int pointer = 0;
62
63
64 TokenStream(String pattern) {
65 this(pattern, new RegularEscapeUtil());
66 }
67
68 TokenStream(String pattern, IEscapeUtil escapeUtil) {
69 if (pattern == null || pattern.length() == 0) {
70 throw new IllegalArgumentException("null or empty pattern string not allowed");
71 }
72 this.pattern = pattern;
73 patternLength = pattern.length();
74 this.escapeUtil = escapeUtil;
75 }
76
77 List<Token> tokenize() throws ScanException {
78 List<Token> tokenList = new ArrayList<Token>();
79 StringBuffer buf = new StringBuffer();
80
81 while (pointer < patternLength) {
82 char c = pattern.charAt(pointer);
83 pointer++;
84
85 switch (state) {
86 case LITERAL_STATE:
87 handleLiteralState(c, tokenList, buf);
88 break;
89 case FORMAT_MODIFIER_STATE:
90 handleFormatModifierState(c, tokenList, buf);
91 break;
92 case OPTION_STATE:
93 processOption(c, tokenList, buf);
94 break;
95 case KEYWORD_STATE:
96 handleKeywordState(c, tokenList, buf);
97 break;
98 case RIGHT_PARENTHESIS_STATE:
99 handleRightParenthesisState(c, tokenList, buf);
100 break;
101
102 default:
103 }
104 }
105
106
107 switch (state) {
108 case LITERAL_STATE:
109 addValuedToken(Token.LITERAL, buf, tokenList);
110 break;
111 case KEYWORD_STATE:
112 tokenList.add(new Token(Token.SIMPLE_KEYWORD, buf.toString()));
113 break;
114 case RIGHT_PARENTHESIS_STATE:
115 tokenList.add(Token.RIGHT_PARENTHESIS_TOKEN);
116 break;
117
118 case FORMAT_MODIFIER_STATE:
119 case OPTION_STATE:
120 throw new ScanException("Unexpected end of pattern string");
121 }
122
123 return tokenList;
124 }
125
126 private void handleRightParenthesisState(char c, List<Token> tokenList, StringBuffer buf) {
127 tokenList.add(Token.RIGHT_PARENTHESIS_TOKEN);
128 switch (c) {
129 case CoreConstants.RIGHT_PARENTHESIS_CHAR:
130 break;
131 case CURLY_LEFT:
132 state = TokenizerState.OPTION_STATE;
133 break;
134 case ESCAPE_CHAR:
135 escape("%{}", buf);
136 state = TokenizerState.LITERAL_STATE;
137 break;
138 default:
139 buf.append(c);
140 state = TokenizerState.LITERAL_STATE;
141 }
142 }
143
144 private void processOption(char c, List<Token> tokenList, StringBuffer buf) throws ScanException {
145 OptionTokenizer ot = new OptionTokenizer(this);
146 ot.tokenize(c, tokenList);
147 }
148
149 private void handleFormatModifierState(char c, List<Token> tokenList, StringBuffer buf) {
150 if (c == CoreConstants.LEFT_PARENTHESIS_CHAR) {
151 addValuedToken(Token.FORMAT_MODIFIER, buf, tokenList);
152 tokenList.add(Token.BARE_COMPOSITE_KEYWORD_TOKEN);
153 state = TokenizerState.LITERAL_STATE;
154 } else if (Character.isJavaIdentifierStart(c)) {
155 addValuedToken(Token.FORMAT_MODIFIER, buf, tokenList);
156 state = TokenizerState.KEYWORD_STATE;
157 buf.append(c);
158 } else {
159 buf.append(c);
160 }
161 }
162
163 private void handleLiteralState(char c, List<Token> tokenList, StringBuffer buf) {
164 switch (c) {
165 case ESCAPE_CHAR:
166 escape("%()", buf);
167 break;
168
169 case CoreConstants.PERCENT_CHAR:
170 addValuedToken(Token.LITERAL, buf, tokenList);
171 tokenList.add(Token.PERCENT_TOKEN);
172 state = TokenizerState.FORMAT_MODIFIER_STATE;
173 break;
174
175 case CoreConstants.RIGHT_PARENTHESIS_CHAR:
176 addValuedToken(Token.LITERAL, buf, tokenList);
177 state = TokenizerState.RIGHT_PARENTHESIS_STATE;
178 break;
179
180 default:
181 buf.append(c);
182 }
183 }
184
185 private void handleKeywordState(char c, List<Token> tokenList, StringBuffer buf) {
186
187 if (Character.isJavaIdentifierPart(c)) {
188 buf.append(c);
189 } else if (c == CURLY_LEFT) {
190 addValuedToken(Token.SIMPLE_KEYWORD, buf, tokenList);
191 state = TokenizerState.OPTION_STATE;
192 } else if (c == CoreConstants.LEFT_PARENTHESIS_CHAR) {
193 addValuedToken(Token.COMPOSITE_KEYWORD, buf, tokenList);
194 state = TokenizerState.LITERAL_STATE;
195 } else if (c == CoreConstants.PERCENT_CHAR) {
196 addValuedToken(Token.SIMPLE_KEYWORD, buf, tokenList);
197 tokenList.add(Token.PERCENT_TOKEN);
198 state = TokenizerState.FORMAT_MODIFIER_STATE;
199 } else if (c == CoreConstants.RIGHT_PARENTHESIS_CHAR) {
200 addValuedToken(Token.SIMPLE_KEYWORD, buf, tokenList);
201 state = TokenizerState.RIGHT_PARENTHESIS_STATE;
202 } else {
203 addValuedToken(Token.SIMPLE_KEYWORD, buf, tokenList);
204 if (c == ESCAPE_CHAR) {
205 if ((pointer < patternLength)) {
206 char next = pattern.charAt(pointer++);
207 escapeUtil.escape("%()", buf, next, pointer);
208 }
209 } else {
210 buf.append(c);
211 }
212 state = TokenizerState.LITERAL_STATE;
213 }
214 }
215
216 void escape(String escapeChars, StringBuffer buf) {
217 if ((pointer < patternLength)) {
218 char next = pattern.charAt(pointer++);
219 escapeUtil.escape(escapeChars, buf, next, pointer);
220 }
221 }
222
223 void optionEscape(String escapeChars, StringBuffer buf) {
224 if ((pointer < patternLength)) {
225 char next = pattern.charAt(pointer++);
226 optionEscapeUtil.escape(escapeChars, buf, next, pointer);
227 }
228 }
229
230 private void addValuedToken(int type, StringBuffer buf, List<Token> tokenList) {
231 if (buf.length() > 0) {
232 tokenList.add(new Token(type, buf.toString()));
233 buf.setLength(0);
234 }
235 }
236 }