1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.trinidad.convert;
20
21 import java.text.DecimalFormat;
22 import java.text.DecimalFormatSymbols;
23 import java.text.NumberFormat;
24 import java.text.ParsePosition;
25
26 import java.util.Currency;
27 import java.util.HashMap;
28 import java.util.Locale;
29 import java.util.Map;
30
31 import java.util.regex.Matcher;
32
33 import javax.el.ValueExpression;
34
35 import javax.faces.application.FacesMessage;
36 import javax.faces.component.UIComponent;
37 import javax.faces.context.FacesContext;
38 import javax.faces.convert.ConverterException;
39
40 import javax.faces.el.ValueBinding;
41
42 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFConverter;
43 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
44 import org.apache.myfaces.trinidad.bean.FacesBean;
45 import org.apache.myfaces.trinidad.bean.PropertyKey;
46 import org.apache.myfaces.trinidad.context.RequestContext;
47 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
48 import org.apache.myfaces.trinidad.util.ComponentUtils;
49 import org.apache.myfaces.trinidad.util.MessageFactory;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 @JSFConverter(configExcluded=true)
122 public class NumberConverter extends javax.faces.convert.NumberConverter
123 {
124
125
126
127
128 public static final String CONVERTER_ID = "org.apache.myfaces.trinidad.Number";
129
130
131
132
133
134
135
136
137
138 public static final String CONVERT_PATTERN_MESSAGE_ID =
139 "org.apache.myfaces.trinidad.convert.NumberConverter.CONVERT_PATTERN";
140
141
142
143
144
145
146
147
148
149 public static final String CONVERT_NUMBER_MESSAGE_ID =
150 "org.apache.myfaces.trinidad.convert.NumberConverter.CONVERT_NUMBER";
151
152
153
154
155
156
157
158
159
160
161 public static final String CONVERT_CURRENCY_MESSAGE_ID =
162 "org.apache.myfaces.trinidad.convert.NumberConverter.CONVERT_CURRENCY";
163
164
165
166
167
168
169
170
171
172
173 public static final String CONVERT_PERCENT_MESSAGE_ID =
174 "org.apache.myfaces.trinidad.convert.NumberConverter.CONVERT_PERCENT";
175
176
177
178
179
180
181
182
183
184 @Override
185 public Object getAsObject(
186 FacesContext context,
187 UIComponent component,
188 String value)
189 {
190 if (null == context || null == component)
191 {
192 throw new NullPointerException(_LOG.getMessage(
193 "NULL_FACESCONTEXT_OR_UICOMPONENT"));
194 }
195
196 if (null == value)
197 return null;
198
199 if (isDisabled())
200 return value;
201
202 value = value.trim();
203 if (value.length() < 1)
204 return null;
205
206 String pattern = getPattern();
207 String type = getType();
208
209 if (null == pattern && null == type)
210 {
211 throw new IllegalArgumentException(_LOG.getMessage(
212 "EITHER_PATTERN_OR_TYPE_MUST_SPECIFIED"));
213 }
214
215 RequestContext reqCtx = RequestContext.getCurrentInstance();
216 Locale locale = _getLocale(reqCtx, context);
217
218 NumberFormat fmt = _getNumberFormat(pattern, type, locale, reqCtx);
219
220 DecimalFormat df = (DecimalFormat)fmt;
221 df.setParseBigDecimal(true);
222 DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
223
224
225
226 boolean changed = false;
227 if (dfs.getGroupingSeparator() == '\u00a0')
228 {
229
230
231
232
233 dfs.setGroupingSeparator(' ');
234 df.setDecimalFormatSymbols(dfs);
235
236
237
238
239 value = value.replace('\u00a0', ' ');
240
241 changed = true;
242 }
243
244 ParsePosition pp = new ParsePosition(0);
245 Number num = (Number)fmt.parseObject(value, pp);
246
247
248 int typeIdx = _getType(pattern, type);
249 if (num == null && (typeIdx == _CURRENCY_TYPE || typeIdx == _PERCENT_TYPE))
250 {
251
252 NumberFormat nfmt = NumberFormat.getNumberInstance(locale);
253 DecimalFormat ndf = (DecimalFormat)nfmt;
254 ndf.setParseBigDecimal(true);
255 DecimalFormatSymbols ndfs = null;
256
257 if (changed)
258 {
259 ndfs = ndf.getDecimalFormatSymbols();
260 ndfs.setGroupingSeparator(' ');
261 ndf.setDecimalFormatSymbols(ndfs);
262 }
263
264
265
266
267 num = (Number)nfmt.parseObject(value, pp);
268 if (typeIdx == _PERCENT_TYPE && num != null)
269 num = num.doubleValue() / 100.0;
270 }
271
272
273
274 if (changed)
275 {
276 dfs.setGroupingSeparator('\u00a0');
277 df.setDecimalFormatSymbols(dfs);
278 }
279
280 if (pp.getIndex() != value.length())
281 {
282
283
284
285
286
287 Object label = ConverterUtils.getComponentLabel(component);
288 Object[] params = null;
289
290 if (typeIdx == _PATTERN_TYPE)
291 {
292
293
294 pattern = getLocalizedPattern(context, pattern, dfs);
295
296 params = new Object[] {label, value, pattern};
297 }
298 else if (typeIdx == _NUMBER_TYPE)
299 {
300 params = new Object[] {label, value};
301 }
302 else if (typeIdx == _CURRENCY_TYPE)
303 {
304 params = new Object[] {label, value, fmt.format(_EXAMPLE_CURRENCY)};
305 }
306 else if (typeIdx == _PERCENT_TYPE)
307 {
308 params = new Object[] {label, value, fmt.format(_EXAMPLE_PERCENT)};
309 }
310
311 throw new ConverterException(
312 getConvertMessage(context, component, value, params));
313 }
314
315
316
317
318
319
320
321
322
323 if (isIntegerOnly())
324 return Long.valueOf(num.longValue());
325
326 return num;
327 }
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348 @Override
349 public String getAsString(
350 FacesContext context,
351 UIComponent component,
352 Object value)
353 {
354 if ( null == context || null == component )
355 {
356 throw new NullPointerException(_LOG.getMessage(
357 "NULL_FACESCONTEXT_OR_UICOMPONENT"));
358 }
359
360 if(value == null)
361 return "";
362
363 if(value instanceof String)
364 return (String)value;
365
366 if (isDisabled())
367 return value.toString();
368
369 if (!(value instanceof Number))
370 throw new IllegalArgumentException(_LOG.getMessage(
371 "VALUE_NOT_JAVA_LANG_NUMBER_TYPE"));
372
373 String pattern = getPattern();
374 String type = getType();
375
376 if (null == pattern && null == type)
377 {
378 throw new IllegalArgumentException(_LOG.getMessage(
379 "EITHER_PATTERN_OR_TYPE_MUST_SPECIFIED"));
380 }
381
382
383 RequestContext reqCtx = RequestContext.getCurrentInstance();
384 Locale locale = _getLocale(reqCtx, context);
385
386 NumberFormat formatter = _getNumberFormat(pattern, type, locale, reqCtx);
387
388 _setFormatProperties(formatter);
389
390 if("currency".equals(type))
391 {
392 _setCurrencyFormattingProperties(reqCtx, formatter);
393 }
394
395 return formatter.format(value);
396 }
397
398 @Override
399 public void restoreState(
400 FacesContext context,
401 Object state)
402 {
403 _facesBean.restoreState(context, state);
404 }
405
406 @Override
407 public Object saveState(FacesContext context)
408 {
409 return _facesBean.saveState(context);
410 }
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425 public void setValueExpression(String name, ValueExpression expression)
426 {
427 ConverterUtils.setValueExpression(_facesBean, name, expression) ;
428 }
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443 public ValueExpression getValueExpression(String name)
444 {
445 return ConverterUtils.getValueExpression(_facesBean, name);
446 }
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462 public void setValueBinding(String name, ValueBinding binding)
463 {
464 ConverterUtils.setValueBinding(_facesBean, name, binding) ;
465 }
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480 public ValueBinding getValueBinding(String name)
481 {
482 return ConverterUtils.getValueBinding(_facesBean, name);
483 }
484
485
486
487
488
489
490
491
492 public void setMessageDetailConvertPattern(String convertPatternMessageDetail)
493 {
494 _facesBean.setProperty(_CONVERT_PATTERN_MESSAGE_DETAIL_KEY, convertPatternMessageDetail);
495 }
496
497
498
499
500
501
502
503
504 @JSFProperty
505 public String getMessageDetailConvertPattern()
506 {
507 Object msg = _facesBean.getProperty(_CONVERT_PATTERN_MESSAGE_DETAIL_KEY);
508 return ComponentUtils.resolveString(msg);
509 }
510
511
512
513
514
515
516
517
518
519 public void setMessageDetailConvertNumber(String convertNumberMessageDetail)
520 {
521 _facesBean.setProperty(_CONVERT_NUMBER_MESSAGE_DETAIL_KEY, convertNumberMessageDetail);
522 }
523
524
525
526
527
528
529
530
531 @JSFProperty
532 public String getMessageDetailConvertNumber()
533 {
534 Object msg = _facesBean.getProperty(_CONVERT_NUMBER_MESSAGE_DETAIL_KEY);
535 return ComponentUtils.resolveString(msg);
536 }
537
538
539
540
541
542
543
544
545
546
547 public void setMessageDetailConvertCurrency(String convertCurrencyMessageDetail)
548 {
549 _facesBean.setProperty(_CONVERT_CURRENCY_MESSAGE_DETAIL_KEY,convertCurrencyMessageDetail);
550 }
551
552
553
554
555
556
557
558
559 @JSFProperty
560 public String getMessageDetailConvertCurrency()
561 {
562 Object msg = _facesBean.getProperty(_CONVERT_CURRENCY_MESSAGE_DETAIL_KEY);
563 return ComponentUtils.resolveString(msg);
564 }
565
566
567
568
569
570
571
572
573
574 public void setMessageDetailConvertPercent(String convertPercentMessageDetail)
575 {
576 _facesBean.setProperty(_CONVERT_PERCENT_MESSAGE_DETAIL_KEY, convertPercentMessageDetail);
577 }
578
579
580
581
582
583
584
585
586
587 @JSFProperty
588 public String getMessageDetailConvertPercent()
589 {
590 Object msg = _facesBean.getProperty(_CONVERT_PERCENT_MESSAGE_DETAIL_KEY);
591 return ComponentUtils.resolveString(msg);
592 }
593
594
595
596
597
598
599 public void setHintPattern(String hintPattern)
600 {
601 _facesBean.setProperty(_HINT_PATTERN_KEY, hintPattern);
602 }
603
604
605
606
607
608
609 public String getHintPattern()
610 {
611 Object obj = _facesBean.getProperty(_HINT_PATTERN_KEY);
612 return ComponentUtils.resolveString(obj);
613 }
614
615
616
617 @Override
618 public void setCurrencyCode(String currencyCode)
619 {
620 _facesBean.setProperty(_CURRENCY_CODE_KEY, currencyCode);
621 }
622
623 @JSFProperty
624 @Override
625 public String getCurrencyCode()
626 {
627 Object currCode = _facesBean.getProperty(_CURRENCY_CODE_KEY);
628 return ComponentUtils.resolveString(currCode);
629 }
630
631 @Override
632 public void setCurrencySymbol(String currencySymbol)
633 {
634 _facesBean.setProperty(_CURRENCY_SYMBOL_KEY, currencySymbol);
635 }
636
637 @JSFProperty
638 @Override
639 public String getCurrencySymbol()
640 {
641 Object currSymbol = _facesBean.getProperty(_CURRENCY_SYMBOL_KEY);
642 return ComponentUtils.resolveString(currSymbol);
643 }
644
645 @Override
646 public void setGroupingUsed(boolean groupingUsed)
647 {
648 Boolean grpUsed = _getBooleanValue(groupingUsed);
649 _facesBean.setProperty(_GROUPING_USED_KEY, grpUsed);
650 }
651
652 @JSFProperty(defaultValue="true")
653 @Override
654 public boolean isGroupingUsed()
655 {
656 Object grpUSed = _facesBean.getProperty(_GROUPING_USED_KEY);
657 return ComponentUtils.resolveBoolean(grpUSed, true);
658 }
659
660 @Override
661 public void setIntegerOnly(boolean integerOnly)
662 {
663 _facesBean.setProperty(_INTEGER_ONLY_KEY, _getBooleanValue(integerOnly));
664 }
665
666 @JSFProperty(defaultValue="false")
667 @Override
668 public boolean isIntegerOnly()
669 {
670 Object isInt = _facesBean.getProperty(_INTEGER_ONLY_KEY);
671 return ComponentUtils.resolveBoolean(isInt, false);
672 }
673
674
675
676
677
678
679
680
681
682 @Override
683 public void setLocale(Locale locale)
684 {
685 _facesBean.setProperty(_LOCALE_KEY, locale);
686 }
687
688
689
690
691
692
693
694
695 @JSFProperty
696 @Override
697 public Locale getLocale()
698 {
699 Object locale = _facesBean.getProperty(_LOCALE_KEY);
700 return ComponentUtils.resolveLocale(locale);
701 }
702
703
704
705
706 @Override
707 public void setMaxFractionDigits(int maxFractionDigits)
708 {
709 _facesBean.setProperty(_MAX_FRACTION_DIGITS_KEY, _getIntValue(maxFractionDigits));
710 }
711
712 @JSFProperty
713 @Override
714 public int getMaxFractionDigits()
715 {
716 Object value = _facesBean.getProperty(_MAX_FRACTION_DIGITS_KEY);
717 return ComponentUtils.resolveInteger(value);
718 }
719
720 @Override
721 public void setMaxIntegerDigits(int maxIntegerDigits)
722 {
723 _facesBean.setProperty(_MAX_INTEGER_DIGITS_KEY, _getIntValue(maxIntegerDigits));
724 }
725
726 @JSFProperty
727 @Override
728 public int getMaxIntegerDigits()
729 {
730 Object value = _facesBean.getProperty(_MAX_INTEGER_DIGITS_KEY);
731 return ComponentUtils.resolveInteger(value);
732 }
733
734 @Override
735 public void setMinFractionDigits(int minFractionDigits)
736 {
737 _facesBean.setProperty(_MIN_FRACTION_DIGITS_KEY, _getIntValue(minFractionDigits));
738 }
739
740 @JSFProperty
741 @Override
742 public int getMinFractionDigits()
743 {
744 Object value = _facesBean.getProperty(_MIN_FRACTION_DIGITS_KEY);
745 return ComponentUtils.resolveInteger(value);
746 }
747
748 @Override
749 public void setMinIntegerDigits(int minIntegerDigits)
750 {
751 _facesBean.setProperty(_MIN_INTEGER_DIGITS_KEY, _getIntValue(minIntegerDigits));
752 }
753
754 @JSFProperty
755 @Override
756 public int getMinIntegerDigits()
757 {
758 Object value = _facesBean.getProperty(_MIN_INTEGER_DIGITS_KEY);
759 return ComponentUtils.resolveInteger(value);
760 }
761
762 @Override
763 public void setPattern(String pattern)
764 {
765 _facesBean.setProperty(_PATTERN_KEY, pattern);
766 }
767
768 @JSFProperty
769 @Override
770 public String getPattern()
771 {
772 Object pattern = _facesBean.getProperty(_PATTERN_KEY);
773 return ComponentUtils.resolveString(pattern, true);
774 }
775
776
777
778
779
780
781
782
783
784 public String getLocalizedPattern(FacesContext context, String pattern, DecimalFormatSymbols dfs)
785 {
786 if (pattern == null)
787 return null;
788
789 RequestContext reqCtx = RequestContext.getCurrentInstance();
790 String type = getType();
791 Locale locale = _getLocale(reqCtx, context);
792 DecimalFormat df = (DecimalFormat) _getNumberFormat(pattern, type, locale, reqCtx);
793 if (dfs == null)
794 {
795 dfs = df.getDecimalFormatSymbols();
796 }
797
798
799
800 char decSep = dfs.getDecimalSeparator();
801 char groupSep = dfs.getGroupingSeparator();
802
803 char[] patternArr = pattern.toCharArray();
804 for (int i = 0; i < patternArr.length; i++)
805 {
806 char c = patternArr[i];
807 if (c == '\u002E')
808 patternArr[i] = decSep;
809 else if (c == '\u002C')
810 patternArr[i] = groupSep;
811 }
812 pattern = new String(patternArr);
813
814
815
816
817
818
819
820
821
822 int idx = pattern.indexOf('\u00A4');
823 if (idx == -1)
824 return pattern;
825
826 if (idx + 1 < pattern.length() && pattern.charAt(idx + 1) == '\u00A4')
827 {
828
829 String symbol = dfs.getInternationalCurrencySymbol();
830 if (symbol.length() > 0)
831 pattern = pattern.replaceAll(new String(new char[] {'\u00A4', '\u00A4'}), Matcher.quoteReplacement(symbol));
832 }
833 else
834 {
835
836 String symbol = dfs.getCurrencySymbol();
837 if (symbol.length() > 0)
838 pattern = pattern.replaceAll(new String(new char[] {'\u00A4'}), Matcher.quoteReplacement(symbol));
839 }
840
841 return pattern;
842 }
843
844 @Override
845 public void setType(String type)
846 {
847 _facesBean.setProperty(_TYPE_KEY, type);
848 }
849
850 @JSFProperty(defaultValue="number")
851 @Override
852 public String getType()
853 {
854 Object type = _facesBean.getProperty(_TYPE_KEY);
855 return ComponentUtils.resolveString(type, "number");
856 }
857
858
859
860
861 @Override
862 public int hashCode()
863 {
864 int result = 17;
865 result = result * 37 + _getHashValue(getLocale());
866 result = result * 37 + _getHashValue(getCurrencyCode());
867 result = result * 37 + _getHashValue(getCurrencySymbol());
868 result = result * 37 + _getHashValue(getType());
869 result = result * 37 + _getHashValue(getPattern());
870 result = result * 37 + getMaxFractionDigits();
871 result = result * 37 + getMaxIntegerDigits();
872 result = result * 37 + getMinFractionDigits();
873 result = result * 37 + getMinIntegerDigits();
874 result = result * 37 + (isDisabled() ? 1 : 0);
875 result = result * 37 + (isGroupingUsed() ? 1: 0);
876 result = result * 37 + (isIntegerOnly()? 1: 0);
877 result = result * 37 + (isTransient() ? 1: 0);
878 result = result * 37 + _getHashValue(getMessageDetailConvertPattern());
879 result = result * 37 + _getHashValue(getMessageDetailConvertNumber());
880 result = result * 37 + _getHashValue(getMessageDetailConvertCurrency());
881 result = result * 37 + _getHashValue(getMessageDetailConvertPercent());
882 return result;
883 }
884
885
886
887
888 @Override
889 public boolean equals(Object numberConverter)
890 {
891 if (this == numberConverter)
892 return true;
893
894 if (numberConverter instanceof NumberConverter)
895 {
896
897 NumberConverter nConv = (NumberConverter) numberConverter;
898
899 return
900 getMaxFractionDigits() == nConv.getMaxFractionDigits() &&
901 getMaxIntegerDigits() == nConv.getMaxIntegerDigits() &&
902 getMinFractionDigits() == nConv.getMinFractionDigits() &&
903 getMinIntegerDigits() == nConv.getMinIntegerDigits() &&
904 isDisabled() == nConv.isDisabled() &&
905 isTransient() == nConv.isTransient() &&
906 isGroupingUsed() == nConv.isGroupingUsed() &&
907 isIntegerOnly() == nConv.isIntegerOnly() &&
908 ConverterUtils.equals(getType(), nConv.getType()) &&
909 ConverterUtils.equals(getLocale(), nConv.getLocale()) &&
910 ConverterUtils.equals(getCurrencyCode(), nConv.getCurrencyCode()) &&
911 ConverterUtils.equals(getCurrencySymbol(), nConv.getCurrencySymbol()) &&
912 ConverterUtils.equals(getPattern(), nConv.getPattern()) &&
913 ConverterUtils.equals(getMessageDetailConvertPattern(),
914 nConv.getMessageDetailConvertPattern()) &&
915 ConverterUtils.equals(getMessageDetailConvertNumber(),
916 nConv.getMessageDetailConvertNumber()) &&
917 ConverterUtils.equals(getMessageDetailConvertCurrency(),
918 nConv.getMessageDetailConvertCurrency())&&
919 ConverterUtils.equals(getMessageDetailConvertPercent(),
920 nConv.getMessageDetailConvertPercent());
921
922 }
923 return false;
924 }
925
926
927
928
929
930 public void setDisabled(boolean isDisabled)
931 {
932 _facesBean.setProperty(_DISABLED_KEY, Boolean.valueOf(isDisabled));
933 }
934
935
936
937
938
939 public boolean isDisabled()
940 {
941 Boolean disabled = (Boolean) _facesBean.getProperty(_DISABLED_KEY);
942
943 return (disabled != null) ? disabled.booleanValue() : false;
944 }
945
946 private static int _getHashValue(Object obj)
947 {
948 return obj == null? 0 : obj.hashCode();
949 }
950
951 private static DecimalFormatSymbols _getCachedDecimalFormatSymbol(Locale locale)
952 {
953 synchronized(_SYMBOLS_LOCK)
954 {
955
956 Object dfs = _patternFormatSymbolsHolder.get(locale);
957
958
959
960 if (dfs != null)
961 return (DecimalFormatSymbols) ((DecimalFormatSymbols) dfs).clone();
962 else
963 return null;
964 }
965 }
966
967 private static void _cacheDecimalFormatSymbols(
968 Locale locale,
969 DecimalFormatSymbols symbols)
970 {
971 synchronized(_SYMBOLS_LOCK)
972 {
973
974
975
976 if (_patternFormatSymbolsHolder == null)
977 _patternFormatSymbolsHolder = new HashMap<Locale, DecimalFormatSymbols>();
978 else
979
980
981 _patternFormatSymbolsHolder.put(locale, (DecimalFormatSymbols)symbols.clone());
982 }
983 }
984
985 private static Boolean _getBooleanValue(boolean value)
986 {
987 return (value ? Boolean.TRUE : Boolean.FALSE);
988 }
989
990 private static Integer _getIntValue(int value)
991 {
992 return Integer.valueOf(value);
993 }
994
995 private NumberFormat _getCachedNumberFormat(
996 String pattern,
997 String type,
998 Locale locale)
999 {
1000 synchronized(_TYPE_LOCK)
1001 {
1002
1003
1004 String key = ((pattern != null) ? pattern : type);
1005 Map<Locale, NumberFormat> nfMap = _numberFormatHolder.get(key);
1006
1007 if (nfMap == null)
1008 return null;
1009 else
1010 {
1011 NumberFormat nf = nfMap.get(locale);
1012 if (nf != null)
1013 return (NumberFormat) nf.clone();
1014 }
1015 return null;
1016 }
1017 }
1018
1019 private void _cacheNumberFormat(
1020 NumberFormat format,
1021 String pattern,
1022 String type,
1023 Locale locale)
1024 {
1025 synchronized(_TYPE_LOCK)
1026 {
1027
1028
1029
1030 if (_numberFormatHolder == null)
1031 _numberFormatHolder = new HashMap<String, Map<Locale, NumberFormat>>();
1032
1033 else
1034 {
1035
1036
1037 String key = ((pattern != null) ? pattern : type);
1038
1039 Map<Locale, NumberFormat> nfMap = _numberFormatHolder.get(key);
1040
1041
1042
1043 if (nfMap == null)
1044 {
1045 nfMap = new HashMap<Locale, NumberFormat>();
1046 _numberFormatHolder.put(key, nfMap);
1047
1048 }
1049
1050
1051 nfMap.put(locale, (NumberFormat)format.clone());
1052 }
1053 }
1054 }
1055
1056 private NumberFormat _getNumberFormat(
1057 String pattern,
1058 String type,
1059 Locale locale,
1060 RequestContext reqCtx
1061 )
1062 {
1063 NumberFormat nfmt;
1064
1065 int formatType = _getType(pattern, type);
1066
1067 nfmt = _getCachedNumberFormat(pattern, type, locale);
1068
1069 if (nfmt == null)
1070 {
1071 nfmt = _getNumberFormatter(formatType, pattern, locale);
1072
1073 _cacheNumberFormat(nfmt,pattern, type, locale);
1074 }
1075
1076 if (nfmt instanceof DecimalFormat)
1077 {
1078 DecimalFormat dfmt = (DecimalFormat)nfmt;
1079
1080
1081 DecimalFormatSymbols dfSymbols = dfmt.getDecimalFormatSymbols();
1082
1083 _setUpDecimalSymbolFormatProperties(dfSymbols, reqCtx, locale);
1084
1085
1086 ((DecimalFormat) nfmt).setDecimalFormatSymbols(dfSymbols);
1087 }
1088 else
1089 {
1090 if(_LOG.isWarning())
1091 {
1092 _LOG.warning("Failed to get hold of DecimalFormat for type: +" + type + "\n" +
1093 "decimal separator," +
1094 "number grouping separator," +
1095 "currency code" +
1096 "will be defaulted based on locale " + locale.toString());
1097 }
1098 }
1099 return nfmt;
1100 }
1101
1102 private void _setUpDecimalSymbolFormatProperties(
1103 DecimalFormatSymbols symbols,
1104 RequestContext reqCtx,
1105 Locale locale
1106 )
1107 {
1108 if (reqCtx != null)
1109 {
1110 char ch = (char) 0;
1111
1112 if ((ch = reqCtx.getDecimalSeparator()) != (char)0)
1113 symbols.setDecimalSeparator(ch);
1114
1115 if ((ch = reqCtx.getNumberGroupingSeparator()) != (char)0)
1116 symbols.setGroupingSeparator(ch);
1117
1118 }
1119 else
1120 {
1121 if (_LOG.isWarning())
1122 {
1123 _LOG.warning("NULL_REQUESTCONTEXT", locale.toString());
1124 }
1125 }
1126 }
1127
1128
1129
1130
1131 private void _setFormatProperties(NumberFormat formatter) {
1132
1133 formatter.setGroupingUsed(isGroupingUsed());
1134
1135 if (isMaximumFractionDigitsSet())
1136 {
1137 formatter.setMaximumFractionDigits(getMaxFractionDigits());
1138 }
1139
1140 if (isMaximumIntegerDigitsSet())
1141 {
1142 formatter.setMaximumIntegerDigits(getMaxIntegerDigits());
1143 }
1144
1145 if (isMinimumFractionDigitsSet())
1146 {
1147 formatter.setMinimumFractionDigits(getMinFractionDigits());
1148 }
1149
1150 if (isMinimumIntegerDigitsSet())
1151 {
1152 formatter.setMinimumIntegerDigits(getMinIntegerDigits());
1153 }
1154 }
1155
1156 private void _setCurrencyInformation(
1157 RequestContext context,
1158 DecimalFormatSymbols symbols)
1159 {
1160 String currencyCode = _getCurrencyCode(context);
1161
1162
1163 if (currencyCode != null)
1164 {
1165 symbols.setCurrency(Currency.getInstance(currencyCode));
1166 return;
1167 }
1168
1169 if (getCurrencySymbol() != null)
1170 {
1171 symbols.setCurrencySymbol(getCurrencySymbol());
1172
1173
1174 _LOG.fine("Using currency symbol as currecny code evaluates to null");
1175 }
1176
1177 }
1178
1179 private NumberFormat _getNumberFormatter(
1180 int formatType,
1181 String pattern,
1182 Locale locale)
1183 {
1184 NumberFormat nfmt;
1185
1186 if(_PATTERN_TYPE == formatType)
1187 {
1188 DecimalFormatSymbols symbols = _getCachedDecimalFormatSymbol(locale);
1189
1190 if (null == symbols)
1191 {
1192 symbols = new DecimalFormatSymbols(locale);
1193
1194 _cacheDecimalFormatSymbols(locale, symbols);
1195 }
1196 nfmt = new DecimalFormat(pattern, symbols);
1197 }
1198
1199 else if(_NUMBER_TYPE == formatType)
1200 {
1201 nfmt = NumberFormat.getNumberInstance(locale);
1202 }
1203 else if(_CURRENCY_TYPE == formatType)
1204 {
1205 nfmt = NumberFormat.getCurrencyInstance(locale);
1206 }
1207 else if(_PERCENT_TYPE == formatType)
1208 {
1209 nfmt = NumberFormat.getPercentInstance(locale);
1210 }
1211 else
1212 {
1213
1214 assert (formatType > _PATTERN_TYPE || formatType < _NUMBER_TYPE) : "invalid type" ;
1215 nfmt = null;
1216 }
1217 return nfmt;
1218 }
1219
1220 private Object _getRawConvertCurrencyMessageDetail()
1221 {
1222 return _facesBean.getRawProperty(_CONVERT_CURRENCY_MESSAGE_DETAIL_KEY);
1223 }
1224
1225 private Object _getRawConvertNumberMessageDetail()
1226 {
1227 return _facesBean.getRawProperty(_CONVERT_NUMBER_MESSAGE_DETAIL_KEY);
1228 }
1229
1230 private Object _getRawConvertPatternMessageDetail()
1231 {
1232 return _facesBean.getRawProperty(_CONVERT_PATTERN_MESSAGE_DETAIL_KEY);
1233 }
1234
1235 private Object _getRawConvertPercentMessageDetail()
1236 {
1237 return _facesBean.getRawProperty(_CONVERT_PERCENT_MESSAGE_DETAIL_KEY);
1238 }
1239
1240 protected final FacesMessage getConvertMessage(
1241 FacesContext context,
1242 UIComponent component,
1243 String inputValue,
1244 Object[] params
1245 )
1246 {
1247 int type = _getType(getPattern(), getType());
1248 Object convMsgDet;
1249 String msgId;
1250
1251
1252 if (_PATTERN_TYPE == type)
1253 {
1254 convMsgDet = _getRawConvertPatternMessageDetail();
1255 msgId = CONVERT_PATTERN_MESSAGE_ID;
1256 }
1257 else if(_NUMBER_TYPE == type)
1258 {
1259 convMsgDet = _getRawConvertNumberMessageDetail();
1260 msgId = CONVERT_NUMBER_MESSAGE_ID;
1261 }
1262 else if(_CURRENCY_TYPE == type)
1263 {
1264 convMsgDet = _getRawConvertCurrencyMessageDetail();
1265 msgId = CONVERT_CURRENCY_MESSAGE_ID;
1266 }
1267 else if(_PERCENT_TYPE == type)
1268 {
1269 convMsgDet = _getRawConvertPercentMessageDetail();
1270 msgId = CONVERT_PERCENT_MESSAGE_ID;
1271 }
1272 else
1273 {
1274 throw new IllegalArgumentException("Invalid type: " + getType());
1275 }
1276
1277 return MessageFactory.getMessage(context,
1278 msgId,
1279 convMsgDet,
1280 params,
1281 component);
1282 }
1283
1284 private Locale _getLocale(RequestContext rc, FacesContext context)
1285 {
1286 Locale locale = getLocale();
1287 if (locale == null )
1288 {
1289 locale = rc.getFormattingLocale();
1290 if (locale == null)
1291 {
1292 locale = context.getViewRoot().getLocale();
1293 }
1294 }
1295
1296 return locale;
1297 }
1298
1299 private String _getCurrencyCode(
1300 RequestContext context
1301 )
1302 {
1303 String currencyCode = getCurrencyCode();
1304 if (currencyCode == null)
1305 {
1306 if (context != null)
1307 {
1308 currencyCode = context.getCurrencyCode();
1309 }
1310 else
1311 {
1312 _LOG.warning("NULL_REQUEST_CONTEXT_UNABLE_GET_CURRENCY_CODE");
1313 }
1314 }
1315
1316 return currencyCode;
1317 }
1318
1319
1320 private void _setCurrencyFormattingProperties(
1321 RequestContext context,
1322 NumberFormat numberFormatter
1323 )
1324 {
1325
1326
1327
1328 if (numberFormatter instanceof DecimalFormat)
1329 {
1330 DecimalFormat dfmt = (DecimalFormat)numberFormatter;
1331 DecimalFormatSymbols symbols = dfmt.getDecimalFormatSymbols();
1332 _setCurrencyInformation(context, symbols);
1333 dfmt.setDecimalFormatSymbols(symbols);
1334 }
1335 else
1336 {
1337 _LOG.warning("NUMBER_NOT_DECIMALFORMAT_IGNORE_CURRENCY");
1338 }
1339 }
1340
1341 private static int _getType(String pattern, String type)
1342 {
1343
1344 if (pattern != null)
1345 return _PATTERN_TYPE;
1346
1347 else if ("number".equals(type))
1348 return _NUMBER_TYPE;
1349
1350 else if ("currency".equals(type))
1351 return _CURRENCY_TYPE;
1352
1353 else if ("percent".equals(type))
1354 return _PERCENT_TYPE;
1355 else
1356 throw new IllegalArgumentException(_LOG.getMessage(
1357 "NOT_VALID_TYPE", type));
1358 }
1359
1360
1361
1362
1363
1364 public boolean isMaximumFractionDigitsSet()
1365 {
1366 return _facesBean.getProperty(_MAX_FRACTION_DIGITS_KEY) != null;
1367 }
1368
1369
1370
1371
1372
1373 public boolean isMinimumFractionDigitsSet()
1374 {
1375 return _facesBean.getProperty(_MIN_FRACTION_DIGITS_KEY) != null;
1376 }
1377
1378
1379
1380
1381
1382 public boolean isMaximumIntegerDigitsSet()
1383 {
1384 return _facesBean.getProperty(_MAX_INTEGER_DIGITS_KEY) != null;
1385 }
1386
1387
1388
1389
1390
1391 public boolean isMinimumIntegerDigitsSet()
1392 {
1393 return _facesBean.getProperty(_MIN_INTEGER_DIGITS_KEY) != null;
1394 }
1395
1396 private static final FacesBean.Type _TYPE = new FacesBean.Type();
1397
1398 private static final PropertyKey _CONVERT_CURRENCY_MESSAGE_DETAIL_KEY
1399 = _TYPE.registerKey("messageDetailConvertCurrency", String.class);
1400
1401 private static final PropertyKey _CONVERT_NUMBER_MESSAGE_DETAIL_KEY
1402 = _TYPE.registerKey("messageDetailConvertNumber", String.class);
1403
1404 private static final PropertyKey _CONVERT_PATTERN_MESSAGE_DETAIL_KEY
1405 = _TYPE.registerKey("messageDetailConvertPattern", String.class);
1406
1407 private static final PropertyKey _CONVERT_PERCENT_MESSAGE_DETAIL_KEY
1408 = _TYPE.registerKey("messageDetailConvertPercent", String.class);
1409
1410 private static final PropertyKey _HINT_PATTERN_KEY =
1411 _TYPE.registerKey("hintPattern", String.class);
1412
1413 private static final PropertyKey _CURRENCY_CODE_KEY
1414 = _TYPE.registerKey("currencyCode", String.class);
1415
1416 private static final PropertyKey _CURRENCY_SYMBOL_KEY
1417 = _TYPE.registerKey("currencySymbol", String.class);
1418
1419 private static final PropertyKey _GROUPING_USED_KEY
1420 = _TYPE.registerKey("groupingUsed", boolean.class, Boolean.TRUE);
1421
1422 private static final PropertyKey _INTEGER_ONLY_KEY
1423 = _TYPE.registerKey("integerOnly", boolean.class, Boolean.FALSE);
1424
1425 private static final PropertyKey _LOCALE_KEY
1426 = _TYPE.registerKey("locale", Locale.class);
1427
1428 private static final PropertyKey _MAX_FRACTION_DIGITS_KEY
1429 = _TYPE.registerKey("maxFractionDigits", int.class);
1430
1431 private static final PropertyKey _MAX_INTEGER_DIGITS_KEY
1432 = _TYPE.registerKey("maxIntegerDigits", int.class);
1433
1434 private static final PropertyKey _MIN_FRACTION_DIGITS_KEY
1435 = _TYPE.registerKey("minFractionDigits", int.class);
1436
1437 private static final PropertyKey _MIN_INTEGER_DIGITS_KEY
1438 = _TYPE.registerKey("minIntegerDigits", int.class);
1439
1440 private static final PropertyKey _PATTERN_KEY
1441 = _TYPE.registerKey("pattern", String.class);
1442
1443 private static final PropertyKey _TYPE_KEY
1444 = _TYPE.registerKey("type", String.class, "numeric");
1445
1446
1447 private static final PropertyKey _DISABLED_KEY =
1448 _TYPE.registerKey("disabled", Boolean.class, Boolean.FALSE);
1449
1450 private FacesBean _facesBean = ConverterUtils.getFacesBean(_TYPE);
1451
1452 private static TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(NumberConverter.class);
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462 private static Map<String, Map<Locale, NumberFormat>> _numberFormatHolder =
1463 new HashMap<String, Map<Locale, NumberFormat>>();
1464
1465
1466
1467
1468
1469
1470 private static Map<Locale, DecimalFormatSymbols> _patternFormatSymbolsHolder =
1471 new HashMap<Locale, DecimalFormatSymbols>();
1472
1473 private static final Object _TYPE_LOCK = new Object();
1474
1475 private static final Object _SYMBOLS_LOCK = new Object();
1476
1477 private static final int _NUMBER_TYPE = 1;
1478
1479 private static final int _CURRENCY_TYPE = 2;
1480
1481 private static final int _PERCENT_TYPE = 3;
1482
1483 private static final int _PATTERN_TYPE = 4;
1484
1485 private static final Number _EXAMPLE_PERCENT;
1486
1487 private static final Number _EXAMPLE_CURRENCY;
1488
1489 static
1490 {
1491 _EXAMPLE_PERCENT = 0.3423d;
1492 _EXAMPLE_CURRENCY = 10250;
1493 }
1494 }