• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }