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