Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(3437)

Unified Diff: icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/ScientificFormat.java

Issue 315420043: DecimalFormat rewrite, Java Base URL: svn+icussh://source.icu-project.org/repos/icu/branches/shane/numberformat/
Patch Set: r39857: Last commit before merge to trunk. Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/ScientificFormat.java
===================================================================
--- icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/ScientificFormat.java (revision 0)
+++ icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/ScientificFormat.java (revision 39857)
@@ -0,0 +1,233 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.impl.number.formatters;
+
+import com.ibm.icu.impl.number.Format;
+import com.ibm.icu.impl.number.FormatQuantity;
+import com.ibm.icu.impl.number.FormatQuantitySelector;
+import com.ibm.icu.impl.number.ModifierHolder;
+import com.ibm.icu.impl.number.Properties;
+import com.ibm.icu.impl.number.Rounder;
+import com.ibm.icu.impl.number.modifiers.ConstantAffixModifier;
+import com.ibm.icu.impl.number.modifiers.PositiveNegativeAffixModifier;
+import com.ibm.icu.impl.number.rounders.IncrementRounder;
+import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder;
+import com.ibm.icu.text.DecimalFormatSymbols;
+import com.ibm.icu.text.NumberFormat;
+
+public class ScientificFormat extends Format.BeforeFormat implements Rounder.MultiplierGenerator {
+
+ public static interface IProperties
+ extends RoundingFormat.IProperties, CurrencyFormat.IProperties {
+
+ static boolean DEFAULT_EXPONENT_SIGN_ALWAYS_SHOWN = false;
+
+ /** @see #setExponentSignAlwaysShown */
+ public boolean getExponentSignAlwaysShown();
+
+ /**
+ * Sets whether to show the plus sign in the exponent part of numbers with a zero or positive
+ * exponent. For example, the number "1200" with the pattern "0.0E0" would be formatted as
+ * "1.2E+3" instead of "1.2E3" in <em>en-US</em>.
+ *
+ * @param exponentSignAlwaysShown Whether to show the plus sign in positive exponents.
+ * @return The property bag, for chaining.
+ */
+ public IProperties setExponentSignAlwaysShown(boolean exponentSignAlwaysShown);
+
+ static int DEFAULT_MINIMUM_EXPONENT_DIGITS = -1;
+
+ /** @see #setMinimumExponentDigits */
+ public int getMinimumExponentDigits();
+
+ /**
+ * Sets the minimum number of digits to display in the exponent. For example, the number "1200"
+ * with the pattern "0.0E00", which has 2 exponent digits, would be formatted as "1.2E03" in
+ * <em>en-US</em>.
+ *
+ * @param minimumExponentDigits The minimum number of digits to display in the exponent field.
+ * @return The property bag, for chaining.
+ */
+ public IProperties setMinimumExponentDigits(int minimumExponentDigits);
+
+ @Override
+ public IProperties clone();
+ }
+
+ public static boolean useScientificNotation(IProperties properties) {
+ return properties.getMinimumExponentDigits() != IProperties.DEFAULT_MINIMUM_EXPONENT_DIGITS;
+ }
+
+ private static final ThreadLocal<Properties> threadLocalProperties =
+ new ThreadLocal<Properties>() {
+ @Override
+ protected Properties initialValue() {
+ return new Properties();
+ }
+ };
+
+ public static ScientificFormat getInstance(DecimalFormatSymbols symbols, IProperties properties) {
+ // If significant digits or rounding interval are specified through normal means, we use those.
+ // Otherwise, we use the special significant digit rules for scientific notation.
+ Rounder rounder;
+ if (IncrementRounder.useRoundingIncrement(properties)) {
+ rounder = IncrementRounder.getInstance(properties);
+ } else if (SignificantDigitsRounder.useSignificantDigits(properties)) {
+ rounder = SignificantDigitsRounder.getInstance(properties);
+ } else {
+ Properties rprops = threadLocalProperties.get().clear();
+
+ int minInt = properties.getMinimumIntegerDigits();
+ int maxInt = properties.getMaximumIntegerDigits();
+ int minFrac = properties.getMinimumFractionDigits();
+ int maxFrac = properties.getMaximumFractionDigits();
+
+ // If currency is in use, pull information from CurrencyUsage.
+ if (CurrencyFormat.useCurrency(properties)) {
+ // Use rprops as the vehicle (it is still clean)
+ CurrencyFormat.populateCurrencyRounderProperties(rprops, symbols, properties);
+ minFrac = rprops.getMinimumFractionDigits();
+ maxFrac = rprops.getMaximumFractionDigits();
+ rprops.clear();
+ }
+
+ // TODO: Mark/Andy, take a look at this logic and see if it makes sense to you.
+ // I fiddled with the settings and fallbacks to make the unit tests pass, but I
+ // don't feel that it's the "right way" to do things.
+
+ if (minInt < 0) minInt = 0;
+ if (maxInt < minInt) maxInt = minInt;
+ if (minFrac < 0) minFrac = 0;
+ if (maxFrac < minFrac) maxFrac = minFrac;
+
+ rprops.setRoundingMode(properties.getRoundingMode());
+
+ if (minInt == 0 && maxFrac == 0) {
+ // Special case for the pattern "#E0" with no significant digits specified.
+ rprops.setMinimumSignificantDigits(1);
+ rprops.setMaximumSignificantDigits(Integer.MAX_VALUE);
+ } else if (minInt == 0 && minFrac == 0) {
+ // Special case for patterns like "#.##E0" with no significant digits specified.
+ rprops.setMinimumSignificantDigits(1);
+ rprops.setMaximumSignificantDigits(1 + maxFrac);
+ } else {
+ rprops.setMinimumSignificantDigits(minInt + minFrac);
+ rprops.setMaximumSignificantDigits(minInt + maxFrac);
+ }
+ rprops.setMinimumIntegerDigits(maxInt == 0 ? 0 : Math.max(1, minInt + minFrac - maxFrac));
+ rprops.setMaximumIntegerDigits(maxInt);
+ rprops.setMinimumFractionDigits(Math.max(0, minFrac + minInt - maxInt));
+ rprops.setMaximumFractionDigits(maxFrac);
+ rounder = SignificantDigitsRounder.getInstance(rprops);
+ }
+
+ return new ScientificFormat(symbols, properties, rounder);
+ }
+
+ public static ScientificFormat getInstance(
+ DecimalFormatSymbols symbols, IProperties properties, Rounder rounder) {
+ return new ScientificFormat(symbols, properties, rounder);
+ }
+
+ // Properties
+ private final boolean exponentShowPlusSign;
+ private final int exponentDigits;
+ private final int minInt;
+ private final int maxInt;
+ private final int interval;
+ private final Rounder rounder;
+ private final ConstantAffixModifier separatorMod;
+ private final PositiveNegativeAffixModifier signMod;
+
+ // Symbols
+ private final String[] digitStrings;
+
+ private ScientificFormat(DecimalFormatSymbols symbols, IProperties properties, Rounder rounder) {
+ exponentShowPlusSign = properties.getExponentSignAlwaysShown();
+ exponentDigits = Math.max(1, properties.getMinimumExponentDigits());
+ int _maxInt = properties.getMaximumIntegerDigits();
+ int _minInt = properties.getMinimumIntegerDigits();
+ // Special behavior:
+ if (_maxInt > 8) {
+ _maxInt = _minInt;
+ }
+ maxInt = _maxInt < 0 ? Integer.MAX_VALUE : _maxInt;
+ minInt = _minInt < 0 ? 0 : _minInt < maxInt ? _minInt : maxInt;
+ interval = Math.max(1, maxInt);
+ this.rounder = rounder;
+ digitStrings = symbols.getDigitStrings(); // makes a copy
+
+ separatorMod =
+ new ConstantAffixModifier(
+ "", symbols.getExponentSeparator(), NumberFormat.Field.EXPONENT_SYMBOL, true);
+ signMod =
+ new PositiveNegativeAffixModifier(
+ new ConstantAffixModifier(
+ "",
+ exponentShowPlusSign ? symbols.getPlusSignString() : "",
+ NumberFormat.Field.EXPONENT_SIGN,
+ true),
+ new ConstantAffixModifier(
+ "", symbols.getMinusSignString(), NumberFormat.Field.EXPONENT_SIGN, true));
+ }
+
+ private static final ThreadLocal<StringBuilder> threadLocalStringBuilder =
+ new ThreadLocal<StringBuilder>() {
+ @Override
+ protected StringBuilder initialValue() {
+ return new StringBuilder();
+ }
+ };
+
+ @Override
+ public void before(FormatQuantity input, ModifierHolder mods) {
+
+ // Treat zero as if it had magnitude 0
+ int exponent;
+ if (input.isZero()) {
+ rounder.apply(input);
+ exponent = 0;
+ } else {
+ exponent = -rounder.chooseMultiplierAndApply(input, this);
+ }
+
+ // Format the exponent part of the scientific format.
+ // Insert digits starting from the left so that append can be used.
+ // TODO: Use thread locals here.
+ FormatQuantity exponentQ = FormatQuantitySelector.from(exponent);
+ StringBuilder exponentSB = threadLocalStringBuilder.get();
+ exponentSB.setLength(0);
+ exponentQ.setIntegerFractionLength(exponentDigits, Integer.MAX_VALUE, 0, 0);
+ for (int i = exponentQ.getUpperDisplayMagnitude(); i >= 0; i--) {
+ exponentSB.append(digitStrings[exponentQ.getDigit(i)]);
+ }
+
+ // Add modifiers from the outside in.
+ mods.add(
+ new ConstantAffixModifier("", exponentSB.toString(), NumberFormat.Field.EXPONENT, true));
+ mods.add(signMod.getModifier(exponent < 0));
+ mods.add(separatorMod);
+ }
+
+ @Override
+ public int getMultiplier(int magnitude) {
+ int digitsShown = ((magnitude % interval + interval) % interval) + 1;
+ if (digitsShown < minInt) {
+ digitsShown = minInt;
+ } else if (digitsShown > maxInt) {
+ digitsShown = maxInt;
+ }
+ int retval = digitsShown - magnitude - 1;
+ return retval;
+ }
+
+ @Override
+ public void export(Properties properties) {
+ properties.setMinimumExponentDigits(exponentDigits);
+ properties.setExponentSignAlwaysShown(exponentShowPlusSign);
+
+ // Set the transformed object into the property bag. This may result in a pattern string that
+ // uses different syntax from the original, but it will be functionally equivalent.
+ rounder.export(properties);
+ }
+}
Property changes on: icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/ScientificFormat.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain;charset=utf-8
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b