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