• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /**
4  *******************************************************************************
5  * Copyright (C) 2001-2016, International Business Machines Corporation and
6  * others. All Rights Reserved.
7  *******************************************************************************
8  */
9 
10 #include "utypeinfo.h"  // for 'typeid' to work
11 
12 #include "unicode/utypes.h"
13 
14 #if !UCONFIG_NO_SERVICE
15 
16 #include "cmemory.h"
17 #include "icusvtst.h"
18 #include "servloc.h"
19 #include <stdio.h>
20 
21 
22 class MyListener : public EventListener {
23 };
24 
25 class WrongListener : public EventListener {
26 };
27 
28 class ICUNSubclass : public ICUNotifier {
29     public:
acceptsListener(const EventListener &) const30     UBool acceptsListener(const EventListener& /*l*/) const {
31         return TRUE;
32         // return l instanceof MyListener;
33     }
34 
notifyListener(EventListener &) const35     virtual void notifyListener(EventListener& /*l*/) const {
36     }
37 };
38 
39 // This factory does nothing
40 class LKFSubclass0 : public LocaleKeyFactory {
41 public:
LKFSubclass0()42         LKFSubclass0()
43                 : LocaleKeyFactory(VISIBLE, "LKFSubclass0")
44         {
45         }
46 };
47 
48 class LKFSubclass : public LocaleKeyFactory {
49     Hashtable table;
50 
51     public:
LKFSubclass(UBool visible)52     LKFSubclass(UBool visible)
53         : LocaleKeyFactory(visible ? VISIBLE : INVISIBLE, "LKFSubclass")
54     {
55         UErrorCode status = U_ZERO_ERROR;
56         table.put("en_US", this, status);
57     }
58 
59     protected:
getSupportedIDs(UErrorCode &) const60     virtual const Hashtable* getSupportedIDs(UErrorCode &/*status*/) const {
61         return &table;
62     }
63 };
64 
65 class Integer : public UObject {
66     public:
67     const int32_t _val;
68 
Integer(int32_t val)69     Integer(int32_t val) : _val(val) {
70     }
71 
Integer(const Integer & rhs)72     Integer(const Integer& rhs) : UObject(rhs), _val(rhs._val) {
73     }
~Integer()74     virtual ~Integer() {
75     }
76 
77     public:
78     /**
79      * UObject boilerplate.
80      */
getStaticClassID()81     static UClassID getStaticClassID() {
82         return (UClassID)&fgClassID;
83     }
84 
getDynamicClassID() const85     virtual UClassID getDynamicClassID() const {
86         return getStaticClassID();
87     }
88 
operator ==(const UObject & other) const89     virtual UBool operator==(const UObject& other) const
90     {
91         return typeid(*this) == typeid(other) &&
92             _val == ((Integer&)other)._val;
93     }
94 
95     public:
debug(UnicodeString & result) const96     virtual UnicodeString& debug(UnicodeString& result) const {
97         debugClass(result);
98         result.append(" val: ");
99         result.append(_val);
100         return result;
101     }
102 
debugClass(UnicodeString & result) const103     virtual UnicodeString& debugClass(UnicodeString& result) const {
104         return result.append("Integer");
105     }
106 
107     private:
108     static const char fgClassID;
109 };
110 
111 const char Integer::fgClassID = '\0';
112 
113 // use locale keys
114 class TestIntegerService : public ICUService {
115     public:
createKey(const UnicodeString * id,UErrorCode & status) const116     ICUServiceKey* createKey(const UnicodeString* id, UErrorCode& status) const {
117         return LocaleKey::createWithCanonicalFallback(id, NULL, status); // no fallback locale
118     }
119 
createSimpleFactory(UObject * obj,const UnicodeString & id,UBool visible,UErrorCode & status)120     virtual ICUServiceFactory* createSimpleFactory(UObject* obj, const UnicodeString& id, UBool visible, UErrorCode& status)
121     {
122         Integer* i;
123         if (U_SUCCESS(status) && obj && (i = dynamic_cast<Integer*>(obj)) != NULL) {
124             return new SimpleFactory(i, id, visible);
125         }
126         return NULL;
127     }
128 
cloneInstance(UObject * instance) const129     virtual UObject* cloneInstance(UObject* instance) const {
130         return instance ? new Integer(*(Integer*)instance) : NULL;
131     }
132 };
133 
134 
ICUServiceTest()135 ICUServiceTest::ICUServiceTest() {
136 }
137 
~ICUServiceTest()138 ICUServiceTest::~ICUServiceTest() {
139 }
140 
141 void
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)142 ICUServiceTest::runIndexedTest(int32_t index, UBool exec, const char* &name,
143 char* /*par*/)
144 {
145     switch (index) {
146         TESTCASE(0,testAPI_One);
147         TESTCASE(1,testAPI_Two);
148         TESTCASE(2,testRBF);
149         TESTCASE(3,testNotification);
150         TESTCASE(4,testLocale);
151         TESTCASE(5,testWrapFactory);
152         TESTCASE(6,testCoverage);
153     default: name = ""; break;
154     }
155 }
156 
append(UnicodeString & result,const UObject * obj)157 UnicodeString append(UnicodeString& result, const UObject* obj)
158 {
159     char buffer[128];
160     if (obj == NULL) {
161         result.append("NULL");
162     } else {
163         const UnicodeString* s;
164         const Locale* loc;
165         const Integer* i;
166         if ((s = dynamic_cast<const UnicodeString*>(obj)) != NULL) {
167             result.append(*s);
168         } else if ((loc = dynamic_cast<const Locale*>(obj)) != NULL) {
169             result.append(loc->getName());
170         } else if ((i = dynamic_cast<const Integer*>(obj)) != NULL) {
171             sprintf(buffer, "%d", (int)i->_val);
172             result.append(buffer);
173         } else {
174             sprintf(buffer, "%p", (const void*)obj);
175             result.append(buffer);
176         }
177     }
178     return result;
179 }
180 
181 UnicodeString&
lrmsg(UnicodeString & result,const UnicodeString & message,const UObject * lhs,const UObject * rhs) const182 ICUServiceTest::lrmsg(UnicodeString& result, const UnicodeString& message, const UObject* lhs, const UObject* rhs) const
183 {
184     result.append(message);
185     result.append(" lhs: ");
186     append(result, lhs);
187     result.append(", rhs: ");
188     append(result, rhs);
189     return result;
190 }
191 
192 void
confirmBoolean(const UnicodeString & message,UBool val)193 ICUServiceTest::confirmBoolean(const UnicodeString& message, UBool val)
194 {
195     if (val) {
196         logln(message);
197     } else {
198         errln(message);
199     }
200 }
201 
202 #if 0
203 void
204 ICUServiceTest::confirmEqual(const UnicodeString& message, const UObject* lhs, const UObject* rhs)
205 {
206     UBool equ = (lhs == NULL)
207         ? (rhs == NULL)
208         : (rhs != NULL && lhs->operator==(*rhs));
209 
210     UnicodeString temp;
211     lrmsg(temp, message, lhs, rhs);
212 
213     if (equ) {
214         logln(temp);
215     } else {
216         errln(temp);
217     }
218 }
219 #else
220 void
confirmEqual(const UnicodeString & message,const Integer * lhs,const Integer * rhs)221 ICUServiceTest::confirmEqual(const UnicodeString& message, const Integer* lhs, const Integer* rhs)
222 {
223     UBool equ = (lhs == NULL)
224         ? (rhs == NULL)
225         : (rhs != NULL && lhs->operator==(*rhs));
226 
227     UnicodeString temp;
228     lrmsg(temp, message, lhs, rhs);
229 
230     if (equ) {
231         logln(temp);
232     } else {
233         errln(temp);
234     }
235 }
236 
237 void
confirmEqual(const UnicodeString & message,const UnicodeString * lhs,const UnicodeString * rhs)238 ICUServiceTest::confirmEqual(const UnicodeString& message, const UnicodeString* lhs, const UnicodeString* rhs)
239 {
240     UBool equ = (lhs == NULL)
241         ? (rhs == NULL)
242         : (rhs != NULL && lhs->operator==(*rhs));
243 
244     UnicodeString temp;
245     lrmsg(temp, message, lhs, rhs);
246 
247     if (equ) {
248         logln(temp);
249     } else {
250         errln(temp);
251     }
252 }
253 
254 void
confirmEqual(const UnicodeString & message,const Locale * lhs,const Locale * rhs)255 ICUServiceTest::confirmEqual(const UnicodeString& message, const Locale* lhs, const Locale* rhs)
256 {
257     UBool equ = (lhs == NULL)
258         ? (rhs == NULL)
259         : (rhs != NULL && lhs->operator==(*rhs));
260 
261     UnicodeString temp;
262     lrmsg(temp, message, lhs, rhs);
263 
264     if (equ) {
265         logln(temp);
266     } else {
267         errln(temp);
268     }
269 }
270 #endif
271 
272 // use these for now
273 void
confirmStringsEqual(const UnicodeString & message,const UnicodeString & lhs,const UnicodeString & rhs)274 ICUServiceTest::confirmStringsEqual(const UnicodeString& message, const UnicodeString& lhs, const UnicodeString& rhs)
275 {
276     UBool equ = lhs == rhs;
277 
278     UnicodeString temp = message;
279     temp.append(" lhs: ");
280     temp.append(lhs);
281     temp.append(" rhs: ");
282     temp.append(rhs);
283 
284     if (equ) {
285         logln(temp);
286     } else {
287         dataerrln(temp);
288     }
289 }
290 
291 
292 void
confirmIdentical(const UnicodeString & message,const UObject * lhs,const UObject * rhs)293 ICUServiceTest::confirmIdentical(const UnicodeString& message, const UObject* lhs, const UObject *rhs)
294 {
295     UnicodeString temp;
296     lrmsg(temp, message, lhs, rhs);
297     if (lhs == rhs) {
298         logln(temp);
299     } else {
300         errln(temp);
301     }
302 }
303 
304 void
confirmIdentical(const UnicodeString & message,int32_t lhs,int32_t rhs)305 ICUServiceTest::confirmIdentical(const UnicodeString& message, int32_t lhs, int32_t rhs)
306 {
307     if (lhs == rhs) {
308         logln(message + " lhs: " + lhs + " rhs: " + rhs);
309     } else {
310         errln(message + " lhs: " + lhs + " rhs: " + rhs);
311     }
312 }
313 
314 void
msgstr(const UnicodeString & message,UObject * obj,UBool err)315 ICUServiceTest::msgstr(const UnicodeString& message, UObject* obj, UBool err)
316 {
317     if (obj) {
318     UnicodeString* str = (UnicodeString*)obj;
319         logln(message + *str);
320         delete str;
321     } else if (err) {
322         errln("Error " + message + "string is NULL");
323     }
324 }
325 
326 void
testAPI_One()327 ICUServiceTest::testAPI_One()
328 {
329     // create a service using locale keys,
330     TestIntegerService service;
331 
332     // register an object with one locale,
333     // search for an object with a more specific locale
334     // should return the original object
335     UErrorCode status = U_ZERO_ERROR;
336     Integer* singleton0 = new Integer(0);
337     service.registerInstance(singleton0, "en_US", status);
338     {
339         UErrorCode status = U_ZERO_ERROR;
340         Integer* result = (Integer*)service.get("en_US_FOO", status);
341         confirmEqual("1) en_US_FOO -> en_US", result, singleton0);
342         delete result;
343     }
344 
345     // register a new object with the more specific locale
346     // search for an object with that locale
347     // should return the new object
348     Integer* singleton1 = new Integer(1);
349     service.registerInstance(singleton1, "en_US_FOO", status);
350     {
351         UErrorCode status = U_ZERO_ERROR;
352         Integer* result = (Integer*)service.get("en_US_FOO", status);
353         confirmEqual("2) en_US_FOO -> en_US_FOO", result, singleton1);
354         delete result;
355     }
356 
357     // search for an object that falls back to the first registered locale
358     {
359         UErrorCode status = U_ZERO_ERROR;
360         Integer* result = (Integer*)service.get("en_US_BAR", status);
361         confirmEqual("3) en_US_BAR -> en_US", result, singleton0);
362         delete result;
363     }
364 
365     // get a list of the factories, should be two
366     {
367         confirmIdentical("4) factory size", service.countFactories(), 2);
368     }
369 
370     // register a new object with yet another locale
371     Integer* singleton2 = new Integer(2);
372     service.registerInstance(singleton2, "en", status);
373     {
374         confirmIdentical("5) factory size", service.countFactories(), 3);
375     }
376 
377     // search for an object with the new locale
378     // stack of factories is now en, en_US_FOO, en_US
379     // search for en_US should still find en_US object
380     {
381         UErrorCode status = U_ZERO_ERROR;
382         Integer* result = (Integer*)service.get("en_US_BAR", status);
383         confirmEqual("6) en_US_BAR -> en_US", result, singleton0);
384         delete result;
385     }
386 
387     // register a new object with an old id, should hide earlier factory using this id, but leave it there
388     Integer* singleton3 = new Integer(3);
389     URegistryKey s3key = service.registerInstance(singleton3, "en_US", status);
390     {
391         confirmIdentical("9) factory size", service.countFactories(), 4);
392     }
393 
394     // should get data from that new factory
395     {
396         UErrorCode status = U_ZERO_ERROR;
397         Integer* result = (Integer*)service.get("en_US_BAR", status);
398         confirmEqual("10) en_US_BAR -> (3)", result, singleton3);
399         delete result;
400     }
401 
402     // remove new factory
403     // should have fewer factories again
404     // singleton3 dead!
405     {
406         UErrorCode status = U_ZERO_ERROR;
407         service.unregister(s3key, status);
408         confirmIdentical("11) factory size", service.countFactories(), 3);
409     }
410 
411     // should get original data again after remove factory
412     {
413         UErrorCode status = U_ZERO_ERROR;
414         Integer* result = (Integer*)service.get("en_US_BAR", status);
415         confirmEqual("12) en_US_BAR -> (3)", result, singleton0);
416         delete result;
417     }
418 
419     // shouldn't find unregistered ids
420     {
421         UErrorCode status = U_ZERO_ERROR;
422         Integer* result = (Integer*)service.get("foo", status);
423         confirmIdentical("13) foo -> null", result, NULL);
424         delete result;
425     }
426 
427     // should find non-canonical strings
428     {
429         UnicodeString resultID;
430         UErrorCode status = U_ZERO_ERROR;
431         Integer* result = (Integer*)service.get("EN_us_fOo", &resultID, status);
432         confirmEqual("14a) find-non-canonical", result, singleton1);
433         confirmStringsEqual("14b) find non-canonical", resultID, "en_US_FOO");
434         delete result;
435     }
436 
437     // should be able to register non-canonical strings and get them canonicalized
438     Integer* singleton4 = new Integer(4);
439     service.registerInstance(singleton4, "eN_ca_dUde", status);
440     {
441         UnicodeString resultID;
442         UErrorCode status = U_ZERO_ERROR;
443         Integer* result = (Integer*)service.get("En_Ca_DuDe", &resultID, status);
444         confirmEqual("15a) find-non-canonical", result, singleton4);
445         confirmStringsEqual("15b) register non-canonical", resultID, "en_CA_DUDE");
446         delete result;
447     }
448 
449     // should be able to register invisible factories, these will not
450     // be visible by default, but if you know the secret password you
451     // can still access these services...
452     Integer* singleton5 = new Integer(5);
453     service.registerInstance(singleton5, "en_US_BAR", FALSE, status);
454     {
455         UErrorCode status = U_ZERO_ERROR;
456         Integer* result = (Integer*)service.get("en_US_BAR", status);
457         confirmEqual("17) get invisible", result, singleton5);
458         delete result;
459     }
460 
461     // should not be able to locate invisible services
462     {
463         UErrorCode status = U_ZERO_ERROR;
464         UVector ids(uprv_deleteUObject, uhash_compareUnicodeString, status);
465         service.getVisibleIDs(ids, status);
466         UnicodeString target = "en_US_BAR";
467         confirmBoolean("18) find invisible", !ids.contains(&target));
468     }
469 
470     // clear factory and caches
471     service.reset();
472     confirmBoolean("19) is default", service.isDefault());
473 }
474 
475 /*
476  ******************************************************************
477  */
478 class TestStringSimpleKeyService : public ICUService {
479 public:
480 
createSimpleFactory(UObject * obj,const UnicodeString & id,UBool visible,UErrorCode & status)481         virtual ICUServiceFactory* createSimpleFactory(UObject* obj, const UnicodeString& id, UBool visible, UErrorCode& status)
482     {
483                 // We could put this type check into ICUService itself, but we'd still
484                 // have to implement cloneInstance.  Otherwise we could just tell the service
485                 // what the object type is when we create it, and the default implementation
486                 // could handle everything for us.  Phooey.
487         if (obj && dynamic_cast<UnicodeString*>(obj) != NULL) {
488                         return ICUService::createSimpleFactory(obj, id, visible, status);
489         }
490         return NULL;
491     }
492 
cloneInstance(UObject * instance) const493     virtual UObject* cloneInstance(UObject* instance) const {
494         return instance ? new UnicodeString(*(UnicodeString*)instance) : NULL;
495     }
496 };
497 
498 class TestStringService : public ICUService {
499     public:
createKey(const UnicodeString * id,UErrorCode & status) const500     ICUServiceKey* createKey(const UnicodeString* id, UErrorCode& status) const {
501         return LocaleKey::createWithCanonicalFallback(id, NULL, status); // no fallback locale
502     }
503 
createSimpleFactory(UObject * obj,const UnicodeString & id,UBool visible,UErrorCode &)504   virtual ICUServiceFactory* createSimpleFactory(UObject* obj, const UnicodeString& id, UBool visible, UErrorCode& /* status */)
505     {
506         UnicodeString* s;
507         if (obj && (s = dynamic_cast<UnicodeString*>(obj)) != NULL) {
508             return new SimpleFactory(s, id, visible);
509         }
510         return NULL;
511     }
512 
cloneInstance(UObject * instance) const513     virtual UObject* cloneInstance(UObject* instance) const {
514         return instance ? new UnicodeString(*(UnicodeString*)instance) : NULL;
515     }
516 };
517 
518 // this creates a string for any id, but doesn't report anything
519 class AnonymousStringFactory : public ICUServiceFactory
520 {
521     public:
create(const ICUServiceKey & key,const ICUService *,UErrorCode &) const522     virtual UObject* create(const ICUServiceKey& key, const ICUService* /* service */, UErrorCode& /* status */) const {
523         return new UnicodeString(key.getID());
524     }
525 
updateVisibleIDs(Hashtable &,UErrorCode &) const526     virtual void updateVisibleIDs(Hashtable& /*result*/, UErrorCode& /*status*/) const {
527         // do nothing
528     }
529 
getDisplayName(const UnicodeString &,const Locale &,UnicodeString & result) const530     virtual UnicodeString& getDisplayName(const UnicodeString& /*id*/, const Locale& /*locale*/, UnicodeString& result) const {
531         // do nothing
532         return result;
533     }
534 
getStaticClassID()535     static UClassID getStaticClassID() {
536         return (UClassID)&fgClassID;
537     }
538 
getDynamicClassID() const539     virtual UClassID getDynamicClassID() const {
540         return getStaticClassID();
541     }
542 
543     private:
544     static const char fgClassID;
545 };
546 
547 const char AnonymousStringFactory::fgClassID = '\0';
548 
549 class TestMultipleKeyStringFactory : public ICUServiceFactory {
550     UErrorCode _status;
551     UVector _ids;
552     UnicodeString _factoryID;
553 
554     public:
TestMultipleKeyStringFactory(const UnicodeString ids[],int32_t count,const UnicodeString & factoryID)555     TestMultipleKeyStringFactory(const UnicodeString ids[], int32_t count, const UnicodeString& factoryID)
556         : _status(U_ZERO_ERROR)
557         , _ids(uprv_deleteUObject, uhash_compareUnicodeString, count, _status)
558         , _factoryID(factoryID + ": ")
559     {
560         for (int i = 0; i < count; ++i) {
561             _ids.addElement(new UnicodeString(ids[i]), _status);
562         }
563     }
564 
~TestMultipleKeyStringFactory()565     ~TestMultipleKeyStringFactory() {
566     }
567 
create(const ICUServiceKey & key,const ICUService *,UErrorCode & status) const568     UObject* create(const ICUServiceKey& key, const ICUService* /* service */, UErrorCode& status) const {
569         if (U_FAILURE(status)) {
570         return NULL;
571         }
572         UnicodeString temp;
573         key.currentID(temp);
574         if (U_SUCCESS(_status)) {
575         if (_ids.contains(&temp)) {
576                 return new UnicodeString(_factoryID + temp);
577         }
578         } else {
579         status = _status;
580     }
581         return NULL;
582     }
583 
updateVisibleIDs(Hashtable & result,UErrorCode & status) const584     void updateVisibleIDs(Hashtable& result, UErrorCode& status) const {
585         if (U_SUCCESS(_status)) {
586             for (int32_t i = 0; i < _ids.size(); ++i) {
587                 result.put(*(UnicodeString*)_ids[i], (void*)this, status);
588             }
589         }
590     }
591 
getDisplayName(const UnicodeString & id,const Locale & locale,UnicodeString & result) const592     UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const {
593         if (U_SUCCESS(_status) && _ids.contains((void*)&id)) {
594             char buffer[128];
595             UErrorCode status = U_ZERO_ERROR;
596             int32_t len = id.extract(buffer, sizeof(buffer), NULL, status);
597             if (U_SUCCESS(status)) {
598                 if (len == sizeof(buffer)) {
599                     --len;
600                 }
601                 buffer[len] = 0;
602                 Locale loc = Locale::createFromName(buffer);
603                 loc.getDisplayName(locale, result);
604                 return result;
605             }
606         }
607         result.setToBogus(); // shouldn't happen
608         return result;
609     }
610 
getStaticClassID()611     static UClassID getStaticClassID() {
612         return (UClassID)&fgClassID;
613     }
614 
getDynamicClassID() const615     virtual UClassID getDynamicClassID() const {
616         return getStaticClassID();
617     }
618 
619     private:
620     static const char fgClassID;
621 };
622 
623 const char TestMultipleKeyStringFactory::fgClassID = '\0';
624 
625 void
testAPI_Two()626 ICUServiceTest::testAPI_Two()
627 {
628     UErrorCode status = U_ZERO_ERROR;
629     TestStringService service;
630     service.registerFactory(new AnonymousStringFactory(), status);
631 
632     // anonymous factory will still handle the id
633     {
634         UErrorCode status = U_ZERO_ERROR;
635         const UnicodeString en_US = "en_US";
636         UnicodeString* result = (UnicodeString*)service.get(en_US, status);
637         confirmEqual("21) locale", result, &en_US);
638         delete result;
639     }
640 
641     // still normalizes id
642     {
643         UErrorCode status = U_ZERO_ERROR;
644         const UnicodeString en_US_BAR = "en_US_BAR";
645         UnicodeString resultID;
646         UnicodeString* result = (UnicodeString*)service.get("EN_us_bar", &resultID, status);
647         confirmEqual("22) locale", &resultID, &en_US_BAR);
648         delete result;
649     }
650 
651     // we can override for particular ids
652     UnicodeString* singleton0 = new UnicodeString("Zero");
653     service.registerInstance(singleton0, "en_US_BAR", status);
654     {
655         UErrorCode status = U_ZERO_ERROR;
656         UnicodeString* result = (UnicodeString*)service.get("en_US_BAR", status);
657         confirmEqual("23) override super", result, singleton0);
658         delete result;
659     }
660 
661     // empty service should not recognize anything
662     service.reset();
663     {
664         UErrorCode status = U_ZERO_ERROR;
665         UnicodeString* result = (UnicodeString*)service.get("en_US", status);
666         confirmIdentical("24) empty", result, NULL);
667     }
668 
669     // create a custom multiple key factory
670     {
671         UnicodeString xids[] = {
672             "en_US_VALLEY_GIRL",
673             "en_US_VALLEY_BOY",
674             "en_US_SURFER_GAL",
675             "en_US_SURFER_DUDE"
676         };
677         int32_t count = UPRV_LENGTHOF(xids);
678 
679         ICUServiceFactory* f = new TestMultipleKeyStringFactory(xids, count, "Later");
680         service.registerFactory(f, status);
681     }
682 
683     // iterate over the visual ids returned by the multiple factory
684     {
685         UErrorCode status = U_ZERO_ERROR;
686         UVector ids(uprv_deleteUObject, uhash_compareUnicodeString, 0, status);
687         service.getVisibleIDs(ids, status);
688         for (int i = 0; i < ids.size(); ++i) {
689             const UnicodeString* id = (const UnicodeString*)ids[i];
690             UnicodeString* result = (UnicodeString*)service.get(*id, status);
691             if (result) {
692                 logln("  " + *id + " --> " + *result);
693                 delete result;
694             } else {
695                 errln("could not find " + *id);
696             }
697         }
698         // four visible ids
699         confirmIdentical("25) visible ids", ids.size(), 4);
700     }
701 
702     // iterate over the display names
703     {
704         UErrorCode status = U_ZERO_ERROR;
705         UVector names(status);
706         service.getDisplayNames(names, status);
707         for (int i = 0; i < names.size(); ++i) {
708             const StringPair* pair = (const StringPair*)names[i];
709             logln("  " + pair->displayName + " --> " + pair->id);
710         }
711         confirmIdentical("26) display names", names.size(), 4);
712     }
713 
714     // no valid display name
715     {
716         UnicodeString name;
717         service.getDisplayName("en_US_VALLEY_GEEK", name);
718         confirmBoolean("27) get display name", name.isBogus());
719     }
720 
721     {
722         UnicodeString name;
723         service.getDisplayName("en_US_SURFER_DUDE", name, Locale::getEnglish());
724         confirmStringsEqual("28) get display name", name, "English (United States, SURFER_DUDE)");
725     }
726 
727     // register another multiple factory
728     {
729         UnicodeString xids[] = {
730             "en_US_SURFER",
731             "en_US_SURFER_GAL",
732             "en_US_SILICON",
733             "en_US_SILICON_GEEK",
734         };
735         int32_t count = UPRV_LENGTHOF(xids);
736 
737         ICUServiceFactory* f = new TestMultipleKeyStringFactory(xids, count, "Rad dude");
738         service.registerFactory(f, status);
739     }
740 
741     // this time, we have seven display names
742     // Rad dude's surfer gal 'replaces' Later's surfer gal
743     {
744         UErrorCode status = U_ZERO_ERROR;
745         UVector names(status);
746         service.getDisplayNames(names, Locale("es"), status);
747         for (int i = 0; i < names.size(); ++i) {
748             const StringPair* pair = (const StringPair*)names[i];
749             logln("  " + pair->displayName + " --> " + pair->id);
750         }
751         confirmIdentical("29) display names", names.size(), 7);
752     }
753 
754     // we should get the display name corresponding to the actual id
755     // returned by the id we used.
756     {
757         UErrorCode status = U_ZERO_ERROR;
758         UnicodeString actualID;
759         UnicodeString id = "en_us_surfer_gal";
760         UnicodeString* gal = (UnicodeString*)service.get(id, &actualID, status);
761         if (gal != NULL) {
762             UnicodeString displayName;
763             logln("actual id: " + actualID);
764             service.getDisplayName(actualID, displayName, Locale::getEnglish());
765             logln("found actual: " + *gal + " with display name: " + displayName);
766             confirmBoolean("30) found display name for actual", !displayName.isBogus());
767 
768             service.getDisplayName(id, displayName, Locale::getEnglish());
769             logln("found actual: " + *gal + " with display name: " + displayName);
770             confirmBoolean("31) found display name for query", displayName.isBogus());
771 
772             delete gal;
773         } else {
774             errln("30) service could not find entry for " + id);
775         }
776     }
777 
778     // this should be handled by the 'dude' factory, since it overrides en_US_SURFER.
779     {
780         UErrorCode status = U_ZERO_ERROR;
781         UnicodeString actualID;
782         UnicodeString id = "en_US_SURFER_BOZO";
783         UnicodeString* bozo = (UnicodeString*)service.get(id, &actualID, status);
784         if (bozo != NULL) {
785             UnicodeString displayName;
786             service.getDisplayName(actualID, displayName, Locale::getEnglish());
787             logln("found actual: " + *bozo + " with display name: " + displayName);
788             confirmBoolean("32) found display name for actual", !displayName.isBogus());
789 
790             service.getDisplayName(id, displayName, Locale::getEnglish());
791             logln("found actual: " + *bozo + " with display name: " + displayName);
792             confirmBoolean("33) found display name for query", displayName.isBogus());
793 
794             delete bozo;
795         } else {
796             errln("32) service could not find entry for " + id);
797         }
798     }
799 
800     // certainly not default...
801     {
802         confirmBoolean("34) is default ", !service.isDefault());
803     }
804 
805     {
806         UErrorCode status = U_ZERO_ERROR;
807         UVector ids(uprv_deleteUObject, uhash_compareUnicodeString, 0, status);
808         service.getVisibleIDs(ids, status);
809         for (int i = 0; i < ids.size(); ++i) {
810             const UnicodeString* id = (const UnicodeString*)ids[i];
811             msgstr(*id + "? ", service.get(*id, status));
812         }
813 
814         logstr("valleygirl?  ", service.get("en_US_VALLEY_GIRL", status));
815         logstr("valleyboy?   ", service.get("en_US_VALLEY_BOY", status));
816         logstr("valleydude?  ", service.get("en_US_VALLEY_DUDE", status));
817         logstr("surfergirl?  ", service.get("en_US_SURFER_GIRL", status));
818     }
819 }
820 
821 
822 class CalifornioLanguageFactory : public ICUResourceBundleFactory
823 {
824     public:
825     static const char* californio; // = "en_US_CA";
826     static const char* valley; // = californio ## "_VALLEY";
827     static const char* surfer; // = californio ## "_SURFER";
828     static const char* geek; // = californio ## "_GEEK";
829     static Hashtable* supportedIDs; // = NULL;
830 
cleanup(void)831     static void cleanup(void) {
832       delete supportedIDs;
833       supportedIDs = NULL;
834     }
835 
getSupportedIDs(UErrorCode & status) const836     const Hashtable* getSupportedIDs(UErrorCode& status) const
837     {
838         if (supportedIDs == NULL) {
839             Hashtable* table = new Hashtable();
840             table->put(UnicodeString(californio), (void*)table, status);
841             table->put(UnicodeString(valley), (void*)table, status);
842             table->put(UnicodeString(surfer), (void*)table, status);
843             table->put(UnicodeString(geek), (void*)table, status);
844 
845             // not necessarily atomic, but this is a test...
846             supportedIDs = table;
847         }
848         return supportedIDs;
849     }
850 
getDisplayName(const UnicodeString & id,const Locale & locale,UnicodeString & result) const851     UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const
852     {
853         UnicodeString prefix = "";
854         UnicodeString suffix = "";
855         UnicodeString ls = locale.getName();
856         if (LocaleUtility::isFallbackOf(californio, ls)) {
857             if (!ls.caseCompare(valley, 0)) {
858                 prefix = "Like, you know, it's so totally ";
859             } else if (!ls.caseCompare(surfer, 0)) {
860                 prefix = "Dude, it's ";
861             } else if (!ls.caseCompare(geek, 0)) {
862                 prefix = "I'd estimate it is approximately ";
863             } else {
864                 prefix = "Huh?  Maybe ";
865             }
866         }
867         if (LocaleUtility::isFallbackOf(californio, id)) {
868             if (!id.caseCompare(valley, 0)) {
869                 suffix = "like the Valley, you know?  Let's go to the mall!";
870             } else if (!id.caseCompare(surfer, 0)) {
871                 suffix = "time to hit those gnarly waves, Dude!!!";
872             } else if (!id.caseCompare(geek, 0)) {
873                 suffix = "all systems go.  T-Minus 9, 8, 7...";
874             } else {
875                 suffix = "No Habla Englais";
876             }
877         } else {
878             suffix = ICUResourceBundleFactory::getDisplayName(id, locale, result);
879         }
880 
881         result = prefix + suffix;
882         return result;
883     }
884 };
885 
886 const char* CalifornioLanguageFactory::californio = "en_US_CA";
887 const char* CalifornioLanguageFactory::valley = "en_US_CA_VALLEY";
888 const char* CalifornioLanguageFactory::surfer = "en_US_CA_SURFER";
889 const char* CalifornioLanguageFactory::geek = "en_US_CA_GEEK";
890 Hashtable* CalifornioLanguageFactory::supportedIDs = NULL;
891 
892 void
testRBF()893 ICUServiceTest::testRBF()
894 {
895     // resource bundle factory.
896     UErrorCode status = U_ZERO_ERROR;
897     TestStringService service;
898     service.registerFactory(new ICUResourceBundleFactory(), status);
899 
900     // list all of the resources
901     {
902         UErrorCode status = U_ZERO_ERROR;
903         UVector ids(uprv_deleteUObject, uhash_compareUnicodeString, 0, status);
904         service.getVisibleIDs(ids, status);
905         logln("all visible ids:");
906         for (int i = 0; i < ids.size(); ++i) {
907             const UnicodeString* id = (const UnicodeString*)ids[i];
908             logln(*id);
909         }
910     }
911 
912     // get all the display names of these resources
913     // this should be fast since the display names were cached.
914     {
915         UErrorCode status = U_ZERO_ERROR;
916         UVector names(status);
917         service.getDisplayNames(names, Locale::getGermany(), status);
918         logln("service display names for de_DE");
919         for (int i = 0; i < names.size(); ++i) {
920             const StringPair* pair = (const StringPair*)names[i];
921             logln("  " + pair->displayName + " --> " + pair->id);
922         }
923     }
924 
925     service.registerFactory(new CalifornioLanguageFactory(), status);
926 
927     // get all the display names of these resources
928     {
929         logln("californio language factory:");
930         const char* idNames[] = {
931             CalifornioLanguageFactory::californio,
932             CalifornioLanguageFactory::valley,
933             CalifornioLanguageFactory::surfer,
934             CalifornioLanguageFactory::geek,
935         };
936         int32_t count = UPRV_LENGTHOF(idNames);
937 
938         for (int i = 0; i < count; ++i) {
939             logln(UnicodeString("\n  --- ") + idNames[i] + " ---");
940             {
941                 UErrorCode status = U_ZERO_ERROR;
942                 UVector names(status);
943                 service.getDisplayNames(names, idNames[i], status);
944                 for (int i = 0; i < names.size(); ++i) {
945                     const StringPair* pair = (const StringPair*)names[i];
946                     logln("  " + pair->displayName + " --> " + pair->id);
947                 }
948             }
949         }
950     }
951     CalifornioLanguageFactory::cleanup();
952 }
953 
954 class SimpleListener : public ServiceListener {
955     ICUServiceTest* _test;
956     UnicodeString _name;
957 
958     public:
SimpleListener(ICUServiceTest * test,const UnicodeString & name)959     SimpleListener(ICUServiceTest* test, const UnicodeString& name) : _test(test), _name(name) {}
960 
serviceChanged(const ICUService & service) const961     virtual void serviceChanged(const ICUService& service) const {
962         UnicodeString serviceName = "listener ";
963         serviceName.append(_name);
964         serviceName.append(" n++");
965         serviceName.append(" service changed: " );
966         service.getName(serviceName);
967         _test->logln(serviceName);
968     }
969 };
970 
971 void
testNotification()972 ICUServiceTest::testNotification()
973 {
974     SimpleListener one(this, "one");
975     SimpleListener two(this, "two");
976     {
977         UErrorCode status = U_ZERO_ERROR;
978 
979         logln("simple registration notification");
980         TestStringService ls;
981         ls.addListener(&one, status);
982         ls.addListener(&two, status);
983 
984         logln("registering foo... ");
985         ls.registerInstance(new UnicodeString("Foo"), "en_FOO", status);
986         logln("registering bar... ");
987         ls.registerInstance(new UnicodeString("Bar"), "en_BAR", status);
988         logln("getting foo...");
989         UnicodeString* result = (UnicodeString*)ls.get("en_FOO", status);
990         logln(*result);
991         delete result;
992 
993         logln("removing listener 2...");
994         ls.removeListener(&two, status);
995         logln("registering baz...");
996         ls.registerInstance(new UnicodeString("Baz"), "en_BAZ", status);
997         logln("removing listener 1");
998         ls.removeListener(&one, status);
999         logln("registering burp...");
1000         ls.registerInstance(new UnicodeString("Burp"), "en_BURP", status);
1001 
1002         // should only get one notification even if register multiple times
1003         logln("... trying multiple registration");
1004         ls.addListener(&one, status);
1005         ls.addListener(&one, status);
1006         ls.addListener(&one, status);
1007         ls.addListener(&two, status);
1008         ls.registerInstance(new UnicodeString("Foo"), "en_FOO", status);
1009         logln("... registered foo");
1010     }
1011 #if 0
1012     // same thread, so we can't callback within notification, unlike Java
1013     ServiceListener l3 = new ServiceListener() {
1014 private int n;
1015 public void serviceChanged(ICUService s) {
1016     logln("listener 3 report " + n++ + " service changed...");
1017     if (s.get("en_BOINK") == null) { // don't recurse on ourselves!!!
1018         logln("registering boink...");
1019         s.registerInstance("boink", "en_BOINK");
1020     }
1021 }
1022     };
1023     ls.addListener(l3);
1024     logln("registering boo...");
1025     ls.registerInstance("Boo", "en_BOO");
1026 #endif
1027 
1028     logln("...done");
1029 }
1030 
1031 class TestStringLocaleService : public ICULocaleService {
1032     public:
cloneInstance(UObject * instance) const1033     virtual UObject* cloneInstance(UObject* instance) const {
1034         return instance ? new UnicodeString(*(UnicodeString*)instance) : NULL;
1035     }
1036 };
1037 
testLocale()1038 void ICUServiceTest::testLocale() {
1039     UErrorCode status = U_ZERO_ERROR;
1040     TestStringLocaleService service;
1041 
1042     UnicodeString* root = new UnicodeString("root");
1043     UnicodeString* german = new UnicodeString("german");
1044     UnicodeString* germany = new UnicodeString("german_Germany");
1045     UnicodeString* japanese = new UnicodeString("japanese");
1046     UnicodeString* japan = new UnicodeString("japanese_Japan");
1047 
1048     service.registerInstance(root, "", status);
1049     service.registerInstance(german, "de", status);
1050     service.registerInstance(germany, Locale::getGermany(), status);
1051     service.registerInstance(japanese, (UnicodeString)"ja", TRUE, status);
1052     service.registerInstance(japan, Locale::getJapan(), status);
1053 
1054     {
1055         UErrorCode status = U_ZERO_ERROR;
1056         UnicodeString* target = (UnicodeString*)service.get("de_US", status);
1057         confirmEqual("test de_US", german, target);
1058         delete target;
1059     }
1060 
1061     {
1062         UErrorCode status = U_ZERO_ERROR;
1063         UnicodeString* target = (UnicodeString*)service.get("de_US", LocaleKey::KIND_ANY, status);
1064         confirmEqual("test de_US 2", german, target);
1065         delete target;
1066     }
1067 
1068     {
1069         UErrorCode status = U_ZERO_ERROR;
1070         UnicodeString* target = (UnicodeString*)service.get("de_US", 1234, status);
1071         confirmEqual("test de_US 3", german, target);
1072         delete target;
1073     }
1074 
1075     {
1076         UErrorCode status = U_ZERO_ERROR;
1077         Locale actualReturn;
1078         UnicodeString* target = (UnicodeString*)service.get("de_US", &actualReturn, status);
1079         confirmEqual("test de_US 5", german, target);
1080         confirmEqual("test de_US 6", &actualReturn, &Locale::getGerman());
1081         delete target;
1082     }
1083 
1084     {
1085         UErrorCode status = U_ZERO_ERROR;
1086         Locale actualReturn;
1087         UnicodeString* target = (UnicodeString*)service.get("de_US", LocaleKey::KIND_ANY, &actualReturn, status);
1088         confirmEqual("test de_US 7", &actualReturn, &Locale::getGerman());
1089         delete target;
1090     }
1091 
1092     {
1093         UErrorCode status = U_ZERO_ERROR;
1094         Locale actualReturn;
1095         UnicodeString* target = (UnicodeString*)service.get("de_US", 1234, &actualReturn, status);
1096         confirmEqual("test de_US 8", german, target);
1097         confirmEqual("test de_US 9", &actualReturn, &Locale::getGerman());
1098         delete target;
1099     }
1100 
1101     UnicodeString* one = new UnicodeString("one/de_US");
1102     UnicodeString* two = new UnicodeString("two/de_US");
1103 
1104     service.registerInstance(one, Locale("de_US"), 1, status);
1105     service.registerInstance(two, Locale("de_US"), 2, status);
1106 
1107     {
1108         UErrorCode status = U_ZERO_ERROR;
1109         UnicodeString* target = (UnicodeString*)service.get("de_US", 1, status);
1110         confirmEqual("test de_US kind 1", one, target);
1111         delete target;
1112     }
1113 
1114     {
1115         UErrorCode status = U_ZERO_ERROR;
1116         UnicodeString* target = (UnicodeString*)service.get("de_US", 2, status);
1117         confirmEqual("test de_US kind 2", two, target);
1118         delete target;
1119     }
1120 
1121     {
1122         UErrorCode status = U_ZERO_ERROR;
1123         UnicodeString* target = (UnicodeString*)service.get("de_US", status);
1124         confirmEqual("test de_US kind 3", german, target);
1125         delete target;
1126     }
1127 
1128     {
1129         UErrorCode status = U_ZERO_ERROR;
1130         UnicodeString english = "en";
1131         Locale localeResult;
1132         UnicodeString result;
1133         LocaleKey* lkey = LocaleKey::createWithCanonicalFallback(&english, NULL, 1234, status);
1134         logln("lkey prefix: " + lkey->prefix(result));
1135         result.remove();
1136         logln("lkey descriptor: " + lkey->currentDescriptor(result));
1137         result.remove();
1138         logln(UnicodeString("lkey current locale: ") + lkey->currentLocale(localeResult).getName());
1139         result.remove();
1140 
1141         lkey->fallback();
1142         logln("lkey descriptor 2: " + lkey->currentDescriptor(result));
1143         result.remove();
1144 
1145         lkey->fallback();
1146         logln("lkey descriptor 3: " + lkey->currentDescriptor(result));
1147         result.remove();
1148         delete lkey; // tentatively weiv
1149     }
1150 
1151     {
1152         UErrorCode status = U_ZERO_ERROR;
1153         UnicodeString* target = (UnicodeString*)service.get("za_PPP", status);
1154         confirmEqual("test zappp", root, target);
1155         delete target;
1156     }
1157 
1158     Locale loc = Locale::getDefault();
1159     Locale::setDefault(Locale::getJapanese(), status);
1160     {
1161         UErrorCode status = U_ZERO_ERROR;
1162         UnicodeString* target = (UnicodeString*)service.get("za_PPP", status);
1163         confirmEqual("test with ja locale", japanese, target);
1164         delete target;
1165     }
1166 
1167     {
1168         UErrorCode status = U_ZERO_ERROR;
1169         UVector ids(uprv_deleteUObject, uhash_compareUnicodeString, 0, status);
1170         service.getVisibleIDs(ids, status);
1171         logln("all visible ids:");
1172         for (int i = 0; i < ids.size(); ++i) {
1173             const UnicodeString* id = (const UnicodeString*)ids[i];
1174             logln(*id);
1175         }
1176     }
1177 
1178     Locale::setDefault(loc, status);
1179     {
1180         UErrorCode status = U_ZERO_ERROR;
1181         UVector ids(uprv_deleteUObject, uhash_compareUnicodeString, 0, status);
1182         service.getVisibleIDs(ids, status);
1183         logln("all visible ids:");
1184         for (int i = 0; i < ids.size(); ++i) {
1185             const UnicodeString* id = (const UnicodeString*)ids[i];
1186             logln(*id);
1187         }
1188     }
1189 
1190     {
1191         UErrorCode status = U_ZERO_ERROR;
1192         UnicodeString* target = (UnicodeString*)service.get("za_PPP", status);
1193         confirmEqual("test with en locale", root, target);
1194         delete target;
1195     }
1196 
1197     {
1198         UErrorCode status = U_ZERO_ERROR;
1199         StringEnumeration* locales = service.getAvailableLocales();
1200         if (locales) {
1201             confirmIdentical("test available locales", locales->count(status), 6);
1202             logln("locales: ");
1203             {
1204                 const char* p;
1205                 while ((p = locales->next(NULL, status))) {
1206                     logln(p);
1207                 }
1208             }
1209             logln(" ");
1210             delete locales;
1211         } else {
1212             errln("could not create available locales");
1213         }
1214     }
1215 }
1216 
1217 class WrapFactory : public ICUServiceFactory {
1218     public:
getGreetingID()1219     static const UnicodeString& getGreetingID() {
1220       if (greetingID == NULL) {
1221     greetingID = new UnicodeString("greeting");
1222       }
1223       return *greetingID;
1224     }
1225 
cleanup()1226   static void cleanup() {
1227     delete greetingID;
1228     greetingID = NULL;
1229   }
1230 
create(const ICUServiceKey & key,const ICUService * service,UErrorCode & status) const1231     UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const {
1232         if (U_SUCCESS(status)) {
1233             UnicodeString temp;
1234             if (key.currentID(temp).compare(getGreetingID()) == 0) {
1235                 UnicodeString* previous = (UnicodeString*)service->getKey((ICUServiceKey&)key, NULL, this, status);
1236                 if (previous) {
1237                     previous->insert(0, "A different greeting: \"");
1238                     previous->append("\"");
1239                     return previous;
1240                 }
1241             }
1242         }
1243         return NULL;
1244     }
1245 
updateVisibleIDs(Hashtable & result,UErrorCode & status) const1246     void updateVisibleIDs(Hashtable& result, UErrorCode& status) const {
1247         if (U_SUCCESS(status)) {
1248             result.put("greeting", (void*)this, status);
1249         }
1250     }
1251 
getDisplayName(const UnicodeString & id,const Locale &,UnicodeString & result) const1252     UnicodeString& getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const {
1253         result.append("wrap '");
1254         result.append(id);
1255         result.append("'");
1256         return result;
1257     }
1258 
1259     /**
1260      * UObject boilerplate.
1261      */
getStaticClassID()1262     static UClassID getStaticClassID() {
1263         return (UClassID)&fgClassID;
1264     }
1265 
getDynamicClassID() const1266     virtual UClassID getDynamicClassID() const {
1267         return getStaticClassID();
1268     }
1269 
1270     private:
1271     static const char fgClassID;
1272     static UnicodeString* greetingID;
1273 };
1274 
1275 UnicodeString* WrapFactory::greetingID = NULL;
1276 const char WrapFactory::fgClassID = '\0';
1277 
1278 void
testWrapFactory()1279 ICUServiceTest::testWrapFactory()
1280 {
1281     UnicodeString* greeting = new UnicodeString("Hello There");
1282     UnicodeString greetingID = "greeting";
1283     UErrorCode status = U_ZERO_ERROR;
1284     TestStringService service;
1285     service.registerInstance(greeting, greetingID, status);
1286 
1287     {
1288         UErrorCode status = U_ZERO_ERROR;
1289         UnicodeString* result = (UnicodeString*)service.get(greetingID, status);
1290         if (result) {
1291             logln("test one: " + *result);
1292             delete result;
1293         }
1294     }
1295 
1296     service.registerFactory(new WrapFactory(), status);
1297     {
1298         UErrorCode status = U_ZERO_ERROR;
1299         UnicodeString* result = (UnicodeString*)service.get(greetingID, status);
1300         UnicodeString target = "A different greeting: \"Hello There\"";
1301         confirmEqual("wrap test: ", result, &target);
1302         delete result;
1303     }
1304 
1305     WrapFactory::cleanup();
1306 }
1307 
1308   // misc coverage tests
testCoverage()1309 void ICUServiceTest::testCoverage()
1310 {
1311   // ICUServiceKey
1312   {
1313     UnicodeString temp;
1314     ICUServiceKey key("foobar");
1315     logln("ID: " + key.getID());
1316     logln("canonicalID: " + key.canonicalID(temp));
1317     logln("currentID: " + key.currentID(temp.remove()));
1318     logln("has fallback: " + UnicodeString(key.fallback() ? "true" : "false"));
1319 
1320     if (key.getDynamicClassID() != ICUServiceKey::getStaticClassID()) {
1321       errln("service key rtt failed.");
1322     }
1323   }
1324 
1325   // SimpleFactory
1326   {
1327     UErrorCode status = U_ZERO_ERROR;
1328 
1329     UnicodeString* obj = new UnicodeString("An Object");
1330     SimpleFactory* sf = new SimpleFactory(obj, "object");
1331 
1332     UnicodeString temp;
1333     logln(sf->getDisplayName("object", Locale::getDefault(), temp));
1334 
1335     if (sf->getDynamicClassID() != SimpleFactory::getStaticClassID()) {
1336       errln("simple factory rtti failed.");
1337     }
1338 
1339     // ICUService
1340         {
1341                 TestStringService service;
1342                 service.registerFactory(sf,     status);
1343 
1344                 {
1345                         UnicodeString* result   = (UnicodeString*)service.get("object", status);
1346                         if (result) {
1347                                 logln("object is: "     + *result);
1348                                 delete result;
1349                         }       else {
1350                                 errln("could not get object");
1351                         }
1352                 }
1353         }
1354   }
1355 
1356   // ICUServiceKey
1357   {
1358       UErrorCode status = U_ZERO_ERROR;
1359           UnicodeString* howdy = new UnicodeString("Howdy");
1360 
1361           TestStringSimpleKeyService service;
1362           service.registerInstance(howdy, "Greetings", status);
1363           {
1364                   UnicodeString* result = (UnicodeString*)service.get("Greetings",      status);
1365                   if (result) {
1366                           logln("object is: "   + *result);
1367                           delete result;
1368                   }     else {
1369                           errln("could not get object");
1370                   }
1371           }
1372 
1373       UVector ids(uprv_deleteUObject, uhash_compareUnicodeString, status);
1374           // yuck, this is awkward to use.  All because we pass null in an overload.
1375           // TODO: change this.
1376           UnicodeString str("Greet");
1377       service.getVisibleIDs(ids, &str, status);
1378       confirmIdentical("no fallback of greet", ids.size(), 0);
1379   }
1380 
1381   // ICULocaleService
1382 
1383   // LocaleKey
1384   {
1385     UnicodeString primary("en_US");
1386     UnicodeString fallback("ja_JP");
1387     UErrorCode status = U_ZERO_ERROR;
1388     LocaleKey* key = LocaleKey::createWithCanonicalFallback(&primary, &fallback, status);
1389 
1390     if (key->getDynamicClassID() != LocaleKey::getStaticClassID()) {
1391       errln("localekey rtti error");
1392     }
1393 
1394     if (!key->isFallbackOf("en_US_FOOBAR")) {
1395       errln("localekey should be fallback for en_US_FOOBAR");
1396     }
1397     if (!key->isFallbackOf("en_US")) {
1398       errln("localekey should be fallback for en_US");
1399     }
1400     if (key->isFallbackOf("en")) {
1401       errln("localekey should not be fallback for en");
1402     }
1403 
1404     do {
1405       Locale loc;
1406       logln(UnicodeString("current locale: ") + key->currentLocale(loc).getName());
1407       logln(UnicodeString("canonical locale: ") + key->canonicalLocale(loc).getName());
1408       logln(UnicodeString("is fallback of en: ") + (key->isFallbackOf("en") ? "true" : " false"));
1409     } while (key->fallback());
1410     delete key;
1411 
1412     // LocaleKeyFactory
1413     key = LocaleKey::createWithCanonicalFallback(&primary, &fallback, status);
1414 
1415     UnicodeString result;
1416     LKFSubclass lkf(TRUE); // empty
1417     Hashtable table;
1418 
1419     UObject *obj = lkf.create(*key, NULL, status);
1420     logln("obj: " + UnicodeString(obj ? "obj" : "null"));
1421     logln(lkf.getDisplayName("en_US", Locale::getDefault(), result));
1422     lkf.updateVisibleIDs(table, status);
1423     delete obj;
1424     if (table.count() != 1) {
1425       errln("visible IDs does not contain en_US");
1426     }
1427 
1428     LKFSubclass invisibleLKF(FALSE);
1429     obj = lkf.create(*key, NULL, status);
1430     logln("obj: " + UnicodeString(obj ? "obj" : "null"));
1431     logln(invisibleLKF.getDisplayName("en_US", Locale::getDefault(), result.remove()));
1432     invisibleLKF.updateVisibleIDs(table, status);
1433     if (table.count() != 0) {
1434       errln("visible IDs contains en_US");
1435     }
1436     delete obj;
1437     delete key;
1438 
1439         key = LocaleKey::createWithCanonicalFallback(&primary, &fallback, 123, status);
1440         if (U_SUCCESS(status)) {
1441                 UnicodeString str;
1442                 key->currentDescriptor(str);
1443                 key->parsePrefix(str);
1444                 if (str != "123") {
1445                         errln("did not get expected prefix");
1446                 }
1447                 delete key;
1448         }
1449 
1450         // coverage, getSupportedIDs is either overridden or the calling method is
1451         LKFSubclass0 lkFactory;
1452         Hashtable table0;
1453         lkFactory.updateVisibleIDs(table0, status);
1454         if (table0.count() != 0) {
1455                 errln("LKF returned non-empty hashtable");
1456         }
1457 
1458 
1459         // ResourceBundleFactory
1460     key = LocaleKey::createWithCanonicalFallback(&primary, &fallback, status);
1461         ICUResourceBundleFactory rbf;
1462         UObject* icurb = rbf.create(*key, NULL, status);
1463         if (icurb != NULL) {
1464                 logln("got resource bundle for key");
1465                 delete icurb;
1466         }
1467         delete key;
1468   }
1469 
1470  #if 0
1471  // ICUNotifier
1472   ICUNotifier nf = new ICUNSubclass();
1473   try {
1474     nf.addListener(null);
1475     errln("added null listener");
1476   }
1477   catch (NullPointerException e) {
1478     logln(e.getMessage());
1479   }
1480   catch (Exception e) {
1481     errln("got wrong exception");
1482   }
1483 
1484   try {
1485     nf.addListener(new WrongListener());
1486     errln("added wrong listener");
1487   }
1488   catch (InternalError e) {
1489     logln(e.getMessage());
1490   }
1491   catch (Exception e) {
1492     errln("got wrong exception");
1493   }
1494 
1495   try {
1496     nf.removeListener(null);
1497     errln("removed null listener");
1498   }
1499   catch (NullPointerException e) {
1500     logln(e.getMessage());
1501   }
1502   catch (Exception e) {
1503     errln("got wrong exception");
1504   }
1505 
1506   nf.removeListener(new MyListener());
1507   nf.notifyChanged();
1508   nf.addListener(new MyListener());
1509   nf.removeListener(new MyListener());
1510 #endif
1511 }
1512 
1513 
1514 /* !UCONFIG_NO_SERVICE */
1515 #endif
1516 
1517 
1518