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