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, "<sup>", "</sup>"); 31 * </pre> 32 * <pre> 33 * // Output: "1.23456×10<sup>-78</sup>" 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