DoubleFormat.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.text.numbers;

  18. import java.text.DecimalFormatSymbols;
  19. import java.util.Objects;
  20. import java.util.function.DoubleFunction;
  21. import java.util.function.Function;
  22. import java.util.function.Supplier;

  23. /**
  24.  * Enum containing standard double format types with methods to produce
  25.  * configured formatter instances. This type is intended to provide a
  26.  * quick and convenient way to create lightweight, thread-safe double format functions
  27.  * for common format types using a builder pattern. Output can be localized by
  28.  * passing a {@link DecimalFormatSymbols} instance to the
  29.  * {@link Builder#formatSymbols(DecimalFormatSymbols) formatSymbols} method or by
  30.  * directly calling the various other builder configuration methods, such as
  31.  * {@link Builder#digits(String) digits}.
  32.  *
  33.  * <p><strong>Comparison with DecimalFormat</strong></p>
  34.  * <p>
  35.  * This type provides some of the same functionality as Java's own
  36.  * {@link java.text.DecimalFormat}. However, unlike {@code DecimalFormat}, the format
  37.  * functions produced by this type are lightweight and thread-safe, making them
  38.  * much easier to work with in multi-threaded environments. They also provide performance
  39.  * comparable to, and in many cases faster than, {@code DecimalFormat}.
  40.  * </p>
  41.  * <p>
  42.  * It should be noted that the output {@code String} is created by formatting the output of
  43.  * {@link Double#toString()}. This limits the output precision to the precision required
  44.  * to exactly represent the input {@code double} and is dependent on the JDK implementation
  45.  * of {@link Double#toString()}. A number formatted with the maximum
  46.  * precision should be parsed to the same input {@code double}. This implementation
  47.  * cannot extend the {@code String} to the required length to represent the exact decimal
  48.  * value of the {@code double} as per
  49.  * {@link java.math.BigDecimal#toString() BigDecimal#toString()}.
  50.  * </p>
  51.  * <p><strong>Examples</strong></p>
  52.  * <pre>
  53.  * // construct a formatter equivalent to Double.toString()
  54.  * DoubleFunction&lt;String&gt; fmt = DoubleFormat.MIXED.builder().build();
  55.  *
  56.  * // construct a formatter equivalent to Double.toString() but using
  57.  * // format symbols for a specific locale
  58.  * DoubleFunction&lt;String&gt; fmt = DoubleFormat.MIXED.builder()
  59.  *      .formatSymbols(DecimalFormatSymbols.getInstance(locale))
  60.  *      .build();
  61.  *
  62.  * // construct a formatter equivalent to the DecimalFormat pattern "0.0##"
  63.  * DoubleFunction&lt;String&gt; fmt = DoubleFormat.PLAIN.builder()
  64.  *      .minDecimalExponent(-3)
  65.  *      .build();
  66.  *
  67.  * // construct a formatter equivalent to the DecimalFormat pattern "#,##0.0##",
  68.  * // where whole number groups of thousands are separated
  69.  * DoubleFunction&lt;String&gt; fmt = DoubleFormat.PLAIN.builder()
  70.  *      .minDecimalExponent(-3)
  71.  *      .groupThousands(true)
  72.  *      .build();
  73.  *
  74.  * // construct a formatter equivalent to the DecimalFormat pattern "0.0##E0"
  75.  * DoubleFunction&lt;String&gt; fmt = DoubleFormat.SCIENTIFIC.builder()
  76.  *      .maxPrecision(4)
  77.  *      .alwaysIncludeExponent(true)
  78.  *      .build()
  79.  *
  80.  * // construct a formatter equivalent to the DecimalFormat pattern "##0.0##E0",
  81.  * // i.e. "engineering format"
  82.  * DoubleFunction&lt;String&gt; fmt = DoubleFormat.ENGINEERING.builder()
  83.  *      .maxPrecision(6)
  84.  *      .alwaysIncludeExponent(true)
  85.  *      .build()
  86.  * </pre>
  87.  *
  88.  * <p><strong>Implementation Notes</strong></p>
  89.  * <p>
  90.  * {@link java.math.RoundingMode#HALF_EVEN Half-even} rounding is used in cases where the
  91.  * decimal value must be rounded in order to meet the configuration requirements of the formatter
  92.  * instance.
  93.  * </p>
  94.  *
  95.  * @since 1.10.0
  96.  */
  97. public enum DoubleFormat {

  98.     /**
  99.      * Number format without exponents.
  100.      * <p>
  101.      * For example:
  102.      * </p>
  103.      *
  104.      * <pre>
  105.      * 0.0
  106.      * 12.401
  107.      * 100000.0
  108.      * 1450000000.0
  109.      * 0.0000000000123
  110.      * </pre>
  111.      */
  112.     PLAIN(PlainDoubleFormat::new),

  113.     /**
  114.      * Number format that uses exponents and contains a single digit to the left of the decimal point.
  115.      * <p>
  116.      * For example:
  117.      * </p>
  118.      *
  119.      * <pre>
  120.      * 0.0
  121.      * 1.2401E1
  122.      * 1.0E5
  123.      * 1.45E9
  124.      * 1.23E-11
  125.      * </pre>
  126.      */
  127.     SCIENTIFIC(ScientificDoubleFormat::new),

  128.     /**
  129.      * Number format similar to {@link #SCIENTIFIC scientific format} but adjusted so that the exponent value is always a multiple of 3, allowing easier
  130.      * alignment with SI prefixes.
  131.      * <p>
  132.      * For example:
  133.      * </p>
  134.      *
  135.      * <pre>
  136.      * 0.0
  137.      * 12.401
  138.      * 100.0E3
  139.      * 1.45E9
  140.      * 12.3E-12
  141.      * </pre>
  142.      */
  143.     ENGINEERING(EngineeringDoubleFormat::new),

  144.     /**
  145.      * Number format that uses {@link #PLAIN plain format} for small numbers and {@link #SCIENTIFIC scientific format} for large numbers. The number thresholds
  146.      * can be configured through the {@link Builder#plainFormatMinDecimalExponent(int) plainFormatMinDecimalExponent} and
  147.      * {@link Builder#plainFormatMaxDecimalExponent(int) plainFormatMaxDecimalExponent} properties.
  148.      * <p>
  149.      * For example:
  150.      * </p>
  151.      *
  152.      * <pre>
  153.      * 0.0
  154.      * 12.401
  155.      * 100000.0
  156.      * 1.45E9
  157.      * 1.23E-11
  158.      * </pre>
  159.      */
  160.     MIXED(MixedDoubleFormat::new);

  161.     /**
  162.      * Base class for standard double formatting classes.
  163.      */
  164.     private abstract static class AbstractDoubleFormat implements DoubleFunction<String>, ParsedDecimal.FormatOptions {

  165.         /** Maximum precision; 0 indicates no limit. */
  166.         private final int maxPrecision;

  167.         /** Minimum decimal exponent. */
  168.         private final int minDecimalExponent;

  169.         /** String representing positive infinity. */
  170.         private final String positiveInfinity;

  171.         /** String representing negative infinity. */
  172.         private final String negativeInfinity;

  173.         /** String representing NaN. */
  174.         private final String nan;

  175.         /** Flag determining if fraction placeholders should be used. */
  176.         private final boolean fractionPlaceholder;

  177.         /** Flag determining if signed zero strings are allowed. */
  178.         private final boolean signedZero;

  179.         /** String containing the digits 0-9. */
  180.         private final char[] digits;

  181.         /** Decimal separator character. */
  182.         private final char decimalSeparator;

  183.         /** Thousands grouping separator. */
  184.         private final char groupingSeparator;

  185.         /** Flag indicating if thousands should be grouped. */
  186.         private final boolean groupThousands;

  187.         /** Minus sign character. */
  188.         private final char minusSign;

  189.         /** Exponent separator character. */
  190.         private final char[] exponentSeparatorChars;

  191.         /** Flag indicating if exponent values should always be included, even if zero. */
  192.         private final boolean alwaysIncludeExponent;

  193.         /**
  194.          * Constructs a new instance.
  195.          *
  196.          * @param builder builder instance containing configuration values
  197.          */
  198.         AbstractDoubleFormat(final Builder builder) {
  199.             this.maxPrecision = builder.maxPrecision;
  200.             this.minDecimalExponent = builder.minDecimalExponent;

  201.             this.positiveInfinity = builder.infinity;
  202.             this.negativeInfinity = builder.minusSign + builder.infinity;
  203.             this.nan = builder.nan;

  204.             this.fractionPlaceholder = builder.fractionPlaceholder;
  205.             this.signedZero = builder.signedZero;
  206.             this.digits = builder.digits.toCharArray();
  207.             this.decimalSeparator = builder.decimalSeparator;
  208.             this.groupingSeparator = builder.groupingSeparator;
  209.             this.groupThousands = builder.groupThousands;
  210.             this.minusSign = builder.minusSign;
  211.             this.exponentSeparatorChars = builder.exponentSeparator.toCharArray();
  212.             this.alwaysIncludeExponent = builder.alwaysIncludeExponent;
  213.         }

  214.         /** {@inheritDoc} */
  215.         @Override
  216.         public String apply(final double d) {
  217.             if (Double.isFinite(d)) {
  218.                 return applyFinite(d);
  219.             }
  220.             if (Double.isInfinite(d)) {
  221.                 return d > 0.0 ? positiveInfinity : negativeInfinity;
  222.             }
  223.             return nan;
  224.         }

  225.         /**
  226.          * Returns a formatted string representation of the given finite value.
  227.          *
  228.          * @param d double value
  229.          */
  230.         private String applyFinite(final double d) {
  231.             final ParsedDecimal n = ParsedDecimal.from(d);

  232.             int roundExponent = Math.max(n.getExponent(), minDecimalExponent);
  233.             if (maxPrecision > 0) {
  234.                 roundExponent = Math.max(n.getScientificExponent() - maxPrecision + 1, roundExponent);
  235.             }
  236.             n.round(roundExponent);

  237.             return applyFiniteInternal(n);
  238.         }

  239.         /**
  240.          * Returns a formatted representation of the given rounded decimal value to {@code dst}.
  241.          *
  242.          * @param val value to format
  243.          * @return a formatted representation of the given rounded decimal value to {@code dst}.
  244.          */
  245.         protected abstract String applyFiniteInternal(ParsedDecimal val);

  246.         /** {@inheritDoc} */
  247.         @Override
  248.         public char getDecimalSeparator() {
  249.             return decimalSeparator;
  250.         }

  251.         /** {@inheritDoc} */
  252.         @Override
  253.         public char[] getDigits() {
  254.             return digits;
  255.         }

  256.         /** {@inheritDoc} */
  257.         @Override
  258.         public char[] getExponentSeparatorChars() {
  259.             return exponentSeparatorChars;
  260.         }

  261.         /** {@inheritDoc} */
  262.         @Override
  263.         public char getGroupingSeparator() {
  264.             return groupingSeparator;
  265.         }

  266.         /** {@inheritDoc} */
  267.         @Override
  268.         public char getMinusSign() {
  269.             return minusSign;
  270.         }

  271.         /** {@inheritDoc} */
  272.         @Override
  273.         public boolean isAlwaysIncludeExponent() {
  274.             return alwaysIncludeExponent;
  275.         }

  276.         /** {@inheritDoc} */
  277.         @Override
  278.         public boolean isGroupThousands() {
  279.             return groupThousands;
  280.         }

  281.         /** {@inheritDoc} */
  282.         @Override
  283.         public boolean isIncludeFractionPlaceholder() {
  284.             return fractionPlaceholder;
  285.         }

  286.         /** {@inheritDoc} */
  287.         @Override
  288.         public boolean isSignedZero() {
  289.             return signedZero;
  290.         }
  291.     }

  292.     /**
  293.      * Builds configured format functions for standard double format types.
  294.      */
  295.     public static final class Builder implements Supplier<DoubleFunction<String>> {

  296.         /** Default value for the plain format max decimal exponent. */
  297.         private static final int DEFAULT_PLAIN_FORMAT_MAX_DECIMAL_EXPONENT = 6;

  298.         /** Default value for the plain format min decimal exponent. */
  299.         private static final int DEFAULT_PLAIN_FORMAT_MIN_DECIMAL_EXPONENT = -3;

  300.         /** Default decimal digit characters. */
  301.         private static final String DEFAULT_DECIMAL_DIGITS = "0123456789";

  302.         /**
  303.          * Gets a string containing the localized digits 0-9 for the given symbols object. The string is constructed by starting at the
  304.          * {@link DecimalFormatSymbols#getZeroDigit() zero digit} and adding the next 9 consecutive characters.
  305.          *
  306.          * @param symbols symbols object
  307.          * @return string containing the localized digits 0-9
  308.          */
  309.         private static String getDigitString(final DecimalFormatSymbols symbols) {
  310.             final int zeroDelta = symbols.getZeroDigit() - DEFAULT_DECIMAL_DIGITS.charAt(0);

  311.             final char[] digitChars = new char[DEFAULT_DECIMAL_DIGITS.length()];
  312.             for (int i = 0; i < DEFAULT_DECIMAL_DIGITS.length(); ++i) {
  313.                 digitChars[i] = (char) (DEFAULT_DECIMAL_DIGITS.charAt(i) + zeroDelta);
  314.             }

  315.             return String.valueOf(digitChars);
  316.         }

  317.         /** Function used to construct format instances. */
  318.         private final Function<Builder, DoubleFunction<String>> factory;

  319.         /** Maximum number of significant decimal digits in formatted strings. */
  320.         private int maxPrecision;

  321.         /** Minimum decimal exponent. */
  322.         private int minDecimalExponent = Integer.MIN_VALUE;

  323.         /** Max decimal exponent to use with plain formatting with the mixed format type. */
  324.         private int plainFormatMaxDecimalExponent = DEFAULT_PLAIN_FORMAT_MAX_DECIMAL_EXPONENT;

  325.         /** Min decimal exponent to use with plain formatting with the mixed format type. */
  326.         private int plainFormatMinDecimalExponent = DEFAULT_PLAIN_FORMAT_MIN_DECIMAL_EXPONENT;

  327.         /** String representing infinity. */
  328.         private String infinity = "Infinity";

  329.         /** String representing NaN. */
  330.         private String nan = "NaN";

  331.         /** Flag determining if fraction placeholders should be used. */
  332.         private boolean fractionPlaceholder = true;

  333.         /** Flag determining if signed zero strings are allowed. */
  334.         private boolean signedZero = true;

  335.         /** String of digit characters 0-9. */
  336.         private String digits = DEFAULT_DECIMAL_DIGITS;

  337.         /** Decimal separator character. */
  338.         private char decimalSeparator = '.';

  339.         /** Character used to separate groups of thousands. */
  340.         private char groupingSeparator = ',';

  341.         /** If {@code true}, thousands groups will be separated by the grouping separator. */
  342.         private boolean groupThousands;

  343.         /** Minus sign character. */
  344.         private char minusSign = '-';

  345.         /** Exponent separator character. */
  346.         private String exponentSeparator = "E";

  347.         /** Flag indicating if the exponent value should always be included, even if zero. */
  348.         private boolean alwaysIncludeExponent;

  349.         /**
  350.          * Builds a new instance that delegates double function construction to the given factory object.
  351.          *
  352.          * @param factory factory function
  353.          */
  354.         private Builder(final Function<Builder, DoubleFunction<String>> factory) {
  355.             this.factory = factory;
  356.         }

  357.         /**
  358.          * 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.
  359.          * 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
  360.          * returned, regardless of the sign of the input number. The default value is {@code true}.
  361.          *
  362.          * @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
  363.          *                   returned in its positive form
  364.          * @return this instance
  365.          */
  366.         public Builder allowSignedZero(final boolean signedZero) {
  367.             this.signedZero = signedZero;
  368.             return this;
  369.         }

  370.         /**
  371.          * 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
  372.          * only applies to formats that use scientific notation, namely {@link DoubleFormat#SCIENTIFIC SCIENTIFIC}, {@link DoubleFormat#ENGINEERING
  373.          * ENGINEERING}, and {@link DoubleFormat#MIXED MIXED}. The default value is {@code false}.
  374.          *
  375.          * @param alwaysIncludeExponent if {@code true}, exponents will always be included in formatted output even if the exponent value is zero
  376.          * @return this instance
  377.          */
  378.         public Builder alwaysIncludeExponent(final boolean alwaysIncludeExponent) {
  379.             this.alwaysIncludeExponent = alwaysIncludeExponent;
  380.             return this;
  381.         }

  382.         /**
  383.          * Builds a new double format function.
  384.          *
  385.          * @return format function
  386.          * @deprecated Use {@link #get()}.
  387.          */
  388.         @Deprecated
  389.         public DoubleFunction<String> build() {
  390.             return get();
  391.         }

  392.         /**
  393.          * Sets the decimal separator character, i.e., the character placed between the whole number and fractional portions of the formatted strings. The
  394.          * default value is {@code '.'}.
  395.          *
  396.          * @param decimalSeparator decimal separator character
  397.          * @return this instance
  398.          */
  399.         public Builder decimalSeparator(final char decimalSeparator) {
  400.             this.decimalSeparator = decimalSeparator;
  401.             return this;
  402.         }

  403.         /**
  404.          * Sets the string containing the digit characters 0-9, in that order. The default value is the string {@code "0123456789"}.
  405.          *
  406.          * @param digits string containing the digit characters 0-9
  407.          * @return this instance
  408.          * @throws NullPointerException     if the argument is {@code null}
  409.          * @throws IllegalArgumentException if the argument does not have a length of exactly 10
  410.          */
  411.         public Builder digits(final String digits) {
  412.             Objects.requireNonNull(digits, "digits");
  413.             if (digits.length() != DEFAULT_DECIMAL_DIGITS.length()) {
  414.                 throw new IllegalArgumentException("Digits string must contain exactly " + DEFAULT_DECIMAL_DIGITS.length() + " characters.");
  415.             }

  416.             this.digits = digits;
  417.             return this;
  418.         }

  419.         /**
  420.          * Sets the exponent separator character, i.e., the string placed between the mantissa and the exponent. The default value is {@code "E"}, as in
  421.          * {@code "1.2E6"}.
  422.          *
  423.          * @param exponentSeparator exponent separator string
  424.          * @return this instance
  425.          * @throws NullPointerException if the argument is {@code null}
  426.          */
  427.         public Builder exponentSeparator(final String exponentSeparator) {
  428.             this.exponentSeparator = Objects.requireNonNull(exponentSeparator, "exponentSeparator");
  429.             return this;
  430.         }

  431.         /**
  432.          * Configures this instance with the given format symbols. The following values are set:
  433.          * <ul>
  434.          * <li>{@link #digits(String) digit characters}</li>
  435.          * <li>{@link #decimalSeparator(char) decimal separator}</li>
  436.          * <li>{@link #groupingSeparator(char) thousands grouping separator}</li>
  437.          * <li>{@link #minusSign(char) minus sign}</li>
  438.          * <li>{@link #exponentSeparator(String) exponent separator}</li>
  439.          * <li>{@link #infinity(String) infinity}</li>
  440.          * <li>{@link #nan(String) NaN}</li>
  441.          * </ul>
  442.          * The digit character string is constructed by starting at the configured {@link DecimalFormatSymbols#getZeroDigit() zero digit} and adding the next 9
  443.          * consecutive characters.
  444.          *
  445.          * @param symbols format symbols
  446.          * @return this instance
  447.          * @throws NullPointerException if the argument is {@code null}
  448.          */
  449.         public Builder formatSymbols(final DecimalFormatSymbols symbols) {
  450.             Objects.requireNonNull(symbols, "symbols");

  451.             return digits(getDigitString(symbols)).decimalSeparator(symbols.getDecimalSeparator()).groupingSeparator(symbols.getGroupingSeparator())
  452.                     .minusSign(symbols.getMinusSign()).exponentSeparator(symbols.getExponentSeparator()).infinity(symbols.getInfinity()).nan(symbols.getNaN());
  453.         }

  454.         /**
  455.          * Builds a new double format function.
  456.          *
  457.          * @return format function
  458.          */
  459.         @Override
  460.         public DoubleFunction<String> get() {
  461.             return factory.apply(this);
  462.         }

  463.         /**
  464.          * Sets the character used to separate groups of thousands. Default value is {@code ','}.
  465.          *
  466.          * @param groupingSeparator character used to separate groups of thousands
  467.          * @return this instance
  468.          * @see #groupThousands(boolean)
  469.          */
  470.         public Builder groupingSeparator(final char groupingSeparator) {
  471.             this.groupingSeparator = groupingSeparator;
  472.             return this;
  473.         }

  474.         /**
  475.          * If set to {@code true}, thousands will be grouped with the {@link #groupingSeparator(char) grouping separator}. For example, if set to {@code true},
  476.          * the number {@code 1000} could be formatted as {@code "1,000"}. This property only applies to the {@link DoubleFormat#PLAIN PLAIN} format. Default
  477.          * value is {@code false}.
  478.          *
  479.          * @param groupThousands if {@code true}, thousands will be grouped
  480.          * @return this instance
  481.          * @see #groupingSeparator(char)
  482.          */
  483.         public Builder groupThousands(final boolean groupThousands) {
  484.             this.groupThousands = groupThousands;
  485.             return this;
  486.         }

  487.         /**
  488.          * 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
  489.          * 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
  490.          * is {@code true}.
  491.          *
  492.          * @param fractionPlaceholder if {@code true}, a zero character is placed in the fraction position when no fractional value is present; if
  493.          *                            {@code false}, fractional digits are only included when needed
  494.          * @return this instance
  495.          */
  496.         public Builder includeFractionPlaceholder(final boolean fractionPlaceholder) {
  497.             this.fractionPlaceholder = fractionPlaceholder;
  498.             return this;
  499.         }

  500.         /**
  501.          * Sets the string used to represent infinity. For negative infinity, this string is prefixed with the {@link #minusSign(char) minus sign}.
  502.          *
  503.          * @param infinity string used to represent infinity
  504.          * @return this instance
  505.          * @throws NullPointerException if the argument is {@code null}
  506.          */
  507.         public Builder infinity(final String infinity) {
  508.             this.infinity = Objects.requireNonNull(infinity, "infinity");
  509.             return this;
  510.         }

  511.         /**
  512.          * Sets the maximum number of significant decimal digits used in format results. A value of {@code 0} indicates no limit. The default value is
  513.          * {@code 0}.
  514.          *
  515.          * @param maxPrecision maximum precision
  516.          * @return this instance
  517.          */
  518.         public Builder maxPrecision(final int maxPrecision) {
  519.             this.maxPrecision = maxPrecision;
  520.             return this;
  521.         }

  522.         /**
  523.          * Sets the minimum decimal exponent for formatted strings. No digits with an absolute value of less than <code>10<sup>minDecimalExponent</sup></code>
  524.          * will be included in format results. If the number being formatted does not contain any such digits, then zero is returned. For example, if
  525.          * {@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
  526.          * {@code 0.001} is formatted, then the result is the zero string.
  527.          *
  528.          * @param minDecimalExponent minimum decimal exponent
  529.          * @return this instance
  530.          */
  531.         public Builder minDecimalExponent(final int minDecimalExponent) {
  532.             this.minDecimalExponent = minDecimalExponent;
  533.             return this;
  534.         }

  535.         /**
  536.          * Sets the character used as the minus sign.
  537.          *
  538.          * @param minusSign character to use as the minus sign
  539.          * @return this instance
  540.          */
  541.         public Builder minusSign(final char minusSign) {
  542.             this.minusSign = minusSign;
  543.             return this;
  544.         }

  545.         /**
  546.          * Sets the string used to represent {@link Double#NaN}.
  547.          *
  548.          * @param nan string used to represent {@link Double#NaN}
  549.          * @return this instance
  550.          * @throws NullPointerException if the argument is {@code null}
  551.          */
  552.         public Builder nan(final String nan) {
  553.             this.nan = Objects.requireNonNull(nan, "nan");
  554.             return this;
  555.         }

  556.         /**
  557.          * Sets the maximum decimal exponent for numbers formatted as plain decimal strings when using the {@link DoubleFormat#MIXED MIXED} format type. If the
  558.          * number being formatted has an absolute value less than <code>10<sup>plainFormatMaxDecimalExponent + 1</sup></code> and greater than or equal to
  559.          * <code>10<sup>plainFormatMinDecimalExponent</sup></code> after any necessary rounding, then the formatted result will use the
  560.          * {@link DoubleFormat#PLAIN PLAIN} format type. Otherwise, {@link DoubleFormat#SCIENTIFIC SCIENTIFIC} format will be used. For example, if this value
  561.          * 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"}.
  562.          *
  563.          * <p>
  564.          * The default value is {@code 6}.
  565.          *
  566.          * <p>
  567.          * This value is ignored for formats other than {@link DoubleFormat#MIXED}.
  568.          *
  569.          * @param plainFormatMaxDecimalExponent maximum decimal exponent for values formatted as plain strings when using the {@link DoubleFormat#MIXED MIXED}
  570.          *                                      format type.
  571.          * @return this instance
  572.          * @see #plainFormatMinDecimalExponent(int)
  573.          */
  574.         public Builder plainFormatMaxDecimalExponent(final int plainFormatMaxDecimalExponent) {
  575.             this.plainFormatMaxDecimalExponent = plainFormatMaxDecimalExponent;
  576.             return this;
  577.         }

  578.         /**
  579.          * Sets the minimum decimal exponent for numbers formatted as plain decimal strings when using the {@link DoubleFormat#MIXED MIXED} format type. If the
  580.          * number being formatted has an absolute value less than <code>10<sup>plainFormatMaxDecimalExponent + 1</sup></code> and greater than or equal to
  581.          * <code>10<sup>plainFormatMinDecimalExponent</sup></code> after any necessary rounding, then the formatted result will use the
  582.          * {@link DoubleFormat#PLAIN PLAIN} format type. Otherwise, {@link DoubleFormat#SCIENTIFIC SCIENTIFIC} format will be used. For example, if this value
  583.          * 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"}.
  584.          *
  585.          * <p>
  586.          * The default value is {@code -3}.
  587.          *
  588.          * <p>
  589.          * This value is ignored for formats other than {@link DoubleFormat#MIXED}.
  590.          *
  591.          * @param plainFormatMinDecimalExponent maximum decimal exponent for values formatted as plain strings when using the {@link DoubleFormat#MIXED MIXED}
  592.          *                                      format type.
  593.          * @return this instance
  594.          * @see #plainFormatMinDecimalExponent(int)
  595.          */
  596.         public Builder plainFormatMinDecimalExponent(final int plainFormatMinDecimalExponent) {
  597.             this.plainFormatMinDecimalExponent = plainFormatMinDecimalExponent;
  598.             return this;
  599.         }
  600.     }

  601.     /**
  602.      * Format class that uses engineering notation for all values.
  603.      */
  604.     private static final class EngineeringDoubleFormat extends AbstractDoubleFormat {

  605.         /**
  606.          * Constructs a new instance.
  607.          *
  608.          * @param builder builder instance containing configuration values
  609.          */
  610.         EngineeringDoubleFormat(final Builder builder) {
  611.             super(builder);
  612.         }

  613.         /** {@inheritDoc} */
  614.         @Override
  615.         public String applyFiniteInternal(final ParsedDecimal val) {
  616.             return val.toEngineeringString(this);
  617.         }
  618.     }

  619.     /**
  620.      * Format class producing results similar to {@link Double#toString()}, with plain decimal notation for small numbers relatively close to zero and
  621.      * scientific notation otherwise.
  622.      */
  623.     private static final class MixedDoubleFormat extends AbstractDoubleFormat {

  624.         /** Max decimal exponent for plain format. */
  625.         private final int plainMaxExponent;

  626.         /** Min decimal exponent for plain format. */
  627.         private final int plainMinExponent;

  628.         /**
  629.          * Constructs a new instance.
  630.          *
  631.          * @param builder builder instance containing configuration values
  632.          */
  633.         MixedDoubleFormat(final Builder builder) {
  634.             super(builder);

  635.             this.plainMaxExponent = builder.plainFormatMaxDecimalExponent;
  636.             this.plainMinExponent = builder.plainFormatMinDecimalExponent;
  637.         }

  638.         /** {@inheritDoc} */
  639.         @Override
  640.         protected String applyFiniteInternal(final ParsedDecimal val) {
  641.             final int sciExp = val.getScientificExponent();
  642.             if (sciExp <= plainMaxExponent && sciExp >= plainMinExponent) {
  643.                 return val.toPlainString(this);
  644.             }
  645.             return val.toScientificString(this);
  646.         }
  647.     }

  648.     /**
  649.      * Format class that produces plain decimal strings that do not use scientific notation.
  650.      */
  651.     private static final class PlainDoubleFormat extends AbstractDoubleFormat {

  652.         /**
  653.          * Constructs a new instance.
  654.          *
  655.          * @param builder builder instance containing configuration values
  656.          */
  657.         PlainDoubleFormat(final Builder builder) {
  658.             super(builder);
  659.         }

  660.         /**
  661.          * {@inheritDoc}
  662.          */
  663.         @Override
  664.         protected String applyFiniteInternal(final ParsedDecimal val) {
  665.             return val.toPlainString(this);
  666.         }
  667.     }

  668.     /**
  669.      * Format class that uses scientific notation for all values.
  670.      */
  671.     private static final class ScientificDoubleFormat extends AbstractDoubleFormat {

  672.         /**
  673.          * Constructs a new instance.
  674.          *
  675.          * @param builder builder instance containing configuration values
  676.          */
  677.         ScientificDoubleFormat(final Builder builder) {
  678.             super(builder);
  679.         }

  680.         /** {@inheritDoc} */
  681.         @Override
  682.         public String applyFiniteInternal(final ParsedDecimal val) {
  683.             return val.toScientificString(this);
  684.         }
  685.     }

  686.     /** Function used to construct instances for this format type. */
  687.     private final Function<Builder, DoubleFunction<String>> factory;

  688.     /**
  689.      * Constructs a new instance.
  690.      *
  691.      * @param factory function used to construct format instances
  692.      */
  693.     DoubleFormat(final Function<Builder, DoubleFunction<String>> factory) {
  694.         this.factory = factory;
  695.     }

  696.     /**
  697.      * Creates a {@link Builder} for building formatter functions for this format type.
  698.      *
  699.      * @return builder instance
  700.      */
  701.     public Builder builder() {
  702.         return new Builder(factory);
  703.     }
  704. }