com.ibm.icu.text.ScientificNumberFormatter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
*******************************************************************************
* Copyright (C) 2014-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.text;
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.CharacterIterator;
import java.util.Map;
import com.ibm.icu.impl.StaticUnicodeSets;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.util.ULocale;
/**
*A formatter that formats numbers in user-friendly scientific notation.
*
* ScientificNumberFormatter instances are immutable and thread-safe.
*
* Sample code:
*
* ULocale en = new ULocale("en");
* ScientificNumberFormatter fmt = ScientificNumberFormatter.getMarkupInstance(
* en, "<sup>", "</sup>");
*
*
* // Output: "1.23456×10<sup>-78</sup>"
* System.out.println(fmt.format(1.23456e-78));
*
*
* @stable ICU 55
*
*/
public final class ScientificNumberFormatter {
private final String preExponent;
private final DecimalFormat fmt;
private final Style style;
/**
* Gets a ScientificNumberFormatter instance that uses
* superscript characters for exponents for this locale.
* @param locale The locale
* @return The ScientificNumberFormatter instance.
*
* @stable ICU 55
*/
public static ScientificNumberFormatter getSuperscriptInstance(ULocale locale) {
return getInstanceForLocale(locale, SUPER_SCRIPT);
}
/**
* Gets a ScientificNumberFormatter instance that uses
* superscript characters for exponents.
* @param df The DecimalFormat must be configured for scientific
* notation. Caller may safely change df after this call as this method
* clones it when creating the ScientificNumberFormatter.
* @return the ScientificNumberFormatter instance.
*
* @stable ICU 55
*/
public static ScientificNumberFormatter getSuperscriptInstance(
DecimalFormat df) {
return getInstance(df, SUPER_SCRIPT);
}
/**
* Gets a ScientificNumberFormatter instance that uses
* markup for exponents for this locale.
* @param locale The locale
* @param beginMarkup the markup to start superscript e.g {@code }
* @param endMarkup the markup to end superscript e.g {@code }
* @return The ScientificNumberFormatter instance.
*
* @stable ICU 55
*/
public static ScientificNumberFormatter getMarkupInstance(
ULocale locale,
String beginMarkup,
String endMarkup) {
return getInstanceForLocale(
locale, new MarkupStyle(beginMarkup, endMarkup));
}
/**
* Gets a ScientificNumberFormatter instance that uses
* markup for exponents.
* @param df The DecimalFormat must be configured for scientific
* notation. Caller may safely change df after this call as this method
* clones it when creating the ScientificNumberFormatter.
* @param beginMarkup the markup to start superscript e.g {@code }
* @param endMarkup the markup to end superscript e.g {@code }
* @return The ScientificNumberFormatter instance.
*
* @stable ICU 55
*/
public static ScientificNumberFormatter getMarkupInstance(
DecimalFormat df,
String beginMarkup,
String endMarkup) {
return getInstance(
df, new MarkupStyle(beginMarkup, endMarkup));
}
/**
* Formats a number
* @param number Can be a double, int, Number or
* anything that DecimalFormat#format(Object) accepts.
* @return the formatted string.
*
* @stable ICU 55
*/
public String format(Object number) {
synchronized (fmt) {
return style.format(
fmt.formatToCharacterIterator(number),
preExponent);
}
}
/**
* A style type for ScientificNumberFormatter. All Style instances are immutable
* and thread-safe.
*/
private static abstract class Style {
abstract String format(
AttributedCharacterIterator iterator,
String preExponent); // '* 10^'
static void append(
AttributedCharacterIterator iterator,
int start,
int limit,
StringBuilder result) {
int oldIndex = iterator.getIndex();
iterator.setIndex(start);
for (int i = start; i < limit; i++) {
result.append(iterator.current());
iterator.next();
}
iterator.setIndex(oldIndex);
}
}
private static class MarkupStyle extends Style {
private final String beginMarkup;
private final String endMarkup;
MarkupStyle(String beginMarkup, String endMarkup) {
this.beginMarkup = beginMarkup;
this.endMarkup = endMarkup;
}
@Override
String format(
AttributedCharacterIterator iterator,
String preExponent) {
int copyFromOffset = 0;
StringBuilder result = new StringBuilder();
for (
iterator.first();
iterator.current() != CharacterIterator.DONE;
) {
Map attributeSet = iterator.getAttributes();
if (attributeSet.containsKey(NumberFormat.Field.EXPONENT_SYMBOL)) {
append(
iterator,
copyFromOffset,
iterator.getRunStart(NumberFormat.Field.EXPONENT_SYMBOL),
result);
copyFromOffset = iterator.getRunLimit(NumberFormat.Field.EXPONENT_SYMBOL);
iterator.setIndex(copyFromOffset);
result.append(preExponent);
result.append(beginMarkup);
} else if (attributeSet.containsKey(NumberFormat.Field.EXPONENT)) {
int limit = iterator.getRunLimit(NumberFormat.Field.EXPONENT);
append(
iterator,
copyFromOffset,
limit,
result);
copyFromOffset = limit;
iterator.setIndex(copyFromOffset);
result.append(endMarkup);
} else {
iterator.next();
}
}
append(iterator, copyFromOffset, iterator.getEndIndex(), result);
return result.toString();
}
}
private static class SuperscriptStyle extends Style {
private static final char[] SUPERSCRIPT_DIGITS = {
0x2070, 0xB9, 0xB2, 0xB3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079
};
private static final char SUPERSCRIPT_PLUS_SIGN = 0x207A;
private static final char SUPERSCRIPT_MINUS_SIGN = 0x207B;
@Override
String format(
AttributedCharacterIterator iterator,
String preExponent) {
int copyFromOffset = 0;
StringBuilder result = new StringBuilder();
for (
iterator.first();
iterator.current() != CharacterIterator.DONE;
) {
Map attributeSet = iterator.getAttributes();
if (attributeSet.containsKey(NumberFormat.Field.EXPONENT_SYMBOL)) {
append(
iterator,
copyFromOffset,
iterator.getRunStart(NumberFormat.Field.EXPONENT_SYMBOL),
result);
copyFromOffset = iterator.getRunLimit(NumberFormat.Field.EXPONENT_SYMBOL);
iterator.setIndex(copyFromOffset);
result.append(preExponent);
} else if (attributeSet.containsKey(NumberFormat.Field.EXPONENT_SIGN)) {
int start = iterator.getRunStart(NumberFormat.Field.EXPONENT_SIGN);
int limit = iterator.getRunLimit(NumberFormat.Field.EXPONENT_SIGN);
int aChar = char32AtAndAdvance(iterator);
if (StaticUnicodeSets.get(StaticUnicodeSets.Key.MINUS_SIGN).contains(aChar)) {
append(
iterator,
copyFromOffset,
start,
result);
result.append(SUPERSCRIPT_MINUS_SIGN);
} else if (StaticUnicodeSets.get(StaticUnicodeSets.Key.PLUS_SIGN).contains(aChar)) {
append(
iterator,
copyFromOffset,
start,
result);
result.append(SUPERSCRIPT_PLUS_SIGN);
} else {
throw new IllegalArgumentException();
}
copyFromOffset = limit;
iterator.setIndex(copyFromOffset);
} else if (attributeSet.containsKey(NumberFormat.Field.EXPONENT)) {
int start = iterator.getRunStart(NumberFormat.Field.EXPONENT);
int limit = iterator.getRunLimit(NumberFormat.Field.EXPONENT);
append(
iterator,
copyFromOffset,
start,
result);
copyAsSuperscript(iterator, start, limit, result);
copyFromOffset = limit;
iterator.setIndex(copyFromOffset);
} else {
iterator.next();
}
}
append(iterator, copyFromOffset, iterator.getEndIndex(), result);
return result.toString();
}
private static void copyAsSuperscript(
AttributedCharacterIterator iterator, int start, int limit, StringBuilder result) {
int oldIndex = iterator.getIndex();
iterator.setIndex(start);
while (iterator.getIndex() < limit) {
int aChar = char32AtAndAdvance(iterator);
int digit = UCharacter.digit(aChar);
if (digit < 0) {
throw new IllegalArgumentException();
}
result.append(SUPERSCRIPT_DIGITS[digit]);
}
iterator.setIndex(oldIndex);
}
private static int char32AtAndAdvance(AttributedCharacterIterator iterator) {
char c1 = iterator.current();
char c2 = iterator.next();
if (UCharacter.isHighSurrogate(c1)) {
// If c2 is DONE, it will fail the low surrogate test and we
// skip this block.
if (UCharacter.isLowSurrogate(c2)) {
iterator.next();
return UCharacter.toCodePoint(c1, c2);
}
}
return c1;
}
}
private static String getPreExponent(DecimalFormatSymbols dfs) {
StringBuilder preExponent = new StringBuilder();
preExponent.append(dfs.getExponentMultiplicationSign());
char[] digits = dfs.getDigits();
preExponent.append(digits[1]).append(digits[0]);
return preExponent.toString();
}
private static ScientificNumberFormatter getInstance(
DecimalFormat decimalFormat, Style style) {
DecimalFormatSymbols dfs = decimalFormat.getDecimalFormatSymbols();
return new ScientificNumberFormatter(
(DecimalFormat) decimalFormat.clone(), getPreExponent(dfs), style);
}
private static ScientificNumberFormatter getInstanceForLocale(
ULocale locale, Style style) {
DecimalFormat decimalFormat =
(DecimalFormat) DecimalFormat.getScientificInstance(locale);
return new ScientificNumberFormatter(
decimalFormat,
getPreExponent(decimalFormat.getDecimalFormatSymbols()),
style);
}
private static final Style SUPER_SCRIPT = new SuperscriptStyle();
private ScientificNumberFormatter(
DecimalFormat decimalFormat, String preExponent, Style style) {
this.fmt = decimalFormat;
this.preExponent = preExponent;
this.style = style;
}
}