001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.text.numbers;
018
019import java.text.DecimalFormatSymbols;
020import java.util.Objects;
021import java.util.function.DoubleFunction;
022import java.util.function.Function;
023import java.util.function.Supplier;
024
025/**
026 * Enum containing standard double format types with methods to produce
027 * configured formatter instances. This type is intended to provide a
028 * quick and convenient way to create lightweight, thread-safe double format functions
029 * for common format types using a builder pattern. Output can be localized by
030 * passing a {@link DecimalFormatSymbols} instance to the
031 * {@link Builder#formatSymbols(DecimalFormatSymbols) formatSymbols} method or by
032 * directly calling the various other builder configuration methods, such as
033 * {@link Builder#digits(String) digits}.
034 *
035 * <p><strong>Comparison with DecimalFormat</strong></p>
036 * <p>
037 * This type provides some of the same functionality as Java's own
038 * {@link java.text.DecimalFormat}. However, unlike {@code DecimalFormat}, the format
039 * functions produced by this type are lightweight and thread-safe, making them
040 * much easier to work with in multi-threaded environments. They also provide performance
041 * comparable to, and in many cases faster than, {@code DecimalFormat}.
042 * </p>
043 * <p>
044 * It should be noted that the output {@code String} is created by formatting the output of
045 * {@link Double#toString()}. This limits the output precision to the precision required
046 * to exactly represent the input {@code double} and is dependent on the JDK implementation
047 * of {@link Double#toString()}. A number formatted with the maximum
048 * precision should be parsed to the same input {@code double}. This implementation
049 * cannot extend the {@code String} to the required length to represent the exact decimal
050 * value of the {@code double} as per
051 * {@link java.math.BigDecimal#toString() BigDecimal#toString()}.
052 * </p>
053 * <p><strong>Examples</strong></p>
054 * <pre>
055 * // construct a formatter equivalent to Double.toString()
056 * DoubleFunction&lt;String&gt; fmt = DoubleFormat.MIXED.builder().build();
057 *
058 * // construct a formatter equivalent to Double.toString() but using
059 * // format symbols for a specific locale
060 * DoubleFunction&lt;String&gt; fmt = DoubleFormat.MIXED.builder()
061 *      .formatSymbols(DecimalFormatSymbols.getInstance(locale))
062 *      .build();
063 *
064 * // construct a formatter equivalent to the DecimalFormat pattern "0.0##"
065 * DoubleFunction&lt;String&gt; fmt = DoubleFormat.PLAIN.builder()
066 *      .minDecimalExponent(-3)
067 *      .build();
068 *
069 * // construct a formatter equivalent to the DecimalFormat pattern "#,##0.0##",
070 * // where whole number groups of thousands are separated
071 * DoubleFunction&lt;String&gt; fmt = DoubleFormat.PLAIN.builder()
072 *      .minDecimalExponent(-3)
073 *      .groupThousands(true)
074 *      .build();
075 *
076 * // construct a formatter equivalent to the DecimalFormat pattern "0.0##E0"
077 * DoubleFunction&lt;String&gt; fmt = DoubleFormat.SCIENTIFIC.builder()
078 *      .maxPrecision(4)
079 *      .alwaysIncludeExponent(true)
080 *      .build()
081 *
082 * // construct a formatter equivalent to the DecimalFormat pattern "##0.0##E0",
083 * // i.e. "engineering format"
084 * DoubleFunction&lt;String&gt; fmt = DoubleFormat.ENGINEERING.builder()
085 *      .maxPrecision(6)
086 *      .alwaysIncludeExponent(true)
087 *      .build()
088 * </pre>
089 *
090 * <p><strong>Implementation Notes</strong></p>
091 * <p>
092 * {@link java.math.RoundingMode#HALF_EVEN Half-even} rounding is used in cases where the
093 * decimal value must be rounded in order to meet the configuration requirements of the formatter
094 * instance.
095 * </p>
096 *
097 * @since 1.10.0
098 */
099public 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}