ICU-2203 C++ version of NumberFormat registration

X-SVN-Rev: 11160
This commit is contained in:
Doug Felt 2003-02-25 23:36:48 +00:00
parent 5bc8014e68
commit c31688a777
7 changed files with 447 additions and 6 deletions

View file

@ -767,6 +767,9 @@ public:
}
void reset(UErrorCode& status) {
if (status == U_ENUM_OUT_OF_SYNC_ERROR) {
status = U_ZERO_ERROR;
}
if (U_SUCCESS(status)) {
_timestamp = _service->getTimestamp();
_pos = 0;

View file

@ -31,6 +31,7 @@
#include "unicode/resbund.h"
#include "unicode/dcfmtsym.h"
#include "unicode/decimfmt.h"
#include "iculserv.h"
#include <float.h>
// If no number pattern can be located for a locale, this is the last
@ -300,6 +301,155 @@ NumberFormat::getAvailableLocales(int32_t& count)
return Locale::getAvailableLocales(count);
}
// ------------------------------------------
//
// Registration
//
//-------------------------------------------
static ICULocaleService* gService = NULL;
// -------------------------------------
class ICUNumberFormatFactory : public ICUResourceBundleFactory {
protected:
virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* service, UErrorCode& status) const {
// !!! kind is not an EStyles, need to determine how to handle this
return NumberFormat::makeInstance(loc, (NumberFormat::EStyles)kind, status);
}
};
// -------------------------------------
class NFFactory : public LocaleKeyFactory {
private:
NumberFormatFactory* _delegate;
public:
NFFactory(NumberFormatFactory* delegate)
: LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE)
, _delegate(delegate)
{
}
virtual ~NFFactory()
{
delete _delegate;
}
virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const
{
if (handlesKey(key, status)) {
const LocaleKey& lkey = (const LocaleKey&)key;
Locale loc;
lkey.canonicalLocale(loc);
int32_t kind = lkey.kind();
UObject* result = _delegate->createFormat(loc, (UNumberFormatStyle)(kind+1));
if (result == NULL) {
result = service->getKey((ICUServiceKey&)key /* cast away const */, NULL, this, status);
}
return result;
}
return NULL;
}
protected:
/**
* Return the set of ids that this factory supports (visible or
* otherwise). This can be called often and might need to be
* cached if it is expensive to create.
*/
virtual const Hashtable* getSupportedIDs(UErrorCode& status) const
{
return _delegate->getSupportedIDs(status);
}
};
class ICUNumberFormatService : public ICULocaleService {
public:
ICUNumberFormatService()
: ICULocaleService("Number Format")
{
UErrorCode status = U_ZERO_ERROR;
registerFactory(new ICUNumberFormatFactory(), status);
}
virtual UObject* cloneInstance(UObject* instance) const {
return ((NumberFormat*)instance)->clone();
}
virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* actualID, UErrorCode& status) const {
LocaleKey& lkey = (LocaleKey&)key;
int32_t kind = lkey.kind();
Locale loc;
lkey.currentLocale(loc);
return NumberFormat::makeInstance(loc, (NumberFormat::EStyles)kind, status);
}
virtual UBool isDefault() const {
return countFactories() == 1;
}
};
// -------------------------------------
static UMTX gLock = 0;
static ICULocaleService*
getService(void)
{
if (gService == NULL) {
Mutex mutex(&gLock);
if (gService == NULL) {
gService = new ICUNumberFormatService();
}
}
return gService;
}
// -------------------------------------
NumberFormat*
NumberFormat::createInstance(const Locale& loc, EStyles kind, UErrorCode& status)
{
if (gService != NULL) {
return (NumberFormat*)gService->get(loc, kind, status);
} else {
return makeInstance(loc, kind, status);
}
}
// -------------------------------------
URegistryKey
NumberFormat::registerFactory(NumberFormatFactory* toAdopt, UErrorCode& status)
{
return getService()->registerFactory(new NFFactory(toAdopt), status);
}
// -------------------------------------
UBool
NumberFormat::unregister(URegistryKey key, UErrorCode& status)
{
if (U_SUCCESS(status)) {
if (gService != NULL) {
return gService->unregister(key, status);
}
status = U_ILLEGAL_ARGUMENT_ERROR;
}
return FALSE;
}
// -------------------------------------
StringEnumeration*
NumberFormat::getAvailableLocales(void)
{
return getService()->getAvailableLocales();
}
// -------------------------------------
// Checks if the thousand/10 thousand grouping is used in the
// NumberFormat instance.
@ -412,9 +562,9 @@ NumberFormat::setMinimumFractionDigits(int32_t newValue)
// or percent) for the desired locale.
NumberFormat*
NumberFormat::createInstance(const Locale& desiredLocale,
EStyles style,
UErrorCode& status)
NumberFormat::makeInstance(const Locale& desiredLocale,
EStyles style,
UErrorCode& status)
{
if (U_FAILURE(status)) return NULL;
@ -505,6 +655,20 @@ NumberFormat::createInstance(const Locale& desiredLocale,
U_NAMESPACE_END
// defined in ucln_cmn.h
/**
* Release all static memory held by numberformat.
*/
U_CFUNC UBool numfmt_cleanup(void) {
if (gService) {
delete gService;
gService = NULL;
}
umtx_destroy(&gLock);
return TRUE;
}
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof

View file

@ -25,6 +25,7 @@ static UBool i18n_cleanup(void)
#if !UCONFIG_NO_FORMATTING
timeZone_cleanup();
numfmt_cleanup();
#endif
#if !UCONFIG_NO_COLLATION

View file

@ -34,4 +34,6 @@ U_CFUNC UBool ucol_bld_cleanup(void);
U_CFUNC UBool regex_cleanup(void);
U_CFUNC UBool numfmt_cleanup(void);
#endif

View file

@ -26,10 +26,16 @@
#include "unicode/unistr.h"
#include "unicode/format.h"
#include "unicode/unum.h" // UNumberFormatStyle
#include "hash.h"
#include "unicode/locid.h"
U_NAMESPACE_BEGIN
class Locale;
class NumberFormatFactory;
class StringEnumeration;
typedef const void* URegistryKey;
/**
* Abstract base class for all number formats. Provides interface for
@ -420,6 +426,34 @@ public:
*/
static const Locale* getAvailableLocales(int32_t& count);
/**
* Register a new NumberFormatFactory. The factory will be adopted.
* @param toAdopt the NumberFormatFactory instance to be adopted
* @param status the in/out status code, no special meanings are assigned
* @return a registry key that can be used to unregister this factory
* @draft ICU 2.6
*/
static URegistryKey registerFactory(NumberFormatFactory* toAdopt, UErrorCode& status);
/**
* Unregister a previously-registered NumberFormatFactory using the key returned from the
* register call. Key becomes invalid after a successful call and should not be used again.
* The NumberFormatFactory corresponding to the key will be deleted.
* @param key the registry key returned by a previous call to registerFactory
* @param status the in/out status code, no special meanings are assigned
* @return TRUE if the factory for the key was successfully unregistered
* @draft ICU 2.6
*/
static UBool unregister(URegistryKey key, UErrorCode& status);
/**
* Return a StringEnumeration over the locales available at the time of the call,
* including registered locales.
* @return a StringEnumeration over the locales available at the time of the call
* @draft ICU 2.6
*/
static StringEnumeration* getAvailableLocales(void);
/**
* Returns true if grouping is used in this format. For example,
* in the English locale, with grouping on, the number 1234567
@ -545,7 +579,6 @@ public:
static inline UClassID getStaticClassID(void);
/**
* Override Calendar
* Returns a unique class ID POLYMORPHICALLY. Pure virtual override.
* This method is to implement a simple version of RTTI, since not all
* C++ compilers support genuine RTTI. Polymorphic operator==() and
@ -595,6 +628,8 @@ private:
/**
* Creates the specified decimal format style of the desired locale.
* Hook for service registration, uses makeInstance directly if no services
* registered.
* @param desiredLocale the given locale.
* @param choice the given style.
* @param success Output param filled with success/failure status.
@ -602,6 +637,14 @@ private:
*/
static NumberFormat* createInstance(const Locale& desiredLocale, EStyles choice, UErrorCode& success);
/**
* Creates the specified decimal format style of the desired locale.
* @param desiredLocale the given locale.
* @param choice the given style.
* @param success Output param filled with success/failure status.
* @return A new NumberFormat instance.
*/
static NumberFormat* makeInstance(const Locale& desiredLocale, EStyles choice, UErrorCode& success);
static const int32_t fgNumberPatternsCount;
static const UChar* const fgLastResortNumberPatterns[];
@ -611,8 +654,75 @@ private:
int32_t fMaxFractionDigits;
int32_t fMinFractionDigits;
UBool fParseIntegerOnly;
friend class ICUNumberFormatFactory; // access to makeInstance, EStyles
friend class ICUNumberFormatService;
};
/**
* A NumberFormatFactory is used to register new number formats. The factory
* should be able to create any of the predefined formats for each locale it
* supports. When registered, the locales it supports extend or override the
* locale already supported by ICU.
*
* @prototype
*/
class U_I18N_API NumberFormatFactory : public UObject {
public:
/**
* Return true if this factory will be visible. Default is true.
* If not visible, the locales supported by this factory will not
* be listed by getAvailableLocales.
*/
virtual UBool visible(void) const = 0;
/**
* Return an unmodifiable collection of the locale names directly
* supported by this factory.
*/
virtual const Hashtable* getSupportedIDs(UErrorCode& status) const = 0;
/**
* Return a number format of the appropriate type. If the locale
* is not supported, return null. If the locale is supported, but
* the type is not provided by this service, return null. Otherwise
* return an appropriate instance of NumberFormat.
*/
virtual NumberFormat* createFormat(const Locale& loc, UNumberFormatStyle formatType) = 0;
};
/**
* A NumberFormatFactory that supports a single locale. It can be visible or invisible.
* @prototype
*/
class U_I18N_API SimpleNumberFormatFactory : public NumberFormatFactory {
protected:
const UBool _visible;
Hashtable _ids;
public:
SimpleNumberFormatFactory(const Locale& locale, UBool visible = TRUE)
: _visible(visible)
{
UErrorCode status = U_ZERO_ERROR;
_ids.put(locale.getName(), this, status); // um, ignore error code...
}
virtual UBool visible(void) const {
return _visible;
}
virtual const Hashtable* getSupportedIDs(UErrorCode& status) const
{
if (U_SUCCESS(status)) {
return &_ids;
}
return NULL;
}
};
// -------------------------------------
inline UClassID

View file

@ -13,6 +13,8 @@
#include "unicode/numfmt.h"
#include "unicode/decimfmt.h"
#include "unicode/locid.h"
#include "unicode/unum.h"
#include "unicode/strenum.h"
// This is an API test, not a unit test. It doesn't test very many cases, and doesn't
// try to test the full functionality. It just calls each function in the class and
@ -33,7 +35,17 @@ void IntlTestNumberFormatAPI::runIndexedTest( int32_t index, UBool exec, const c
testAPI(/* par */);
}
break;
case 1: name = "NumberFormatRegistration";
if (exec) {
logln("NumberFormat Registration test---"); logln("");
UErrorCode status = U_ZERO_ERROR;
Locale::setDefault(Locale::getEnglish(), status);
if(U_FAILURE(status)) {
errln("ERROR: Could not set default locale, test may not give correct results");
}
testRegistration();
}
break;
default: name = ""; break;
}
}
@ -226,4 +238,152 @@ void IntlTestNumberFormatAPI::testAPI(/* char* par */)
delete per_fr;
}
#define SRC_LOC Locale::getFrance()
#define SWAP_LOC Locale::getUS()
class TestFactory : public SimpleNumberFormatFactory {
NumberFormat* currencyStyle;
public:
TestFactory()
: SimpleNumberFormatFactory(SRC_LOC, TRUE)
{
UErrorCode status = U_ZERO_ERROR;
currencyStyle = NumberFormat::createInstance(SWAP_LOC, status);
}
virtual ~TestFactory()
{
delete currencyStyle;
}
virtual NumberFormat* createFormat(const Locale& loc, UNumberFormatStyle formatType)
{
if (formatType == UNUM_CURRENCY) {
return (NumberFormat*)currencyStyle->clone();
}
return NULL;
}
virtual inline UClassID getDynamicClassID() const
{
return (UClassID)gID;
}
static inline UClassID getStaticClassID()
{
return (UClassID)gID;
}
private:
static char gID;
};
char TestFactory::gID = 0;
void
IntlTestNumberFormatAPI::testRegistration()
{
UErrorCode status = U_ZERO_ERROR;
NumberFormat* f0 = NumberFormat::createInstance(SWAP_LOC, status);
NumberFormat* f1 = NumberFormat::createInstance(SRC_LOC, status);
NumberFormat* f2 = NumberFormat::createCurrencyInstance(SRC_LOC, status);
URegistryKey key = NumberFormat::registerFactory(new TestFactory(), status);
NumberFormat* f3 = NumberFormat::createCurrencyInstance(SRC_LOC, status);
NumberFormat* f3a = NumberFormat::createCurrencyInstance(SRC_LOC, status);
NumberFormat* f4 = NumberFormat::createInstance(SRC_LOC, status);
StringEnumeration* locs = NumberFormat::getAvailableLocales();
UNumberFormat* uf3 = unum_open(UNUM_CURRENCY, NULL, 0, SRC_LOC.getName(),NULL, &status);
UNumberFormat* uf4 = unum_open(UNUM_DEFAULT, NULL, 0, SRC_LOC.getName(), NULL, &status);
for (const UnicodeString* res = locs->snext(status); res; res = locs->snext(status)) {
logln(*res); // service is still in synch
}
NumberFormat::unregister(key, status); // restore for other tests
NumberFormat* f5 = NumberFormat::createCurrencyInstance(SRC_LOC, status);
UNumberFormat* uf5 = unum_open(UNUM_CURRENCY, NULL, 0, SRC_LOC.getName(),NULL, &status);
float n = 1234.567f;
UnicodeString res0, res1, res2, res3, res4, res5;
UChar ures3[50];
UChar ures4[50];
UChar ures5[50];
f0->format(n, res0);
f1->format(n, res1);
f2->format(n, res2);
f3->format(n, res3);
f4->format(n, res4);
f5->format(n, res5);
unum_formatDouble(uf3, n, ures3, 50, NULL, &status);
unum_formatDouble(uf4, n, ures4, 50, NULL, &status);
unum_formatDouble(uf5, n, ures5, 50, NULL, &status);
logln((UnicodeString)"f0 swap int: " + res0);
logln((UnicodeString)"f1 src int: " + res1);
logln((UnicodeString)"f2 src cur: " + res2);
logln((UnicodeString)"f3 reg cur: " + res3);
logln((UnicodeString)"f4 reg int: " + res4);
logln((UnicodeString)"f5 unreg cur: " + res5);
log("uf3 reg cur: ");
logln(ures3);
log("uf4 reg int: ");
logln(ures4);
log("uf5 ureg cur: ");
logln(ures5);
if (f3 == f3a) {
errln("did not get new instance from service");
} else {
delete f3a;
}
if (res3 != res0) {
errln("registered service did not match");
}
if (res4 != res1) {
errln("registered service did not inherit");
}
if (res5 != res2) {
errln("unregistered service did not match original");
}
if (res0 != ures3) {
errln("registered service did not match / unum");
}
if (res1 != ures4) {
errln("registered service did not inherit / unum");
}
if (res2 != ures5) {
errln("unregistered service did not match original / unum");
}
unum_close(uf5);
delete f5;
unum_close(uf4);
unum_close(uf3);
delete f4;
delete f3;
delete f2;
delete f1;
delete f0;
for (const UnicodeString* res = locs->snext(status); res; res = locs->snext(status)) {
errln(*res); // service should be out of synch
}
locs->reset(status); // now in synch again, we hope
for (const UnicodeString* res = locs->snext(status); res; res = locs->snext(status)) {
logln(*res);
}
delete locs;
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -25,6 +25,7 @@ private:
* executes tests of API functions, see detailed comments in source code
**/
void testAPI(/* char* par */);
void testRegistration();
};
#endif /* #if !UCONFIG_NO_FORMATTING */