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