1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package de.smartics.exceptions.i18n.message;
17
18 import java.lang.reflect.Field;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.text.ChoiceFormat;
22 import java.text.MessageFormat;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Map;
26 import java.util.MissingResourceException;
27 import java.util.ResourceBundle;
28
29 import ognl.OgnlContext;
30 import ognl.OgnlException;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34
35 import de.smartics.exceptions.i18n.ConfigurationException;
36 import de.smartics.exceptions.i18n.MessageComposer;
37 import de.smartics.exceptions.i18n.MethodAccessConfigurationException;
38 import de.smartics.exceptions.i18n.PropertyAccessConfigurationException;
39 import de.smartics.exceptions.i18n.app.ConfigurationExceptionCode;
40 import de.smartics.exceptions.i18n.message.MessageParamParser.MessageParamInfo;
41 import de.smartics.exceptions.ognl.OgnlExpression;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 @Deprecated
58 public class DefaultMessageComposer implements MessageComposer
59 {
60
61
62
63
64
65
66
67
68
69 private final Log log = LogFactory.getLog(DefaultMessageComposer.class);
70
71
72
73
74
75
76
77
78 public DefaultMessageComposer()
79 {
80 }
81
82
83
84
85
86
87
88
89
90
91
92 @Override
93 public String composeMessage(final Object exception, final Locale locale,
94 final ResourceBundle bundle, final String keyPrefix,
95 final MessageType messageType)
96 {
97 if (messageType == null)
98 {
99 throw new NullPointerException(
100 "Message type to compose a message must not be 'null'.");
101 }
102
103 final String key = messageType.createKey(keyPrefix);
104 final String messageTemplate = bundle.getString(key);
105
106 final byte maxIndex = MessageTemplateAnalyser.maxIndex(messageTemplate);
107 if (maxIndex > -1)
108 {
109 final Object[] messageArguments = new Object[maxIndex + 1];
110
111 final MessageFormat formatter =
112 new MessageFormat(messageTemplate, locale);
113 Class<?> currentClass = exception.getClass();
114 do
115 {
116 supplyParentPropertyValues(exception, key, bundle, formatter,
117 messageType, messageArguments);
118
119 final Field[] declaredFields = currentClass.getDeclaredFields();
120 supplyPropertyValues(exception, key, bundle, formatter, messageType,
121 messageArguments, declaredFields);
122
123 final Method[] declaredMethods = currentClass.getDeclaredMethods();
124 supplyPropertyValues(exception, key, bundle, formatter, messageType,
125 messageArguments, declaredMethods);
126
127 currentClass = currentClass.getSuperclass();
128 }
129 while (currentClass != null && currentClass != Exception.class
130 && !isAllInformationProvided(messageArguments));
131
132 replaceMissingInformationDefaults(messageArguments);
133
134 final String output = formatter.format(messageArguments);
135 return output;
136 }
137 else
138 {
139 return messageTemplate;
140 }
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154 private void supplyParentPropertyValues(final Object exception,
155 final String key, final ResourceBundle bundle,
156 final MessageFormat formatter, final MessageType messageType,
157 final Object[] messageArguments)
158 {
159 final Class<?> clazz = exception.getClass();
160 final ParentMessageParam annotation =
161 clazz.getAnnotation(ParentMessageParam.class);
162 if (annotation != null)
163 {
164 final Map<String, List<MessageParamInfo>> map =
165 messageType.getParentMessageParamInfos(annotation);
166 for (final Map.Entry<String, List<MessageParamInfo>> entry : map
167 .entrySet())
168 {
169 final String attribute = entry.getKey();
170 final List<MessageParamInfo> infos = entry.getValue();
171
172 for (final MessageParamInfo info : infos)
173 {
174 final int index = Integer.parseInt(info.getPlaceholderId());
175 if (index < messageArguments.length)
176 {
177 Object value = getProperty(exception, attribute);
178 if (value != null)
179 {
180 value = applyOgnl(exception, attribute, info, value);
181 applyCompoundMessageInfo(exception, key, bundle, formatter,
182 attribute, index);
183 messageArguments[index] = value;
184 }
185 }
186 }
187 }
188 }
189 }
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206 private Object applyOgnl(final Object exception, final String attribute,
207 final MessageParamInfo info, final Object value)
208 throws PropertyAccessConfigurationException
209 {
210 final Object newValue;
211 final String ognlPath = info.getOgnlPath();
212 if (ognlPath != null)
213 {
214 newValue = evaluateOgnl(exception, attribute, value, ognlPath);
215 }
216 else
217 {
218 newValue = value;
219 }
220
221 return newValue;
222 }
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237 private void applyCompoundMessageInfo(final Object exception,
238 final String key, final ResourceBundle bundle,
239 final MessageFormat formatter, final String attribute, final int index)
240 throws PropertyAccessConfigurationException
241 {
242 final String[] limitStrings =
243 createLimitStrings(exception, attribute, key, index, bundle);
244 if (limitStrings != null)
245 {
246 final ChoiceFormat choiceFormat =
247 new ChoiceFormat(new double[] { 0d, 1d, 2d }, limitStrings);
248 formatter.setFormat(index, choiceFormat);
249 }
250 }
251
252
253
254
255
256
257
258
259 private static boolean isAllInformationProvided(
260 final Object[] messageArguments)
261 {
262 for (final Object element : messageArguments)
263 {
264 if (element == null)
265 {
266 return false;
267 }
268 }
269 return true;
270 }
271
272 private static void replaceMissingInformationDefaults(
273 final Object[] messageArguments)
274 {
275 for (int i = messageArguments.length - 1; i >= 0; i--)
276 {
277 if (messageArguments[i] == null)
278 {
279 messageArguments[i] = "-/-";
280 }
281 }
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299 private void supplyPropertyValues(final Object bean, final String key,
300 final ResourceBundle bundle, final MessageFormat formatter,
301 final MessageType messageType, final Object[] messageArguments,
302 final Field[] fields) throws PropertyAccessConfigurationException
303 {
304 String fieldName;
305 for (final Field field : fields)
306 {
307 fieldName = field.getName();
308 if (log.isTraceEnabled())
309 {
310 log.trace("Processing annotations for field '" + fieldName + "'...");
311 }
312
313 final MessageParam messageParam = field.getAnnotation(MessageParam.class);
314 if (messageParam != null)
315 {
316 final List<MessageParamInfo> infos =
317 messageType.getMessageParamInfos(fieldName, messageParam);
318 for (final MessageParamInfo info : infos)
319 {
320 final int index = Integer.parseInt(info.getPlaceholderId());
321 if (index < messageArguments.length)
322 {
323 Object value = getValue(bean, field);
324 if (value != null)
325 {
326 value = applyOgnl(bean, fieldName, info, value);
327 applyCompoundMessageInfo(bean, key, bundle, formatter, fieldName,
328 index);
329 messageArguments[index] = value;
330 }
331 }
332 }
333 }
334 }
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352 private void supplyPropertyValues(final Object bean, final String key,
353 final ResourceBundle bundle, final MessageFormat formatter,
354 final MessageType messageType, final Object[] messageArguments,
355 final Method[] methods) throws PropertyAccessConfigurationException
356 {
357 String methodName;
358 for (final Method method : methods)
359 {
360 methodName = method.getName();
361 if (log.isTraceEnabled())
362 {
363 log.trace("Processing annotations for method '" + methodName + "'...");
364 }
365
366 final MessageParam messageParam =
367 method.getAnnotation(MessageParam.class);
368 if (messageParam != null)
369 {
370 final String propertyName = calcPropertyName(methodName);
371 final List<MessageParamInfo> infos =
372 messageType.getMessageParamInfos(propertyName, messageParam);
373 for (final MessageParamInfo info : infos)
374 {
375 final int index = Integer.parseInt(info.getPlaceholderId());
376 if (index < messageArguments.length)
377 {
378 Object value = getValue(bean, method);
379 if (value != null)
380 {
381 value = applyOgnl(bean, propertyName, info, value);
382 applyCompoundMessageInfo(bean, key, bundle, formatter,
383 methodName, index);
384 messageArguments[index] = value;
385 }
386 }
387 }
388 }
389 }
390 }
391
392 private static String calcPropertyName(final String methodName)
393 {
394 return methodName.startsWith("get") && methodName.length() > 3 ? methodName
395 .substring(3) : methodName;
396 }
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414 private static String[] createLimitStrings(final Object bean,
415 final String fieldName, final String key, final int index,
416 final ResourceBundle bundle) throws PropertyAccessConfigurationException
417 {
418 try
419 {
420 final String compoundKeyPrefix = key + '.' + index;
421
422 final String key0 = compoundKeyPrefix + ".0";
423 final String key1 = compoundKeyPrefix + ".1";
424 final String keyM = compoundKeyPrefix + ".m";
425
426
427
428
429 final boolean isCompoundMessage =
430 containsKey(bundle, key0) || containsKey(bundle, key1)
431 || containsKey(bundle, keyM);
432 final String[] limitStrings;
433 if (isCompoundMessage)
434 {
435 limitStrings =
436 new String[] { bundle.getString(key0), bundle.getString(key1),
437 bundle.getString(keyM) };
438 }
439 else
440 {
441 limitStrings = null;
442 }
443 return limitStrings;
444 }
445 catch (final MissingResourceException e)
446 {
447 throw new PropertyAccessConfigurationException(e,
448 ConfigurationExceptionCode.COMPOUND_MESSAGE_MISSING, fieldName,
449 bean.getClass());
450 }
451 }
452
453
454
455
456
457
458
459
460
461 private static boolean containsKey(final ResourceBundle bundle,
462 final String key)
463 {
464 try
465 {
466 bundle.getString(key);
467 return true;
468 }
469 catch (final Exception e)
470 {
471 return false;
472 }
473 }
474
475
476
477
478
479
480
481
482
483
484
485
486
487 private Object evaluateOgnl(final Object bean, final String fieldName,
488 final Object value, final String ognlPath)
489 throws PropertyAccessConfigurationException
490 {
491 try
492 {
493 final OgnlExpression expression = new OgnlExpression(ognlPath);
494 final OgnlContext context = new OgnlContext();
495 return expression.getValue(context, value);
496 }
497 catch (final OgnlException e)
498 {
499 throw new PropertyAccessConfigurationException(e,
500 ConfigurationExceptionCode.CONFIGURATION_OGNL_SYNTAX_ERROR,
501 fieldName, bean.getClass());
502 }
503 }
504
505
506
507
508
509
510
511
512
513
514
515
516
517 private static Object getValue(final Object bean, final Field field)
518 throws ConfigurationException
519 {
520 final PropertyInfo propertyInfo = field.getAnnotation(PropertyInfo.class);
521 final Object value;
522 if (propertyInfo != null && !"".equals(propertyInfo.getter()))
523 {
524 final String getter = propertyInfo.getter();
525 value = getValue(bean, field.getName(), getter);
526 }
527 else
528 {
529 final boolean accessible = field.isAccessible();
530 try
531 {
532 field.setAccessible(true);
533 value = getFieldValue(bean, field);
534 }
535 finally
536 {
537 field.setAccessible(accessible);
538 }
539
540
541
542
543
544
545
546
547
548 }
549
550 return value;
551 }
552
553 private static Object getValue(final Object bean, final Method method)
554 throws ConfigurationException
555 {
556 try
557 {
558 final Object value = method.invoke(bean, new Object[0]);
559 return value;
560 }
561 catch (final SecurityException e)
562 {
563 throw new MethodAccessConfigurationException(e,
564 ConfigurationExceptionCode.CONFIGURATION_SECURITY,
565 calcPropertyName(method.getName()), bean.getClass(), method.getName());
566 }
567 catch (final IllegalArgumentException e)
568 {
569 throw new MethodAccessConfigurationException(e,
570 ConfigurationExceptionCode.CONFIGURATION_NOARG,
571 calcPropertyName(method.getName()), bean.getClass(), method.getName());
572 }
573 catch (final IllegalAccessException e)
574 {
575 throw new MethodAccessConfigurationException(e,
576 ConfigurationExceptionCode.CONFIGURATION_MISSING_GETTER,
577 calcPropertyName(method.getName()), bean.getClass(), method.getName());
578 }
579 catch (final InvocationTargetException e)
580 {
581 throw new MethodAccessConfigurationException(e,
582 ConfigurationExceptionCode.CONFIGURATION_RUNTIME_ACCESS,
583 calcPropertyName(method.getName()), bean.getClass(), method.getName());
584 }
585 }
586
587
588
589
590
591
592
593
594
595
596
597 private static Object getFieldValue(final Object bean, final Field field)
598 {
599 try
600 {
601 final Object value = field.get(bean);
602 return value;
603 }
604 catch (final IllegalArgumentException e)
605 {
606 assert false : "The field '" + field.getName()
607 + "' is not a member of the class '" + bean.getClass()
608 + "': " + e.getMessage();
609 }
610 catch (final IllegalAccessException e)
611 {
612 assert false : "The field '" + field.getName() + "' in class '"
613 + bean.getClass() + "' is not accessible: "
614 + e.getMessage();
615 }
616
617
618 assert false : "The system should never reach this point.";
619 return null;
620 }
621
622
623
624
625
626
627
628
629
630
631
632
633
634 private static Object getValue(final Object bean, final String propertyName,
635 final String methodName) throws MethodAccessConfigurationException
636 {
637 try
638 {
639 final Method method = bean.getClass().getMethod(methodName, new Class[0]);
640 final Object value = method.invoke(bean, new Object[0]);
641 return value;
642 }
643 catch (final SecurityException e)
644 {
645 throw new MethodAccessConfigurationException(e,
646 ConfigurationExceptionCode.CONFIGURATION_SECURITY, propertyName,
647 bean.getClass(), methodName);
648 }
649 catch (final NoSuchMethodException e)
650 {
651 throw new MethodAccessConfigurationException(e,
652 ConfigurationExceptionCode.CONFIGURATION_MISSING_GETTER,
653 propertyName, bean.getClass(), methodName);
654 }
655 catch (final IllegalArgumentException e)
656 {
657 throw new MethodAccessConfigurationException(e,
658 ConfigurationExceptionCode.CONFIGURATION_NOARG, propertyName,
659 bean.getClass(), methodName);
660 }
661 catch (final IllegalAccessException e)
662 {
663 throw new MethodAccessConfigurationException(e,
664 ConfigurationExceptionCode.CONFIGURATION_MISSING_GETTER,
665 propertyName, bean.getClass(), methodName);
666 }
667 catch (final InvocationTargetException e)
668 {
669 throw new MethodAccessConfigurationException(e,
670 ConfigurationExceptionCode.CONFIGURATION_RUNTIME_ACCESS,
671 propertyName, bean.getClass(), methodName);
672 }
673 }
674
675
676
677
678
679
680
681
682
683
684
685 private static Object getProperty(final Object bean, final String propertyName)
686 throws PropertyAccessConfigurationException
687 {
688 try
689 {
690
691 final String methodName = "get" + Helper.capitalize(propertyName);
692 final Method method = bean.getClass().getMethod(methodName, new Class[0]);
693 final Object value = method.invoke(bean, new Object[0]);
694 return value;
695 }
696 catch (final IllegalAccessException e)
697 {
698 throw new PropertyAccessConfigurationException(e,
699 ConfigurationExceptionCode.CONFIGURATION_INACCESSIBLE_PROPERTY,
700 propertyName, bean.getClass());
701 }
702 catch (final InvocationTargetException e)
703 {
704 throw new PropertyAccessConfigurationException(e,
705 ConfigurationExceptionCode.CONFIGURATION_PROPERTY_RUNTIME_ACCESS,
706 propertyName, bean.getClass());
707 }
708 catch (final NoSuchMethodException e)
709 {
710 throw new PropertyAccessConfigurationException(e,
711 ConfigurationExceptionCode.CONFIGURATION_NO_GETTER_FOR_PROPERTY,
712 propertyName, bean.getClass());
713 }
714 }
715
716
717 }