View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.text.numbers;
18  
19  import java.text.DecimalFormatSymbols;
20  import java.util.Objects;
21  import java.util.function.DoubleFunction;
22  import java.util.function.Function;
23  import java.util.function.Supplier;
24  
25  /**
26   * Enum containing standard double format types with methods to produce
27   * configured formatter instances. This type is intended to provide a
28   * quick and convenient way to create lightweight, thread-safe double format functions
29   * for common format types using a builder pattern. Output can be localized by
30   * passing a {@link DecimalFormatSymbols} instance to the
31   * {@link Builder#formatSymbols(DecimalFormatSymbols) formatSymbols} method or by
32   * directly calling the various other builder configuration methods, such as
33   * {@link Builder#digits(String) digits}.
34   *
35   * <p><strong>Comparison with DecimalFormat</strong></p>
36   * <p>
37   * This type provides some of the same functionality as Java's own
38   * {@link java.text.DecimalFormat}. However, unlike {@code DecimalFormat}, the format
39   * functions produced by this type are lightweight and thread-safe, making them
40   * much easier to work with in multi-threaded environments. They also provide performance
41   * comparable to, and in many cases faster than, {@code DecimalFormat}.
42   * </p>
43   * <p>
44   * It should be noted that the output {@code String} is created by formatting the output of
45   * {@link Double#toString()}. This limits the output precision to the precision required
46   * to exactly represent the input {@code double} and is dependent on the JDK implementation
47   * of {@link Double#toString()}. A number formatted with the maximum
48   * precision should be parsed to the same input {@code double}. This implementation
49   * cannot extend the {@code String} to the required length to represent the exact decimal
50   * value of the {@code double} as per
51   * {@link java.math.BigDecimal#toString() BigDecimal#toString()}.
52   * </p>
53   * <p><strong>Examples</strong></p>
54   * <pre>
55   * // construct a formatter equivalent to Double.toString()
56   * DoubleFunction&lt;String&gt; fmt = DoubleFormat.MIXED.builder().build();
57   *
58   * // construct a formatter equivalent to Double.toString() but using
59   * // format symbols for a specific locale
60   * DoubleFunction&lt;String&gt; fmt = DoubleFormat.MIXED.builder()
61   *      .formatSymbols(DecimalFormatSymbols.getInstance(locale))
62   *      .build();
63   *
64   * // construct a formatter equivalent to the DecimalFormat pattern "0.0##"
65   * DoubleFunction&lt;String&gt; fmt = DoubleFormat.PLAIN.builder()
66   *      .minDecimalExponent(-3)
67   *      .build();
68   *
69   * // construct a formatter equivalent to the DecimalFormat pattern "#,##0.0##",
70   * // where whole number groups of thousands are separated
71   * DoubleFunction&lt;String&gt; fmt = DoubleFormat.PLAIN.builder()
72   *      .minDecimalExponent(-3)
73   *      .groupThousands(true)
74   *      .build();
75   *
76   * // construct a formatter equivalent to the DecimalFormat pattern "0.0##E0"
77   * DoubleFunction&lt;String&gt; fmt = DoubleFormat.SCIENTIFIC.builder()
78   *      .maxPrecision(4)
79   *      .alwaysIncludeExponent(true)
80   *      .build()
81   *
82   * // construct a formatter equivalent to the DecimalFormat pattern "##0.0##E0",
83   * // i.e. "engineering format"
84   * DoubleFunction&lt;String&gt; fmt = DoubleFormat.ENGINEERING.builder()
85   *      .maxPrecision(6)
86   *      .alwaysIncludeExponent(true)
87   *      .build()
88   * </pre>
89   *
90   * <p><strong>Implementation Notes</strong></p>
91   * <p>
92   * {@link java.math.RoundingMode#HALF_EVEN Half-even} rounding is used in cases where the
93   * decimal value must be rounded in order to meet the configuration requirements of the formatter
94   * instance.
95   * </p>
96   *
97   * @since 1.10.0
98   */
99  public enum DoubleFormat {
100 
101     /**
102      * Number format without exponents.
103      * <p>
104      * For example:
105      * </p>
106      *
107      * <pre>
108      * 0.0
109      * 12.401
110      * 100000.0
111      * 1450000000.0
112      * 0.0000000000123
113      * </pre>
114      */
115     PLAIN(PlainDoubleFormat::new),
116 
117     /**
118      * Number format that uses exponents and contains a single digit to the left of the decimal point.
119      * <p>
120      * For example:
121      * </p>
122      *
123      * <pre>
124      * 0.0
125      * 1.2401E1
126      * 1.0E5
127      * 1.45E9
128      * 1.23E-11
129      * </pre>
130      */
131     SCIENTIFIC(ScientificDoubleFormat::new),
132 
133     /**
134      * Number format similar to {@link #SCIENTIFIC scientific format} but adjusted so that the exponent value is always a multiple of 3, allowing easier
135      * alignment with SI prefixes.
136      * <p>
137      * For example:
138      * </p>
139      *
140      * <pre>
141      * 0.0
142      * 12.401
143      * 100.0E3
144      * 1.45E9
145      * 12.3E-12
146      * </pre>
147      */
148     ENGINEERING(EngineeringDoubleFormat::new),
149 
150     /**
151      * Number format that uses {@link #PLAIN plain format} for small numbers and {@link #SCIENTIFIC scientific format} for large numbers. The number thresholds
152      * can be configured through the {@link Builder#plainFormatMinDecimalExponent(int) plainFormatMinDecimalExponent} and
153      * {@link Builder#plainFormatMaxDecimalExponent(int) plainFormatMaxDecimalExponent} properties.
154      * <p>
155      * For example:
156      * </p>
157      *
158      * <pre>
159      * 0.0
160      * 12.401
161      * 100000.0
162      * 1.45E9
163      * 1.23E-11
164      * </pre>
165      */
166     MIXED(MixedDoubleFormat::new);
167 
168     /**
169      * Base class for standard double formatting classes.
170      */
171     private abstract static class AbstractDoubleFormat implements DoubleFunction<String>, ParsedDecimal.FormatOptions {
172 
173         /** Maximum precision; 0 indicates no limit. */
174         private final int maxPrecision;
175 
176         /** Minimum decimal exponent. */
177         private final int minDecimalExponent;
178 
179         /** String representing positive infinity. */
180         private final String positiveInfinity;
181 
182         /** String representing negative infinity. */
183         private final String negativeInfinity;
184 
185         /** String representing NaN. */
186         private final String nan;
187 
188         /** Flag determining if fraction placeholders should be used. */
189         private final boolean fractionPlaceholder;
190 
191         /** Flag determining if signed zero strings are allowed. */
192         private final boolean signedZero;
193 
194         /** String containing the digits 0-9. */
195         private final char[] digits;
196 
197         /** Decimal separator character. */
198         private final char decimalSeparator;
199 
200         /** Thousands grouping separator. */
201         private final char groupingSeparator;
202 
203         /** Flag indicating if thousands should be grouped. */
204         private final boolean groupThousands;
205 
206         /** Minus sign character. */
207         private final char minusSign;
208 
209         /** Exponent separator character. */
210         private final char[] exponentSeparatorChars;
211 
212         /** Flag indicating if exponent values should always be included, even if zero. */
213         private final boolean alwaysIncludeExponent;
214 
215         /**
216          * Constructs a new instance.
217          *
218          * @param builder builder instance containing configuration values
219          */
220         AbstractDoubleFormat(final Builder builder) {
221             this.maxPrecision = builder.maxPrecision;
222             this.minDecimalExponent = builder.minDecimalExponent;
223 
224             this.positiveInfinity = builder.infinity;
225             this.negativeInfinity = builder.minusSign + builder.infinity;
226             this.nan = builder.nan;
227 
228             this.fractionPlaceholder = builder.fractionPlaceholder;
229             this.signedZero = builder.signedZero;
230             this.digits = builder.digits.toCharArray();
231             this.decimalSeparator = builder.decimalSeparator;
232             this.groupingSeparator = builder.groupingSeparator;
233             this.groupThousands = builder.groupThousands;
234             this.minusSign = builder.minusSign;
235             this.exponentSeparatorChars = builder.exponentSeparator.toCharArray();
236             this.alwaysIncludeExponent = builder.alwaysIncludeExponent;
237         }
238 
239         /** {@inheritDoc} */
240         @Override
241         public String apply(final double d) {
242             if (Double.isFinite(d)) {
243                 return applyFinite(d);
244             }
245             if (Double.isInfinite(d)) {
246                 return d > 0.0 ? positiveInfinity : negativeInfinity;
247             }
248             return nan;
249         }
250 
251         /**
252          * Returns a formatted string representation of the given finite value.
253          *
254          * @param d double value
255          */
256         private String applyFinite(final double d) {
257             final ParsedDecimal n = ParsedDecimal.from(d);
258 
259             int roundExponent = Math.max(n.getExponent(), minDecimalExponent);
260             if (maxPrecision > 0) {
261                 roundExponent = Math.max(n.getScientificExponent() - maxPrecision + 1, roundExponent);
262             }
263             n.round(roundExponent);
264 
265             return applyFiniteInternal(n);
266         }
267 
268         /**
269          * Returns a formatted representation of the given rounded decimal value to {@code dst}.
270          *
271          * @param val value to format
272          * @return a formatted representation of the given rounded decimal value to {@code dst}.
273          */
274         protected abstract String applyFiniteInternal(ParsedDecimal val);
275 
276         /** {@inheritDoc} */
277         @Override
278         public char getDecimalSeparator() {
279             return decimalSeparator;
280         }
281 
282         /** {@inheritDoc} */
283         @Override
284         public char[] getDigits() {
285             return digits;
286         }
287 
288         /** {@inheritDoc} */
289         @Override
290         public char[] getExponentSeparatorChars() {
291             return exponentSeparatorChars;
292         }
293 
294         /** {@inheritDoc} */
295         @Override
296         public char getGroupingSeparator() {
297             return groupingSeparator;
298         }
299 
300         /** {@inheritDoc} */
301         @Override
302         public char getMinusSign() {
303             return minusSign;
304         }
305 
306         /** {@inheritDoc} */
307         @Override
308         public boolean isAlwaysIncludeExponent() {
309             return alwaysIncludeExponent;
310         }
311 
312         /** {@inheritDoc} */
313         @Override
314         public boolean isGroupThousands() {
315             return groupThousands;
316         }
317 
318         /** {@inheritDoc} */
319         @Override
320         public boolean isIncludeFractionPlaceholder() {
321             return fractionPlaceholder;
322         }
323 
324         /** {@inheritDoc} */
325         @Override
326         public boolean isSignedZero() {
327             return signedZero;
328         }
329     }
330 
331     /**
332      * Builds configured format functions for standard double format types.
333      */
334     public static final class Builder implements Supplier<DoubleFunction<String>> {
335 
336         /** Default value for the plain format max decimal exponent. */
337         private static final int DEFAULT_PLAIN_FORMAT_MAX_DECIMAL_EXPONENT = 6;
338 
339         /** Default value for the plain format min decimal exponent. */
340         private static final int DEFAULT_PLAIN_FORMAT_MIN_DECIMAL_EXPONENT = -3;
341 
342         /** Default decimal digit characters. */
343         private static final String DEFAULT_DECIMAL_DIGITS = "0123456789";
344 
345         /**
346          * Gets a string containing the localized digits 0-9 for the given symbols object. The string is constructed by starting at the
347          * {@link DecimalFormatSymbols#getZeroDigit() zero digit} and adding the next 9 consecutive characters.
348          *
349          * @param symbols symbols object
350          * @return string containing the localized digits 0-9
351          */
352         private static String getDigitString(final DecimalFormatSymbols symbols) {
353             final int zeroDelta = symbols.getZeroDigit() - DEFAULT_DECIMAL_DIGITS.charAt(0);
354 
355             final char[] digitChars = new char[DEFAULT_DECIMAL_DIGITS.length()];
356             for (int i = 0; i < DEFAULT_DECIMAL_DIGITS.length(); ++i) {
357                 digitChars[i] = (char) (DEFAULT_DECIMAL_DIGITS.charAt(i) + zeroDelta);
358             }
359 
360             return String.valueOf(digitChars);
361         }
362 
363         /** Function used to construct format instances. */
364         private final Function<Builder, DoubleFunction<String>> factory;
365 
366         /** Maximum number of significant decimal digits in formatted strings. */
367         private int maxPrecision;
368 
369         /** Minimum decimal exponent. */
370         private int minDecimalExponent = Integer.MIN_VALUE;
371 
372         /** Max decimal exponent to use with plain formatting with the mixed format type. */
373         private int plainFormatMaxDecimalExponent = DEFAULT_PLAIN_FORMAT_MAX_DECIMAL_EXPONENT;
374 
375         /** Min decimal exponent to use with plain formatting with the mixed format type. */
376         private int plainFormatMinDecimalExponent = DEFAULT_PLAIN_FORMAT_MIN_DECIMAL_EXPONENT;
377 
378         /** String representing infinity. */
379         private String infinity = "Infinity";
380 
381         /** String representing NaN. */
382         private String nan = "NaN";
383 
384         /** Flag determining if fraction placeholders should be used. */
385         private boolean fractionPlaceholder = true;
386 
387         /** Flag determining if signed zero strings are allowed. */
388         private boolean signedZero = true;
389 
390         /** String of digit characters 0-9. */
391         private String digits = DEFAULT_DECIMAL_DIGITS;
392 
393         /** Decimal separator character. */
394         private char decimalSeparator = '.';
395 
396         /** Character used to separate groups of thousands. */
397         private char groupingSeparator = ',';
398 
399         /** If {@code true}, thousands groups will be separated by the grouping separator. */
400         private boolean groupThousands;
401 
402         /** Minus sign character. */
403         private char minusSign = '-';
404 
405         /** Exponent separator character. */
406         private String exponentSeparator = "E";
407 
408         /** Flag indicating if the exponent value should always be included, even if zero. */
409         private boolean alwaysIncludeExponent;
410 
411         /**
412          * Builds a new instance that delegates double function construction to the given factory object.
413          *
414          * @param factory factory function
415          */
416         private Builder(final Function<Builder, DoubleFunction<String>> factory) {
417             this.factory = factory;
418         }
419 
420         /**
421          * Sets the flag determining whether or not the zero string may be returned with the minus sign or if it will always be returned in the positive form.
422          * For example, if set to {@code true}, the string {@code "-0.0"} may be returned for some input numbers. If {@code false}, only {@code "0.0"} will be
423          * returned, regardless of the sign of the input number. The default value is {@code true}.
424          *
425          * @param signedZero if {@code true}, the zero string may be returned with a preceding minus sign; if {@code false}, the zero string will only be
426          *                   returned in its positive form
427          * @return this instance
428          */
429         public Builder allowSignedZero(final boolean signedZero) {
430             this.signedZero = signedZero;
431             return this;
432         }
433 
434         /**
435          * Sets the flag indicating if an exponent value should always be included in the formatted value, even if the exponent value is zero. This property
436          * only applies to formats that use scientific notation, namely {@link DoubleFormat#SCIENTIFIC SCIENTIFIC}, {@link DoubleFormat#ENGINEERING
437          * ENGINEERING}, and {@link DoubleFormat#MIXED MIXED}. The default value is {@code false}.
438          *
439          * @param alwaysIncludeExponent if {@code true}, exponents will always be included in formatted output even if the exponent value is zero
440          * @return this instance
441          */
442         public Builder alwaysIncludeExponent(final boolean alwaysIncludeExponent) {
443             this.alwaysIncludeExponent = alwaysIncludeExponent;
444             return this;
445         }
446 
447         /**
448          * Builds a new double format function.
449          *
450          * @return format function
451          * @deprecated Use {@link #get()}.
452          */
453         @Deprecated
454         public DoubleFunction<String> build() {
455             return get();
456         }
457 
458         /**
459          * Sets the decimal separator character, i.e., the character placed between the whole number and fractional portions of the formatted strings. The
460          * default value is {@code '.'}.
461          *
462          * @param decimalSeparator decimal separator character
463          * @return this instance
464          */
465         public Builder decimalSeparator(final char decimalSeparator) {
466             this.decimalSeparator = decimalSeparator;
467             return this;
468         }
469 
470         /**
471          * Sets the string containing the digit characters 0-9, in that order. The default value is the string {@code "0123456789"}.
472          *
473          * @param digits string containing the digit characters 0-9
474          * @return this instance
475          * @throws NullPointerException     if the argument is {@code null}
476          * @throws IllegalArgumentException if the argument does not have a length of exactly 10
477          */
478         public Builder digits(final String digits) {
479             Objects.requireNonNull(digits, "Digits string cannot be null");
480             if (digits.length() != DEFAULT_DECIMAL_DIGITS.length()) {
481                 throw new IllegalArgumentException("Digits string must contain exactly " + DEFAULT_DECIMAL_DIGITS.length() + " characters.");
482             }
483 
484             this.digits = digits;
485             return this;
486         }
487 
488         /**
489          * Sets the exponent separator character, i.e., the string placed between the mantissa and the exponent. The default value is {@code "E"}, as in
490          * {@code "1.2E6"}.
491          *
492          * @param exponentSeparator exponent separator string
493          * @return this instance
494          * @throws NullPointerException if the argument is {@code null}
495          */
496         public Builder exponentSeparator(final String exponentSeparator) {
497             this.exponentSeparator = Objects.requireNonNull(exponentSeparator, "Exponent separator cannot be null");
498             return this;
499         }
500 
501         /**
502          * Configures this instance with the given format symbols. The following values are set:
503          * <ul>
504          * <li>{@link #digits(String) digit characters}</li>
505          * <li>{@link #decimalSeparator(char) decimal separator}</li>
506          * <li>{@link #groupingSeparator(char) thousands grouping separator}</li>
507          * <li>{@link #minusSign(char) minus sign}</li>
508          * <li>{@link #exponentSeparator(String) exponent separator}</li>
509          * <li>{@link #infinity(String) infinity}</li>
510          * <li>{@link #nan(String) NaN}</li>
511          * </ul>
512          * The digit character string is constructed by starting at the configured {@link DecimalFormatSymbols#getZeroDigit() zero digit} and adding the next 9
513          * consecutive characters.
514          *
515          * @param symbols format symbols
516          * @return this instance
517          * @throws NullPointerException if the argument is {@code null}
518          */
519         public Builder formatSymbols(final DecimalFormatSymbols symbols) {
520             Objects.requireNonNull(symbols, "Decimal format symbols cannot be null");
521 
522             return digits(getDigitString(symbols)).decimalSeparator(symbols.getDecimalSeparator()).groupingSeparator(symbols.getGroupingSeparator())
523                     .minusSign(symbols.getMinusSign()).exponentSeparator(symbols.getExponentSeparator()).infinity(symbols.getInfinity()).nan(symbols.getNaN());
524         }
525 
526         /**
527          * Builds a new double format function.
528          *
529          * @return format function
530          */
531         @Override
532         public DoubleFunction<String> get() {
533             return factory.apply(this);
534         }
535 
536         /**
537          * Sets the character used to separate groups of thousands. Default value is {@code ','}.
538          *
539          * @param groupingSeparator character used to separate groups of thousands
540          * @return this instance
541          * @see #groupThousands(boolean)
542          */
543         public Builder groupingSeparator(final char groupingSeparator) {
544             this.groupingSeparator = groupingSeparator;
545             return this;
546         }
547 
548         /**
549          * If set to {@code true}, thousands will be grouped with the {@link #groupingSeparator(char) grouping separator}. For example, if set to {@code true},
550          * the number {@code 1000} could be formatted as {@code "1,000"}. This property only applies to the {@link DoubleFormat#PLAIN PLAIN} format. Default
551          * value is {@code false}.
552          *
553          * @param groupThousands if {@code true}, thousands will be grouped
554          * @return this instance
555          * @see #groupingSeparator(char)
556          */
557         public Builder groupThousands(final boolean groupThousands) {
558             this.groupThousands = groupThousands;
559             return this;
560         }
561 
562         /**
563          * Sets the flag determining whether or not a zero character is added in the fraction position when no fractional value is present. For example, if set
564          * to {@code true}, the number {@code 1} would be formatted as {@code "1.0"}. If {@code false}, it would be formatted as {@code "1"}. The default value
565          * is {@code true}.
566          *
567          * @param fractionPlaceholder if {@code true}, a zero character is placed in the fraction position when no fractional value is present; if
568          *                            {@code false}, fractional digits are only included when needed
569          * @return this instance
570          */
571         public Builder includeFractionPlaceholder(final boolean fractionPlaceholder) {
572             this.fractionPlaceholder = fractionPlaceholder;
573             return this;
574         }
575 
576         /**
577          * Sets the string used to represent infinity. For negative infinity, this string is prefixed with the {@link #minusSign(char) minus sign}.
578          *
579          * @param infinity string used to represent infinity
580          * @return this instance
581          * @throws NullPointerException if the argument is {@code null}
582          */
583         public Builder infinity(final String infinity) {
584             this.infinity = Objects.requireNonNull(infinity, "Infinity string cannot be null");
585             return this;
586         }
587 
588         /**
589          * Sets the maximum number of significant decimal digits used in format results. A value of {@code 0} indicates no limit. The default value is
590          * {@code 0}.
591          *
592          * @param maxPrecision maximum precision
593          * @return this instance
594          */
595         public Builder maxPrecision(final int maxPrecision) {
596             this.maxPrecision = maxPrecision;
597             return this;
598         }
599 
600         /**
601          * Sets the minimum decimal exponent for formatted strings. No digits with an absolute value of less than <code>10<sup>minDecimalExponent</sup></code>
602          * will be included in format results. If the number being formatted does not contain any such digits, then zero is returned. For example, if
603          * {@code minDecimalExponent} is set to {@code -2} and the number {@code 3.14159} is formatted, the plain format result will be {@code "3.14"}. If
604          * {@code 0.001} is formatted, then the result is the zero string.
605          *
606          * @param minDecimalExponent minimum decimal exponent
607          * @return this instance
608          */
609         public Builder minDecimalExponent(final int minDecimalExponent) {
610             this.minDecimalExponent = minDecimalExponent;
611             return this;
612         }
613 
614         /**
615          * Sets the character used as the minus sign.
616          *
617          * @param minusSign character to use as the minus sign
618          * @return this instance
619          */
620         public Builder minusSign(final char minusSign) {
621             this.minusSign = minusSign;
622             return this;
623         }
624 
625         /**
626          * Sets the string used to represent {@link Double#NaN}.
627          *
628          * @param nan string used to represent {@link Double#NaN}
629          * @return this instance
630          * @throws NullPointerException if the argument is {@code null}
631          */
632         public Builder nan(final String nan) {
633             this.nan = Objects.requireNonNull(nan, "NaN string cannot be null");
634             return this;
635         }
636 
637         /**
638          * Sets the maximum decimal exponent for numbers formatted as plain decimal strings when using the {@link DoubleFormat#MIXED MIXED} format type. If the
639          * number being formatted has an absolute value less than <code>10<sup>plainFormatMaxDecimalExponent + 1</sup></code> and greater than or equal to
640          * <code>10<sup>plainFormatMinDecimalExponent</sup></code> after any necessary rounding, then the formatted result will use the
641          * {@link DoubleFormat#PLAIN PLAIN} format type. Otherwise, {@link DoubleFormat#SCIENTIFIC SCIENTIFIC} format will be used. For example, if this value
642          * is set to {@code 2}, the number {@code 999} will be formatted as {@code "999.0"} while {@code 1000} will be formatted as {@code "1.0E3"}.
643          *
644          * <p>
645          * The default value is {@code 6}.
646          *
647          * <p>
648          * This value is ignored for formats other than {@link DoubleFormat#MIXED}.
649          *
650          * @param plainFormatMaxDecimalExponent maximum decimal exponent for values formatted as plain strings when using the {@link DoubleFormat#MIXED MIXED}
651          *                                      format type.
652          * @return this instance
653          * @see #plainFormatMinDecimalExponent(int)
654          */
655         public Builder plainFormatMaxDecimalExponent(final int plainFormatMaxDecimalExponent) {
656             this.plainFormatMaxDecimalExponent = plainFormatMaxDecimalExponent;
657             return this;
658         }
659 
660         /**
661          * Sets the minimum decimal exponent for numbers formatted as plain decimal strings when using the {@link DoubleFormat#MIXED MIXED} format type. If the
662          * number being formatted has an absolute value less than <code>10<sup>plainFormatMaxDecimalExponent + 1</sup></code> and greater than or equal to
663          * <code>10<sup>plainFormatMinDecimalExponent</sup></code> after any necessary rounding, then the formatted result will use the
664          * {@link DoubleFormat#PLAIN PLAIN} format type. Otherwise, {@link DoubleFormat#SCIENTIFIC SCIENTIFIC} format will be used. For example, if this value
665          * is set to {@code -2}, the number {@code 0.01} will be formatted as {@code "0.01"} while {@code 0.0099} will be formatted as {@code "9.9E-3"}.
666          *
667          * <p>
668          * The default value is {@code -3}.
669          *
670          * <p>
671          * This value is ignored for formats other than {@link DoubleFormat#MIXED}.
672          *
673          * @param plainFormatMinDecimalExponent maximum decimal exponent for values formatted as plain strings when using the {@link DoubleFormat#MIXED MIXED}
674          *                                      format type.
675          * @return this instance
676          * @see #plainFormatMinDecimalExponent(int)
677          */
678         public Builder plainFormatMinDecimalExponent(final int plainFormatMinDecimalExponent) {
679             this.plainFormatMinDecimalExponent = plainFormatMinDecimalExponent;
680             return this;
681         }
682     }
683 
684     /**
685      * Format class that uses engineering notation for all values.
686      */
687     private static final class EngineeringDoubleFormat extends AbstractDoubleFormat {
688 
689         /**
690          * Constructs a new instance.
691          *
692          * @param builder builder instance containing configuration values
693          */
694         EngineeringDoubleFormat(final Builder builder) {
695             super(builder);
696         }
697 
698         /** {@inheritDoc} */
699         @Override
700         public String applyFiniteInternal(final ParsedDecimal val) {
701             return val.toEngineeringString(this);
702         }
703     }
704 
705     /**
706      * Format class producing results similar to {@link Double#toString()}, with plain decimal notation for small numbers relatively close to zero and
707      * scientific notation otherwise.
708      */
709     private static final class MixedDoubleFormat extends AbstractDoubleFormat {
710 
711         /** Max decimal exponent for plain format. */
712         private final int plainMaxExponent;
713 
714         /** Min decimal exponent for plain format. */
715         private final int plainMinExponent;
716 
717         /**
718          * Constructs a new instance.
719          *
720          * @param builder builder instance containing configuration values
721          */
722         MixedDoubleFormat(final Builder builder) {
723             super(builder);
724 
725             this.plainMaxExponent = builder.plainFormatMaxDecimalExponent;
726             this.plainMinExponent = builder.plainFormatMinDecimalExponent;
727         }
728 
729         /** {@inheritDoc} */
730         @Override
731         protected String applyFiniteInternal(final ParsedDecimal val) {
732             final int sciExp = val.getScientificExponent();
733             if (sciExp <= plainMaxExponent && sciExp >= plainMinExponent) {
734                 return val.toPlainString(this);
735             }
736             return val.toScientificString(this);
737         }
738     }
739 
740     /**
741      * Format class that produces plain decimal strings that do not use scientific notation.
742      */
743     private static final class PlainDoubleFormat extends AbstractDoubleFormat {
744 
745         /**
746          * Constructs a new instance.
747          *
748          * @param builder builder instance containing configuration values
749          */
750         PlainDoubleFormat(final Builder builder) {
751             super(builder);
752         }
753 
754         /**
755          * {@inheritDoc}
756          */
757         @Override
758         protected String applyFiniteInternal(final ParsedDecimal val) {
759             return val.toPlainString(this);
760         }
761     }
762 
763     /**
764      * Format class that uses scientific notation for all values.
765      */
766     private static final class ScientificDoubleFormat extends AbstractDoubleFormat {
767 
768         /**
769          * Constructs a new instance.
770          *
771          * @param builder builder instance containing configuration values
772          */
773         ScientificDoubleFormat(final Builder builder) {
774             super(builder);
775         }
776 
777         /** {@inheritDoc} */
778         @Override
779         public String applyFiniteInternal(final ParsedDecimal val) {
780             return val.toScientificString(this);
781         }
782     }
783 
784     /** Function used to construct instances for this format type. */
785     private final Function<Builder, DoubleFunction<String>> factory;
786 
787     /**
788      * Constructs a new instance.
789      *
790      * @param factory function used to construct format instances
791      */
792     DoubleFormat(final Function<Builder, DoubleFunction<String>> factory) {
793         this.factory = factory;
794     }
795 
796     /**
797      * Creates a {@link Builder} for building formatter functions for this format type.
798      *
799      * @return builder instance
800      */
801     public Builder builder() {
802         return new Builder(factory);
803     }
804 }