com.ibm.icu.impl.DateNumberFormat 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
/*
*******************************************************************************
* Copyright (C) 2007-2016, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.math.BigInteger;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.util.Arrays;
import java.util.MissingResourceException;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
/*
* NumberFormat implementation dedicated/optimized for DateFormat,
* used by SimpleDateFormat implementation.
* This class is not thread-safe.
*/
public final class DateNumberFormat extends NumberFormat {
private static final long serialVersionUID = -6315692826916346953L;
private char[] digits;
private char zeroDigit; // For backwards compatibility
private char minusSign;
private boolean positiveOnly = false;
private static final int DECIMAL_BUF_SIZE = 20; // 20 digits is good enough to store Long.MAX_VALUE
private transient char[] decimalBuf = new char[DECIMAL_BUF_SIZE];
private static SimpleCache CACHE = new SimpleCache();
private int maxIntDigits;
private int minIntDigits;
public DateNumberFormat(ULocale loc, String digitString, String nsName) {
if (digitString.length() > 10) {
throw new UnsupportedOperationException("DateNumberFormat does not support digits out of BMP.");
}
initialize(loc,digitString,nsName);
}
public DateNumberFormat(ULocale loc, char zeroDigit, String nsName) {
StringBuffer buf = new StringBuffer();
for ( int i = 0 ; i < 10 ; i++ ) {
buf.append((char)(zeroDigit+i));
}
initialize(loc,buf.toString(),nsName);
}
private void initialize(ULocale loc,String digitString,String nsName) {
char[] elems = CACHE.get(loc);
if (elems == null) {
// Missed cache
String minusString;
ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, loc);
try {
minusString = rb.getStringWithFallback("NumberElements/"+nsName+"/symbols/minusSign");
} catch (MissingResourceException ex) {
if ( !nsName.equals("latn") ) {
try {
minusString = rb.getStringWithFallback("NumberElements/latn/symbols/minusSign");
} catch (MissingResourceException ex1) {
minusString = "-";
}
} else {
minusString = "-";
}
}
elems = new char[11];
for ( int i = 0 ; i < 10 ; i++ ) {
elems[i] = digitString.charAt(i);
}
elems[10] = minusString.charAt(0);
CACHE.put(loc, elems);
}
digits = new char[10];
System.arraycopy(elems, 0, digits, 0, 10);
zeroDigit = digits[0];
minusSign = elems[10];
}
@Override
public void setMaximumIntegerDigits(int newValue) {
maxIntDigits = newValue;
}
@Override
public int getMaximumIntegerDigits() {
return maxIntDigits;
}
@Override
public void setMinimumIntegerDigits(int newValue) {
minIntDigits = newValue;
}
@Override
public int getMinimumIntegerDigits() {
return minIntDigits;
}
/* For supporting SimpleDateFormat.parseInt */
public void setParsePositiveOnly(boolean isPositiveOnly) {
positiveOnly = isPositiveOnly;
}
public char getZeroDigit() {
return zeroDigit;
}
public void setZeroDigit(char zero) {
zeroDigit = zero;
if (digits == null) {
digits = new char[10];
}
digits[0] = zero;
for ( int i = 1 ; i < 10 ; i++ ) {
digits[i] = (char)(zero+i);
}
}
public char[] getDigits() {
return digits.clone();
}
@Override
public StringBuffer format(double number, StringBuffer toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(double, StringBuffer, FieldPosition) is not implemented");
}
@Override
public StringBuffer format(long numberL, StringBuffer toAppendTo,
FieldPosition pos) {
if (numberL < 0) {
// negative
toAppendTo.append(minusSign);
numberL = -numberL;
}
// Note: NumberFormat used by DateFormat only uses int numbers.
// Remainder operation on 32bit platform using long is significantly slower
// than int. So, this method casts long number into int.
int number = (int)numberL;
int limit = decimalBuf.length < maxIntDigits ? decimalBuf.length : maxIntDigits;
int index = limit - 1;
while (true) {
decimalBuf[index] = digits[(number % 10)];
number /= 10;
if (index == 0 || number == 0) {
break;
}
index--;
}
int padding = minIntDigits - (limit - index);
for (; padding > 0; padding--) {
decimalBuf[--index] = digits[0];
}
int length = limit - index;
toAppendTo.append(decimalBuf, index, length);
pos.setBeginIndex(0);
if (pos.getField() == NumberFormat.INTEGER_FIELD) {
pos.setEndIndex(length);
} else {
pos.setEndIndex(0);
}
return toAppendTo;
}
@Override
public StringBuffer format(BigInteger number, StringBuffer toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(BigInteger, StringBuffer, FieldPosition) is not implemented");
}
@Override
public StringBuffer format(java.math.BigDecimal number, StringBuffer toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(BigDecimal, StringBuffer, FieldPosition) is not implemented");
}
@Override
public StringBuffer format(BigDecimal number,
StringBuffer toAppendTo, FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(BigDecimal, StringBuffer, FieldPosition) is not implemented");
}
/*
* Note: This method only parse integer numbers which can be represented by long
*/
private static final long PARSE_THRESHOLD = 922337203685477579L; // (Long.MAX_VALUE / 10) - 1
@Override
public Number parse(String text, ParsePosition parsePosition) {
long num = 0;
boolean sawNumber = false;
boolean negative = false;
int base = parsePosition.getIndex();
int offset = 0;
for (; base + offset < text.length(); offset++) {
char ch = text.charAt(base + offset);
if (offset == 0 && ch == minusSign) {
if (positiveOnly) {
break;
}
negative = true;
} else {
int digit = ch - digits[0];
if (digit < 0 || 9 < digit) {
digit = UCharacter.digit(ch);
}
if (digit < 0 || 9 < digit) {
for ( digit = 0 ; digit < 10 ; digit++ ) {
if ( ch == digits[digit]) {
break;
}
}
}
if (0 <= digit && digit <= 9 && num < PARSE_THRESHOLD) {
sawNumber = true;
num = num * 10 + digit;
} else {
break;
}
}
}
Number result = null;
if (sawNumber) {
num = negative ? num * (-1) : num;
result = num;
parsePosition.setIndex(base + offset);
}
return result;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !super.equals(obj) || !(obj instanceof DateNumberFormat)) {
return false;
}
DateNumberFormat other = (DateNumberFormat)obj;
return (this.maxIntDigits == other.maxIntDigits
&& this.minIntDigits == other.minIntDigits
&& this.minusSign == other.minusSign
&& this.positiveOnly == other.positiveOnly
&& Arrays.equals(this.digits, other.digits));
}
@Override
public int hashCode() {
return super.hashCode();
}
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
if (digits == null) {
setZeroDigit(zeroDigit);
}
// re-allocate the work buffer
decimalBuf = new char[DECIMAL_BUF_SIZE];
}
@Override
public Object clone() {
DateNumberFormat dnfmt = (DateNumberFormat)super.clone();
dnfmt.digits = this.digits.clone();
dnfmt.decimalBuf = new char[DECIMAL_BUF_SIZE];
return dnfmt;
}
}
//eof