1 package com.github.mygreen.supercsv.localization;
2
3 import java.util.Formatter;
4 import java.util.HashMap;
5 import java.util.LinkedHashMap;
6 import java.util.LinkedList;
7 import java.util.Map;
8 import java.util.Objects;
9 import java.util.Optional;
10
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13
14 import com.github.mygreen.supercsv.expression.CustomFunctions;
15 import com.github.mygreen.supercsv.expression.ExpressionEvaluationException;
16 import com.github.mygreen.supercsv.expression.ExpressionLanguage;
17 import com.github.mygreen.supercsv.expression.ExpressionLanguageJEXLImpl;
18 import com.github.mygreen.supercsv.util.StackUtils;
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 public class MessageInterpolator {
34
35 private static final Logger logger = LoggerFactory.getLogger(MessageInterpolator.class);
36
37 private ExpressionLanguage expressionLanguage;
38
39
40
41
42
43
44
45
46 public MessageInterpolator() {
47
48
49 ExpressionLanguageJEXLImplpressionLanguageJEXLImpl.html#ExpressionLanguageJEXLImpl">ExpressionLanguageJEXLImpl el = new ExpressionLanguageJEXLImpl();
50
51 Map<String, Object> funcs = new HashMap<>();
52 funcs.put("f", CustomFunctions.class);
53 el.getJexlEngine().setFunctions(funcs);
54
55 setExpressionLanguage(el);
56 }
57
58
59
60
61
62 public MessageInterpolator(final ExpressionLanguage expressionLanguage) {
63 Objects.requireNonNull(expressionLanguage, "expressionLanguage should not be null.");
64 this.expressionLanguage = expressionLanguage;
65 }
66
67
68
69
70
71
72
73
74 public String interpolate(final String message, final Map<String, ?> vars) {
75 return interpolate(message, vars, false);
76 }
77
78
79
80
81
82
83
84
85
86 public String interpolate(final String message, final Map<String, ?> vars, boolean recursive) {
87 return parse(message, vars, recursive, null);
88 }
89
90
91
92
93
94
95
96
97
98
99
100 public String interpolate(final String message, final Map<String, ?> vars, boolean recursive,
101 final MessageResolver messageResolver) {
102 return parse(message, vars, recursive, messageResolver);
103 }
104
105
106
107
108
109
110
111
112 protected String parse(final String message, final Map<String, ?> vars, boolean recursive, final MessageResolver messageResolver) {
113
114
115 final StringBuilder sb = new StringBuilder(message.length());
116
117
118
119
120
121
122 final LinkedList<String> stack = new LinkedList<String>();
123
124 final int length = message.length();
125
126 for(int i=0; i < length; i++) {
127 final char c = message.charAt(i);
128
129 if(StackUtils.equalsTopElement(stack, "\\")) {
130
131 String escapedChar = StackUtils.popup(stack) + c;
132
133 if(!stack.isEmpty()) {
134
135 stack.push(escapedChar);
136
137 } else {
138
139 sb.append(c);
140
141 }
142
143 } else if(c == '\\') {
144
145 stack.push(String.valueOf(c));
146
147 } else if(c == '$') {
148 stack.push(String.valueOf(c));
149
150 } else if(c == '{') {
151
152 if(!stack.isEmpty() && !StackUtils.equalsAnyBottomElement(stack, new String[]{"$", "{"})) {
153
154 throw new MessageParseException(message, "expression not start with '{' or '$'");
155
156 } else {
157 stack.push(String.valueOf(c));
158 }
159
160
161 } else if(c == '}') {
162
163 if(StackUtils.equalsAnyBottomElement(stack, new String[]{"{", "$"})) {
164
165 String expression = StackUtils.popupAndConcat(stack) + c;
166
167
168 expression = removeEscapeChar(expression, '\\');
169
170 String result = evaluate(expression, vars, recursive, messageResolver);
171 sb.append(result);
172
173 } else {
174 sb.append(c);
175
176 }
177
178 } else {
179
180 if(stack.isEmpty()) {
181 sb.append(c);
182
183 } else {
184 stack.push(String.valueOf(c));
185 }
186
187 }
188
189 }
190
191 if(!stack.isEmpty()) {
192 String val = StackUtils.popupAndConcat(stack);
193 val = removeEscapeChar(val, '\\');
194 sb.append(val);
195 }
196
197 return sb.toString();
198 }
199
200 private String evaluate(final String expression, final Map<String, ?> values, final boolean recursive,
201 final MessageResolver messageResolver) {
202
203 if(expression.startsWith("{")) {
204
205 final String varName = expression.substring(1, expression.length()-1);
206
207 if(values.containsKey(varName)) {
208
209 final Object value = values.get(varName);
210 final String eval = (value == null) ? "" : value.toString();
211 if(!eval.isEmpty() && recursive) {
212 return parse(eval, values, recursive, messageResolver);
213 } else {
214 return eval;
215 }
216
217 } else if(messageResolver != null) {
218
219 final Optional<String> eval = messageResolver.getMessage(varName);
220 if(!eval.isPresent()) {
221
222 return String.format("{%s}", varName);
223 }
224
225 if(recursive) {
226 return parse(eval.get(), values, recursive, messageResolver);
227 } else {
228 return eval.get();
229 }
230
231 } else {
232
233 return expression.toString();
234 }
235
236 } else if(expression.startsWith("${")) {
237
238 final String expr = expression.substring(2, expression.length()-1);
239 final String eval = evaluateExpression(expr, values);
240 if(recursive) {
241 return parse(eval, values, recursive, messageResolver);
242 } else {
243 return eval;
244 }
245
246 }
247
248 throw new MessageParseException(expression, "not support expression.");
249
250 }
251
252
253
254
255
256
257
258
259 protected String evaluateExpression(final String expression, final Map<String, ?> values) throws ExpressionEvaluationException {
260
261 final Map<String, Object> context = new LinkedHashMap<String, Object>();
262 context.putAll(values);
263
264
265 context.computeIfAbsent("formatter", key -> new Formatter());
266
267 final String value = expressionLanguage.evaluate(expression, context).toString();
268 if(logger.isTraceEnabled()) {
269 logger.trace("evaluate expression language: expression='{}' ===> value='{}'", expression, value);
270 }
271
272 return value;
273 }
274
275
276
277
278
279
280
281 private String removeEscapeChar(final String str, final char escapeChar) {
282
283 if(str == null || str.isEmpty()) {
284 return str;
285 }
286
287 final String escapeStr = String.valueOf(escapeChar);
288 StringBuilder sb = new StringBuilder();
289
290 final LinkedList<String> stack = new LinkedList<>();
291
292 final int length = str.length();
293 for(int i=0; i < length; i++) {
294 final char c = str.charAt(i);
295
296 if(StackUtils.equalsTopElement(stack, escapeStr)) {
297
298 StackUtils.popup(stack);
299 sb.append(c);
300
301 } else if(c == escapeChar) {
302
303 stack.push(String.valueOf(c));
304
305 } else {
306 sb.append(c);
307 }
308
309 }
310
311 if(!stack.isEmpty()) {
312 sb.append(StackUtils.popupAndConcat(stack));
313 }
314
315 return sb.toString();
316
317 }
318
319
320
321
322
323 public ExpressionLanguage getExpressionLanguage() {
324 return expressionLanguage;
325 }
326
327
328
329
330
331 public void setExpressionLanguage(ExpressionLanguage expressionLanguage) {
332 this.expressionLanguage = expressionLanguage;
333 }
334
335 }