blob: 7f75bfae654e648df4ec8eb1dc258569b1df6c91 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2007-2014, 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<ULocale, char[]> CACHE = new SimpleCache<ULocale, char[]>();
private int maxIntDigits;
private int minIntDigits;
public DateNumberFormat(ULocale loc, String digitString, String nsName) {
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(ICUResourceBundle.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];
}
public void setMaximumIntegerDigits(int newValue) {
maxIntDigits = newValue;
}
public int getMaximumIntegerDigits() {
return maxIntDigits;
}
public void setMinimumIntegerDigits(int newValue) {
minIntDigits = newValue;
}
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();
}
public StringBuffer format(double number, StringBuffer toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(double, StringBuffer, FieldPostion) is not implemented");
}
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;
}
public StringBuffer format(BigInteger number, StringBuffer toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(BigInteger, StringBuffer, FieldPostion) is not implemented");
}
public StringBuffer format(java.math.BigDecimal number, StringBuffer toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(BigDecimal, StringBuffer, FieldPostion) is not implemented");
}
public StringBuffer format(BigDecimal number,
StringBuffer toAppendTo, FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(BigDecimal, StringBuffer, FieldPostion) 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
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 = Long.valueOf(num);
parsePosition.setIndex(base + offset);
}
return result;
}
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));
}
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