ICU-2825 add Measure, MeasureUnit, CurrencyUnit, and CurrencyAmount classes, and operate through them

X-SVN-Rev: 15059
This commit is contained in:
Alan Liu 2004-04-27 21:47:09 +00:00
parent 540823fffa
commit 1db13e2b86
17 changed files with 941 additions and 186 deletions

View File

@ -23,7 +23,7 @@
/* Uncomment the following line to disable renaming on platforms
that do not use Autoconf. */
/* #define U_DISABLE_RENAMING 1 */
#define U_DISABLE_RENAMING 1
#if !U_DISABLE_RENAMING

View File

@ -72,7 +72,7 @@ cpdtrans.o rbt.o rbt_data.o rbt_pars.o rbt_rule.o rbt_set.o \
nultrans.o remtrans.o titletrn.o tolowtrn.o toupptrn.o anytrans.o \
name2uni.o uni2name.o nortrans.o quant.o transreg.o \
regexcmp.o rematch.o repattrn.o regexst.o uregex.o ulocdata.o \
measfmt.o currfmt.o
measfmt.o currfmt.o curramt.o currunit.o measure.o
STATIC_OBJECTS = $(OBJECTS:.o=.$(STATIC_O))

View File

@ -0,0 +1,50 @@
/*
**********************************************************************
* Copyright (c) 2004, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Author: Alan Liu
* Created: April 26, 2004
* Since: ICU 3.0
**********************************************************************
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/curramt.h"
#include "unicode/currunit.h"
U_NAMESPACE_BEGIN
CurrencyAmount::CurrencyAmount(const Formattable& amount, const UChar* isoCode,
UErrorCode& ec) :
Measure(amount, new CurrencyUnit(isoCode, ec), ec) {
}
CurrencyAmount::CurrencyAmount(double amount, const UChar* isoCode,
UErrorCode& ec) :
Measure(Formattable(amount), new CurrencyUnit(isoCode, ec), ec) {
}
CurrencyAmount::CurrencyAmount(const CurrencyAmount& other) :
Measure(other) {
}
CurrencyAmount& CurrencyAmount::operator=(const CurrencyAmount& other) {
Measure::operator=(other);
return *this;
}
UObject* CurrencyAmount::clone() const {
return new CurrencyAmount(*this);
}
CurrencyAmount::~CurrencyAmount() {
}
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyAmount)
U_NAMESPACE_END
#endif // !UCONFIG_NO_FORMATTING

View File

@ -0,0 +1,59 @@
/*
**********************************************************************
* Copyright (c) 2004, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Author: Alan Liu
* Created: April 26, 2004
* Since: ICU 3.0
**********************************************************************
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/currunit.h"
#include "unicode/ustring.h"
U_NAMESPACE_BEGIN
CurrencyUnit::CurrencyUnit(const UChar* _isoCode, UErrorCode& ec) {
*isoCode = 0;
if (U_SUCCESS(ec)) {
if (_isoCode && u_strlen(_isoCode)==3) {
u_strcpy(isoCode, _isoCode);
} else {
ec = U_ILLEGAL_ARGUMENT_ERROR;
}
}
}
CurrencyUnit::CurrencyUnit(const CurrencyUnit& other) {
*this = other;
}
CurrencyUnit& CurrencyUnit::operator=(const CurrencyUnit& other) {
if (this != &other) {
u_strcpy(isoCode, other.isoCode);
}
return *this;
}
UObject* CurrencyUnit::clone() const {
return new CurrencyUnit(*this);
}
CurrencyUnit::~CurrencyUnit() {
}
UBool CurrencyUnit::operator==(const UObject& other) const {
const CurrencyUnit& c = (const CurrencyUnit&) other;
return other.getDynamicClassID() == CurrencyUnit::getStaticClassID() &&
u_strcmp(isoCode, c.isoCode) == 0;
}
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyUnit)
U_NAMESPACE_END
#endif // !UCONFIG_NO_FORMATTING

View File

@ -48,6 +48,7 @@
#include "unicode/dcfmtsym.h"
#include "unicode/resbund.h"
#include "unicode/uchar.h"
#include "unicode/curramt.h"
#include "ucurrimp.h"
#include "uprops.h"
#include "digitlst.h"
@ -1253,59 +1254,58 @@ void DecimalFormat::parse(const UnicodeString& text,
return;
}
if (parseCurrency) {
result.setCurrency(curbuf);
}
// Handle infinity
if (status[fgStatusInfinite]) {
double inf = uprv_getInfinity();
result.setDouble(digits.fIsPositive ? inf : -inf);
return;
}
// Do as much of the multiplier conversion as possible without
// losing accuracy.
int32_t mult = fMultiplier; // Don't modify this.multiplier
while (mult % 10 == 0) {
mult /= 10;
--digits.fDecimalAt;
}
// Handle integral values. We want to return the most
// parsimonious type that will accommodate all of the result's
// precision. We therefore only return a long if the result fits
// entirely within a long (taking into account the multiplier) --
// otherwise we fall through and return a double. When more
// numeric types are supported by Formattable (e.g., 64-bit
// integers, bignums) we will extend this logic to include them.
if (digits.fitsIntoLong(isParseIntegerOnly())) {
int32_t n = digits.getLong();
if (n % mult == 0) {
result.setLong(n / mult);
return;
}
else { // else handle the remainder
result.setDouble(((double)n) / mult);
return;
}
}
else if (digits.fitsIntoInt64(isParseIntegerOnly())) {
int64_t n = digits.getInt64();
if (n % mult == 0) {
result.setInt64(n / mult);
return;
}
else { // else handle the remainder
result.setDouble(((double)n) / mult);
return;
}
}
else {
// Handle non-integral or very large values
// Dividing by one is okay and not that costly.
result.setDouble(digits.getDouble() / mult);
return;
// Do as much of the multiplier conversion as possible without
// losing accuracy.
int32_t mult = fMultiplier; // Don't modify this.multiplier
while (mult % 10 == 0) {
mult /= 10;
--digits.fDecimalAt;
}
// Handle integral values. We want to return the most
// parsimonious type that will accommodate all of the result's
// precision. We therefore only return a long if the result fits
// entirely within a long (taking into account the multiplier) --
// otherwise we fall through and return a double. When more
// numeric types are supported by Formattable (e.g., 64-bit
// integers, bignums) we will extend this logic to include them.
if (digits.fitsIntoLong(isParseIntegerOnly())) {
int32_t n = digits.getLong();
if (n % mult == 0) {
result.setLong(n / mult);
}
else { // else handle the remainder
result.setDouble(((double)n) / mult);
}
}
else if (digits.fitsIntoInt64(isParseIntegerOnly())) {
int64_t n = digits.getInt64();
if (n % mult == 0) {
result.setInt64(n / mult);
}
else { // else handle the remainder
result.setDouble(((double)n) / mult);
}
}
else {
// Handle non-integral or very large values
// Dividing by one is okay and not that costly.
result.setDouble(digits.getDouble() / mult);
}
}
if (parseCurrency) {
UErrorCode ec = U_ZERO_ERROR;
Formattable n(result);
result.adoptObject(new CurrencyAmount(n, curbuf, ec));
U_ASSERT(U_SUCCESS(ec)); // should always succeed
}
}

View File

@ -19,6 +19,8 @@
#include "unicode/fmtable.h"
#include "unicode/ustring.h"
#include "unicode/measure.h"
#include "unicode/curramt.h"
#include "cmemory.h"
// *****************************************************************************
@ -29,6 +31,38 @@ U_NAMESPACE_BEGIN
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Formattable)
//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
// NOTE: As of 3.0, there are limitations to the UObject API. It does
// not (yet) support cloning, operator=, nor operator==. RTTI is also
// restricted in that subtype testing is not (yet) implemented. To
// work around this, I implement some simple inlines here. Later
// these can be modified or removed. [alan]
// NOTE: These inlines assume that all fObjects are in fact instances
// of the Measure class, which is true as of 3.0. [alan]
// Return TRUE if *a == *b.
inline UBool objectEquals(const UObject* a, const UObject* b) {
// LATER: return *a == *b;
return *((const Measure*) a) == *((const Measure*) b);
}
// Return a clone of *a.
inline UObject* objectClone(const UObject* a) {
// LATER: return a->clone();
return ((const Measure*) a)->clone();
}
// Return TRUE if *a is an instance of Measure.
inline UBool instanceOfMeasure(const UObject* a) {
// LATER: return a->instanceof(Measure::getStaticClassID());
return a->getDynamicClassID() ==
CurrencyAmount::getStaticClassID();
}
//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
/**
* Set 'ec' to 'err' only if 'ec' is not already set to a failing UErrorCode.
*/
@ -47,7 +81,6 @@ Formattable::Formattable()
{
fBogus.setToBogus();
fValue.fInt64 = 0;
fCurrency[0] = 0;
}
// -------------------------------------
@ -58,7 +91,6 @@ Formattable::Formattable(UDate date, ISDATE /*isDate*/)
{
fBogus.setToBogus();
fValue.fDate = date;
fCurrency[0] = 0;
}
// -------------------------------------
@ -69,7 +101,6 @@ Formattable::Formattable(double value)
{
fBogus.setToBogus();
fValue.fDouble = value;
fCurrency[0] = 0;
}
// -------------------------------------
@ -80,7 +111,6 @@ Formattable::Formattable(int32_t value)
{
fBogus.setToBogus();
fValue.fInt64 = value;
fCurrency[0] = 0;
}
// -------------------------------------
@ -91,25 +121,6 @@ Formattable::Formattable(int64_t value)
{
fBogus.setToBogus();
fValue.fInt64 = value;
fCurrency[0] = 0;
}
Formattable::Formattable(double n, const UChar* currency) : UObject(), fType(kDouble) {
fBogus.setToBogus();
fValue.fDouble = n;
setCurrency(currency);
}
Formattable::Formattable(int32_t n, const UChar* currency) : UObject(), fType(kLong) {
fBogus.setToBogus();
fValue.fInt64 = n;
setCurrency(currency);
}
Formattable::Formattable(int64_t n, const UChar* currency) : UObject(), fType(kInt64) {
fBogus.setToBogus();
fValue.fInt64 = n;
setCurrency(currency);
}
// -------------------------------------
@ -120,7 +131,6 @@ Formattable::Formattable(const char* stringToCopy)
{
fBogus.setToBogus();
fValue.fString = new UnicodeString(stringToCopy);
fCurrency[0] = 0;
}
// -------------------------------------
@ -131,7 +141,6 @@ Formattable::Formattable(const UnicodeString& stringToCopy)
{
fBogus.setToBogus();
fValue.fString = new UnicodeString(stringToCopy);
fCurrency[0] = 0;
}
// -------------------------------------
@ -143,7 +152,13 @@ Formattable::Formattable(UnicodeString* stringToAdopt)
{
fBogus.setToBogus();
fValue.fString = stringToAdopt;
fCurrency[0] = 0;
}
Formattable::Formattable(UObject* objectToAdopt)
: UObject(), fType(kObject)
{
fBogus.setToBogus();
fValue.fObject = objectToAdopt;
}
// -------------------------------------
@ -154,7 +169,6 @@ Formattable::Formattable(const Formattable* arrayToCopy, int32_t count)
fBogus.setToBogus();
fValue.fArrayAndCount.fArray = createArrayCopy(arrayToCopy, count);
fValue.fArrayAndCount.fCount = count;
fCurrency[0] = 0;
}
// -------------------------------------
@ -205,9 +219,10 @@ Formattable::operator=(const Formattable& source)
// Sets the Date value.
fValue.fDate = source.fValue.fDate;
break;
case kObject:
fValue.fObject = objectClone(source.fValue.fObject);
break;
}
u_strcpy(fCurrency, source.fCurrency);
}
return *this;
}
@ -251,10 +266,9 @@ Formattable::operator==(const Formattable& that) const
}
}
break;
}
if (equal) {
equal = (u_strcmp(fCurrency, that.fCurrency) == 0);
case kObject:
equal = objectEquals(fValue.fObject, that.fValue.fObject);
break;
}
return equal;
@ -279,6 +293,9 @@ void Formattable::dispose()
case kArray:
delete[] fValue.fArrayAndCount.fArray;
break;
case kObject:
delete fValue.fObject;
break;
default:
break;
}
@ -297,6 +314,18 @@ Formattable::getType() const
return fType;
}
UBool
Formattable::isNumeric() const {
switch (fType) {
case kDouble:
case kLong:
case kInt64:
return TRUE;
default:
return FALSE;
}
}
// -------------------------------------
int32_t
//Formattable::getLong(UErrorCode* status) const
@ -329,6 +358,12 @@ Formattable::getLong(UErrorCode& status) const
} else {
return (int32_t)fValue.fDouble; // loses fraction
}
case Formattable::kObject:
// TODO Later replace this with instanceof call
if (instanceOfMeasure(fValue.fObject)) {
return ((const Measure*) fValue.fObject)->
getNumber().getLong(status);
}
default:
status = U_INVALID_FORMAT_ERROR;
return 0;
@ -357,6 +392,12 @@ Formattable::getInt64(UErrorCode& status) const
} else {
return (int64_t)fValue.fDouble;
}
case Formattable::kObject:
// TODO Later replace this with instanceof call
if (instanceOfMeasure(fValue.fObject)) {
return ((const Measure*) fValue.fObject)->
getNumber().getInt64(status);
}
default:
status = U_INVALID_FORMAT_ERROR;
return 0;
@ -377,15 +418,21 @@ Formattable::getDouble(UErrorCode& status) const
return (double)fValue.fInt64;
case Formattable::kDouble:
return fValue.fDouble;
case Formattable::kObject:
// TODO Later replace this with instanceof call
if (instanceOfMeasure(fValue.fObject)) {
return ((const Measure*) fValue.fObject)->
getNumber().getDouble(status);
}
default:
status = U_INVALID_FORMAT_ERROR;
return 0;
}
}
const UChar*
Formattable::getCurrency() const {
return (fCurrency[0] != 0) ? fCurrency : NULL;
const UObject*
Formattable::getObject() const {
return (fType == kObject) ? fValue.fObject : NULL;
}
// -------------------------------------
@ -479,12 +526,10 @@ Formattable::adoptArray(Formattable* array, int32_t count)
}
void
Formattable::setCurrency(const UChar* currency) {
fCurrency[0] = 0;
if (currency != NULL) {
uprv_memcpy(&fCurrency[0], currency, 3*sizeof(fCurrency[0]));
fCurrency[3] = 0;
}
Formattable::adoptObject(UObject* objectToAdopt) {
dispose();
fType = kObject;
fValue.fObject = objectToAdopt;
}
// -------------------------------------

View File

@ -457,12 +457,38 @@
Outputs="..\..\include\unicode\$(InputFileName)"/>
</FileConfiguration>
</File>
<File
RelativePath=".\curramt.cpp">
</File>
<File
RelativePath=".\unicode\curramt.h">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
Outputs="..\..\include\unicode\$(InputFileName)"/>
</FileConfiguration>
</File>
<File
RelativePath=".\currfmt.cpp">
</File>
<File
RelativePath=".\currfmt.h">
</File>
<File
RelativePath=".\currunit.cpp">
</File>
<File
RelativePath=".\unicode\currunit.h">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
Outputs="..\..\include\unicode\$(InputFileName)"/>
</FileConfiguration>
</File>
<File
RelativePath=".\datefmt.cpp">
</File>
@ -673,11 +699,36 @@
RelativePath=".\unicode\measfmt.h">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode
"
Outputs="..\..\include\unicode\$(InputFileName)"/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode
"
Outputs="..\..\include\unicode\$(InputFileName)"/>
</FileConfiguration>
</File>
<File
RelativePath=".\unicode\measunit.h">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode"
Outputs="..\..\include\unicode\$(InputFileName)"/>
</FileConfiguration>
</File>
<File
RelativePath=".\measure.cpp">
</File>
<File
RelativePath=".\unicode\measure.h">
<FileConfiguration
Name="Debug|Win32">
<Tool

View File

@ -0,0 +1,63 @@
/*
**********************************************************************
* Copyright (c) 2004, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Author: Alan Liu
* Created: April 26, 2004
* Since: ICU 3.0
**********************************************************************
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/measure.h"
#include "unicode/measunit.h"
U_NAMESPACE_BEGIN
Measure::Measure(const Formattable& _number, MeasureUnit* adoptedUnit,
UErrorCode& ec) :
number(_number), unit(adoptedUnit) {
if (U_SUCCESS(ec) &&
(!number.isNumeric() || adoptedUnit == 0)) {
ec = U_ILLEGAL_ARGUMENT_ERROR;
}
}
Measure::Measure(const Measure& other) :
unit(0) {
*this = other;
}
Measure& Measure::operator=(const Measure& other) {
if (this != &other) {
delete unit;
number = other.number;
unit = (MeasureUnit*) other.unit->clone();
}
return *this;
}
Measure::~Measure() {
delete unit;
}
UBool Measure::operator==(const UObject& other) const {
const Measure* m = (const Measure*) &other;
return getDynamicClassID() == other.getDynamicClassID() &&
number == m->getNumber() &&
*unit == m->getUnit();
}
//----------------------------------------------------------------------
// MeasureUnit implementation
MeasureUnit:: MeasureUnit() {}
MeasureUnit::~MeasureUnit() {}
U_NAMESPACE_END
#endif // !UCONFIG_NO_FORMATTING

View File

@ -33,6 +33,7 @@
#include "unicode/decimfmt.h"
#include "unicode/ustring.h"
#include "unicode/ucurr.h"
#include "unicode/curramt.h"
#include "uhash.h"
#include "iculserv.h"
#include "ucln_in.h"
@ -222,29 +223,34 @@ NumberFormat::format(const Formattable& obj,
if (U_FAILURE(status)) return appendTo;
NumberFormat* nonconst = (NumberFormat*) this;
const Formattable* n = &obj;
UChar save[4];
UBool setCurr = FALSE;
const UChar *curr = obj.getCurrency(); // most commonly curr==NULL
if (curr != NULL) {
// getCurrency() returns a pointer to internal storage, so we
const UObject* o = obj.getObject(); // most commonly o==NULL
if (o != NULL &&
o->getDynamicClassID() == CurrencyAmount::getStaticClassID()) {
// getISOCurrency() returns a pointer to internal storage, so we
// copy it to retain it across the call to setCurrency().
const CurrencyAmount* amt = (const CurrencyAmount*) o;
const UChar* curr = amt->getISOCurrency();
u_strcpy(save, getCurrency());
setCurr = (u_strcmp(curr, save) != 0);
if (setCurr) {
nonconst->setCurrency(curr, status);
}
n = &amt->getNumber();
}
switch (obj.getType()) {
switch (n->getType()) {
case Formattable::kDouble:
format(obj.getDouble(), appendTo, pos);
format(n->getDouble(), appendTo, pos);
break;
case Formattable::kLong:
format(obj.getLong(), appendTo, pos);
format(n->getLong(), appendTo, pos);
break;
case Formattable::kInt64:
format(obj.getInt64(), appendTo, pos);
format(n->getInt64(), appendTo, pos);
break;
default:
status = U_INVALID_FORMAT_ERROR;
@ -342,7 +348,11 @@ Formattable& NumberFormat::parseCurrency(const UnicodeString& text,
UErrorCode ec = U_ZERO_ERROR;
getEffectiveCurrency(curr, ec);
if (U_SUCCESS(ec)) {
result.setCurrency(curr);
Formattable n(result);
result.adoptObject(new CurrencyAmount(n, curr, ec));
if (U_FAILURE(ec)) {
pos.setIndex(start); // indicate failure
}
}
}
return result;

View File

@ -0,0 +1,124 @@
/*
**********************************************************************
* Copyright (c) 2004, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Author: Alan Liu
* Created: April 26, 2004
* Since: ICU 3.0
**********************************************************************
*/
#ifndef __CURRENCYAMOUNT_H__
#define __CURRENCYAMOUNT_H__
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/measure.h"
#include "unicode/currunit.h"
U_NAMESPACE_BEGIN
/**
* A currency together with a numeric amount, such as 200 USD.
*
* @author Alan Liu
* @internal
*/
class U_I18N_API CurrencyAmount: public Measure {
public:
/**
* Construct an object with the given numeric amount and the given
* ISO currency code.
* @param amount a numeric object; amount.isNumeric() must be TRUE
* @param isoCode the 3-letter ISO 4217 currency code; must not be
* NULL and must have length 3
* @param ec input-output error code. If the amount or the isoCode
* is invalid, then this will be set to a failing value.
* @internal
*/
CurrencyAmount(const Formattable& amount, const UChar* isoCode,
UErrorCode &ec);
/**
* Construct an object with the given numeric amount and the given
* ISO currency code.
* @param amount the amount of the given currency
* @param isoCode the 3-letter ISO 4217 currency code; must not be
* NULL and must have length 3
* @param ec input-output error code. If the isoCode is invalid,
* then this will be set to a failing value.
* @internal
*/
CurrencyAmount(double amount, const UChar* isoCode,
UErrorCode &ec);
/**
* Copy constructor
* @internal
*/
CurrencyAmount(const CurrencyAmount& other);
/**
* Assignment operator
* @internal
*/
CurrencyAmount& operator=(const CurrencyAmount& other);
/**
* Return a polymorphic clone of this object. The result will
* have the same class as returned by getDynamicClassID().
* @internal
*/
virtual UObject* clone() const;
/**
* Destructor
* @internal
*/
virtual ~CurrencyAmount();
/**
* Returns a unique class ID for this object POLYMORPHICALLY.
* This method implements a simple form of RTTI used by ICU.
* @return The class ID for this object. All objects of a given
* class have the same class ID. Objects of other classes have
* different class IDs.
* @internal
*/
virtual UClassID getDynamicClassID() const;
/**
* Returns the class ID for this class. This is used to compare to
* the return value of getDynamicClassID().
* @return The class ID for all objects of this class.
* @internal
*/
static UClassID getStaticClassID();
/**
* Return the currency unit object of this object.
* @internal
*/
inline const CurrencyUnit& getCurrency() const;
/**
* Return the ISO currency code of this object.
* @internal
*/
inline const UChar* getISOCurrency() const;
};
inline const CurrencyUnit& CurrencyAmount::getCurrency() const {
return (const CurrencyUnit&) getUnit();
}
inline const UChar* CurrencyAmount::getISOCurrency() const {
return getCurrency().getISOCurrency();
}
U_NAMESPACE_END
#endif // !UCONFIG_NO_FORMATTING
#endif // __CURRENCYAMOUNT_H__

View File

@ -0,0 +1,112 @@
/*
**********************************************************************
* Copyright (c) 2004, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Author: Alan Liu
* Created: April 26, 2004
* Since: ICU 3.0
**********************************************************************
*/
#ifndef __CURRENCYUNIT_H__
#define __CURRENCYUNIT_H__
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/measunit.h"
U_NAMESPACE_BEGIN
/**
* A unit of currency, such as USD (U.S. dollars) or JPY (Japanese
* yen). This class is a thin wrapper over a UChar string that
* subclasses MeasureUnit, for use with Measure and MeasureFormat.
*
* @author Alan Liu
* @internal
*/
class U_I18N_API CurrencyUnit: public MeasureUnit {
public:
/**
* Construct an object with the given ISO currency code.
* @param isoCode the 3-letter ISO 4217 currency code; must not be
* NULL and must have length 3
* @param ec input-output error code. If the isoCode is invalid,
* then this will be set to a failing value.
* @internal
*/
CurrencyUnit(const UChar* isoCode, UErrorCode &ec);
/**
* Copy constructor
* @internal
*/
CurrencyUnit(const CurrencyUnit& other);
/**
* Assignment operator
* @internal
*/
CurrencyUnit& operator=(const CurrencyUnit& other);
/**
* Return a polymorphic clone of this object. The result will
* have the same class as returned by getDynamicClassID().
* @internal
*/
virtual UObject* clone() const;
/**
* Destructor
* @internal
*/
virtual ~CurrencyUnit();
/**
* Equality operator. Return true if this object is equal
* to the given object.
* @internal
*/
UBool operator==(const UObject& other) const;
/**
* Returns a unique class ID for this object POLYMORPHICALLY.
* This method implements a simple form of RTTI used by ICU.
* @return The class ID for this object. All objects of a given
* class have the same class ID. Objects of other classes have
* different class IDs.
* @internal
*/
virtual UClassID getDynamicClassID() const;
/**
* Returns the class ID for this class. This is used to compare to
* the return value of getDynamicClassID().
* @return The class ID for all objects of this class.
* @internal
*/
static UClassID getStaticClassID();
/**
* Return the ISO currency code of this object.
* @internal
*/
inline const UChar* getISOCurrency() const;
private:
/**
* The ISO 4217 code of this object.
*/
UChar isoCode[4];
};
inline const UChar* CurrencyUnit::getISOCurrency() const {
return isoCode;
}
U_NAMESPACE_END
#endif // !UCONFIG_NO_FORMATTING
#endif // __CURRENCYUNIT_H__

View File

@ -98,30 +98,6 @@ public:
*/
Formattable(int64_t ll);
/**
* Creates a Formattable for a currency amount.
* @param n the numeric value
* @param currency the currency code
* @draft ICU 3.0
*/
Formattable(double n, const UChar* currency);
/**
* Creates a Formattable for a currency amount.
* @param n the numeric value
* @param currency the currency code
* @draft ICU 3.0
*/
Formattable(int32_t n, const UChar* currency);
/**
* Creates a Formattable for a currency amount.
* @param n the numeric value
* @param currency the currency code
* @draft ICU 3.0
*/
Formattable(int64_t n, const UChar* currency);
/**
* Creates a Formattable object with a char string pointer.
* Assumes that the char string is null terminated.
@ -152,6 +128,13 @@ public:
*/
Formattable(const Formattable* arrayToCopy, int32_t count);
/**
* Creates a Formattable object that adopts the given UObject.
* @param objectToAdopt the UObject to set this object to
* @draft ICU 3.0
*/
Formattable(UObject* objectToAdopt);
/**
* Copy constructor.
* @stable ICU 2.0
@ -202,22 +185,60 @@ public:
Formattable *clone() const;
/**
* The list of possible data types of this Formattable object.
* Selector for flavor of data type contained within a
* Formattable object. Formattable is a union of several
* different types, and at any time contains exactly one type.
* @stable ICU 2.4
*/
enum Type {
/** @stable ICU 2.4 */
kDate, // Date
/** @stable ICU 2.4 */
kDouble, // double
/** @stable ICU 2.4 */
kLong, // long
/** @stable ICU 2.4 */
kString, // UnicodeString
/** @stable ICU 2.4 */
kArray, // Formattable[]
/** @draft ICU 2.8 */
kInt64 // int64
/**
* Selector indicating a UDate value. Use getDate to retrieve
* the value.
* @stable ICU 2.4
*/
kDate,
/**
* Selector indicating a double value. Use getDouble to
* retrieve the value.
* @stable ICU 2.4
*/
kDouble,
/**
* Selector indicating a 32-bit integer value. Use getLong to
* retrieve the value.
* @stable ICU 2.4
*/
kLong,
/**
* Selector indicating a UnicodeString value. Use getString
* to retrieve the value.
* @stable ICU 2.4
*/
kString,
/**
* Selector indicating an array of Formattables. Use getArray
* to retrieve the value.
* @stable ICU 2.4
*/
kArray,
/**
* Selector indicating a 64-bit integer value. Use getInt64
* to retrieve the value.
* @draft ICU 2.8
*/
kInt64,
/**
* Selector indicating a UObject value. Use getObject to
* retrieve the value.
* @draft ICU 3.0
*/
kObject
};
/**
@ -227,6 +248,14 @@ public:
*/
Type getType(void) const;
/**
* Returns TRUE if the data type of this Formattable object
* is kDouble, kLong, or kInt64.
* @return TRUE if this is a pure numeric object
* @draft ICU 3.0
*/
UBool isNumeric() const;
/**
* Gets the double value of this object. If this object is not of type
* kDouble then the result is undefined.
@ -238,8 +267,11 @@ public:
/**
* Gets the double value of this object. If this object is of type
* long or int64 then a casting conversion is peformed, with
* possible loss of precision. If the type is not a numeric type,
* 0 is returned and the status is set to U_INVALID_FORMAT_ERROR.
* possible loss of precision. If the type is kObject and the
* object is a Measure, then the result of
* getNumber().getDouble(status) is returned. If this object is
* neither a numeric type nor a Measure, then 0 is returned and
* the status is set to U_INVALID_FORMAT_ERROR.
* @param status the error code
* @return the double value of this object.
* @draft ICU 3.0
@ -261,9 +293,11 @@ public:
* U_INVALID_FORMAT_ERROR. If this object is of type kInt64 and
* it fits within a long, then no precision is lost. If it is of
* type kDouble, then a casting conversion is peformed, with
* truncation of any fractional part. If the type is not a
* numeric type, 0 is returned and the status is set to
* U_INVALID_FORMAT_ERROR.
* truncation of any fractional part. If the type is kObject and
* the object is a Measure, then the result of
* getNumber().getLong(status) is returned. If this object is
* neither a numeric type nor a Measure, then 0 is returned and
* the status is set to U_INVALID_FORMAT_ERROR.
* @param status the error code
* @return the long value of this object.
* @draft ICU 3.0
@ -285,8 +319,10 @@ public:
* and the status is set to U_INVALID_FORMAT_ERROR. If the
* magnitude fits in an int64, then a casting conversion is
* peformed, with truncation of any fractional part. If the type
* is not a numeric type, 0 is returned and the status is set to
* U_INVALID_FORMAT_ERROR.
* is kObject and the object is a Measure, then the result of
* getNumber().getDouble(status) is returned. If this object is
* neither a numeric type nor a Measure, then 0 is returned and
* the status is set to U_INVALID_FORMAT_ERROR.
* @param status the error code
* @return the int64 value of this object.
* @draft ICU 3.0
@ -401,13 +437,12 @@ public:
Formattable& operator[](int32_t index) { return fValue.fArrayAndCount.fArray[index]; }
/**
* Returns the currency of this object, or NULL if this object has
* no associated currency. Any type may have a currency, although
* this is intended for use with numeric types.
* @return a null-terminated 3-letter ISO 4217 code, or NULL
* Returns a pointer to the UObject contained within this
* formattable, or NULL if this object does not contain a UObject.
* @return a UObject pointer, or NULL
* @draft ICU 3.0
*/
const UChar* getCurrency() const;
const UObject* getObject() const;
/**
* Sets the double value of this object and changes the type to
@ -474,15 +509,13 @@ public:
void adoptArray(Formattable* array, int32_t count);
/**
* Sets the currency of this object. Only numeric types may have
* a currency. If isoCode is NULL then the currency is removed
* from this object. Any type may have a currency, although
* this is intended for use with numeric types.
* @param currency a null-terminated 3-letter ISO 4217 code, or NULL.
* If longer than 3 characters, the extra characters are ignored.
* Sets and adopts the UObject value of this object and changes
* the type to kObject. After this call, the caller must not
* delete the given object.
* @param objectToAdopt the UObject value to be adopted
* @draft ICU 3.0
*/
void setCurrency(const UChar* currency);
void adoptObject(UObject* objectToAdopt);
/**
* ICU "poor man's RTTI", returns a UClassID for the actual class.
@ -525,19 +558,18 @@ private:
UnicodeString* getBogus() const;
union {
UObject* fObject;
UnicodeString* fString;
double fDouble;
int64_t fInt64;
UDate fDate;
struct
{
Formattable* fArray;
int32_t fCount;
struct {
Formattable* fArray;
int32_t fCount;
} fArrayAndCount;
} fValue;
} fValue;
Type fType;
UChar fCurrency[4];
UnicodeString fBogus; // Bogus string when it's needed.
};

View File

@ -0,0 +1,65 @@
/*
**********************************************************************
* Copyright (c) 2004, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Author: Alan Liu
* Created: April 26, 2004
* Since: ICU 3.0
**********************************************************************
*/
#ifndef __MEASUREUNIT_H__
#define __MEASUREUNIT_H__
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/fmtable.h"
U_NAMESPACE_BEGIN
/**
* A unit such as length, mass, volume, currency, etc. A unit is
* coupled with a numeric amount to produce a Measure.
*
* <p>This is an abstract class.
*
* @author Alan Liu
* @internal
*/
class U_I18N_API MeasureUnit: public UObject {
public:
/**
* Return a polymorphic clone of this object. The result will
* have the same class as returned by getDynamicClassID().
* @internal
*/
virtual UObject* clone() const = 0;
/**
* Destructor
* @internal
*/
virtual ~MeasureUnit();
/**
* Equality operator. Return true if this object is equal
* to the given object.
* @internal
*/
virtual UBool operator==(const UObject& other) const = 0;
protected:
/**
* Default constructor.
*/
MeasureUnit();
};
U_NAMESPACE_END
// NOTE: There is no measunit.cpp. For implementation, see measure.cpp. [alan]
#endif // !UCONFIG_NO_FORMATTING
#endif // __MEASUREUNIT_H__

View File

@ -0,0 +1,125 @@
/*
**********************************************************************
* Copyright (c) 2004, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Author: Alan Liu
* Created: April 26, 2004
* Since: ICU 3.0
**********************************************************************
*/
#ifndef __MEASURE_H__
#define __MEASURE_H__
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/fmtable.h"
U_NAMESPACE_BEGIN
class MeasureUnit;
/**
* An amount of a specified unit, consisting of a number and a Unit.
* For example, a length measure consists of a number and a length
* unit, such as feet or meters. This is an abstract class.
* Subclasses specify a concrete Unit type.
*
* <p>Measure objects are parsed and formatted by subclasses of
* MeasureFormat.
*
* <p>Measure objects are immutable.
*
* <p>This is an abstract class.
*
* @author Alan Liu
* @draft ICU 3.0
*/
class U_I18N_API Measure: public UObject {
public:
/**
* Construct an object with the given numeric amount and the given
* unit. After this call, the caller must not delete the given
* unit object.
* @param amount a numeric object; amount.isNumeric() must be TRUE
* @param adpoptedUnit the unit object, which must not be NULL
* @param ec input-output error code. If the amount or the unit
* is invalid, then this will be set to a failing value.
* @internal
*/
Measure(const Formattable& number, MeasureUnit* adoptedUnit,
UErrorCode& ec);
/**
* Copy constructor
* @internal
*/
Measure(const Measure& other);
/**
* Assignment operator
* @internal
*/
Measure& operator=(const Measure& other);
/**
* Return a polymorphic clone of this object. The result will
* have the same class as returned by getDynamicClassID().
* @internal
*/
virtual UObject* clone() const = 0;
/**
* Destructor
* @internal
*/
virtual ~Measure();
/**
* Equality operator. Return true if this object is equal
* to the given object.
* @internal
*/
UBool operator==(const UObject& other) const;
/**
* Return a reference to the numeric value of this object. The
* numeric value may be of any numeric type supported by
* Formattable.
* @internal
*/
inline const Formattable& getNumber() const;
/**
* Return a reference to the unit of this object.
* @internal
*/
inline const MeasureUnit& getUnit() const;
private:
/**
* The numeric value of this object, e.g. 2.54 or 100.
*/
Formattable number;
/**
* The unit of this object, e.g., "millimeter" or "JPY". This is
* owned by this object.
*/
MeasureUnit* unit;
};
inline const Formattable& Measure::getNumber() const {
return number;
}
inline const MeasureUnit& Measure::getUnit() const {
return *unit;
}
U_NAMESPACE_END
#endif // !UCONFIG_NO_FORMATTING
#endif // __MEASURE_H__

View File

@ -23,6 +23,7 @@
#include "unicode/ustring.h"
#include "unicode/fmtable.h"
#include "unicode/dcfmtsym.h"
#include "unicode/curramt.h"
#include "uassert.h"
#include "cpputils.h"
@ -270,8 +271,8 @@ unum_formatDoubleCurrency(const UNumberFormat* fmt,
if (pos != 0) {
fp.setField(pos->field);
}
Formattable n(number, currency);
Formattable n(new CurrencyAmount(number, currency, *status));
((const NumberFormat*)fmt)->format(n, res, fp, *status);
if (pos != 0) {
@ -363,8 +364,10 @@ unum_parseDoubleCurrency(const UNumberFormat* fmt,
Formattable res;
parseRes(res, fmt, text, textLength, parsePos, TRUE, status);
currency[0] = 0;
if (res.getCurrency() != 0) {
u_strcpy(currency, res.getCurrency());
if (res.getType() == Formattable::kObject &&
res.getObject()->getDynamicClassID() == CurrencyAmount::getStaticClassID()) {
const CurrencyAmount* c = (const CurrencyAmount*) res.getObject();
u_strcpy(currency, c->getISOCurrency());
}
return res.getDouble(*status);
}

View File

@ -23,6 +23,7 @@
#include "unicode/ucnv.h"
#include "unicode/uclean.h"
#include "unicode/timezone.h"
#include "unicode/curramt.h"
#include "intltest.h"
#include "caltztst.h"
@ -115,17 +116,10 @@ operator+(const UnicodeString& left,
#if !UCONFIG_NO_FORMATTING
/**
* Originally coded this as operator+, but that makes the expression
* + char* ambiguous. - liu
* Return a string display for for this, without surrounding braces.
*/
UnicodeString toString(const Formattable& f) {
UnicodeString _toString(const Formattable& f) {
UnicodeString s;
UnicodeString close;
const UChar* currency = f.getCurrency();
if (currency != NULL) {
close.append((UChar)0x2f/*/*/).append(currency);
}
close.append((UChar)0x5d/*]*/);
switch (f.getType()) {
case Formattable::kDate:
{
@ -134,48 +128,68 @@ UnicodeString toString(const Formattable& f) {
if (U_SUCCESS(status)) {
FieldPosition pos;
fmt.format(f.getDate(), s, pos);
s.insert(0, "[Date:");
s.append(close);
s.insert(0, "Date:");
} else {
s = UnicodeString("[Error creating date format]");
s = UnicodeString("Error creating date format]");
}
}
break;
case Formattable::kDouble:
s = UnicodeString("[double:") + f.getDouble() + close;
s = UnicodeString("double:") + f.getDouble();
break;
case Formattable::kLong:
s = UnicodeString("[long:") + f.getLong() + close;
s = UnicodeString("long:") + f.getLong();
break;
case Formattable::kInt64:
s = UnicodeString("[int64:") + Int64ToUnicodeString(f.getInt64()) + close;
s = UnicodeString("int64:") + Int64ToUnicodeString(f.getInt64());
break;
case Formattable::kString:
f.getString(s);
s.insert(0, "[String:");
s.append(close);
s.insert(0, "String:");
break;
case Formattable::kArray:
{
int32_t i, n;
const Formattable* array = f.getArray(n);
s.insert(0, UnicodeString("[Array:"));
s.insert(0, UnicodeString("Array:"));
UnicodeString delim(", ");
for (i=0; i<n; ++i) {
if (i > 0) {
s.append(delim);
}
s = s + toString(array[i]);
s = s + _toString(array[i]);
}
s.append(close);
}
break;
case Formattable::kObject:
if (f.getObject()->getDynamicClassID() ==
CurrencyAmount::getStaticClassID()) {
const CurrencyAmount& c = (const CurrencyAmount&) *f.getObject();
s = _toString(c.getNumber()) + " " + UnicodeString(c.getISOCurrency());
} else {
s = UnicodeString("Unknown UObject");
}
break;
default:
s = UnicodeString("Unknown Formattable type=") + (int32_t)f.getType();
break;
}
return s;
}
/**
* Originally coded this as operator+, but that makes the expression
* + char* ambiguous. - liu
*/
UnicodeString toString(const Formattable& f) {
UnicodeString s((UChar)91/*[*/);
s.append(_toString(f));
s.append((UChar)0x5d/*]*/);
return s;
}
#endif
// useful when operator+ won't cooperate

View File

@ -18,6 +18,7 @@
#include "unicode/ucurr.h"
#include "unicode/ustring.h"
#include "unicode/measfmt.h"
#include "unicode/curramt.h"
#include "digitlst.h"
#include "textfile.h"
#include "tokiter.h"
@ -1569,8 +1570,9 @@ static void parseCurrencyAmount(const UnicodeString& str,
int32_t i = str.indexOf(delim);
str.extractBetween(0, i, num);
str.extractBetween(i+1, INT32_MAX, cur);
fmt.parse(num, result, ec);
result.setCurrency(cur.getTerminatedBuffer());
Formattable n;
fmt.parse(num, n, ec);
result.adoptObject(new CurrencyAmount(n, cur.getTerminatedBuffer(), ec));
}
void NumberFormatTest::TestCases() {