1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2017 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 package ohos.global.icu.number; 5 6 import java.text.Format.Field; 7 8 import ohos.global.icu.impl.FormattedStringBuilder; 9 import ohos.global.icu.impl.number.ConstantAffixModifier; 10 import ohos.global.icu.impl.number.DecimalQuantity; 11 import ohos.global.icu.impl.number.MicroProps; 12 import ohos.global.icu.impl.number.MicroPropsGenerator; 13 import ohos.global.icu.impl.number.Modifier; 14 import ohos.global.icu.impl.number.MultiplierProducer; 15 import ohos.global.icu.impl.number.RoundingUtils; 16 import ohos.global.icu.number.NumberFormatter.SignDisplay; 17 import ohos.global.icu.number.Precision.SignificantRounderImpl; 18 import ohos.global.icu.text.DecimalFormatSymbols; 19 import ohos.global.icu.text.NumberFormat; 20 21 /** 22 * A class that defines the scientific notation style to be used when formatting numbers in 23 * NumberFormatter. 24 * 25 * <p> 26 * To create a ScientificNotation, use one of the factory methods in {@link Notation}. 27 * 28 * @see NumberFormatter 29 * @hide exposed on OHOS 30 */ 31 public class ScientificNotation extends Notation { 32 33 int engineeringInterval; 34 boolean requireMinInt; 35 int minExponentDigits; 36 SignDisplay exponentSignDisplay; 37 ScientificNotation( int engineeringInterval, boolean requireMinInt, int minExponentDigits, SignDisplay exponentSignDisplay)38 /* package-private */ ScientificNotation( 39 int engineeringInterval, 40 boolean requireMinInt, 41 int minExponentDigits, 42 SignDisplay exponentSignDisplay) { 43 this.engineeringInterval = engineeringInterval; 44 this.requireMinInt = requireMinInt; 45 this.minExponentDigits = minExponentDigits; 46 this.exponentSignDisplay = exponentSignDisplay; 47 } 48 49 /** 50 * Sets the minimum number of digits to show in the exponent of scientific notation, padding with 51 * zeros if necessary. Useful for fixed-width display. 52 * 53 * <p> 54 * For example, with minExponentDigits=2, the number 123 will be printed as "1.23E02" in 55 * <em>en-US</em> instead of the default "1.23E2". 56 * 57 * @param minExponentDigits 58 * The minimum number of digits to show in the exponent. 59 * @return A ScientificNotation, for chaining. 60 * @throws IllegalArgumentException if minExponentDigits is too big or smaller than 1 61 * @see NumberFormatter 62 */ withMinExponentDigits(int minExponentDigits)63 public ScientificNotation withMinExponentDigits(int minExponentDigits) { 64 if (minExponentDigits >= 1 && minExponentDigits <= RoundingUtils.MAX_INT_FRAC_SIG) { 65 ScientificNotation other = createCopy(); 66 other.minExponentDigits = minExponentDigits; 67 return other; 68 } else { 69 throw new IllegalArgumentException("Integer digits must be between 1 and " 70 + RoundingUtils.MAX_INT_FRAC_SIG 71 + " (inclusive)"); 72 } 73 } 74 75 /** 76 * Sets whether to show the sign on positive and negative exponents in scientific notation. The 77 * default is AUTO, showing the minus sign but not the plus sign. 78 * 79 * <p> 80 * For example, with exponentSignDisplay=ALWAYS, the number 123 will be printed as "1.23E+2" in 81 * <em>en-US</em> instead of the default "1.23E2". 82 * 83 * @param exponentSignDisplay 84 * The strategy for displaying the sign in the exponent. 85 * @return A ScientificNotation, for chaining. 86 * @see NumberFormatter 87 */ withExponentSignDisplay(SignDisplay exponentSignDisplay)88 public ScientificNotation withExponentSignDisplay(SignDisplay exponentSignDisplay) { 89 ScientificNotation other = createCopy(); 90 other.exponentSignDisplay = exponentSignDisplay; 91 return other; 92 } 93 94 /** Package-private clone method */ createCopy()95 ScientificNotation createCopy() { 96 return new ScientificNotation( 97 engineeringInterval, 98 requireMinInt, 99 minExponentDigits, 100 exponentSignDisplay 101 ); 102 } 103 withLocaleData( DecimalFormatSymbols symbols, boolean build, MicroPropsGenerator parent)104 /* package-private */ MicroPropsGenerator withLocaleData( 105 DecimalFormatSymbols symbols, 106 boolean build, 107 MicroPropsGenerator parent) { 108 return new ScientificHandler(this, symbols, build, parent); 109 } 110 111 // NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and 112 // C++. 113 // 114 // During formatting, we need to provide an object with state (the exponent) as the inner modifier. 115 // 116 // In Java, where the priority is put on reducing object creations, the unsafe code path re-uses the 117 // ScientificHandler as a ScientificModifier, and the safe code path pre-computes 25 118 // ScientificModifier 119 // instances. This scheme reduces the number of object creations by 1 in both safe and unsafe. 120 // 121 // In C++, MicroProps provides a pre-allocated ScientificModifier, and ScientificHandler simply 122 // populates 123 // the state (the exponent) into that ScientificModifier. There is no difference between safe and 124 // unsafe. 125 126 private static class ScientificHandler implements MicroPropsGenerator, MultiplierProducer, Modifier { 127 128 final ScientificNotation notation; 129 final DecimalFormatSymbols symbols; 130 final ScientificModifier[] precomputedMods; 131 final MicroPropsGenerator parent; 132 /* unsafe */ int exponent; 133 ScientificHandler( ScientificNotation notation, DecimalFormatSymbols symbols, boolean safe, MicroPropsGenerator parent)134 private ScientificHandler( 135 ScientificNotation notation, 136 DecimalFormatSymbols symbols, 137 boolean safe, 138 MicroPropsGenerator parent) { 139 this.notation = notation; 140 this.symbols = symbols; 141 this.parent = parent; 142 143 if (safe) { 144 // Pre-build the modifiers for exponents -12 through 12 145 precomputedMods = new ScientificModifier[25]; 146 for (int i = -12; i <= 12; i++) { 147 precomputedMods[i + 12] = new ScientificModifier(i, this); 148 } 149 } else { 150 precomputedMods = null; 151 } 152 } 153 154 @Override processQuantity(DecimalQuantity quantity)155 public MicroProps processQuantity(DecimalQuantity quantity) { 156 MicroProps micros = parent.processQuantity(quantity); 157 assert micros.rounder != null; 158 159 // Do not apply scientific notation to special doubles 160 if (quantity.isInfinite() || quantity.isNaN()) { 161 micros.modInner = ConstantAffixModifier.EMPTY; 162 return micros; 163 } 164 165 // Treat zero as if it had magnitude 0 166 int exponent; 167 if (quantity.isZeroish()) { 168 if (notation.requireMinInt && micros.rounder instanceof SignificantRounderImpl) { 169 // Show "00.000E0" on pattern "00.000E0" 170 ((SignificantRounderImpl) micros.rounder).apply(quantity, 171 notation.engineeringInterval); 172 exponent = 0; 173 } else { 174 micros.rounder.apply(quantity); 175 exponent = 0; 176 } 177 } else { 178 exponent = -micros.rounder.chooseMultiplierAndApply(quantity, this); 179 } 180 181 // Add the Modifier for the scientific format. 182 if (precomputedMods != null && exponent >= -12 && exponent <= 12) { 183 // Safe code path A 184 micros.modInner = precomputedMods[exponent + 12]; 185 } else if (precomputedMods != null) { 186 // Safe code path B 187 micros.modInner = new ScientificModifier(exponent, this); 188 } else { 189 // Unsafe code path: mutates the object and re-uses it as a Modifier! 190 this.exponent = exponent; 191 micros.modInner = this; 192 } 193 194 // Change the exponent only after we select appropriate plural form 195 // for formatting purposes so that we preserve expected formatted 196 // string behavior. 197 quantity.adjustExponent(exponent); 198 199 // We already performed rounding. Do not perform it again. 200 micros.rounder = null; 201 202 return micros; 203 } 204 205 @Override getMultiplier(int magnitude)206 public int getMultiplier(int magnitude) { 207 int interval = notation.engineeringInterval; 208 int digitsShown; 209 if (notation.requireMinInt) { 210 // For patterns like "000.00E0" and ".00E0" 211 digitsShown = interval; 212 } else if (interval <= 1) { 213 // For patterns like "0.00E0" and "@@@E0" 214 digitsShown = 1; 215 } else { 216 // For patterns like "##0.00" 217 digitsShown = ((magnitude % interval + interval) % interval) + 1; 218 } 219 return digitsShown - magnitude - 1; 220 } 221 222 @Override getPrefixLength()223 public int getPrefixLength() { 224 // TODO: Localized exponent separator location. 225 return 0; 226 } 227 228 @Override getCodePointCount()229 public int getCodePointCount() { 230 // NOTE: This method is only called one place, NumberRangeFormatterImpl. 231 // The call site only cares about != 0 and != 1. 232 // Return a very large value so that if this method is used elsewhere, we should notice. 233 return 999; 234 } 235 236 @Override isStrong()237 public boolean isStrong() { 238 // Scientific is always strong 239 return true; 240 } 241 242 @Override containsField(Field field)243 public boolean containsField(Field field) { 244 // This method is not currently used. (unsafe path not used in range formatting) 245 assert false; 246 return false; 247 } 248 249 @Override getParameters()250 public Parameters getParameters() { 251 // This method is not currently used. 252 assert false; 253 return null; 254 } 255 256 @Override semanticallyEquivalent(Modifier other)257 public boolean semanticallyEquivalent(Modifier other) { 258 // This method is not currently used. (unsafe path not used in range formatting) 259 assert false; 260 return false; 261 } 262 263 @Override apply(FormattedStringBuilder output, int leftIndex, int rightIndex)264 public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) { 265 return doApply(exponent, output, rightIndex); 266 } 267 doApply(int exponent, FormattedStringBuilder output, int rightIndex)268 private int doApply(int exponent, FormattedStringBuilder output, int rightIndex) { 269 // FIXME: Localized exponent separator location. 270 int i = rightIndex; 271 // Append the exponent separator and sign 272 i += output.insert(i, symbols.getExponentSeparator(), NumberFormat.Field.EXPONENT_SYMBOL); 273 if (exponent < 0 && notation.exponentSignDisplay != SignDisplay.NEVER) { 274 i += output.insert(i, symbols.getMinusSignString(), NumberFormat.Field.EXPONENT_SIGN); 275 } else if (exponent >= 0 && notation.exponentSignDisplay == SignDisplay.ALWAYS) { 276 i += output.insert(i, symbols.getPlusSignString(), NumberFormat.Field.EXPONENT_SIGN); 277 } 278 // Append the exponent digits (using a simple inline algorithm) 279 int disp = Math.abs(exponent); 280 for (int j = 0; j < notation.minExponentDigits || disp > 0; j++, disp /= 10) { 281 int d = disp % 10; 282 String digitString = symbols.getDigitStringsLocal()[d]; 283 i += output.insert(i - j, digitString, NumberFormat.Field.EXPONENT); 284 } 285 return i - rightIndex; 286 } 287 } 288 289 private static class ScientificModifier implements Modifier { 290 final int exponent; 291 final ScientificHandler handler; 292 ScientificModifier(int exponent, ScientificHandler handler)293 ScientificModifier(int exponent, ScientificHandler handler) { 294 this.exponent = exponent; 295 this.handler = handler; 296 } 297 298 @Override apply(FormattedStringBuilder output, int leftIndex, int rightIndex)299 public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) { 300 return handler.doApply(exponent, output, rightIndex); 301 } 302 303 @Override getPrefixLength()304 public int getPrefixLength() { 305 // TODO: Localized exponent separator location. 306 return 0; 307 } 308 309 @Override getCodePointCount()310 public int getCodePointCount() { 311 // NOTE: This method is only called one place, NumberRangeFormatterImpl. 312 // The call site only cares about != 0 and != 1. 313 // Return a very large value so that if this method is used elsewhere, we should notice. 314 return 999; 315 } 316 317 @Override isStrong()318 public boolean isStrong() { 319 // Scientific is always strong 320 return true; 321 } 322 323 @Override containsField(Field field)324 public boolean containsField(Field field) { 325 // This method is not used for inner modifiers. 326 assert false; 327 return false; 328 } 329 330 @Override getParameters()331 public Parameters getParameters() { 332 return null; 333 } 334 335 @Override semanticallyEquivalent(Modifier other)336 public boolean semanticallyEquivalent(Modifier other) { 337 if (!(other instanceof ScientificModifier)) { 338 return false; 339 } 340 ScientificModifier _other = (ScientificModifier) other; 341 // TODO: Check for locale symbols and settings as well? Could be less efficient. 342 return exponent == _other.exponent; 343 } 344 } 345 }