1
2
3
4
5
6
7
8
9
10
11 package org.mule.util;
12
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Stack;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24
25
26
27
28
29 public final class TemplateParser
30 {
31 public static final String ANT_TEMPLATE_STYLE = "ant";
32 public static final String SQUARE_TEMPLATE_STYLE = "square";
33 public static final String CURLY_TEMPLATE_STYLE = "curly";
34 public static final String WIGGLY_MULE_TEMPLATE_STYLE = "mule";
35
36 private static final String DOLLAR_ESCAPE = "@@@";
37
38 private static final Map<String, PatternInfo> patterns = new HashMap<String, PatternInfo>();
39
40 static
41 {
42 patterns.put(ANT_TEMPLATE_STYLE, new PatternInfo(ANT_TEMPLATE_STYLE, "\\$\\{[^\\}]+\\}", "${", "}"));
43 patterns.put(SQUARE_TEMPLATE_STYLE, new PatternInfo(SQUARE_TEMPLATE_STYLE, "\\[[^\\]]+\\]", "[", "]"));
44 patterns.put(CURLY_TEMPLATE_STYLE, new PatternInfo(CURLY_TEMPLATE_STYLE, "\\{[^\\}]+\\}", "{", "}"));
45
46
47
48
49
50 patterns.put(WIGGLY_MULE_TEMPLATE_STYLE, new PatternInfo(WIGGLY_MULE_TEMPLATE_STYLE,
51 "#\\[((?:#\\[(?:#\\[.*?\\]|\\[.*?\\]|.)*?\\]|\\[.*?\\]|.)*?)\\]", "#[", "]"));
52
53
54
55 }
56
57
58
59
60 protected static final Log logger = LogFactory.getLog(TemplateParser.class);
61
62 public static final Pattern ANT_TEMPLATE_PATTERN = patterns.get(ANT_TEMPLATE_STYLE).getPattern();
63 public static final Pattern SQUARE_TEMPLATE_PATTERN = patterns.get(SQUARE_TEMPLATE_STYLE).getPattern();
64 public static final Pattern CURLY_TEMPLATE_PATTERN = patterns.get(CURLY_TEMPLATE_STYLE).getPattern();
65 public static final Pattern WIGGLY_MULE_TEMPLATE_PATTERN = patterns.get(WIGGLY_MULE_TEMPLATE_STYLE).getPattern();
66
67 private final Pattern pattern;
68 private final int pre;
69 private final int post;
70 private final PatternInfo style;
71
72
73 public static TemplateParser createAntStyleParser()
74 {
75 return new TemplateParser(ANT_TEMPLATE_STYLE);
76 }
77
78 public static TemplateParser createSquareBracesStyleParser()
79 {
80 return new TemplateParser(SQUARE_TEMPLATE_STYLE);
81 }
82
83 public static TemplateParser createCurlyBracesStyleParser()
84 {
85 return new TemplateParser(CURLY_TEMPLATE_STYLE);
86 }
87
88 public static TemplateParser createMuleStyleParser()
89 {
90 return new TemplateParser(WIGGLY_MULE_TEMPLATE_STYLE);
91 }
92
93 private TemplateParser(String styleName)
94 {
95 this.style = patterns.get(styleName);
96 if (this.style == null)
97 {
98 throw new IllegalArgumentException("Unknown template style: " + styleName);
99
100 }
101 pattern = style.getPattern();
102 pre = style.getPrefix().length();
103 post = style.getSuffix().length();
104 }
105
106
107
108
109
110
111
112
113
114
115
116 public String parse(Map props, String template)
117 {
118 return parse(props, template, null);
119 }
120
121
122
123
124
125
126
127
128
129
130
131 public String parse(TemplateCallback callback, String template)
132 {
133 return parse(null, template, callback);
134 }
135
136 protected String parse(Map props, String template, TemplateCallback callback)
137 {
138 String result = template;
139 Map newProps = props;
140 if (props != null && !(props instanceof CaseInsensitiveHashMap))
141 {
142 newProps = new CaseInsensitiveHashMap(props);
143 }
144
145 Matcher m = pattern.matcher(result);
146
147 while (m.find())
148 {
149 Object value = null;
150
151 String match = m.group();
152 String propname = match.substring(pre, match.length() - post);
153
154 if (callback != null)
155 {
156 value = callback.match(propname);
157 }
158 else if (newProps != null)
159 {
160 value = newProps.get(propname);
161 }
162
163 if (value == null)
164 {
165 if (logger.isDebugEnabled())
166 {
167 logger.debug("Value " + propname + " not found in context");
168 }
169 }
170 else
171 {
172 String matchRegex = escape(match);
173 String valueString = value.toString();
174
175
176 if (valueString.indexOf('$') != -1)
177 {
178 valueString = valueString.replaceAll("\\$", DOLLAR_ESCAPE);
179 }
180
181 if (valueString.indexOf('\\') != -1)
182 {
183 valueString = valueString.replaceAll("\\\\", "\\\\\\\\");
184 }
185
186 result = result.replaceAll(matchRegex, valueString);
187 }
188 }
189 if (result.indexOf(DOLLAR_ESCAPE) != -1)
190 {
191 result = result.replaceAll(DOLLAR_ESCAPE, "\\$");
192 }
193 return result;
194 }
195
196
197
198
199
200
201
202
203
204
205 public List parse(Map props, List templates)
206 {
207 if (templates == null)
208 {
209 return new ArrayList();
210 }
211 List list = new ArrayList(templates.size());
212 for (Iterator iterator = templates.iterator(); iterator.hasNext();)
213 {
214 list.add(parse(props, iterator.next().toString()));
215 }
216 return list;
217 }
218
219
220
221
222
223
224
225
226
227
228
229 public Map parse(final Map props, Map templates)
230 {
231 return parse(new TemplateCallback()
232 {
233 public Object match(String token)
234 {
235 return props.get(token);
236 }
237 }, templates);
238 }
239
240 public Map parse(TemplateCallback callback, Map templates)
241 {
242 if (templates == null)
243 {
244 return new HashMap();
245 }
246 Map map = new HashMap(templates.size());
247 Map.Entry entry;
248 for (Iterator iterator = templates.entrySet().iterator(); iterator.hasNext();)
249 {
250 entry = (Map.Entry) iterator.next();
251 map.put(entry.getKey(), parse(callback, entry.getValue().toString()));
252 }
253 return map;
254 }
255
256 private String escape(String string)
257 {
258 int length = string.length();
259 if (length == 0)
260 {
261
262 return string;
263 }
264 else
265 {
266 StringBuffer buffer = new StringBuffer(length * 2);
267 for (int i = 0; i < length; i++)
268 {
269 char currentCharacter = string.charAt(i);
270 switch (currentCharacter)
271 {
272 case '[':
273 case ']':
274 case '{':
275 case '}':
276 case '(':
277 case ')':
278 case '$':
279 case '#':
280 case '*':
281 buffer.append("\\");
282
283 default:
284 buffer.append(currentCharacter);
285 }
286 }
287 return buffer.toString();
288 }
289 }
290
291 public PatternInfo getStyle()
292 {
293 return style;
294 }
295
296 public boolean isContainsTemplate(String value)
297 {
298 if (value == null)
299 {
300 return false;
301 }
302
303 Matcher m = pattern.matcher(value);
304 return m.find();
305 }
306
307 public boolean isValid(String expression)
308 {
309 try
310 {
311 style.validate(expression);
312 return true;
313 }
314 catch (IllegalArgumentException e)
315 {
316 return false;
317 }
318 }
319
320 public void validate(String expression) throws IllegalArgumentException
321 {
322 style.validate(expression);
323 }
324
325 public static interface TemplateCallback
326 {
327 Object match(String token);
328 }
329
330
331 public static class PatternInfo
332 {
333 String name;
334 String regEx;
335 String prefix;
336 String suffix;
337
338 PatternInfo(String name, String regEx, String prefix, String suffix)
339 {
340 this.name = name;
341 this.regEx = regEx;
342 if (prefix.length() < 1 || prefix.length() > 2)
343 {
344 throw new IllegalArgumentException("Prefix can only be one or two characters long: " + prefix);
345 }
346 this.prefix = prefix;
347 if (suffix.length() != 1)
348 {
349 throw new IllegalArgumentException("Suffic can only be one character long: " + suffix);
350 }
351 this.suffix = suffix;
352 }
353
354 public String getRegEx()
355 {
356 return regEx;
357 }
358
359 public String getPrefix()
360 {
361 return prefix;
362 }
363
364 public String getSuffix()
365 {
366 return suffix;
367 }
368
369 public String getName()
370 {
371 return name;
372 }
373
374 public Pattern getPattern()
375 {
376 return Pattern.compile(regEx, Pattern.CASE_INSENSITIVE);
377 }
378
379 public void validate(String expression) throws IllegalArgumentException
380 {
381 Stack<Character> openDelimiterStack = new Stack<Character>();
382
383 int charCount = expression.length();
384 int index = 0;
385 char nextChar = ' ';
386 char preDelim = 0;
387 char open;
388 char close;
389 boolean inExpression = false;
390 int expressionCount = 0;
391 if (prefix.length() == 2)
392 {
393 preDelim = prefix.charAt(0);
394 open = prefix.charAt(1);
395 }
396 else
397 {
398 open = prefix.charAt(0);
399 }
400 close = suffix.charAt(0);
401
402 for (; index < charCount; index++)
403 {
404 nextChar = expression.charAt(index);
405 if (preDelim != 0 && nextChar == preDelim)
406 {
407
408 if (inExpression)
409 {
410 if (index < charCount && expression.charAt(index + 1) == open)
411 {
412 throw new IllegalArgumentException(String.format("Character %s at position %s suggests an expression inside an expression", open, index));
413 }
414 }
415 else if (openDelimiterStack.isEmpty())
416 {
417 openDelimiterStack.push(nextChar);
418 nextChar = expression.charAt(++index);
419 if (nextChar != open)
420 {
421 throw new IllegalArgumentException(String.format("Character %s at position %s must appear immediately after %s", open, index, preDelim));
422 }
423 inExpression = true;
424
425 }
426 else
427 {
428 throw new IllegalArgumentException(String.format("Character %s at position %s appears out of sequence. Character cannot appear after %s", nextChar, index, openDelimiterStack.pop()));
429 }
430 }
431
432 if (nextChar == open)
433 {
434 if (preDelim == 0 || inExpression)
435 {
436 openDelimiterStack.push(nextChar);
437 }
438
439 else if (openDelimiterStack.size() == 1 && openDelimiterStack.peek().equals(preDelim))
440 {
441 openDelimiterStack.push(nextChar);
442 }
443 else
444 {
445 throw new IllegalArgumentException(String.format("Character %s at position %s appears out of sequence. Character cannot appear after %s", nextChar, index, preDelim));
446 }
447 }
448 else if (nextChar == close)
449 {
450 if (openDelimiterStack.isEmpty())
451 {
452 throw new IllegalArgumentException(String.format("Character %s at position %s appears out of sequence", nextChar, index));
453 }
454 else
455 {
456 openDelimiterStack.pop();
457 if (preDelim != 0 && openDelimiterStack.peek() == preDelim)
458 {
459 openDelimiterStack.pop();
460 }
461
462
463 if (openDelimiterStack.isEmpty())
464 {
465 inExpression = false;
466 expressionCount++;
467
468 }
469 }
470 }
471 }
472 if (expressionCount == 0)
473 {
474 throw new IllegalArgumentException("Not an expression: " + expression);
475 }
476 }
477
478 }
479 }