Source Code Cross Referenced for ICUService.java in  » Internationalization-Localization » icu4j » com » ibm » icu » impl » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Internationalization Localization » icu4j » com.ibm.icu.impl 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**
0002:         *******************************************************************************
0003:         * Copyright (C) 2001-2006, International Business Machines Corporation and    *
0004:         * others. All Rights Reserved.                                                *
0005:         *******************************************************************************
0006:         */package com.ibm.icu.impl;
0007:
0008:        //import com.ibm.icu.text.Collator;
0009:
0010:        import java.lang.ref.SoftReference;
0011:        import java.util.ArrayList;
0012:        import java.util.Collections;
0013:        import java.util.Comparator;
0014:        import java.util.EventListener;
0015:        import java.util.HashMap;
0016:        import java.util.HashSet;
0017:        import java.util.Iterator;
0018:        import java.util.List;
0019:        import java.util.ListIterator;
0020:        import java.util.Locale;
0021:        import java.util.Map;
0022:        import java.util.Map.Entry;
0023:        import java.util.Set;
0024:        import java.util.SortedMap;
0025:        import java.util.TreeMap;
0026:
0027:        import com.ibm.icu.util.ULocale;
0028:
0029:        /**
0030:         * <p>A Service provides access to service objects that implement a
0031:         * particular service, e.g. transliterators.  Users provide a String
0032:         * id (for example, a locale string) to the service, and get back an
0033:         * object for that id.  Service objects can be any kind of object.
0034:         * The service object is cached and returned for later queries, so
0035:         * generally it should not be mutable, or the caller should clone the
0036:         * object before modifying it.</p>
0037:         *
0038:         * <p>Services 'canonicalize' the query id and use the canonical id to
0039:         * query for the service.  The service also defines a mechanism to
0040:         * 'fallback' the id multiple times.  Clients can optionally request
0041:         * the actual id that was matched by a query when they use an id to
0042:         * retrieve a service object.</p>
0043:         *
0044:         * <p>Service objects are instantiated by Factory objects registered with
0045:         * the service.  The service queries each Factory in turn, from most recently
0046:         * registered to earliest registered, until one returns a service object.
0047:         * If none responds with a service object, a fallback id is generated,
0048:         * and the process repeats until a service object is returned or until
0049:         * the id has no further fallbacks.</p>
0050:         *
0051:         * <p>Factories can be dynamically registered and unregistered with the
0052:         * service.  When registered, a Factory is installed at the head of
0053:         * the factory list, and so gets 'first crack' at any keys or fallback
0054:         * keys.  When unregistered, it is removed from the service and can no
0055:         * longer be located through it.  Service objects generated by this
0056:         * factory and held by the client are unaffected.</p>
0057:         *
0058:         * <p>ICUService uses Keys to query factories and perform
0059:         * fallback.  The Key defines the canonical form of the id, and
0060:         * implements the fallback strategy.  Custom Keys can be defined that
0061:         * parse complex IDs into components that Factories can more easily
0062:         * use.  The Key can cache the results of this parsing to save
0063:         * repeated effort.  ICUService provides convenience APIs that
0064:         * take Strings and generate default Keys for use in querying.</p>
0065:         *
0066:         * <p>ICUService provides API to get the list of ids publicly
0067:         * supported by the service (although queries aren't restricted to
0068:         * this list).  This list contains only 'simple' IDs, and not fully
0069:         * unique ids.  Factories are associated with each simple ID and
0070:         * the responsible factory can also return a human-readable localized
0071:         * version of the simple ID, for use in user interfaces.  ICUService
0072:         * can also provide a sorted collection of the all the localized visible
0073:         * ids.</p>
0074:         *
0075:         * <p>ICUService implements ICUNotifier, so that clients can register
0076:         * to receive notification when factories are added or removed from
0077:         * the service.  ICUService provides a default EventListener subinterface,
0078:         * ServiceListener, which can be registered with the service.  When
0079:         * the service changes, the ServiceListener's serviceChanged method
0080:         * is called, with the service as the only argument.</p>
0081:         *
0082:         * <p>The ICUService API is both rich and generic, and it is expected
0083:         * that most implementations will statically 'wrap' ICUService to
0084:         * present a more appropriate API-- for example, to declare the type
0085:         * of the objects returned from get, to limit the factories that can
0086:         * be registered with the service, or to define their own listener
0087:         * interface with a custom callback method.  They might also customize
0088:         * ICUService by overriding it, for example, to customize the Key and
0089:         * fallback strategy.  ICULocaleService is a customized service that
0090:         * uses Locale names as ids and uses Keys that implement the standard
0091:         * resource bundle fallback strategy.<p>
0092:         */
0093:        public class ICUService extends ICUNotifier {
0094:            /**
0095:             * Name used for debugging.
0096:             */
0097:            protected final String name;
0098:
0099:            /**
0100:             * Constructor.
0101:             */
0102:            public ICUService() {
0103:                name = "";
0104:            }
0105:
0106:            private static final boolean DEBUG = ICUDebug.enabled("service");
0107:
0108:            /**
0109:             * Construct with a name (useful for debugging).
0110:             */
0111:            public ICUService(String name) {
0112:                this .name = name;
0113:            }
0114:
0115:            /**
0116:             * Access to factories is protected by a read-write lock.  This is
0117:             * to allow multiple threads to read concurrently, but keep
0118:             * changes to the factory list atomic with respect to all readers.
0119:             */
0120:            private final ICURWLock factoryLock = new ICURWLock();
0121:
0122:            /**
0123:             * All the factories registered with this service.
0124:             */
0125:            private final List factories = new ArrayList();
0126:
0127:            /**
0128:             * Record the default number of factories for this service.
0129:             * Can be set by markDefault.
0130:             */
0131:            private int defaultSize = 0;
0132:
0133:            /**
0134:             * Keys are used to communicate with factories to generate an
0135:             * instance of the service.  Keys define how ids are
0136:             * canonicalized, provide both a current id and a current
0137:             * descriptor to use in querying the cache and factories, and
0138:             * determine the fallback strategy.</p>
0139:             *
0140:             * <p>Keys provide both a currentDescriptor and a currentID.
0141:             * The descriptor contains an optional prefix, followed by '/'
0142:             * and the currentID.  Factories that handle complex keys,
0143:             * for example number format factories that generate multiple
0144:             * kinds of formatters for the same locale, use the descriptor
0145:             * to provide a fully unique identifier for the service object,
0146:             * while using the currentID (in this case, the locale string),
0147:             * as the visible IDs that can be localized.
0148:             *
0149:             * <p> The default implementation of Key has no fallbacks and
0150:             * has no custom descriptors.</p>
0151:             */
0152:            public static class Key {
0153:                private final String id;
0154:
0155:                /**
0156:                 * Construct a key from an id.
0157:                 */
0158:                public Key(String id) {
0159:                    this .id = id;
0160:                }
0161:
0162:                /**
0163:                 * Return the original ID used to construct this key.
0164:                 */
0165:                public final String id() {
0166:                    return id;
0167:                }
0168:
0169:                /**
0170:                 * Return the canonical version of the original ID.  This implementation
0171:                 * returns the original ID unchanged.
0172:                 */
0173:                public String canonicalID() {
0174:                    return id;
0175:                }
0176:
0177:                /**
0178:                 * Return the (canonical) current ID.  This implementation
0179:                 * returns the canonical ID.
0180:                 */
0181:                public String currentID() {
0182:                    return canonicalID();
0183:                }
0184:
0185:                /**
0186:                 * Return the current descriptor.  This implementation returns
0187:                 * the current ID.  The current descriptor is used to fully
0188:                 * identify an instance of the service in the cache.  A
0189:                 * factory may handle all descriptors for an ID, or just a
0190:                 * particular descriptor.  The factory can either parse the
0191:                 * descriptor or use custom API on the key in order to
0192:                 * instantiate the service.
0193:                 */
0194:                public String currentDescriptor() {
0195:                    return "/" + currentID();
0196:                }
0197:
0198:                /**
0199:                 * If the key has a fallback, modify the key and return true,
0200:                 * otherwise return false.  The current ID will change if there
0201:                 * is a fallback.  No currentIDs should be repeated, and fallback
0202:                 * must eventually return false.  This implmentation has no fallbacks
0203:                 * and always returns false.
0204:                 */
0205:                public boolean fallback() {
0206:                    return false;
0207:                }
0208:
0209:                /**
0210:                 * If a key created from id would eventually fallback to match the
0211:                 * canonical ID of this key, return true.
0212:                 */
0213:                public boolean isFallbackOf(String id) {
0214:                    return canonicalID().equals(id);
0215:                }
0216:            }
0217:
0218:            /**
0219:             * Factories generate the service objects maintained by the
0220:             * service.  A factory generates a service object from a key,
0221:             * updates id->factory mappings, and returns the display name for
0222:             * a supported id.
0223:             */
0224:            public static interface Factory {
0225:
0226:                /**
0227:                 * Create a service object from the key, if this factory
0228:                 * supports the key.  Otherwise, return null.
0229:                 *
0230:                 * <p>If the factory supports the key, then it can call
0231:                 * the service's getKey(Key, String[], Factory) method
0232:                 * passing itself as the factory to get the object that
0233:                 * the service would have created prior to the factory's
0234:                 * registration with the service.  This can change the
0235:                 * key, so any information required from the key should
0236:                 * be extracted before making such a callback.
0237:                 */
0238:                public Object create(Key key, ICUService service);
0239:
0240:                /**
0241:                 * Update the result IDs (not descriptors) to reflect the IDs
0242:                 * this factory handles.  This function and getDisplayName are
0243:                 * used to support ICUService.getDisplayNames.  Basically, the
0244:                 * factory has to determine which IDs it will permit to be
0245:                 * available, and of those, which it will provide localized
0246:                 * display names for.  In most cases this reflects the IDs that
0247:                 * the factory directly supports.
0248:                 */
0249:                public void updateVisibleIDs(Map result);
0250:
0251:                /**
0252:                 * Return the display name for this id in the provided locale.
0253:                 * This is an localized id, not a descriptor.  If the id is
0254:                 * not visible or not defined by the factory, return null.
0255:                 * If locale is null, return id unchanged.
0256:                 */
0257:                public String getDisplayName(String id, ULocale locale);
0258:            }
0259:
0260:            /**
0261:             * A default implementation of factory.  This provides default
0262:             * implementations for subclasses, and implements a singleton
0263:             * factory that matches a single id  and returns a single
0264:             * (possibly deferred-initialized) instance.  This implements
0265:             * updateVisibleIDs to add a mapping from its ID to itself
0266:             * if visible is true, or to remove any existing mapping
0267:             * for its ID if visible is false.
0268:             */
0269:            public static class SimpleFactory implements  Factory {
0270:                protected Object instance;
0271:                protected String id;
0272:                protected boolean visible;
0273:
0274:                /**
0275:                 * Convenience constructor that calls SimpleFactory(Object, String, boolean)
0276:                 * with visible true.
0277:                 */
0278:                public SimpleFactory(Object instance, String id) {
0279:                    this (instance, id, true);
0280:                }
0281:
0282:                /**
0283:                 * Construct a simple factory that maps a single id to a single
0284:                 * service instance.  If visible is true, the id will be visible.
0285:                 * Neither the instance nor the id can be null.
0286:                 */
0287:                public SimpleFactory(Object instance, String id, boolean visible) {
0288:                    if (instance == null || id == null) {
0289:                        throw new IllegalArgumentException(
0290:                                "Instance or id is null");
0291:                    }
0292:                    this .instance = instance;
0293:                    this .id = id;
0294:                    this .visible = visible;
0295:                }
0296:
0297:                /**
0298:                 * Return the service instance if the factory's id is equal to
0299:                 * the key's currentID.  Service is ignored.
0300:                 */
0301:                public Object create(Key key, ICUService service) {
0302:                    if (id.equals(key.currentID())) {
0303:                        return instance;
0304:                    }
0305:                    return null;
0306:                }
0307:
0308:                /**
0309:                 * If visible, adds a mapping from id -> this to the result,
0310:                 * otherwise removes id from result.
0311:                 */
0312:                public void updateVisibleIDs(Map result) {
0313:                    if (visible) {
0314:                        result.put(id, this );
0315:                    } else {
0316:                        result.remove(id);
0317:                    }
0318:                }
0319:
0320:                /**
0321:                 * If this.id equals id, returns id regardless of locale,
0322:                 * otherwise returns null.  (This default implementation has
0323:                 * no localized id information.)
0324:                 */
0325:                public String getDisplayName(String id, ULocale locale) {
0326:                    return (visible && this .id.equals(id)) ? id : null;
0327:                }
0328:
0329:                /**
0330:                 * For debugging.
0331:                 */
0332:                public String toString() {
0333:                    StringBuffer buf = new StringBuffer(super .toString());
0334:                    buf.append(", id: ");
0335:                    buf.append(id);
0336:                    buf.append(", visible: ");
0337:                    buf.append(visible);
0338:                    return buf.toString();
0339:                }
0340:            }
0341:
0342:            /**
0343:             * Convenience override for get(String, String[]). This uses
0344:             * createKey to create a key for the provided descriptor.
0345:             */
0346:            public Object get(String descriptor) {
0347:                return getKey(createKey(descriptor), null);
0348:            }
0349:
0350:            /**
0351:             * Convenience override for get(Key, String[]).  This uses
0352:             * createKey to create a key from the provided descriptor.
0353:             */
0354:            public Object get(String descriptor, String[] actualReturn) {
0355:                if (descriptor == null) {
0356:                    throw new NullPointerException(
0357:                            "descriptor must not be null");
0358:                }
0359:                return getKey(createKey(descriptor), actualReturn);
0360:            }
0361:
0362:            /**
0363:             * Convenience override for get(Key, String[]).
0364:             */
0365:            public Object getKey(Key key) {
0366:                return getKey(key, null);
0367:            }
0368:
0369:            /**
0370:             * <p>Given a key, return a service object, and, if actualReturn
0371:             * is not null, the descriptor with which it was found in the
0372:             * first element of actualReturn.  If no service object matches
0373:             * this key, return null, and leave actualReturn unchanged.</p>
0374:             *
0375:             * <p>This queries the cache using the key's descriptor, and if no
0376:             * object in the cache matches it, tries the key on each
0377:             * registered factory, in order.  If none generates a service
0378:             * object for the key, repeats the process with each fallback of
0379:             * the key, until either one returns a service object, or the key
0380:             * has no fallback.</p>
0381:             *
0382:             * <p>If key is null, just returns null.</p>
0383:             */
0384:            public Object getKey(Key key, String[] actualReturn) {
0385:                return getKey(key, actualReturn, null);
0386:            }
0387:
0388:            // debugging
0389:            // Map hardRef;
0390:
0391:            public Object getKey(Key key, String[] actualReturn, Factory factory) {
0392:                if (factories.size() == 0) {
0393:                    return handleDefault(key, actualReturn);
0394:                }
0395:
0396:                if (DEBUG)
0397:                    System.out.println("Service: " + name + " key: "
0398:                            + key.canonicalID());
0399:
0400:                CacheEntry result = null;
0401:                if (key != null) {
0402:                    try {
0403:                        // The factory list can't be modified until we're done,
0404:                        // otherwise we might update the cache with an invalid result.
0405:                        // The cache has to stay in synch with the factory list.
0406:                        factoryLock.acquireRead();
0407:
0408:                        Map cache = null;
0409:                        SoftReference cref = cacheref; // copy so we don't need to sync on this
0410:                        if (cref != null) {
0411:                            if (DEBUG)
0412:                                System.out.println("Service " + name
0413:                                        + " ref exists");
0414:                            cache = (Map) cref.get();
0415:                        }
0416:                        if (cache == null) {
0417:                            if (DEBUG)
0418:                                System.out.println("Service " + name
0419:                                        + " cache was empty");
0420:                            // synchronized since additions and queries on the cache must be atomic
0421:                            // they can be interleaved, though
0422:                            cache = Collections.synchronizedMap(new HashMap());
0423:                            //                  hardRef = cache; // debug
0424:                            cref = new SoftReference(cache);
0425:                        }
0426:
0427:                        String currentDescriptor = null;
0428:                        ArrayList cacheDescriptorList = null;
0429:                        boolean putInCache = false;
0430:
0431:                        int NDebug = 0;
0432:
0433:                        int startIndex = 0;
0434:                        int limit = factories.size();
0435:                        boolean cacheResult = true;
0436:                        if (factory != null) {
0437:                            for (int i = 0; i < limit; ++i) {
0438:                                if (factory == factories.get(i)) {
0439:                                    startIndex = i + 1;
0440:                                    break;
0441:                                }
0442:                            }
0443:                            if (startIndex == 0) {
0444:                                throw new IllegalStateException("Factory "
0445:                                        + factory
0446:                                        + "not registered with service: "
0447:                                        + this );
0448:                            }
0449:                            cacheResult = false;
0450:                        }
0451:
0452:                        outer: do {
0453:                            currentDescriptor = key.currentDescriptor();
0454:                            if (DEBUG)
0455:                                System.out
0456:                                        .println(name + "[" + NDebug++
0457:                                                + "] looking for: "
0458:                                                + currentDescriptor);
0459:                            result = (CacheEntry) cache.get(currentDescriptor);
0460:                            if (result != null) {
0461:                                if (DEBUG)
0462:                                    System.out.println(name
0463:                                            + " found with descriptor: "
0464:                                            + currentDescriptor);
0465:                                break outer;
0466:                            } else {
0467:                                if (DEBUG)
0468:                                    System.out.println("did not find: "
0469:                                            + currentDescriptor + " in cache");
0470:                            }
0471:
0472:                            // first test of cache failed, so we'll have to update
0473:                            // the cache if we eventually succeed-- that is, if we're
0474:                            // going to update the cache at all.
0475:                            putInCache = cacheResult;
0476:
0477:                            //  int n = 0;
0478:                            int index = startIndex;
0479:                            while (index < limit) {
0480:                                Factory f = (Factory) factories.get(index++);
0481:                                if (DEBUG)
0482:                                    System.out
0483:                                            .println("trying factory["
0484:                                                    + (index - 1) + "] "
0485:                                                    + f.toString());
0486:                                Object service = f.create(key, this );
0487:                                if (service != null) {
0488:                                    result = new CacheEntry(currentDescriptor,
0489:                                            service);
0490:                                    if (DEBUG)
0491:                                        System.out.println(name
0492:                                                + " factory supported: "
0493:                                                + currentDescriptor
0494:                                                + ", caching");
0495:                                    break outer;
0496:                                } else {
0497:                                    if (DEBUG)
0498:                                        System.out
0499:                                                .println("factory did not support: "
0500:                                                        + currentDescriptor);
0501:                                }
0502:                            }
0503:
0504:                            // prepare to load the cache with all additional ids that
0505:                            // will resolve to result, assuming we'll succeed.  We
0506:                            // don't want to keep querying on an id that's going to
0507:                            // fallback to the one that succeeded, we want to hit the
0508:                            // cache the first time next goaround.
0509:                            if (cacheDescriptorList == null) {
0510:                                cacheDescriptorList = new ArrayList(5);
0511:                            }
0512:                            cacheDescriptorList.add(currentDescriptor);
0513:
0514:                        } while (key.fallback());
0515:
0516:                        if (result != null) {
0517:                            if (putInCache) {
0518:                                if (DEBUG)
0519:                                    System.out.println("caching '"
0520:                                            + result.actualDescriptor + "'");
0521:                                cache.put(result.actualDescriptor, result);
0522:                                if (cacheDescriptorList != null) {
0523:                                    Iterator iter = cacheDescriptorList
0524:                                            .iterator();
0525:                                    while (iter.hasNext()) {
0526:                                        String desc = (String) iter.next();
0527:                                        if (DEBUG)
0528:                                            System.out.println(name
0529:                                                    + " adding descriptor: '"
0530:                                                    + desc + "' for actual: '"
0531:                                                    + result.actualDescriptor
0532:                                                    + "'");
0533:
0534:                                        cache.put(desc, result);
0535:                                    }
0536:                                }
0537:                                // Atomic update.  We held the read lock all this time
0538:                                // so we know our cache is consistent with the factory list.
0539:                                // We might stomp over a cache that some other thread
0540:                                // rebuilt, but that's the breaks.  They're both good.
0541:                                cacheref = cref;
0542:                            }
0543:
0544:                            if (actualReturn != null) {
0545:                                // strip null prefix
0546:                                if (result.actualDescriptor.indexOf("/") == 0) {
0547:                                    actualReturn[0] = result.actualDescriptor
0548:                                            .substring(1);
0549:                                } else {
0550:                                    actualReturn[0] = result.actualDescriptor;
0551:                                }
0552:                            }
0553:
0554:                            if (DEBUG)
0555:                                System.out.println("found in service: " + name);
0556:
0557:                            return result.service;
0558:                        }
0559:                    } finally {
0560:                        factoryLock.releaseRead();
0561:                    }
0562:                }
0563:
0564:                if (DEBUG)
0565:                    System.out.println("not found in service: " + name);
0566:
0567:                return handleDefault(key, actualReturn);
0568:            }
0569:
0570:            private SoftReference cacheref;
0571:
0572:            // Record the actual id for this service in the cache, so we can return it
0573:            // even if we succeed later with a different id.
0574:            private static final class CacheEntry {
0575:                final String actualDescriptor;
0576:                final Object service;
0577:
0578:                CacheEntry(String actualDescriptor, Object service) {
0579:                    this .actualDescriptor = actualDescriptor;
0580:                    this .service = service;
0581:                }
0582:            }
0583:
0584:            /**
0585:             * Default handler for this service if no factory in the list
0586:             * handled the key.
0587:             */
0588:            protected Object handleDefault(Key key, String[] actualIDReturn) {
0589:                return null;
0590:            }
0591:
0592:            /**
0593:             * Convenience override for getVisibleIDs(String) that passes null
0594:             * as the fallback, thus returning all visible IDs.
0595:             */
0596:            public Set getVisibleIDs() {
0597:                return getVisibleIDs(null);
0598:            }
0599:
0600:            /**
0601:             * <p>Return a snapshot of the visible IDs for this service.  This
0602:             * set will not change as Factories are added or removed, but the
0603:             * supported ids will, so there is no guarantee that all and only
0604:             * the ids in the returned set are visible and supported by the
0605:             * service in subsequent calls.</p>
0606:             *
0607:             * <p>matchID is passed to createKey to create a key.  If the
0608:             * key is not null, it is used to filter out ids that don't have
0609:             * the key as a fallback.
0610:             */
0611:            public Set getVisibleIDs(String matchID) {
0612:                Set result = getVisibleIDMap().keySet();
0613:
0614:                Key fallbackKey = createKey(matchID);
0615:
0616:                if (fallbackKey != null) {
0617:                    Set temp = new HashSet(result.size());
0618:                    Iterator iter = result.iterator();
0619:                    while (iter.hasNext()) {
0620:                        String id = (String) iter.next();
0621:                        if (fallbackKey.isFallbackOf(id)) {
0622:                            temp.add(id);
0623:                        }
0624:                    }
0625:                    result = temp;
0626:                }
0627:                return result;
0628:            }
0629:
0630:            /**
0631:             * Return a map from visible ids to factories.
0632:             */
0633:            private Map getVisibleIDMap() {
0634:                Map idcache = null;
0635:                SoftReference ref = idref;
0636:                if (ref != null) {
0637:                    idcache = (Map) ref.get();
0638:                }
0639:                while (idcache == null) {
0640:                    synchronized (this ) { // or idref-only lock?
0641:                        if (ref == idref || idref == null) {
0642:                            // no other thread updated idref before we got the lock, so
0643:                            // grab the factory list and update it ourselves
0644:                            try {
0645:                                factoryLock.acquireRead();
0646:                                idcache = new HashMap();
0647:                                ListIterator lIter = factories
0648:                                        .listIterator(factories.size());
0649:                                while (lIter.hasPrevious()) {
0650:                                    Factory f = (Factory) lIter.previous();
0651:                                    f.updateVisibleIDs(idcache);
0652:                                }
0653:                                idcache = Collections.unmodifiableMap(idcache);
0654:                                idref = new SoftReference(idcache);
0655:                            } finally {
0656:                                factoryLock.releaseRead();
0657:                            }
0658:                        } else {
0659:                            // another thread updated idref, but gc may have stepped
0660:                            // in and undone its work, leaving idcache null.  If so,
0661:                            // retry.
0662:                            ref = idref;
0663:                            idcache = (Map) ref.get();
0664:                        }
0665:                    }
0666:                }
0667:
0668:                return idcache;
0669:            }
0670:
0671:            private SoftReference idref;
0672:
0673:            /**
0674:             * Convenience override for getDisplayName(String, ULocale) that
0675:             * uses the current default locale.
0676:             */
0677:            public String getDisplayName(String id) {
0678:                return getDisplayName(id, ULocale.getDefault());
0679:            }
0680:
0681:            /**
0682:             * Given a visible id, return the display name in the requested locale.
0683:             * If there is no directly supported id corresponding to this id, return
0684:             * null.
0685:             */
0686:            public String getDisplayName(String id, ULocale locale) {
0687:                Map m = getVisibleIDMap();
0688:                Factory f = (Factory) m.get(id);
0689:                if (f != null) {
0690:                    return f.getDisplayName(id, locale);
0691:                }
0692:
0693:                Key key = createKey(id);
0694:                while (key.fallback()) {
0695:                    f = (Factory) m.get(key.currentID());
0696:                    if (f != null) {
0697:                        return f.getDisplayName(id, locale);
0698:                    }
0699:                }
0700:
0701:                return null;
0702:            }
0703:
0704:            /**
0705:             * Convenience override of getDisplayNames(ULocale, Comparator, String) that 
0706:             * uses the current default Locale as the locale, null as
0707:             * the comparator, and null for the matchID.
0708:             */
0709:            public SortedMap getDisplayNames() {
0710:                ULocale locale = ULocale.getDefault();
0711:                return getDisplayNames(locale, null, null);
0712:            }
0713:
0714:            /**
0715:             * Convenience override of getDisplayNames(ULocale, Comparator, String) that
0716:             * uses null for the comparator, and null for the matchID.
0717:             */
0718:            public SortedMap getDisplayNames(ULocale locale) {
0719:                return getDisplayNames(locale, null, null);
0720:            }
0721:
0722:            /**
0723:             * Convenience override of getDisplayNames(ULocale, Comparator, String) that
0724:             * uses null for the matchID, thus returning all display names.
0725:             */
0726:            public SortedMap getDisplayNames(ULocale locale, Comparator com) {
0727:                return getDisplayNames(locale, com, null);
0728:            }
0729:
0730:            /**
0731:             * Convenience override of getDisplayNames(ULocale, Comparator, String) that
0732:             * uses null for the comparator.
0733:             */
0734:            public SortedMap getDisplayNames(ULocale locale, String matchID) {
0735:                return getDisplayNames(locale, null, matchID);
0736:            }
0737:
0738:            /**
0739:             * Return a snapshot of the mapping from display names to visible
0740:             * IDs for this service.  This set will not change as factories
0741:             * are added or removed, but the supported ids will, so there is
0742:             * no guarantee that all and only the ids in the returned map will
0743:             * be visible and supported by the service in subsequent calls,
0744:             * nor is there any guarantee that the current display names match
0745:             * those in the set.  The display names are sorted based on the
0746:             * comparator provided.
0747:             */
0748:            public SortedMap getDisplayNames(ULocale locale, Comparator com,
0749:                    String matchID) {
0750:                SortedMap dncache = null;
0751:                LocaleRef ref = dnref;
0752:
0753:                if (ref != null) {
0754:                    dncache = ref.get(locale, com);
0755:                }
0756:
0757:                while (dncache == null) {
0758:                    synchronized (this ) {
0759:                        if (ref == dnref || dnref == null) {
0760:                            dncache = new TreeMap(com); // sorted
0761:
0762:                            Map m = getVisibleIDMap();
0763:                            Iterator ei = m.entrySet().iterator();
0764:                            while (ei.hasNext()) {
0765:                                Entry e = (Entry) ei.next();
0766:                                String id = (String) e.getKey();
0767:                                Factory f = (Factory) e.getValue();
0768:                                dncache.put(f.getDisplayName(id, locale), id);
0769:                            }
0770:
0771:                            dncache = Collections
0772:                                    .unmodifiableSortedMap(dncache);
0773:                            dnref = new LocaleRef(dncache, locale, com);
0774:                        } else {
0775:                            ref = dnref;
0776:                            dncache = ref.get(locale, com);
0777:                        }
0778:                    }
0779:                }
0780:
0781:                Key matchKey = createKey(matchID);
0782:                if (matchKey == null) {
0783:                    return dncache;
0784:                }
0785:
0786:                SortedMap result = new TreeMap(dncache);
0787:                Iterator iter = result.entrySet().iterator();
0788:                while (iter.hasNext()) {
0789:                    Entry e = (Entry) iter.next();
0790:                    if (!matchKey.isFallbackOf((String) e.getValue())) {
0791:                        iter.remove();
0792:                    }
0793:                }
0794:                return result;
0795:            }
0796:
0797:            // we define a class so we get atomic simultaneous access to the
0798:            // locale, comparator, and corresponding map.
0799:            private static class LocaleRef {
0800:                private final ULocale locale;
0801:                private SoftReference ref;
0802:                private Comparator com;
0803:
0804:                LocaleRef(Map dnCache, ULocale locale, Comparator com) {
0805:                    this .locale = locale;
0806:                    this .com = com;
0807:                    this .ref = new SoftReference(dnCache);
0808:                }
0809:
0810:                SortedMap get(ULocale locale, Comparator com) {
0811:                    SortedMap m = (SortedMap) ref.get();
0812:                    if (m != null
0813:                            && this .locale.equals(locale)
0814:                            && (this .com == com || (this .com != null && this .com
0815:                                    .equals(com)))) {
0816:
0817:                        return m;
0818:                    }
0819:                    return null;
0820:                }
0821:            }
0822:
0823:            private LocaleRef dnref;
0824:
0825:            /**
0826:             * Return a snapshot of the currently registered factories.  There
0827:             * is no guarantee that the list will still match the current
0828:             * factory list of the service subsequent to this call.
0829:             */
0830:            public final List factories() {
0831:                try {
0832:                    factoryLock.acquireRead();
0833:                    return new ArrayList(factories);
0834:                } finally {
0835:                    factoryLock.releaseRead();
0836:                }
0837:            }
0838:
0839:            /**
0840:             * A convenience override of registerObject(Object, String, boolean)
0841:             * that defaults visible to true.
0842:             */
0843:            public Factory registerObject(Object obj, String id) {
0844:                return registerObject(obj, id, true);
0845:            }
0846:
0847:            /**
0848:             * Register an object with the provided id.  The id will be
0849:             * canonicalized.  The canonicalized ID will be returned by
0850:             * getVisibleIDs if visible is true.
0851:             */
0852:            public Factory registerObject(Object obj, String id, boolean visible) {
0853:                String canonicalID = createKey(id).canonicalID();
0854:                return registerFactory(new SimpleFactory(obj, canonicalID,
0855:                        visible));
0856:            }
0857:
0858:            /**
0859:             * Register a Factory.  Returns the factory if the service accepts
0860:             * the factory, otherwise returns null.  The default implementation
0861:             * accepts all factories.
0862:             */
0863:            public final Factory registerFactory(Factory factory) {
0864:                if (factory == null) {
0865:                    throw new NullPointerException();
0866:                }
0867:                try {
0868:                    factoryLock.acquireWrite();
0869:                    factories.add(0, factory);
0870:                    clearCaches();
0871:                } finally {
0872:                    factoryLock.releaseWrite();
0873:                }
0874:                notifyChanged();
0875:                return factory;
0876:            }
0877:
0878:            /**
0879:             * Unregister a factory.  The first matching registered factory will
0880:             * be removed from the list.  Returns true if a matching factory was
0881:             * removed.
0882:             */
0883:            public final boolean unregisterFactory(Factory factory) {
0884:                if (factory == null) {
0885:                    throw new NullPointerException();
0886:                }
0887:
0888:                boolean result = false;
0889:                try {
0890:                    factoryLock.acquireWrite();
0891:                    if (factories.remove(factory)) {
0892:                        result = true;
0893:                        clearCaches();
0894:                    }
0895:                } finally {
0896:                    factoryLock.releaseWrite();
0897:                }
0898:
0899:                if (result) {
0900:                    notifyChanged();
0901:                }
0902:                return result;
0903:            }
0904:
0905:            /**
0906:             * Reset the service to the default factories.  The factory
0907:             * lock is acquired and then reInitializeFactories is called.
0908:             */
0909:            public final void reset() {
0910:                try {
0911:                    factoryLock.acquireWrite();
0912:                    reInitializeFactories();
0913:                    clearCaches();
0914:                } finally {
0915:                    factoryLock.releaseWrite();
0916:                }
0917:                notifyChanged();
0918:            }
0919:
0920:            /**
0921:             * Reinitialize the factory list to its default state.  By default
0922:             * this clears the list.  Subclasses can override to provide other
0923:             * default initialization of the factory list.  Subclasses must
0924:             * not call this method directly, as it must only be called while
0925:             * holding write access to the factory list.
0926:             */
0927:            protected void reInitializeFactories() {
0928:                factories.clear();
0929:            }
0930:
0931:            /**
0932:             * Return true if the service is in its default state.  The default
0933:             * implementation returns true if there are no factories registered.
0934:             */
0935:            public boolean isDefault() {
0936:                return factories.size() == defaultSize;
0937:            }
0938:
0939:            /**
0940:             * Set the default size to the current number of registered factories.
0941:             * Used by subclasses to customize the behavior of isDefault.
0942:             */
0943:            protected void markDefault() {
0944:                defaultSize = factories.size();
0945:            }
0946:
0947:            /**
0948:             * Create a key from an id.  This creates a Key instance.
0949:             * Subclasses can override to define more useful keys appropriate
0950:             * to the factories they accept.  If id is null, returns null.
0951:             */
0952:            public Key createKey(String id) {
0953:                return id == null ? null : new Key(id);
0954:            }
0955:
0956:            /**
0957:             * Clear caches maintained by this service.  Subclasses can
0958:             * override if they implement additional that need to be cleared
0959:             * when the service changes. Subclasses should generally not call
0960:             * this method directly, as it must only be called while
0961:             * synchronized on this.
0962:             */
0963:            protected void clearCaches() {
0964:                // we don't synchronize on these because methods that use them
0965:                // copy before use, and check for changes if they modify the
0966:                // caches.
0967:                cacheref = null;
0968:                idref = null;
0969:                dnref = null;
0970:            }
0971:
0972:            /**
0973:             * Clears only the service cache.
0974:             * This can be called by subclasses when a change affects the service
0975:             * cache but not the id caches, e.g., when the default locale changes
0976:             * the resolution of ids changes, but not the visible ids themselves.
0977:             */
0978:            protected void clearServiceCache() {
0979:                cacheref = null;
0980:            }
0981:
0982:            /**
0983:             * ServiceListener is the listener that ICUService provides by default.
0984:             * ICUService will notifiy this listener when factories are added to
0985:             * or removed from the service.  Subclasses can provide
0986:             * different listener interfaces that extend EventListener, and modify
0987:             * acceptsListener and notifyListener as appropriate.
0988:             */
0989:            public static interface ServiceListener extends EventListener {
0990:                public void serviceChanged(ICUService service);
0991:            }
0992:
0993:            /**
0994:             * Return true if the listener is accepted; by default this
0995:             * requires a ServiceListener.  Subclasses can override to accept
0996:             * different listeners.
0997:             */
0998:            protected boolean acceptsListener(EventListener l) {
0999:                return l instanceof  ServiceListener;
1000:            }
1001:
1002:            /**
1003:             * Notify the listener, which by default is a ServiceListener.
1004:             * Subclasses can override to use a different listener.
1005:             */
1006:            protected void notifyListener(EventListener l) {
1007:                ((ServiceListener) l).serviceChanged(this );
1008:            }
1009:
1010:            /**
1011:             * Return a string describing the statistics for this service.
1012:             * This also resets the statistics. Used for debugging purposes.
1013:             */
1014:            public String stats() {
1015:                ICURWLock.Stats stats = factoryLock.resetStats();
1016:                if (stats != null) {
1017:                    return stats.toString();
1018:                }
1019:                return "no stats";
1020:            }
1021:
1022:            /**
1023:             * Return the name of this service. This will be the empty string if none was assigned.
1024:             */
1025:            public String getName() {
1026:                return name;
1027:            }
1028:
1029:            /**
1030:             * Returns the result of super.toString, appending the name in curly braces.
1031:             */
1032:            public String toString() {
1033:                return super .toString() + "{" + name + "}";
1034:            }
1035:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.