1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess.impl.expr;
18
19 import java.math.BigDecimal;
20 import java.math.MathContext;
21 import java.math.RoundingMode;
22 import java.text.DecimalFormat;
23 import java.text.DecimalFormatSymbols;
24 import java.text.FieldPosition;
25 import java.text.NumberFormat;
26 import java.text.ParsePosition;
27
28
29
30
31
32
33 public class NumberFormatter
34 {
35 public static final RoundingMode ROUND_MODE = RoundingMode.HALF_EVEN;
36
37
38 public enum NotationType {
39
40 EXP_E_MINUS {
41 @Override
42 protected void format(StringBuffer sb, int eIdx) {
43
44 }
45 },
46
47 EXP_E_PLUS {
48 @Override
49 protected void format(StringBuffer sb, int eIdx) {
50 maybeInsertExpPlus(sb, eIdx);
51 }
52 },
53
54 EXP_e_MINUS {
55 @Override
56 protected void format(StringBuffer sb, int eIdx) {
57 sb.setCharAt(eIdx, 'e');
58 }
59 },
60
61 EXP_e_PLUS {
62 @Override
63 protected void format(StringBuffer sb, int eIdx) {
64 sb.setCharAt(eIdx, 'e');
65 maybeInsertExpPlus(sb, eIdx);
66 }
67 };
68
69 protected abstract void format(StringBuffer sb, int idx);
70 }
71
72 private static final int FLT_SIG_DIGITS = 7;
73 private static final int DBL_SIG_DIGITS = 15;
74 private static final int DEC_SIG_DIGITS = 28;
75
76 public static final MathContext FLT_MATH_CONTEXT =
77 new MathContext(FLT_SIG_DIGITS, ROUND_MODE);
78 public static final MathContext DBL_MATH_CONTEXT =
79 new MathContext(DBL_SIG_DIGITS, ROUND_MODE);
80 public static final MathContext DEC_MATH_CONTEXT =
81 new MathContext(DEC_SIG_DIGITS, ROUND_MODE);
82
83
84 private static final String NAN_STR = "1.#QNAN";
85 private static final String POS_INF_STR = "1.#INF";
86 private static final String NEG_INf_STR = "-1.#INF";
87
88 private final TypeFormatter _fltFmt;
89 private final TypeFormatter _dblFmt;
90 private final TypeFormatter _decFmt;
91
92 public NumberFormatter(DecimalFormatSymbols syms) {
93 _fltFmt = new TypeFormatter(FLT_SIG_DIGITS, syms);
94 _dblFmt = new TypeFormatter(DBL_SIG_DIGITS, syms);
95 _decFmt = new TypeFormatter(DEC_SIG_DIGITS, syms);
96 }
97
98 public String format(float f) {
99
100 if(Float.isNaN(f)) {
101 return NAN_STR;
102 }
103 if(Float.isInfinite(f)) {
104 return ((f < 0f) ? NEG_INf_STR : POS_INF_STR);
105 }
106
107 return _fltFmt.format(new BigDecimal(f, FLT_MATH_CONTEXT));
108 }
109
110 public String format(double d) {
111
112 if(Double.isNaN(d)) {
113 return NAN_STR;
114 }
115 if(Double.isInfinite(d)) {
116 return ((d < 0d) ? NEG_INf_STR : POS_INF_STR);
117 }
118
119 return _dblFmt.format(new BigDecimal(d, DBL_MATH_CONTEXT));
120 }
121
122 public String format(BigDecimal bd) {
123 return _decFmt.format(bd.round(DEC_MATH_CONTEXT));
124 }
125
126 private static ScientificFormat createScientificFormat(
127 int prec, DecimalFormatSymbols syms) {
128 DecimalFormat df = new DecimalFormat("0.#E00", syms);
129 df.setMaximumIntegerDigits(1);
130 df.setMaximumFractionDigits(prec);
131 df.setRoundingMode(ROUND_MODE);
132 return new ScientificFormat(df);
133 }
134
135 private static final class TypeFormatter
136 {
137 private final DecimalFormat _df;
138 private final ScientificFormat _dfS;
139 private final int _prec;
140
141 private TypeFormatter(int prec, DecimalFormatSymbols syms) {
142 _prec = prec;
143 _df = new DecimalFormat("0.#", syms);
144 _df.setMaximumIntegerDigits(prec);
145 _df.setMaximumFractionDigits(prec);
146 _df.setRoundingMode(ROUND_MODE);
147 _dfS = createScientificFormat(prec, syms);
148 }
149
150 public String format(BigDecimal bd) {
151 bd = bd.stripTrailingZeros();
152 int prec = bd.precision();
153 int scale = bd.scale();
154
155 int sigDigits = prec;
156 if(scale < 0) {
157 sigDigits -= scale;
158 } else if(scale > prec) {
159 sigDigits += (scale - prec);
160 }
161
162 return ((sigDigits > _prec) ? _dfS.format(bd) : _df.format(bd));
163 }
164 }
165
166 private static void maybeInsertExpPlus(StringBuffer sb, int eIdx) {
167 if(sb.charAt(eIdx + 1) != '-') {
168 sb.insert(eIdx + 1, '+');
169 }
170 }
171
172 public static class ScientificFormat extends NumberFormat
173 {
174 private static final long serialVersionUID = 0L;
175
176 private final NumberFormat _df;
177 private final NotationType _type;
178
179 public ScientificFormat(NumberFormat df) {
180 this(df, NotationType.EXP_E_PLUS);
181 }
182
183 public ScientificFormat(NumberFormat df, NotationType type) {
184 _df = df;
185 _type = type;
186 }
187
188 @Override
189 public StringBuffer format(Object number, StringBuffer toAppendTo,
190 FieldPosition pos)
191 {
192 StringBuffer sb = _df.format(number, toAppendTo, pos);
193 _type.format(sb, sb.lastIndexOf("E"));
194 return sb;
195 }
196
197 @Override
198 public StringBuffer format(double number, StringBuffer toAppendTo,
199 FieldPosition pos) {
200 throw new UnsupportedOperationException();
201 }
202
203 @Override
204 public Number parse(String source, ParsePosition parsePosition) {
205 throw new UnsupportedOperationException();
206 }
207
208 @Override
209 public StringBuffer format(long number, StringBuffer toAppendTo,
210 FieldPosition pos) {
211 throw new UnsupportedOperationException();
212 }
213
214 @Override
215 public int getMaximumFractionDigits() {
216 return _df.getMaximumFractionDigits();
217 }
218
219 @Override
220 public int getMinimumFractionDigits() {
221 return _df.getMinimumFractionDigits();
222 }
223 }
224 }