1
2
3
4
5
6
7 package org.mule.util;
8
9 import java.util.ArrayList;
10 import java.util.HashMap;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Stack;
14 import java.util.regex.Matcher;
15 import java.util.regex.Pattern;
16
17 import org.apache.commons.logging.Log;
18 import org.apache.commons.logging.LogFactory;
19
20
21
22
23
24 public final class TemplateParser
25 {
26 public static final String ANT_TEMPLATE_STYLE = "ant";
27 public static final String SQUARE_TEMPLATE_STYLE = "square";
28 public static final String CURLY_TEMPLATE_STYLE = "curly";
29 public static final String WIGGLY_MULE_TEMPLATE_STYLE = "mule";
30
31 private static final String DOLLAR_ESCAPE = "@@@";
32 private static final String NULL_AS_STRING = "null";
33
34 private static final Map<String, PatternInfo> patterns = new HashMap<String, PatternInfo>();
35
36 static
37 {
38 patterns.put(ANT_TEMPLATE_STYLE, new PatternInfo(ANT_TEMPLATE_STYLE, "\\$\\{[^\\{\\}]+\\}", "${", "}"));
39 patterns.put(SQUARE_TEMPLATE_STYLE, new PatternInfo(SQUARE_TEMPLATE_STYLE, "\\[[^\\[\\]]+\\]", "[", "]"));
40 patterns.put(CURLY_TEMPLATE_STYLE, new PatternInfo(CURLY_TEMPLATE_STYLE, "\\{[^\\{\\}}]+\\}", "{", "}"));
41
42
43
44
45
46 patterns.put(WIGGLY_MULE_TEMPLATE_STYLE, new PatternInfo(WIGGLY_MULE_TEMPLATE_STYLE,
47 "#\\[((?:#?\\[(?:#?\\[(?:#?\\[(?:#?\\[(?:#?\\[.*?\\]|[^\\[\\]])*?\\]|[^\\[\\]])*?\\]|[^\\[\\]])*?\\]|[^\\[\\]])*?\\]|[^\\[\\]])*?)\\]", "#[", "]"));
48 }
49
50
51
52
53 protected static final Log logger = LogFactory.getLog(TemplateParser.class);
54
55 public static final Pattern ANT_TEMPLATE_PATTERN = patterns.get(ANT_TEMPLATE_STYLE).getPattern();
56 public static final Pattern SQUARE_TEMPLATE_PATTERN = patterns.get(SQUARE_TEMPLATE_STYLE).getPattern();
57 public static final Pattern CURLY_TEMPLATE_PATTERN = patterns.get(CURLY_TEMPLATE_STYLE).getPattern();
58 public static final Pattern WIGGLY_MULE_TEMPLATE_PATTERN = patterns.get(WIGGLY_MULE_TEMPLATE_STYLE).getPattern();
59
60 private final Pattern pattern;
61 private final int pre;
62 private final int post;
63 private final PatternInfo style;
64
65
66 public static TemplateParser createAntStyleParser()
67 {
68 return new TemplateParser(ANT_TEMPLATE_STYLE);
69 }
70
71 public static TemplateParser createSquareBracesStyleParser()
72 {
73 return new TemplateParser(SQUARE_TEMPLATE_STYLE);
74 }
75
76 public static TemplateParser createCurlyBracesStyleParser()
77 {
78 return new TemplateParser(CURLY_TEMPLATE_STYLE);
79 }
80
81 public static TemplateParser createMuleStyleParser()
82 {
83 return new TemplateParser(WIGGLY_MULE_TEMPLATE_STYLE);
84 }
85
86 private TemplateParser(String styleName)
87 {
88 this.style = patterns.get(styleName);
89 if (this.style == null)
90 {
91 throw new IllegalArgumentException("Unknown template style: " + styleName);
92
93 }
94 pattern = style.getPattern();
95 pre = style.getPrefix().length();
96 post = style.getSuffix().length();
97 }
98
99
100
101
102
103
104
105
106
107
108
109 public String parse(Map<?, ?> props, String template)
110 {
111 return parse(props, template, null);
112 }
113
114
115
116
117
118
119
120
121
122
123
124 public String parse(TemplateCallback callback, String template)
125 {
126 return parse(null, template, callback);
127 }
128
129 protected String parse(Map<?, ?> props, String template, TemplateCallback callback)
130 {
131 String result = template;
132 Map<?, ?> newProps = props;
133 if (props != null && !(props instanceof CaseInsensitiveHashMap))
134 {
135 newProps = new CaseInsensitiveHashMap(props);
136 }
137
138 Matcher m = pattern.matcher(result);
139
140 while (m.find())
141 {
142 Object value = null;
143
144 String match = m.group();
145 String propname = match.substring(pre, match.length() - post);
146
147 if (callback != null)
148 {
149 value = callback.match(propname);
150 if (value == null)
151 {
152 value = NULL_AS_STRING;
153 }
154 }
155 else if (newProps != null)
156 {
157 value = newProps.get(propname);
158 }
159
160 if (value == null)
161 {
162 if (logger.isDebugEnabled())
163 {
164 logger.debug("Value " + propname + " not found in context");
165 }
166 }
167 else
168 {
169 String matchRegex = Pattern.quote(match);
170 String valueString = value.toString();
171
172
173 if (valueString.indexOf('$') != -1)
174 {
175 valueString = valueString.replaceAll("\\$", DOLLAR_ESCAPE);
176 }
177
178 if (valueString.indexOf('\\') != -1)
179 {
180 valueString = valueString.replaceAll("\\\\", "\\\\\\\\");
181 }
182
183 result = result.replaceAll(matchRegex, valueString);
184 }
185 }
186 if (result.indexOf(DOLLAR_ESCAPE) != -1)
187 {
188 result = result.replaceAll(DOLLAR_ESCAPE, "\\$");
189 }
190 return result;
191 }
192
193
194
195
196
197
198
199
200
201
202 public List<?> parse(Map<?, ?> props, List<?> templates)
203 {
204 if (templates == null)
205 {
206 return new ArrayList<Object>();
207 }
208
209 List<String> list = new ArrayList<String>(templates.size());
210 for (Object tmpl : templates)
211 {
212 list.add(parse(props, tmpl.toString()));
213 }
214 return list;
215 }
216
217
218
219
220
221
222
223
224
225
226
227 public Map<?, ?> parse(final Map<?, ?> props, Map<?, ?> templates)
228 {
229 return parse(new TemplateCallback()
230 {
231 public Object match(String token)
232 {
233 return props.get(token);
234 }
235 }, templates);
236 }
237
238 public Map<?, ?> parse(TemplateCallback callback, Map<?, ?> templates)
239 {
240 if (templates == null)
241 {
242 return new HashMap<Object, Object>();
243 }
244
245 Map<Object, String> map = new HashMap<Object, String>(templates.size());
246 for (Map.Entry<?, ?> entry : templates.entrySet())
247 {
248 map.put(entry.getKey(), parse(callback, entry.getValue().toString()));
249 }
250 return map;
251 }
252
253 public PatternInfo getStyle()
254 {
255 return style;
256 }
257
258 public boolean isContainsTemplate(String value)
259 {
260 if (value == null)
261 {
262 return false;
263 }
264
265 Matcher m = pattern.matcher(value);
266 return m.find();
267 }
268
269 public boolean isValid(String expression)
270 {
271 try
272 {
273 style.validate(expression);
274 return true;
275 }
276 catch (IllegalArgumentException e)
277 {
278 return false;
279 }
280 }
281
282 public void validate(String expression) throws IllegalArgumentException
283 {
284 style.validate(expression);
285 }
286
287 public static interface TemplateCallback
288 {
289 Object match(String token);
290 }
291
292
293 public static class PatternInfo
294 {
295 String name;
296 String regEx;
297 String prefix;
298 String suffix;
299
300 PatternInfo(String name, String regEx, String prefix, String suffix)
301 {
302 this.name = name;
303 this.regEx = regEx;
304 if (prefix.length() < 1 || prefix.length() > 2)
305 {
306 throw new IllegalArgumentException("Prefix can only be one or two characters long: " + prefix);
307 }
308 this.prefix = prefix;
309 if (suffix.length() != 1)
310 {
311 throw new IllegalArgumentException("Suffix can only be one character long: " + suffix);
312 }
313 this.suffix = suffix;
314 }
315
316 public String getRegEx()
317 {
318 return regEx;
319 }
320
321 public String getPrefix()
322 {
323 return prefix;
324 }
325
326 public String getSuffix()
327 {
328 return suffix;
329 }
330
331 public String getName()
332 {
333 return name;
334 }
335
336 public Pattern getPattern()
337 {
338 return Pattern.compile(regEx, Pattern.CASE_INSENSITIVE);
339 }
340
341 public void validate(String expression) throws IllegalArgumentException
342 {
343 String currentExpression = expression;
344 int lastMatchIdx = 0;
345 while (lastMatchIdx < expression.length())
346 {
347 int start = currentExpression.indexOf(prefix);
348 if (start == -1)
349 {
350
351 break;
352 }
353 lastMatchIdx += start;
354 currentExpression = currentExpression.substring(start);
355 Matcher m = getPattern().matcher(currentExpression);
356 boolean found = m.find();
357 if (found)
358 {
359 if (!currentExpression.startsWith(m.group()))
360 {
361 throw new IllegalArgumentException("Invalid Expression");
362 }
363 int matchSize = m.group().length();
364 lastMatchIdx += matchSize;
365 currentExpression = currentExpression.substring(matchSize);
366 }
367 else
368 {
369 throw new IllegalArgumentException("Invalid Expression");
370 }
371 }
372 }
373
374 }
375 }