ICU-10268 Be sure that MeasureFormat and sub classes correctly implement clone() and getLocale(ULocale.VALID_LOCALE)

X-SVN-Rev: 34780
This commit is contained in:
Travis Keep 2013-12-17 01:09:02 +00:00
parent a53682a55e
commit 3ef485dccf
5 changed files with 173 additions and 25 deletions

View File

@ -38,9 +38,22 @@ class CurrencyFormat extends MeasureFormat {
private transient final MeasureFormat mf;
public CurrencyFormat(ULocale locale) {
// Needed for getLocale(ULocale.VALID_LOCALE).
setLocale(locale, locale);
mf = MeasureFormat.getInstance(locale, FormatWidth.WIDE);
fmt = NumberFormat.getCurrencyInstance(locale.toLocale());
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public Object clone() {
CurrencyFormat result = (CurrencyFormat) super.clone();
result.fmt = (NumberFormat) fmt.clone();
return result;
}
/**
* Override Format.format().
@ -73,33 +86,57 @@ class CurrencyFormat extends MeasureFormat {
// boilerplate code to make CurrencyFormat otherwise follow the contract of
// MeasureFormat
/**
* @draft ICU 53
* @provisional
*/
@Override
public String formatMeasures(Measure... measures) {
return mf.formatMeasures(measures);
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public <T extends Appendable> T formatMeasure(
Measure measure, T appendable, FieldPosition fieldPosition) {
return mf.formatMeasure(measure, appendable, fieldPosition);
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public <T extends Appendable> T formatMeasures(
T appendable, FieldPosition fieldPosition, Measure... measures) {
return mf.formatMeasures(appendable, fieldPosition, measures);
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public MeasureFormat.FormatWidth getWidth() {
return mf.getWidth();
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public ULocale getLocale() {
return mf.getLocale();
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public NumberFormat getNumberFormat() {
return mf.getNumberFormat();
@ -107,15 +144,18 @@ class CurrencyFormat extends MeasureFormat {
// End boilerplate.
/**
* @draft ICU 53
* @provisional
*/
@Override
public int hashCode() {
return getLocale().hashCode() + 154321962;
return mf.hashCode() + 154321962;
}
@Override
protected boolean equalsSameClass(MeasureFormat other) {
return getLocale().equals(other.getLocale());
boolean equalsSameClass(MeasureFormat other) {
return mf.equals(((CurrencyFormat) other).mf);
}
// Serialization

View File

@ -94,8 +94,6 @@ public class MeasureFormat extends UFormat {
// Generated by serialver from JDK 1.4.1_01
static final long serialVersionUID = -7182021401701778240L;
private final transient ULocale locale;
// NumberFormat is known to lack thread-safety, all access to this
// field must be synchronized.
private final transient NumberFormat numberFormat;
@ -339,7 +337,7 @@ public class MeasureFormat extends UFormat {
results[i] = formatMeasure(measures[i], new StringBuilder(), dummyPos);
}
}
ListFormatter listFormatter = ListFormatter.getInstance(locale,
ListFormatter listFormatter = ListFormatter.getInstance(getLocale(),
length == FormatWidth.WIDE ? ListFormatter.Style.DURATION : ListFormatter.Style.DURATION_SHORT);
// Fix up FieldPosition indexes if our field is found.
@ -393,19 +391,16 @@ public class MeasureFormat extends UFormat {
*/
@Override
public int hashCode() {
return (numberFormat.hashCode() * 31 + locale.hashCode()) * 31 + length.hashCode();
return (numberFormat.hashCode() * 31 + getLocale().hashCode()) * 31 + length.hashCode();
}
/**
* Returns true if this object is equal to other. The class of this and the class of other
* are guaranteed to be equal.
*
* @deprecated For ICU internal use only.
* @internal
*/
protected boolean equalsSameClass(MeasureFormat other) {
boolean equalsSameClass(MeasureFormat other) {
return objEquals(numberFormat,other.numberFormat)
&& objEquals(locale, other.locale) && objEquals(length, other.length);
&& objEquals(getLocale(), other.getLocale()) && objEquals(length, other.length);
}
@ -424,7 +419,7 @@ public class MeasureFormat extends UFormat {
* @provisional
*/
public ULocale getLocale() {
return locale;
return getLocale(ULocale.VALID_LOCALE);
}
/**
@ -466,7 +461,7 @@ public class MeasureFormat extends UFormat {
MeasureFormat withNumberFormat(NumberFormat format) {
return new MeasureFormat(
this.locale,
getLocale(),
this.length,
(NumberFormat) format.clone(),
this.rules,
@ -479,7 +474,7 @@ public class MeasureFormat extends UFormat {
NumberFormat format,
PluralRules rules,
Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat) {
this.locale = locale;
setLocale(locale, locale);
this.length = width;
this.numberFormat = format;
this.rules = rules;
@ -494,7 +489,6 @@ public class MeasureFormat extends UFormat {
protected MeasureFormat() {
// Make compiler happy by setting final fields to null.
this.length = null;
this.locale = null;
this.numberFormat = null;
this.rules = null;
this.unitToStyleToCountToFormat = null;
@ -607,16 +601,16 @@ public class MeasureFormat extends UFormat {
}
Object toTimeUnitProxy() {
return new MeasureProxy(locale, length, numberFormat, TIME_UNIT_FORMAT);
return new MeasureProxy(getLocale(), length, numberFormat, TIME_UNIT_FORMAT);
}
Object toCurrencyProxy() {
return new MeasureProxy(locale, length, numberFormat, CURRENCY_FORMAT);
return new MeasureProxy(getLocale(), length, numberFormat, CURRENCY_FORMAT);
}
private Object writeReplace() throws ObjectStreamException {
return new MeasureProxy(
locale, length, numberFormat, MEASURE_FORMAT);
getLocale(), length, numberFormat, MEASURE_FORMAT);
}
static class MeasureProxy implements Externalizable {

View File

@ -147,13 +147,18 @@ public class TimeUnitFormat extends MeasureFormat {
mf = MeasureFormat.getInstance(
locale, style == FULL_NAME ? FormatWidth.WIDE : FormatWidth.SHORT);
this.style = style;
// Needed for getLocale(ULocale.VALID_LOCALE)
setLocale(locale, locale);
this.locale = locale;
isReady = false;
}
private TimeUnitFormat(ULocale locale, int style, NumberFormat numberFormat) {
this(locale, style);
setNumberFormat(numberFormat);
if (numberFormat != null) {
setNumberFormat((NumberFormat) numberFormat.clone());
}
}
/**
@ -171,8 +176,11 @@ public class TimeUnitFormat extends MeasureFormat {
* @stable ICU 4.0
*/
public TimeUnitFormat setLocale(ULocale locale) {
if (locale != this.locale){
if (locale != this.locale){
mf = mf.withLocale(locale);
// Needed for getLocale(ULocale.VALID_LOCALE)
setLocale(locale, locale);
this.locale = locale;
isReady = false;
}
@ -325,6 +333,8 @@ public class TimeUnitFormat extends MeasureFormat {
} else {
locale = ULocale.getDefault(Category.FORMAT);
}
// Needed for getLocale(ULocale.VALID_LOCALE)
setLocale(locale, locale);
}
if (format == null) {
format = NumberFormat.getNumberInstance(locale);
@ -506,33 +516,68 @@ if ( searchPluralCount.equals("other") ) {
// boilerplate code to make TimeUnitFormat otherwise follow the contract of
// MeasureFormat
/**
* @draft ICU 53
* @provisional
*/
@Override
public String formatMeasures(Measure... measures) {
return mf.formatMeasures(measures);
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public <T extends Appendable> T formatMeasure(
Measure measure, T appendable, FieldPosition fieldPosition) {
return mf.formatMeasure(measure, appendable, fieldPosition);
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public <T extends Appendable> T formatMeasures(
T appendable, FieldPosition fieldPosition, Measure... measures) {
return mf.formatMeasures(appendable, fieldPosition, measures);
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public MeasureFormat.FormatWidth getWidth() {
return mf.getWidth();
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public ULocale getLocale() {
return mf.getLocale();
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public Object clone() {
TimeUnitFormat result = (TimeUnitFormat) super.clone();
result.format = (NumberFormat) format.clone();
return result;
}
/**
* @draft ICU 53
* @provisional
*/
@Override
public NumberFormat getNumberFormat() {
return mf.getNumberFormat();
@ -542,14 +587,18 @@ if ( searchPluralCount.equals("other") ) {
// equals / hashcode
/**
* @draft ICU 53
* @provisional
*/
@Override
public int hashCode() {
return getLocale().hashCode() + 911247101;
return mf.hashCode() + 911247101;
}
@Override
protected boolean equalsSameClass(MeasureFormat other) {
return getLocale().equals(other.getLocale());
boolean equalsSameClass(MeasureFormat other) {
return mf.equals(((TimeUnitFormat) other).mf);
}
// End equals / hashcode

View File

@ -28,6 +28,7 @@ import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.MeasureFormat;
import com.ibm.icu.text.MeasureFormat.FormatWidth;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.TimeUnitFormat;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.Measure;
import com.ibm.icu.util.MeasureUnit;
@ -299,6 +300,20 @@ public class MeasureUnitTest extends TestFmwk {
// Expected
}
}
public void testEqHashCode() {
MeasureFormat mf = MeasureFormat.getInstance(ULocale.CANADA, FormatWidth.SHORT);
MeasureFormat mfeq = MeasureFormat.getInstance(ULocale.CANADA, FormatWidth.SHORT);
MeasureFormat mfne = MeasureFormat.getInstance(ULocale.CANADA, FormatWidth.WIDE);
MeasureFormat mfne2 = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.SHORT);
verifyEqualsHashCode(mf, mfeq, mfne);
verifyEqualsHashCode(mf, mfeq, mfne2);
}
public void testGetLocale() {
MeasureFormat mf = MeasureFormat.getInstance(ULocale.GERMAN, FormatWidth.SHORT);
assertEquals("", ULocale.GERMAN, mf.getLocale(ULocale.VALID_LOCALE));
}
static void generateConstants() {
System.out.println("static final MeasureUnit");
@ -384,6 +399,18 @@ public class MeasureUnitTest extends TestFmwk {
return b.append(']').toString();
}
private void verifyEqualsHashCode(Object o, Object eq, Object ne) {
assertEquals("verifyEqualsHashCodeSame", o, o);
assertEquals("verifyEqualsHashCodeEq", o, eq);
assertNotEquals("verifyEqualsHashCodeNe", o, ne);
assertNotEquals("verifyEqualsHashCodeEqTrans", eq, ne);
assertEquals("verifyEqualsHashCodeHashEq", o.hashCode(), eq.hashCode());
// May be a flaky test, but generally should be true.
// May need to comment this out later.
assertNotEquals("verifyEqualsHashCodeHashNe", o.hashCode(), ne.hashCode());
}
public static class MeasureUnitHandler implements SerializableTest.Handler
{
public Object[] getTestObjects()

View File

@ -12,8 +12,10 @@ import java.util.Locale;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.text.MeasureFormat;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.TimeUnitFormat;
import com.ibm.icu.text.MeasureFormat.FormatWidth;
import com.ibm.icu.util.TimeUnit;
import com.ibm.icu.util.TimeUnitAmount;
import com.ibm.icu.util.ULocale;
@ -113,6 +115,30 @@ public class TimeUnitTest extends TestFmwk {
format.setNumberFormat(NumberFormat.getNumberInstance(new Locale("en")));
formatParsing(format);
}
public void TestClone() {
TimeUnitFormat tuf = new TimeUnitFormat(ULocale.ENGLISH, TimeUnitFormat.ABBREVIATED_NAME);
NumberFormat nf = NumberFormat.getInstance();
tuf.setNumberFormat(nf);
TimeUnitFormat tufClone = (TimeUnitFormat) tuf.clone();
tuf.setLocale(Locale.GERMAN);
assertEquals("", "1 hr", tufClone.format(new TimeUnitAmount(1, TimeUnit.HOUR)));
}
public void TestEqHashCode() {
TimeUnitFormat tf = new TimeUnitFormat(ULocale.ENGLISH, TimeUnitFormat.FULL_NAME);
MeasureFormat tfeq = new TimeUnitFormat(ULocale.ENGLISH, TimeUnitFormat.FULL_NAME);
MeasureFormat tfne = new TimeUnitFormat(ULocale.ENGLISH, TimeUnitFormat.ABBREVIATED_NAME);
MeasureFormat tfne2 = new TimeUnitFormat(ULocale.GERMAN, TimeUnitFormat.FULL_NAME);
verifyEqualsHashCode(tf, tfeq, tfne);
verifyEqualsHashCode(tf, tfeq, tfne2);
}
public void TestGetLocale() {
TimeUnitFormat tf = new TimeUnitFormat(ULocale.GERMAN);
assertEquals("", ULocale.GERMAN, tf.getLocale(ULocale.VALID_LOCALE));
}
/*
* @bug 7902
@ -353,4 +379,16 @@ public class TimeUnitTest extends TestFmwk {
tuf1.setNumberFormat(NumberFormat.getInstance());
tuf1.parseObject("", new ParsePosition(0));
}
private void verifyEqualsHashCode(Object o, Object eq, Object ne) {
assertEquals("verifyEqualsHashCodeSame", o, o);
assertEquals("verifyEqualsHashCodeEq", o, eq);
assertNotEquals("verifyEqualsHashCodeNe", o, ne);
assertNotEquals("verifyEqualsHashCodeEqTrans", eq, ne);
assertEquals("verifyEqualsHashCodeHashEq", o.hashCode(), eq.hashCode());
// May be a flaky test, but generally should be true.
// May need to comment this out later.
assertNotEquals("verifyEqualsHashCodeHashNe", o.hashCode(), ne.hashCode());
}
}