1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.expression.common;
18
19 import java.util.LinkedList;
20 import java.util.List;
21 import java.util.Stack;
22
23 import org.springframework.expression.Expression;
24 import org.springframework.expression.ExpressionParser;
25 import org.springframework.expression.ParseException;
26 import org.springframework.expression.ParserContext;
27
28
29
30
31
32
33
34
35
36
37 public abstract class TemplateAwareExpressionParser implements ExpressionParser {
38
39
40
41
42 private static final ParserContext NON_TEMPLATE_PARSER_CONTEXT = new ParserContext() {
43
44 @Override
45 public String getExpressionPrefix() {
46 return null;
47 }
48
49 @Override
50 public String getExpressionSuffix() {
51 return null;
52 }
53
54 @Override
55 public boolean isTemplate() {
56 return false;
57 }
58 };
59
60 @Override
61 public Expression parseExpression(String expressionString) throws ParseException {
62 return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT);
63 }
64
65 @Override
66 public Expression parseExpression(String expressionString, ParserContext context)
67 throws ParseException {
68 if (context == null) {
69 context = NON_TEMPLATE_PARSER_CONTEXT;
70 }
71
72 if (context.isTemplate()) {
73 return parseTemplate(expressionString, context);
74 }
75 else {
76 return doParseExpression(expressionString, context);
77 }
78 }
79
80 private Expression parseTemplate(String expressionString, ParserContext context)
81 throws ParseException {
82 if (expressionString.length() == 0) {
83 return new LiteralExpression("");
84 }
85 Expression[] expressions = parseExpressions(expressionString, context);
86 if (expressions.length == 1) {
87 return expressions[0];
88 }
89 else {
90 return new CompositeStringExpression(expressionString, expressions);
91 }
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 private Expression[] parseExpressions(String expressionString, ParserContext context)
113 throws ParseException {
114 List<Expression> expressions = new LinkedList<Expression>();
115 String prefix = context.getExpressionPrefix();
116 String suffix = context.getExpressionSuffix();
117 int startIdx = 0;
118 while (startIdx < expressionString.length()) {
119 int prefixIndex = expressionString.indexOf(prefix, startIdx);
120 if (prefixIndex >= startIdx) {
121
122 if (prefixIndex > startIdx) {
123 expressions.add(createLiteralExpression(context,
124 expressionString.substring(startIdx, prefixIndex)));
125 }
126 int afterPrefixIndex = prefixIndex + prefix.length();
127 int suffixIndex = skipToCorrectEndSuffix(prefix, suffix,
128 expressionString, afterPrefixIndex);
129
130 if (suffixIndex == -1) {
131 throw new ParseException(expressionString, prefixIndex,
132 "No ending suffix '" + suffix
133 + "' for expression starting at character "
134 + prefixIndex + ": "
135 + expressionString.substring(prefixIndex));
136 }
137
138 if (suffixIndex == afterPrefixIndex) {
139 throw new ParseException(expressionString, prefixIndex,
140 "No expression defined within delimiter '" + prefix + suffix
141 + "' at character " + prefixIndex);
142 }
143
144 String expr = expressionString.substring(prefixIndex + prefix.length(),
145 suffixIndex);
146 expr = expr.trim();
147
148 if (expr.length() == 0) {
149 throw new ParseException(expressionString, prefixIndex,
150 "No expression defined within delimiter '" + prefix + suffix
151 + "' at character " + prefixIndex);
152 }
153
154 expressions.add(doParseExpression(expr, context));
155 startIdx = suffixIndex + suffix.length();
156 }
157 else {
158
159 expressions.add(createLiteralExpression(context,
160 expressionString.substring(startIdx)));
161 startIdx = expressionString.length();
162 }
163 }
164 return expressions.toArray(new Expression[expressions.size()]);
165 }
166
167 private Expression createLiteralExpression(ParserContext context, String text) {
168 return new LiteralExpression(text);
169 }
170
171
172
173
174
175
176
177
178 private boolean isSuffixHere(String expressionString, int pos, String suffix) {
179 int suffixPosition = 0;
180 for (int i = 0; i < suffix.length() && pos < expressionString.length(); i++) {
181 if (expressionString.charAt(pos++) != suffix.charAt(suffixPosition++)) {
182 return false;
183 }
184 }
185 if (suffixPosition != suffix.length()) {
186
187 return false;
188 }
189 return true;
190 }
191
192
193
194
195
196
197
198
199
200
201
202 private int skipToCorrectEndSuffix(String prefix, String suffix,
203 String expressionString, int afterPrefixIndex) throws ParseException {
204
205
206
207 int pos = afterPrefixIndex;
208 int maxlen = expressionString.length();
209 int nextSuffix = expressionString.indexOf(suffix, afterPrefixIndex);
210 if (nextSuffix == -1) {
211 return -1;
212 }
213 Stack<Bracket> stack = new Stack<Bracket>();
214 while (pos < maxlen) {
215 if (isSuffixHere(expressionString, pos, suffix) && stack.isEmpty()) {
216 break;
217 }
218 char ch = expressionString.charAt(pos);
219 switch (ch) {
220 case '{':
221 case '[':
222 case '(':
223 stack.push(new Bracket(ch, pos));
224 break;
225 case '}':
226 case ']':
227 case ')':
228 if (stack.isEmpty()) {
229 throw new ParseException(expressionString, pos, "Found closing '"
230 + ch + "' at position " + pos + " without an opening '"
231 + Bracket.theOpenBracketFor(ch) + "'");
232 }
233 Bracket p = stack.pop();
234 if (!p.compatibleWithCloseBracket(ch)) {
235 throw new ParseException(expressionString, pos, "Found closing '"
236 + ch + "' at position " + pos
237 + " but most recent opening is '" + p.bracket
238 + "' at position " + p.pos);
239 }
240 break;
241 case '\'':
242 case '"':
243
244 int endLiteral = expressionString.indexOf(ch, pos + 1);
245 if (endLiteral == -1) {
246 throw new ParseException(expressionString, pos,
247 "Found non terminating string literal starting at position "
248 + pos);
249 }
250 pos = endLiteral;
251 break;
252 }
253 pos++;
254 }
255 if (!stack.isEmpty()) {
256 Bracket p = stack.pop();
257 throw new ParseException(expressionString, p.pos, "Missing closing '"
258 + Bracket.theCloseBracketFor(p.bracket) + "' for '" + p.bracket
259 + "' at position " + p.pos);
260 }
261 if (!isSuffixHere(expressionString, pos, suffix)) {
262 return -1;
263 }
264 return pos;
265 }
266
267
268
269
270
271
272
273
274 private static class Bracket {
275
276 char bracket;
277
278 int pos;
279
280 Bracket(char bracket, int pos) {
281 this.bracket = bracket;
282 this.pos = pos;
283 }
284
285 boolean compatibleWithCloseBracket(char closeBracket) {
286 if (this.bracket == '{') {
287 return closeBracket == '}';
288 }
289 else if (this.bracket == '[') {
290 return closeBracket == ']';
291 }
292 return closeBracket == ')';
293 }
294
295 static char theOpenBracketFor(char closeBracket) {
296 if (closeBracket == '}') {
297 return '{';
298 }
299 else if (closeBracket == ']') {
300 return '[';
301 }
302 return '(';
303 }
304
305 static char theCloseBracketFor(char openBracket) {
306 if (openBracket == '{') {
307 return '}';
308 }
309 else if (openBracket == '[') {
310 return ']';
311 }
312 return ')';
313 }
314 }
315
316
317
318
319
320
321
322
323 protected abstract Expression doParseExpression(String expressionString,
324 ParserContext context) throws ParseException;
325
326 }