1 package org.djunits.formatter;
2
3 import java.math.BigDecimal;
4 import java.text.DecimalFormat;
5 import java.text.DecimalFormatSymbols;
6 import java.util.Locale;
7
8 import org.djunits.quantity.def.Reference;
9 import org.djunits.unit.Unit;
10 import org.djunits.unit.Units;
11 import org.djunits.value.Value;
12
13
14
15
16
17
18
19
20
21
22
23
24 @SuppressWarnings({"checkstyle:needbraces", "checkstyle:visibilitymodifier"})
25 public abstract class Formatter<C extends FormatContext>
26 {
27
28 final C ctx;
29
30
31 final Value<?, ?> value;
32
33
34 Unit<?, ?> unit;
35
36
37 String unitStr = null;
38
39
40 boolean useSi = false;
41
42
43
44
45
46 Formatter(final Value<?, ?> value, final C ctx)
47 {
48 this.ctx = ctx;
49 this.value = value;
50 this.unit = value.getDisplayUnit();
51 }
52
53
54
55
56
57 abstract String format();
58
59
60
61
62
63 boolean checkSiUnits()
64 {
65 if (!this.ctx.siUnits)
66 return false;
67 this.unit = this.unit.siUnit();
68 this.useSi = true;
69 this.unitStr = this.unit.siUnit().format(this.ctx.siDivisionSymbol, this.ctx.siDotSeparator, this.ctx.siPowerPrefix,
70 this.ctx.siPowerPostfix);
71 return true;
72 }
73
74
75
76
77
78 boolean checkUnitString()
79 {
80 if (this.ctx.unitString != null)
81 {
82 try
83 {
84 this.unit = Units.resolve(this.unit.getClass(), this.ctx.unitString);
85 this.useSi = false;
86 return true;
87 }
88 catch (Exception e)
89 {
90
91 }
92 }
93 return false;
94 }
95
96
97
98
99
100 boolean checkDisplayUnit()
101 {
102 if (this.ctx.displayUnit != null)
103 {
104 try
105 {
106 this.unit = Units.resolve(this.unit.getClass(), this.ctx.displayUnit.getId());
107 this.useSi = false;
108 return true;
109 }
110 catch (Exception e)
111 {
112
113 }
114 }
115 return false;
116 }
117
118
119
120
121 @SuppressWarnings("checkstyle:needbraces")
122 void formatUnit()
123 {
124 boolean formatted = checkSiUnits();
125 if (!formatted)
126 formatted = checkUnitString();
127 if (!formatted)
128 formatted = checkDisplayUnit();
129 if (this.unitStr == null)
130 this.unitStr = this.ctx.textual ? this.unit.getTextualAbbreviation() : this.unit.getDisplayAbbreviation();
131 }
132
133
134
135
136
137
138
139 static String formatReference(final FormatContext ctx, final Reference<?, ?, ?> reference)
140 {
141 if (!ctx.printReference)
142 {
143 return "";
144 }
145 return ctx.referencePrefix + reference.getId() + ctx.referencePostfix;
146 }
147
148
149
150
151
152
153 String formatValue(final double val)
154 {
155 return switch (this.ctx.formatMode)
156 {
157 case VARIABLE_LENGTH -> formatVariableLength(val);
158 case FIXED_FLOAT -> formatFixedFloat(val);
159 case SCIENTIFIC_ALWAYS -> formatScientific(val);
160 case ENGINEERING_ALWAYS -> formatEngineering(val);
161 case FIXED_WITH_SCI_FALLBACK -> formatFixedSciFallback(val);
162 case FIXED_WITH_ENG_FALLBACK -> formatFixedEngFallback(val);
163 case FORMAT_STRING -> String.format(this.ctx.formatString, val);
164 };
165 }
166
167
168
169
170
171
172 String formatVariableLength(final double val)
173 {
174 BigDecimal bd = BigDecimal.valueOf(val).stripTrailingZeros();
175 int left = bd.precision() - bd.scale();
176 DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(Locale.getDefault());
177 DecimalFormat plain = this.ctx.groupingSeparator ? new DecimalFormat("#,##0.############################", sym)
178 : new DecimalFormat("###0.############################", sym);
179 DecimalFormat sci = new DecimalFormat("0.################E0", sym);
180
181
182 if (left > 10 || left < -6)
183 {
184 return sci.format(bd);
185 }
186 else
187 {
188 return plain.format(bd);
189 }
190 }
191
192
193
194
195
196
197 String formatFixedFloat(final double val)
198 {
199 String gs = this.ctx.groupingSeparator ? "%," : "%";
200 String fmt = gs + this.ctx.width + "." + this.ctx.decimals + "f";
201 return String.format(fmt, val);
202 }
203
204
205
206
207
208
209 String formatScientific(final double val)
210 {
211 String fmt = "%" + this.ctx.width + "." + this.ctx.decimals + (this.ctx.upperE ? "E" : "e");
212 return String.format(fmt, val);
213 }
214
215
216
217
218
219
220 String formatEngineering(final double val)
221 {
222 if (val == 0.0)
223 {
224 return formatFixedFloat(0.0);
225 }
226
227 double abs = Math.abs(val);
228 int exp = (int) Math.floor(Math.log10(abs));
229 int engExp = exp - (exp % 3);
230
231 double mantissa = val / Math.pow(10, engExp);
232
233
234 String gs = this.ctx.groupingSeparator ? "%,." : "%.";
235 String mantFmt = gs + this.ctx.decimals + "f";
236 String mant = String.format(mantFmt, mantissa);
237 String result = mant + (this.ctx.upperE ? "E" : "e") + String.format("%+d", engExp);
238 return pad(result, this.ctx.width);
239 }
240
241
242
243
244
245
246 String formatFixedSciFallback(final double val)
247 {
248 String s = formatFixedFloat(val);
249 if (s.length() <= this.ctx.width)
250 return s;
251 return formatScientific(val);
252 }
253
254
255
256
257
258
259 String formatFixedEngFallback(final double val)
260 {
261 String s = formatFixedFloat(val);
262 if (s.length() <= this.ctx.width)
263 return s;
264 return formatEngineering(val);
265 }
266
267
268
269
270
271
272
273 static String pad(final String s, final int width)
274 {
275 if (s.length() >= width)
276 return s;
277 return String.format("%" + width + "s", s);
278 }
279
280
281
282
283
284
285 static Locale saveLocale(final Locale newLocale)
286 {
287 if (newLocale != null)
288 {
289 Locale oldLocale = Locale.getDefault();
290 Locale.setDefault(newLocale);
291 return oldLocale;
292 }
293 return null;
294 }
295
296
297
298
299
300 static void restoreLocale(final Locale oldLocale)
301 {
302 if (oldLocale != null)
303 {
304 Locale.setDefault(oldLocale);
305 }
306 }
307
308 }