• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2016 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html
4 /*
5  *******************************************************************************
6  * Copyright (C) 2014-2016, International Business Machines Corporation and
7  * others. All Rights Reserved.
8  *******************************************************************************
9  */
10 package android.icu.text;
11 
12 import java.text.AttributedCharacterIterator;
13 import java.text.AttributedCharacterIterator.Attribute;
14 import java.text.CharacterIterator;
15 import java.util.Map;
16 
17 import android.icu.impl.StaticUnicodeSets;
18 import android.icu.lang.UCharacter;
19 import android.icu.util.ULocale;
20 
21 /**
22  *A formatter that formats numbers in user-friendly scientific notation.
23  *
24  * ScientificNumberFormatter instances are immutable and thread-safe.
25  *
26  * Sample code:
27  * <pre>
28  * ULocale en = new ULocale("en");
29  * ScientificNumberFormatter fmt = ScientificNumberFormatter.getMarkupInstance(
30  *         en, "&lt;sup&gt;", "&lt;/sup&gt;");
31  * </pre>
32  * <pre>
33  * // Output: "1.23456×10&lt;sup&gt;-78&lt;/sup&gt;"
34  * System.out.println(fmt.format(1.23456e-78));
35  * </pre>
36  *
37  */
38 public final class ScientificNumberFormatter {
39 
40     private final String preExponent;
41     private final DecimalFormat fmt;
42     private final Style style;
43 
44     /**
45      * Gets a ScientificNumberFormatter instance that uses
46      * superscript characters for exponents for this locale.
47      * @param locale The locale
48      * @return The ScientificNumberFormatter instance.
49      */
getSuperscriptInstance(ULocale locale)50     public static ScientificNumberFormatter getSuperscriptInstance(ULocale locale) {
51         return getInstanceForLocale(locale, SUPER_SCRIPT);
52      }
53 
54     /**
55      * Gets a ScientificNumberFormatter instance that uses
56      * superscript characters for exponents.
57      * @param df The DecimalFormat must be configured for scientific
58      *   notation. Caller may safely change df after this call as this method
59      *   clones it when creating the ScientificNumberFormatter.
60      * @return the ScientificNumberFormatter instance.
61      */
getSuperscriptInstance( DecimalFormat df)62      public static ScientificNumberFormatter getSuperscriptInstance(
63              DecimalFormat df) {
64          return getInstance(df, SUPER_SCRIPT);
65      }
66 
67      /**
68       * Gets a ScientificNumberFormatter instance that uses
69       * markup for exponents for this locale.
70       * @param locale The locale
71       * @param beginMarkup the markup to start superscript e.g {@code <sup>}
72       * @param endMarkup the markup to end superscript e.g {@code </sup>}
73       * @return The ScientificNumberFormatter instance.
74       */
getMarkupInstance( ULocale locale, String beginMarkup, String endMarkup)75      public static ScientificNumberFormatter getMarkupInstance(
76              ULocale locale,
77              String beginMarkup,
78              String endMarkup) {
79          return getInstanceForLocale(
80                  locale, new MarkupStyle(beginMarkup, endMarkup));
81      }
82 
83      /**
84       * Gets a ScientificNumberFormatter instance that uses
85       * markup for exponents.
86       * @param df The DecimalFormat must be configured for scientific
87       *   notation. Caller may safely change df after this call as this method
88       *   clones it when creating the ScientificNumberFormatter.
89       * @param beginMarkup the markup to start superscript e.g {@code <sup>}
90       * @param endMarkup the markup to end superscript e.g {@code </sup>}
91       * @return The ScientificNumberFormatter instance.
92       */
getMarkupInstance( DecimalFormat df, String beginMarkup, String endMarkup)93      public static ScientificNumberFormatter getMarkupInstance(
94              DecimalFormat df,
95              String beginMarkup,
96              String endMarkup) {
97          return getInstance(
98                  df, new MarkupStyle(beginMarkup, endMarkup));
99      }
100 
101      /**
102       * Formats a number
103       * @param number Can be a double, int, Number or
104       *  anything that DecimalFormat#format(Object) accepts.
105       * @return the formatted string.
106       */
format(Object number)107      public String format(Object number) {
108          synchronized (fmt) {
109              return style.format(
110                      fmt.formatToCharacterIterator(number),
111                      preExponent);
112          }
113      }
114 
115     /**
116      * A style type for ScientificNumberFormatter. All Style instances are immutable
117      * and thread-safe.
118      */
119     private static abstract class Style {
format( AttributedCharacterIterator iterator, String preExponent)120         abstract String format(
121                 AttributedCharacterIterator iterator,
122                 String preExponent); // '* 10^'
123 
append( AttributedCharacterIterator iterator, int start, int limit, StringBuilder result)124         static void append(
125                 AttributedCharacterIterator iterator,
126                 int start,
127                 int limit,
128                 StringBuilder result) {
129             int oldIndex = iterator.getIndex();
130             iterator.setIndex(start);
131             for (int i = start; i < limit; i++) {
132                 result.append(iterator.current());
133                 iterator.next();
134             }
135             iterator.setIndex(oldIndex);
136         }
137     }
138 
139     private static class MarkupStyle extends Style {
140 
141         private final String beginMarkup;
142         private final String endMarkup;
143 
MarkupStyle(String beginMarkup, String endMarkup)144         MarkupStyle(String beginMarkup, String endMarkup) {
145             this.beginMarkup = beginMarkup;
146             this.endMarkup = endMarkup;
147         }
148 
149         @Override
format( AttributedCharacterIterator iterator, String preExponent)150         String format(
151                 AttributedCharacterIterator iterator,
152                 String preExponent) {
153             int copyFromOffset = 0;
154             StringBuilder result = new StringBuilder();
155             for (
156                     iterator.first();
157                     iterator.current() != CharacterIterator.DONE;
158                 ) {
159                 Map<Attribute, Object> attributeSet = iterator.getAttributes();
160                 if (attributeSet.containsKey(NumberFormat.Field.EXPONENT_SYMBOL)) {
161                     append(
162                             iterator,
163                             copyFromOffset,
164                             iterator.getRunStart(NumberFormat.Field.EXPONENT_SYMBOL),
165                             result);
166                     copyFromOffset = iterator.getRunLimit(NumberFormat.Field.EXPONENT_SYMBOL);
167                     iterator.setIndex(copyFromOffset);
168                     result.append(preExponent);
169                     result.append(beginMarkup);
170                 } else if (attributeSet.containsKey(NumberFormat.Field.EXPONENT)) {
171                     int limit = iterator.getRunLimit(NumberFormat.Field.EXPONENT);
172                     append(
173                             iterator,
174                             copyFromOffset,
175                             limit,
176                             result);
177                     copyFromOffset = limit;
178                     iterator.setIndex(copyFromOffset);
179                     result.append(endMarkup);
180                 } else {
181                     iterator.next();
182                 }
183             }
184             append(iterator, copyFromOffset, iterator.getEndIndex(), result);
185             return result.toString();
186         }
187     }
188 
189     private static class SuperscriptStyle extends Style {
190 
191         private static final char[] SUPERSCRIPT_DIGITS = {
192             0x2070, 0xB9, 0xB2, 0xB3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079
193         };
194 
195         private static final char SUPERSCRIPT_PLUS_SIGN = 0x207A;
196         private static final char SUPERSCRIPT_MINUS_SIGN = 0x207B;
197 
198         @Override
format( AttributedCharacterIterator iterator, String preExponent)199         String format(
200                 AttributedCharacterIterator iterator,
201                 String preExponent) {
202             int copyFromOffset = 0;
203             StringBuilder result = new StringBuilder();
204             for (
205                     iterator.first();
206                     iterator.current() != CharacterIterator.DONE;
207                 ) {
208                 Map<Attribute, Object> attributeSet = iterator.getAttributes();
209                 if (attributeSet.containsKey(NumberFormat.Field.EXPONENT_SYMBOL)) {
210                     append(
211                             iterator,
212                             copyFromOffset,
213                             iterator.getRunStart(NumberFormat.Field.EXPONENT_SYMBOL),
214                             result);
215                     copyFromOffset = iterator.getRunLimit(NumberFormat.Field.EXPONENT_SYMBOL);
216                     iterator.setIndex(copyFromOffset);
217                     result.append(preExponent);
218                 } else if (attributeSet.containsKey(NumberFormat.Field.EXPONENT_SIGN)) {
219                     int start = iterator.getRunStart(NumberFormat.Field.EXPONENT_SIGN);
220                     int limit = iterator.getRunLimit(NumberFormat.Field.EXPONENT_SIGN);
221                     int aChar = char32AtAndAdvance(iterator);
222                     if (StaticUnicodeSets.get(StaticUnicodeSets.Key.MINUS_SIGN).contains(aChar)) {
223                         append(
224                                 iterator,
225                                 copyFromOffset,
226                                 start,
227                                 result);
228                         result.append(SUPERSCRIPT_MINUS_SIGN);
229                     } else if (StaticUnicodeSets.get(StaticUnicodeSets.Key.PLUS_SIGN).contains(aChar)) {
230                         append(
231                                 iterator,
232                                 copyFromOffset,
233                                 start,
234                                 result);
235                         result.append(SUPERSCRIPT_PLUS_SIGN);
236                     } else {
237                         throw new IllegalArgumentException();
238                     }
239                     copyFromOffset = limit;
240                     iterator.setIndex(copyFromOffset);
241                 } else if (attributeSet.containsKey(NumberFormat.Field.EXPONENT)) {
242                     int start = iterator.getRunStart(NumberFormat.Field.EXPONENT);
243                     int limit = iterator.getRunLimit(NumberFormat.Field.EXPONENT);
244                     append(
245                             iterator,
246                             copyFromOffset,
247                             start,
248                             result);
249                     copyAsSuperscript(iterator, start, limit, result);
250                     copyFromOffset = limit;
251                     iterator.setIndex(copyFromOffset);
252                 } else {
253                     iterator.next();
254                 }
255             }
256             append(iterator, copyFromOffset, iterator.getEndIndex(), result);
257             return result.toString();
258         }
259 
copyAsSuperscript( AttributedCharacterIterator iterator, int start, int limit, StringBuilder result)260         private static void copyAsSuperscript(
261                 AttributedCharacterIterator iterator, int start, int limit, StringBuilder result) {
262             int oldIndex = iterator.getIndex();
263             iterator.setIndex(start);
264             while (iterator.getIndex() < limit) {
265                 int aChar = char32AtAndAdvance(iterator);
266                 int digit = UCharacter.digit(aChar);
267                 if (digit < 0) {
268                     throw new IllegalArgumentException();
269                 }
270                 result.append(SUPERSCRIPT_DIGITS[digit]);
271             }
272             iterator.setIndex(oldIndex);
273         }
274 
char32AtAndAdvance(AttributedCharacterIterator iterator)275         private static int char32AtAndAdvance(AttributedCharacterIterator iterator) {
276             char c1 = iterator.current();
277             char c2 = iterator.next();
278             if (UCharacter.isHighSurrogate(c1)) {
279                 // If c2 is DONE, it will fail the low surrogate test and we
280                 // skip this block.
281                 if (UCharacter.isLowSurrogate(c2)) {
282                     iterator.next();
283                     return UCharacter.toCodePoint(c1, c2);
284                 }
285             }
286             return c1;
287         }
288 
289     }
290 
getPreExponent(DecimalFormatSymbols dfs)291     private static String getPreExponent(DecimalFormatSymbols dfs) {
292         StringBuilder preExponent = new StringBuilder();
293         preExponent.append(dfs.getExponentMultiplicationSign());
294         char[] digits = dfs.getDigits();
295         preExponent.append(digits[1]).append(digits[0]);
296         return preExponent.toString();
297     }
298 
getInstance( DecimalFormat decimalFormat, Style style)299     private static ScientificNumberFormatter getInstance(
300             DecimalFormat decimalFormat, Style style) {
301         DecimalFormatSymbols dfs = decimalFormat.getDecimalFormatSymbols();
302         return new ScientificNumberFormatter(
303                 (DecimalFormat) decimalFormat.clone(), getPreExponent(dfs), style);
304     }
305 
getInstanceForLocale( ULocale locale, Style style)306     private static ScientificNumberFormatter getInstanceForLocale(
307             ULocale locale, Style style) {
308         DecimalFormat decimalFormat =
309                 (DecimalFormat) DecimalFormat.getScientificInstance(locale);
310         return new ScientificNumberFormatter(
311                 decimalFormat,
312                 getPreExponent(decimalFormat.getDecimalFormatSymbols()),
313                 style);
314     }
315 
316     private static final Style SUPER_SCRIPT = new SuperscriptStyle();
317 
ScientificNumberFormatter( DecimalFormat decimalFormat, String preExponent, Style style)318     private ScientificNumberFormatter(
319             DecimalFormat decimalFormat, String preExponent, Style style) {
320         this.fmt = decimalFormat;
321         this.preExponent = preExponent;
322         this.style = style;
323     }
324 
325 
326 }
327