Home | History | Annotate | Download | only in util
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 /**
      4  *******************************************************************************
      5  * Copyright (C) 2001-2013, International Business Machines Corporation and    *
      6  * others. All Rights Reserved.                                                *
      7  *******************************************************************************
      8  */
      9 package com.ibm.icu.dev.test.util;
     10 
     11 import java.text.Collator;
     12 import java.util.Arrays;
     13 import java.util.Collections;
     14 import java.util.Comparator;
     15 import java.util.EventListener;
     16 import java.util.HashMap;
     17 import java.util.HashSet;
     18 import java.util.Iterator;
     19 import java.util.List;
     20 import java.util.Map;
     21 import java.util.Map.Entry;
     22 import java.util.Set;
     23 import java.util.SortedMap;
     24 
     25 import org.junit.Test;
     26 import org.junit.runner.RunWith;
     27 import org.junit.runners.JUnit4;
     28 
     29 import com.ibm.icu.dev.test.TestFmwk;
     30 import com.ibm.icu.impl.ICULocaleService;
     31 import com.ibm.icu.impl.ICULocaleService.ICUResourceBundleFactory;
     32 import com.ibm.icu.impl.ICULocaleService.LocaleKey;
     33 import com.ibm.icu.impl.ICULocaleService.LocaleKeyFactory;
     34 import com.ibm.icu.impl.ICUNotifier;
     35 import com.ibm.icu.impl.ICURWLock;
     36 import com.ibm.icu.impl.ICUResourceBundle;
     37 import com.ibm.icu.impl.ICUService;
     38 import com.ibm.icu.impl.ICUService.Factory;
     39 import com.ibm.icu.impl.ICUService.Key;
     40 import com.ibm.icu.impl.ICUService.ServiceListener;
     41 import com.ibm.icu.impl.ICUService.SimpleFactory;
     42 import com.ibm.icu.impl.LocaleUtility;
     43 import com.ibm.icu.util.ULocale;
     44 
     45 @RunWith(JUnit4.class)
     46 public class ICUServiceTest extends TestFmwk
     47 {
     48     private String lrmsg(String message, Object lhs, Object rhs) {
     49     return message + " lhs: " + lhs + " rhs: " + rhs;
     50     }
     51 
     52     public void confirmBoolean(String message, boolean val) {
     53     msg(message, val ? LOG : ERR, !val, true);
     54     }
     55 
     56     public void confirmEqual(String message, Object lhs, Object rhs) {
     57         msg(lrmsg(message, lhs, rhs), (lhs == null ? rhs == null : lhs.equals(rhs)) ? LOG : ERR, true, true);
     58     }
     59 
     60     public void confirmIdentical(String message, Object lhs, Object rhs) {
     61     msg(lrmsg(message, lhs, rhs), lhs == rhs ? LOG : ERR, true, true);
     62     }
     63 
     64     public void confirmIdentical(String message, int lhs, int rhs) {
     65     msg(message + " lhs: " + lhs + " rhs: " + rhs, lhs == rhs ? LOG : ERR, true, true);
     66     }
     67 
     68     /**
     69      * Convenience override of getDisplayNames(ULocale, Comparator, String) that
     70      * uses the current default ULocale as the locale, the default collator for
     71      * the locale as the comparator to sort the display names, and null for
     72      * the matchID.
     73      */
     74     public SortedMap getDisplayNames(ICUService service) {
     75         ULocale locale = ULocale.getDefault();
     76         Collator col = Collator.getInstance(locale.toLocale());
     77         return service.getDisplayNames(locale, col, null);
     78     }
     79 
     80     /**
     81      * Convenience override of getDisplayNames(ULocale, Comparator, String) that
     82      * uses the default collator for the locale as the comparator to
     83      * sort the display names, and null for the matchID.
     84      */
     85     public SortedMap getDisplayNames(ICUService service, ULocale locale) {
     86         Collator col = Collator.getInstance(locale.toLocale());
     87         return service.getDisplayNames(locale, col, null);
     88     }
     89     /**
     90      * Convenience override of getDisplayNames(ULocale, Comparator, String) that
     91      * uses the default collator for the locale as the comparator to
     92      * sort the display names.
     93      */
     94     public SortedMap getDisplayNames(ICUService service, ULocale locale, String matchID) {
     95         Collator col = Collator.getInstance(locale.toLocale());
     96         return service.getDisplayNames(locale, col, matchID);
     97     }
     98 
     99     // use locale keys
    100     static final class TestService extends ICUService {
    101         public TestService() {
    102             super("Test Service");
    103         }
    104 
    105     @Override
    106     public Key createKey(String id) {
    107         return LocaleKey.createWithCanonicalFallback(id, null); // no fallback locale
    108     }
    109     }
    110 
    111     @Test
    112     public void TestAPI() {
    113     // create a service using locale keys,
    114     ICUService service = new TestService();
    115 
    116         logln("service name:" + service.getName());
    117 
    118     // register an object with one locale,
    119     // search for an object with a more specific locale
    120     // should return the original object
    121     Integer singleton0 = new Integer(0);
    122     service.registerObject(singleton0, "en_US");
    123     Object result = service.get("en_US_FOO");
    124     confirmIdentical("1) en_US_FOO -> en_US", result, singleton0);
    125 
    126     // register a new object with the more specific locale
    127     // search for an object with that locale
    128     // should return the new object
    129     Integer singleton1 = new Integer(1);
    130     service.registerObject(singleton1, "en_US_FOO");
    131     result = service.get("en_US_FOO");
    132     confirmIdentical("2) en_US_FOO -> en_US_FOO", result, singleton1);
    133 
    134     // search for an object that falls back to the first registered locale
    135     result = service.get("en_US_BAR");
    136     confirmIdentical("3) en_US_BAR -> en_US", result, singleton0);
    137 
    138     // get a list of the factories, should be two
    139     List factories = service.factories();
    140     confirmIdentical("4) factory size", factories.size(), 2);
    141 
    142     // register a new object with yet another locale
    143     // original factory list is unchanged
    144     Integer singleton2 = new Integer(2);
    145     service.registerObject(singleton2, "en");
    146     confirmIdentical("5) factory size", factories.size(), 2);
    147 
    148     // search for an object with the new locale
    149     // stack of factories is now en, en_US_FOO, en_US
    150     // search for en_US should still find en_US object
    151     result = service.get("en_US_BAR");
    152     confirmIdentical("6) en_US_BAR -> en_US", result, singleton0);
    153 
    154     // register a new object with an old id, should hide earlier factory using this id, but leave it there
    155     Integer singleton3 = new Integer(3);
    156     service.registerObject(singleton3, "en_US");
    157     factories = service.factories();
    158     confirmIdentical("9) factory size", factories.size(), 4);
    159 
    160     // should get data from that new factory
    161     result = service.get("en_US_BAR");
    162     confirmIdentical("10) en_US_BAR -> (3)", result, singleton3);
    163 
    164     // remove new factory
    165     // should have fewer factories again
    166     service.unregisterFactory((Factory)factories.get(0));
    167     factories = service.factories();
    168     confirmIdentical("11) factory size", factories.size(), 3);
    169 
    170     // should get original data again after remove factory
    171     result = service.get("en_US_BAR");
    172     confirmIdentical("12) en_US_BAR -> 0", result, singleton0);
    173 
    174     // shouldn't find unregistered ids
    175     result = service.get("foo");
    176     confirmIdentical("13) foo -> null", result, null);
    177 
    178     // should find non-canonical strings
    179     String[] resultID = new String[1];
    180     result = service.get("EN_us_fOo", resultID);
    181     confirmEqual("14) find non-canonical", resultID[0], "en_US_FOO");
    182 
    183     // should be able to register non-canonical strings and get them canonicalized
    184     service.registerObject(singleton3, "eN_ca_dUde");
    185     result = service.get("En_Ca_DuDe", resultID);
    186     confirmEqual("15) register non-canonical", resultID[0], "en_CA_DUDE");
    187 
    188     // should be able to register invisible factories, these will not
    189     // be visible by default, but if you know the secret password you
    190     // can still access these services...
    191     Integer singleton4 = new Integer(4);
    192     service.registerObject(singleton4, "en_US_BAR", false);
    193     result = service.get("en_US_BAR");
    194     confirmIdentical("17) get invisible", result, singleton4);
    195 
    196     // should not be able to locate invisible services
    197     Set ids = service.getVisibleIDs();
    198     confirmBoolean("18) find invisible", !ids.contains("en_US_BAR"));
    199 
    200     service.reset();
    201     // an anonymous factory than handles all ids
    202     {
    203         Factory factory = new Factory() {
    204             @Override
    205             public Object create(Key key, ICUService unusedService) {
    206                 return new ULocale(key.currentID());
    207             }
    208 
    209             @Override
    210             public void updateVisibleIDs(Map unusedResult) {
    211             }
    212 
    213             @Override
    214             public String getDisplayName(String id, ULocale l) {
    215                 return null;
    216             }
    217         };
    218         service.registerFactory(factory);
    219 
    220         // anonymous factory will still handle the id
    221         result = service.get(ULocale.US.toString());
    222         confirmEqual("21) locale", result, ULocale.US);
    223 
    224         // still normalizes id
    225         result = service.get("EN_US_BAR");
    226         confirmEqual("22) locale", result, new ULocale("en_US_BAR"));
    227 
    228         // we can override for particular ids
    229         service.registerObject(singleton3, "en_US_BAR");
    230         result = service.get("en_US_BAR");
    231         confirmIdentical("23) override super", result, singleton3);
    232 
    233     }
    234 
    235     // empty service should not recognize anything
    236     service.reset();
    237     result = service.get("en_US");
    238     confirmIdentical("24) empty", result, null);
    239 
    240     // create a custom multiple key factory
    241     {
    242         String[] xids = { "en_US_VALLEY_GIRL",
    243                   "en_US_VALLEY_BOY",
    244                   "en_US_SURFER_GAL",
    245                   "en_US_SURFER_DUDE"
    246         };
    247         service.registerFactory(new TestLocaleKeyFactory(xids, "Later"));
    248     }
    249 
    250     // iterate over the visual ids returned by the multiple factory
    251     {
    252         Set vids = service.getVisibleIDs();
    253         Iterator iter = vids.iterator();
    254         int count = 0;
    255         while (iter.hasNext()) {
    256         ++count;
    257                 String id = (String)iter.next();
    258         logln("  " + id + " --> " + service.get(id));
    259         }
    260         // four visible ids
    261         confirmIdentical("25) visible ids", count, 4);
    262     }
    263 
    264     // iterate over the display names
    265     {
    266         Map dids = getDisplayNames(service, ULocale.GERMANY);
    267         Iterator iter = dids.entrySet().iterator();
    268         int count = 0;
    269         while (iter.hasNext()) {
    270         ++count;
    271         Entry e = (Entry)iter.next();
    272         logln("  " + e.getKey() + " -- > " + e.getValue());
    273         }
    274         // four display names, in german
    275         confirmIdentical("26) display names", count, 4);
    276     }
    277 
    278     // no valid display name
    279     confirmIdentical("27) get display name", service.getDisplayName("en_US_VALLEY_GEEK"), null);
    280 
    281     {
    282         String name = service.getDisplayName("en_US_SURFER_DUDE", ULocale.US);
    283         confirmEqual("28) get display name", name, "English (United States, SURFER_DUDE)");
    284     }
    285 
    286     // register another multiple factory
    287     {
    288         String[] xids = {
    289         "en_US_SURFER", "en_US_SURFER_GAL", "en_US_SILICON", "en_US_SILICON_GEEK"
    290         };
    291         service.registerFactory(new TestLocaleKeyFactory(xids, "Rad dude"));
    292     }
    293 
    294     // this time, we have seven display names
    295         // Rad dude's surfer gal 'replaces' later's surfer gal
    296     {
    297         Map dids = getDisplayNames(service);
    298         Iterator iter = dids.entrySet().iterator();
    299         int count = 0;
    300         while (iter.hasNext()) {
    301         ++count;
    302         Entry e = (Entry)iter.next();
    303         logln("  " + e.getKey() + " --> " + e.getValue());
    304         }
    305         // seven display names, in spanish
    306         confirmIdentical("29) display names", count, 7);
    307     }
    308 
    309     // we should get the display name corresponding to the actual id
    310     // returned by the id we used.
    311     {
    312         String[] actualID = new String[1];
    313         String id = "en_us_surfer_gal";
    314         String gal = (String)service.get(id, actualID);
    315         if (gal != null) {
    316                 logln("actual id: " + actualID[0]);
    317         String displayName = service.getDisplayName(actualID[0], ULocale.US);
    318         logln("found actual: " + gal + " with display name: " + displayName);
    319         confirmBoolean("30) found display name for actual", displayName != null);
    320 
    321         displayName = service.getDisplayName(id, ULocale.US);
    322         logln("found query: " + gal + " with display name: " + displayName);
    323         // this is no longer a bug, we want to return display names for anything
    324         // that a factory handles.  since we handle it, we should return a display
    325         // name.  see jb3549
    326         // confirmBoolean("31) found display name for query", displayName == null);
    327         } else {
    328         errln("30) service could not find entry for " + id);
    329         }
    330 
    331             // this should be handled by the 'dude' factory, since it overrides en_US_SURFER.
    332         id = "en_US_SURFER_BOZO";
    333         String bozo = (String)service.get(id, actualID);
    334         if (bozo != null) {
    335         String displayName = service.getDisplayName(actualID[0], ULocale.US);
    336         logln("found actual: " + bozo + " with display name: " + displayName);
    337         confirmBoolean("32) found display name for actual", displayName != null);
    338 
    339         displayName = service.getDisplayName(id, ULocale.US);
    340         logln("found actual: " + bozo + " with display name: " + displayName);
    341         // see above and jb3549
    342         // confirmBoolean("33) found display name for query", displayName == null);
    343         } else {
    344         errln("32) service could not find entry for " + id);
    345         }
    346 
    347             confirmBoolean("34) is default ", !service.isDefault());
    348     }
    349 
    350         /*
    351       // disallow hiding for now
    352 
    353       // hiding factory should obscure 'sublocales'
    354       {
    355       String[] xids = {
    356       "en_US_VALLEY", "en_US_SILICON"
    357       };
    358       service.registerFactory(new TestHidingFactory(xids, "hiding"));
    359       }
    360 
    361       {
    362       Map dids = service.getDisplayNames();
    363       Iterator iter = dids.entrySet().iterator();
    364       int count = 0;
    365       while (iter.hasNext()) {
    366       ++count;
    367       Entry e = (Entry)iter.next();
    368       logln("  " + e.getKey() + " -- > " + e.getValue());
    369       }
    370       confirmIdentical("35) hiding factory", count, 5);
    371       }
    372         */
    373 
    374     {
    375         Set xids = service.getVisibleIDs();
    376         Iterator iter = xids.iterator();
    377         while (iter.hasNext()) {
    378         String xid = (String)iter.next();
    379         logln(xid + "?  " + service.get(xid));
    380         }
    381 
    382         logln("valleygirl?  " + service.get("en_US_VALLEY_GIRL"));
    383         logln("valleyboy?   " + service.get("en_US_VALLEY_BOY"));
    384         logln("valleydude?  " + service.get("en_US_VALLEY_DUDE"));
    385         logln("surfergirl?  " + service.get("en_US_SURFER_GIRL"));
    386     }
    387 
    388     // resource bundle factory.
    389     service.reset();
    390     service.registerFactory(new ICUResourceBundleFactory());
    391 
    392     // list all of the resources
    393     {
    394             logln("all visible ids: " + service.getVisibleIDs());
    395             /*
    396           Set xids = service.getVisibleIDs();
    397           StringBuffer buf = new StringBuffer("{");
    398           boolean notfirst = false;
    399           Iterator iter = xids.iterator();
    400           while (iter.hasNext()) {
    401           String xid = (String)iter.next();
    402           if (notfirst) {
    403           buf.append(", ");
    404           } else {
    405           notfirst = true;
    406           }
    407           buf.append(xid);
    408           }
    409           buf.append("}");
    410           logln(buf.toString());
    411             */
    412     }
    413 
    414         // list only the resources for es, default locale
    415         // since we're using the default Key, only "es" is matched
    416         {
    417             logln("visible ids for es locale: " + service.getVisibleIDs("es"));
    418         }
    419 
    420         // list only the spanish display names for es, spanish collation order
    421         // since we're using the default Key, only "es" is matched
    422         {
    423             logln("display names: " + getDisplayNames(service, new ULocale("es"), "es"));
    424         }
    425 
    426         // list the display names in reverse order
    427         {
    428             logln("display names in reverse order: " +
    429                   service.getDisplayNames(ULocale.US, new Comparator() {
    430                           @Override
    431                         public int compare(Object lhs, Object rhs) {
    432                               return -String.CASE_INSENSITIVE_ORDER.compare((String)lhs, (String)rhs);
    433                           }
    434                       }));
    435         }
    436 
    437     // get all the display names of these resources
    438     // this should be fast since the display names were cached.
    439     {
    440             logln("service display names for de_DE");
    441         Map names = getDisplayNames(service, new ULocale("de_DE"));
    442         StringBuffer buf = new StringBuffer("{");
    443         Iterator iter = names.entrySet().iterator();
    444         while (iter.hasNext()) {
    445         Entry e = (Entry)iter.next();
    446         String name = (String)e.getKey();
    447         String id = (String)e.getValue();
    448         buf.append("\n   " + name + " --> " + id);
    449         }
    450         buf.append("\n}");
    451         logln(buf.toString());
    452     }
    453 
    454         CalifornioLanguageFactory califactory = new CalifornioLanguageFactory();
    455         service.registerFactory(califactory);
    456     // get all the display names of these resources
    457     {
    458             logln("californio language factory");
    459         StringBuffer buf = new StringBuffer("{");
    460             String[] idNames = {
    461                 CalifornioLanguageFactory.californio,
    462         CalifornioLanguageFactory.valley,
    463         CalifornioLanguageFactory.surfer,
    464         CalifornioLanguageFactory.geek
    465             };
    466             for (int i = 0; i < idNames.length; ++i) {
    467                 String idName = idNames[i];
    468                 buf.append("\n  --- " + idName + " ---");
    469                 Map names = getDisplayNames(service, new ULocale(idName));
    470                 Iterator iter = names.entrySet().iterator();
    471                 while (iter.hasNext()) {
    472                     Entry e = (Entry)iter.next();
    473                     String name = (String)e.getKey();
    474                     String id = (String)e.getValue();
    475                     buf.append("\n    " + name + " --> " + id);
    476                 }
    477         }
    478         buf.append("\n}");
    479         logln(buf.toString());
    480     }
    481 
    482     // test notification
    483     // simple registration
    484     {
    485             logln("simple registration notification");
    486         ICULocaleService ls = new ICULocaleService();
    487         ServiceListener l1 = new ServiceListener() {
    488             private int n;
    489             @Override
    490             public void serviceChanged(ICUService s) {
    491             logln("listener 1 report " + n++ + " service changed: " + s);
    492             }
    493         };
    494         ls.addListener(l1);
    495         ServiceListener l2 = new ServiceListener() {
    496             private int n;
    497             @Override
    498             public void serviceChanged(ICUService s) {
    499             logln("listener 2 report " + n++ + " service changed: " + s);
    500             }
    501         };
    502         ls.addListener(l2);
    503         logln("registering foo... ");
    504         ls.registerObject("Foo", "en_FOO");
    505         logln("registering bar... ");
    506         ls.registerObject("Bar", "en_BAR");
    507         logln("getting foo...");
    508         logln((String)ls.get("en_FOO"));
    509         logln("removing listener 2...");
    510         ls.removeListener(l2);
    511         logln("registering baz...");
    512         ls.registerObject("Baz", "en_BAZ");
    513         logln("removing listener 1");
    514         ls.removeListener(l1);
    515         logln("registering burp...");
    516         ls.registerObject("Burp", "en_BURP");
    517 
    518         // should only get one notification even if register multiple times
    519         logln("... trying multiple registration");
    520         ls.addListener(l1);
    521         ls.addListener(l1);
    522         ls.addListener(l1);
    523         ls.addListener(l2);
    524         ls.registerObject("Foo", "en_FOO");
    525         logln("... registered foo");
    526 
    527         // since in a separate thread, we can callback and not deadlock
    528         ServiceListener l3 = new ServiceListener() {
    529             private int n;
    530             @Override
    531             public void serviceChanged(ICUService s) {
    532             logln("listener 3 report " + n++ + " service changed...");
    533             if (s.get("en_BOINK") == null) { // don't recurse on ourselves!!!
    534                 logln("registering boink...");
    535                 s.registerObject("boink", "en_BOINK");
    536             }
    537             }
    538         };
    539         ls.addListener(l3);
    540         logln("registering boo...");
    541         ls.registerObject("Boo", "en_BOO");
    542         logln("...done");
    543 
    544         try {
    545         Thread.sleep(100);
    546         }
    547         catch (InterruptedException e) {
    548         }
    549     }
    550     }
    551 
    552     static class TestLocaleKeyFactory extends LocaleKeyFactory {
    553     protected final Set ids;
    554     protected final String factoryID;
    555 
    556     public TestLocaleKeyFactory(String[] ids, String factoryID) {
    557             super(VISIBLE, factoryID);
    558 
    559         this.ids = Collections.unmodifiableSet(new HashSet(Arrays.asList(ids)));
    560             this.factoryID = factoryID + ": ";
    561     }
    562 
    563     @Override
    564     protected Object handleCreate(ULocale loc, int kind, ICUService service) {
    565             return factoryID + loc.toString();
    566     }
    567 
    568     @Override
    569     protected Set getSupportedIDs() {
    570             return ids;
    571     }
    572     }
    573 
    574     /*
    575       // Disallow hiding for now since it causes gnarly problems, like
    576       // how do you localize the hidden (but still exported) names.
    577 
    578       static class TestHidingFactory implements ICUService.Factory {
    579       protected final String[] ids;
    580       protected final String factoryID;
    581 
    582       public TestHidingFactory(String[] ids) {
    583       this(ids, "Hiding");
    584       }
    585 
    586       public TestHidingFactory(String[] ids, String factoryID) {
    587       this.ids = (String[])ids.clone();
    588 
    589       if (factoryID == null || factoryID.length() == 0) {
    590       this.factoryID = "";
    591       } else {
    592       this.factoryID = factoryID + ": ";
    593       }
    594       }
    595 
    596       public Object create(Key key, ICUService service) {
    597       for (int i = 0; i < ids.length; ++i) {
    598       if (LocaleUtility.isFallbackOf(ids[i], key.currentID())) {
    599       return factoryID + key.canonicalID();
    600       }
    601       }
    602       return null;
    603       }
    604 
    605       public void updateVisibleIDs(Map result) {
    606       for (int i = 0; i < ids.length; ++i) {
    607       String id = ids[i];
    608       Iterator iter = result.keySet().iterator();
    609       while (iter.hasNext()) {
    610       if (LocaleUtility.isFallbackOf(id, (String)iter.next())) {
    611       iter.remove();
    612       }
    613       }
    614       result.put(id, this);
    615       }
    616       }
    617 
    618       public String getDisplayName(String id, ULocale locale) {
    619       return factoryID + new ULocale(id).getDisplayName(locale);
    620       }
    621       }
    622     */
    623 
    624     static class CalifornioLanguageFactory extends ICUResourceBundleFactory {
    625     public static String californio = "en_US_CA";
    626     public static String valley = californio + "_VALLEY";
    627     public static String surfer = californio + "_SURFER";
    628     public static String geek = californio + "_GEEK";
    629         public static Set supportedIDs;
    630         static {
    631             HashSet result = new HashSet();
    632             result.addAll(ICUResourceBundle.getAvailableLocaleNameSet());
    633         result.add(californio);
    634         result.add(valley);
    635         result.add(surfer);
    636         result.add(geek);
    637             supportedIDs = Collections.unmodifiableSet(result);
    638         }
    639 
    640     @Override
    641     public Set getSupportedIDs() {
    642             return supportedIDs;
    643     }
    644 
    645     @Override
    646     public String getDisplayName(String id, ULocale locale) {
    647         String prefix = "";
    648         String suffix = "";
    649         String ls = locale.toString();
    650         if (LocaleUtility.isFallbackOf(californio, ls)) {
    651         if (ls.equalsIgnoreCase(valley)) {
    652             prefix = "Like, you know, it's so totally ";
    653         } else if (ls.equalsIgnoreCase(surfer)) {
    654             prefix = "Dude, its ";
    655         } else if (ls.equalsIgnoreCase(geek)) {
    656             prefix = "I'd estimate it's approximately ";
    657         } else {
    658             prefix = "Huh?  Maybe ";
    659         }
    660         }
    661         if (LocaleUtility.isFallbackOf(californio, id)) {
    662         if (id.equalsIgnoreCase(valley)) {
    663             suffix = "like the Valley, you know?  Let's go to the mall!";
    664         } else if (id.equalsIgnoreCase(surfer)) {
    665             suffix = "time to hit those gnarly waves, Dude!!!";
    666         } else if (id.equalsIgnoreCase(geek)) {
    667             suffix = "all systems go.  T-Minus 9, 8, 7...";
    668         } else {
    669             suffix = "No Habla Englais";
    670         }
    671         } else {
    672         suffix = super.getDisplayName(id, locale);
    673         }
    674 
    675         return prefix + suffix;
    676     }
    677     }
    678 
    679     @Test
    680     public void TestLocale() {
    681     ICULocaleService service = new ICULocaleService("test locale");
    682     service.registerObject("root", ULocale.ROOT);
    683     service.registerObject("german", "de");
    684     service.registerObject("german_Germany", ULocale.GERMANY);
    685     service.registerObject("japanese", "ja");
    686     service.registerObject("japanese_Japan", ULocale.JAPAN);
    687 
    688     Object target = service.get("de_US");
    689     confirmEqual("test de_US", "german", target);
    690 
    691         ULocale de = new ULocale("de");
    692         ULocale de_US = new ULocale("de_US");
    693 
    694         target = service.get(de_US);
    695     confirmEqual("test de_US 2", "german", target);
    696 
    697         target = service.get(de_US, LocaleKey.KIND_ANY);
    698     confirmEqual("test de_US 3", "german", target);
    699 
    700         target = service.get(de_US, 1234);
    701     confirmEqual("test de_US 4", "german", target);
    702 
    703         ULocale[] actualReturn = new ULocale[1];
    704         target = service.get(de_US, actualReturn);
    705         confirmEqual("test de_US 5", "german", target);
    706         confirmEqual("test de_US 6", actualReturn[0], de);
    707 
    708         actualReturn[0] = null;
    709         target = service.get(de_US, LocaleKey.KIND_ANY, actualReturn);
    710         confirmEqual("test de_US 7", actualReturn[0], de);
    711 
    712         actualReturn[0] = null;
    713         target = service.get(de_US, 1234, actualReturn);
    714     confirmEqual("test de_US 8", "german", target);
    715         confirmEqual("test de_US 9", actualReturn[0], de);
    716 
    717         service.registerObject("one/de_US", de_US, 1);
    718         service.registerObject("two/de_US", de_US, 2);
    719 
    720         target = service.get(de_US, 1);
    721         confirmEqual("test de_US kind 1", "one/de_US", target);
    722 
    723         target = service.get(de_US, 2);
    724         confirmEqual("test de_US kind 2", "two/de_US", target);
    725 
    726         target = service.get(de_US);
    727         confirmEqual("test de_US kind 3", "german", target);
    728 
    729         LocaleKey lkey = LocaleKey.createWithCanonicalFallback("en", null, 1234);
    730         logln("lkey prefix: " + lkey.prefix());
    731         logln("lkey descriptor: " + lkey.currentDescriptor());
    732         logln("lkey current locale: " + lkey.currentLocale());
    733 
    734         lkey.fallback();
    735         logln("lkey descriptor 2: " + lkey.currentDescriptor());
    736 
    737         lkey.fallback();
    738         logln("lkey descriptor 3: " + lkey.currentDescriptor());
    739 
    740     target = service.get("za_PPP");
    741     confirmEqual("test zappp", "root", target);
    742 
    743     ULocale loc = ULocale.getDefault();
    744     ULocale.setDefault(ULocale.JAPANESE);
    745     target = service.get("za_PPP");
    746     confirmEqual("test with ja locale", "japanese", target);
    747 
    748     Set ids = service.getVisibleIDs();
    749     for (Iterator iter = ids.iterator(); iter.hasNext();) {
    750         logln("id: " + iter.next());
    751     }
    752 
    753     ULocale.setDefault(loc);
    754     ids = service.getVisibleIDs();
    755     for (Iterator iter = ids.iterator(); iter.hasNext();) {
    756         logln("id: " + iter.next());
    757     }
    758 
    759     target = service.get("za_PPP");
    760     confirmEqual("test with en locale", "root", target);
    761 
    762         ULocale[] locales = service.getAvailableULocales();
    763         confirmIdentical("test available locales", locales.length, 6);
    764         logln("locales: ");
    765         for (int i = 0; i < locales.length; ++i) {
    766             log("\n  [" + i + "] " + locales[i]);
    767         }
    768         logln(" ");
    769 
    770         service.registerFactory(new ICUResourceBundleFactory());
    771         target = service.get(ULocale.JAPAN);
    772 
    773         {
    774             int n = 0;
    775             List factories = service.factories();
    776             Iterator iter = factories.iterator();
    777             while (iter.hasNext()) {
    778                 logln("[" + n++ + "] " + iter.next());
    779             }
    780         }
    781 
    782         // list only the english display names for es, in reverse order
    783         // since we're using locale keys, we should get all and only the es locales
    784         // hmmm, the default toString function doesn't print in sorted order for TreeMap
    785         {
    786             SortedMap map = service.getDisplayNames(ULocale.US,
    787                             new Comparator() {
    788                                 @Override
    789                                 public int compare(Object lhs, Object rhs) {
    790                                 return -String.CASE_INSENSITIVE_ORDER.compare((String)lhs, (String)rhs);
    791                                 }
    792                             },
    793                             "es");
    794 
    795             logln("es display names in reverse order " + map);
    796         }
    797     }
    798 
    799     @Test
    800     public void TestWrapFactory() {
    801         final String greeting = "Hello There";
    802         final String greetingID = "greeting";
    803 
    804         ICUService service = new ICUService("wrap");
    805         service.registerObject(greeting, greetingID);
    806 
    807         logln("test one: " + service.get(greetingID));
    808 
    809         class WrapFactory implements Factory {
    810             @Override
    811             public Object create(Key key, ICUService serviceArg) {
    812                 if (key.currentID().equals(greetingID)) {
    813                     Object previous = serviceArg.getKey(key, null, this);
    814                     return "A different greeting: \"" + previous + "\"";
    815                 }
    816                 return null;
    817             }
    818 
    819             @Override
    820             public void updateVisibleIDs(Map result) {
    821                 result.put("greeting", this);
    822             }
    823 
    824             @Override
    825             public String getDisplayName(String id, ULocale locale) {
    826                 return "wrap '" + id + "'";
    827             }
    828         }
    829         service.registerFactory(new WrapFactory());
    830 
    831         confirmEqual("wrap test: ", service.get(greetingID), "A different greeting: \"" + greeting + "\"");
    832     }
    833 
    834     // misc coverage tests
    835     @Test
    836     public void TestCoverage() {
    837     // Key
    838     Key key = new Key("foobar");
    839     logln("ID: " + key.id());
    840     logln("canonicalID: " + key.canonicalID());
    841     logln("currentID: " + key.currentID());
    842     logln("has fallback: " + key.fallback());
    843 
    844     // SimpleFactory
    845     Object obj = new Object();
    846     SimpleFactory sf = new SimpleFactory(obj, "object");
    847     try {
    848         sf = new SimpleFactory(null, null);
    849         errln("didn't throw exception");
    850     }
    851     catch (IllegalArgumentException e) {
    852         logln("OK: " + e.getMessage());
    853     }
    854     catch (Exception e) {
    855         errln("threw wrong exception" + e);
    856     }
    857     logln(sf.getDisplayName("object", null));
    858 
    859     // ICUService
    860     ICUService service = new ICUService();
    861     service.registerFactory(sf);
    862 
    863     try {
    864         service.get(null, null);
    865         errln("didn't throw exception");
    866     }
    867     catch (NullPointerException e) {
    868         logln("OK: " + e.getMessage());
    869     }
    870         /*
    871       catch (Exception e) {
    872       errln("threw wrong exception" + e);
    873       }
    874         */
    875     try {
    876         service.registerFactory(null);
    877         errln("didn't throw exception");
    878     }
    879     catch (NullPointerException e) {
    880         logln("OK: " + e.getMessage());
    881     }
    882     catch (Exception e) {
    883         errln("threw wrong exception" + e);
    884     }
    885 
    886     try {
    887         service.unregisterFactory(null);
    888         errln("didn't throw exception");
    889     }
    890     catch (NullPointerException e) {
    891         logln("OK: " + e.getMessage());
    892     }
    893     catch (Exception e) {
    894         errln("threw wrong exception" + e);
    895     }
    896 
    897     logln("object is: " + service.get("object"));
    898 
    899     logln("stats: " + service.stats());
    900 
    901     // ICURWLock
    902 
    903     ICURWLock rwlock = new ICURWLock();
    904     rwlock.resetStats();
    905 
    906     rwlock.acquireRead();
    907     rwlock.releaseRead();
    908 
    909     rwlock.acquireWrite();
    910     rwlock.releaseWrite();
    911     logln("stats: " + rwlock.getStats());
    912     logln("stats: " + rwlock.clearStats());
    913     rwlock.acquireRead();
    914     rwlock.releaseRead();
    915     rwlock.acquireWrite();
    916     rwlock.releaseWrite();
    917     logln("stats: " + rwlock.getStats());
    918 
    919     try {
    920         rwlock.releaseRead();
    921         errln("no error thrown");
    922     }
    923     catch (Exception e) {
    924         logln("OK: " + e.getMessage());
    925     }
    926 
    927     try {
    928         rwlock.releaseWrite();
    929         errln("no error thrown");
    930     }
    931     catch (Exception e) {
    932         logln("OK: " + e.getMessage());
    933     }
    934 
    935         // ICULocaleService
    936 
    937     // LocaleKey
    938 
    939     // LocaleKey lkey = LocaleKey.create("en_US", "ja_JP");
    940     // lkey = LocaleKey.create(null, null);
    941     LocaleKey lkey = LocaleKey.createWithCanonicalFallback("en_US", "ja_JP");
    942         logln("lkey: " + lkey);
    943 
    944         lkey = LocaleKey.createWithCanonicalFallback(null, null);
    945         logln("lkey from null,null: " + lkey);
    946 
    947     // LocaleKeyFactory
    948     LocaleKeyFactory lkf = new LKFSubclass(false);
    949         logln("lkf: " + lkf);
    950     logln("obj: " + lkf.create(lkey, null));
    951     logln(lkf.getDisplayName("foo", null));
    952     logln(lkf.getDisplayName("bar", null));
    953     lkf.updateVisibleIDs(new HashMap());
    954 
    955     LocaleKeyFactory invisibleLKF = new LKFSubclass(false);
    956     logln("obj: " + invisibleLKF.create(lkey, null));
    957     logln(invisibleLKF.getDisplayName("foo", null));
    958     logln(invisibleLKF.getDisplayName("bar", null));
    959     invisibleLKF.updateVisibleIDs(new HashMap());
    960 
    961     // ResourceBundleFactory
    962     ICUResourceBundleFactory rbf = new ICUResourceBundleFactory();
    963     logln("RB: " + rbf.create(lkey, null));
    964 
    965     // ICUNotifier
    966     ICUNotifier nf = new ICUNSubclass();
    967     try {
    968         nf.addListener(null);
    969         errln("added null listener");
    970     }
    971     catch (NullPointerException e) {
    972         logln(e.getMessage());
    973     }
    974     catch (Exception e) {
    975         errln("got wrong exception");
    976     }
    977 
    978     try {
    979         nf.addListener(new WrongListener());
    980         errln("added wrong listener");
    981     }
    982     catch (IllegalStateException e) {
    983         logln(e.getMessage());
    984     }
    985     catch (Exception e) {
    986         errln("got wrong exception");
    987     }
    988 
    989     try {
    990         nf.removeListener(null);
    991         errln("removed null listener");
    992     }
    993     catch (NullPointerException e) {
    994         logln(e.getMessage());
    995     }
    996     catch (Exception e) {
    997         errln("got wrong exception");
    998     }
    999 
   1000     nf.removeListener(new MyListener());
   1001     nf.notifyChanged();
   1002     nf.addListener(new MyListener());
   1003     nf.removeListener(new MyListener());
   1004     }
   1005 
   1006     static class MyListener implements EventListener {
   1007     }
   1008 
   1009     static class WrongListener implements EventListener {
   1010     }
   1011 
   1012     static class ICUNSubclass extends ICUNotifier {
   1013         @Override
   1014         public boolean acceptsListener(EventListener l) {
   1015             return l instanceof MyListener;
   1016         }
   1017 
   1018         // not used, just needed to implement abstract base
   1019         @Override
   1020         public void notifyListener(EventListener l) {
   1021         }
   1022     }
   1023 
   1024     static class LKFSubclass extends LocaleKeyFactory {
   1025     LKFSubclass(boolean visible) {
   1026         super(visible ? VISIBLE : INVISIBLE);
   1027     }
   1028 
   1029     @Override
   1030     protected Set getSupportedIDs() {
   1031             return Collections.EMPTY_SET;
   1032     }
   1033     }
   1034 }
   1035