src/share/classes/javax/imageio/spi/ServiceRegistry.java

Print this page
rev 9345 : 8035487: Fix raw and unchecked lint warnings in javax.imageio.spi
Reviewed-by:


  82  * <p> The service provider classes should be to be lightweight and
  83  * quick to load.  Implementations of these interfaces should avoid
  84  * complex dependencies on other classes and on native code. The usual
  85  * pattern for more complex services is to register a lightweight
  86  * proxy for the heavyweight service.
  87  *
  88  * <p> An application may customize the contents of a registry as it
  89  * sees fit, so long as it has the appropriate runtime permission.
  90  *
  91  * <p> For more details on declaring service providers, and the JAR
  92  * format in general, see the <a
  93  * href="../../../../technotes/guides/jar/jar.html">
  94  * JAR File Specification</a>.
  95  *
  96  * @see RegisterableService
  97  *
  98  */
  99 public class ServiceRegistry {
 100 
 101     // Class -> Registry
 102     private Map categoryMap = new HashMap();
 103 
 104     /**
 105      * Constructs a <code>ServiceRegistry</code> instance with a
 106      * set of categories taken from the <code>categories</code>
 107      * argument.
 108      *
 109      * @param categories an <code>Iterator</code> containing
 110      * <code>Class</code> objects to be used to define categories.
 111      *
 112      * @exception IllegalArgumentException if
 113      * <code>categories</code> is <code>null</code>.
 114      */
 115     public ServiceRegistry(Iterator<Class<?>> categories) {
 116         if (categories == null) {
 117             throw new IllegalArgumentException("categories == null!");
 118         }
 119         while (categories.hasNext()) {
 120             Class category = (Class)categories.next();
 121             SubRegistry reg = new SubRegistry(this, category);
 122             categoryMap.put(category, reg);
 123         }
 124     }
 125 
 126     // The following two methods expose functionality from
 127     // sun.misc.Service.  If that class is made public, they may be
 128     // removed.
 129     //
 130     // The sun.misc.ServiceConfigurationError class may also be
 131     // exposed, in which case the references to 'an
 132     // <code>Error</code>' below should be changed to 'a
 133     // <code>ServiceConfigurationError</code>'.
 134 
 135     /**
 136      * Searches for implementations of a particular service class
 137      * using the given class loader.
 138      *
 139      * <p> This method transforms the name of the given service class
 140      * into a provider-configuration filename as described in the


 200      *
 201      * @exception IllegalArgumentException if
 202      * <code>providerClass</code> is <code>null</code>.
 203      */
 204     public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {
 205         if (providerClass == null) {
 206             throw new IllegalArgumentException("providerClass == null!");
 207         }
 208         return ServiceLoader.load(providerClass).iterator();
 209     }
 210 
 211     /**
 212      * Returns an <code>Iterator</code> of <code>Class</code> objects
 213      * indicating the current set of categories.  The iterator will be
 214      * empty if no categories exist.
 215      *
 216      * @return an <code>Iterator</code> containing
 217      * <code>Class</code>objects.
 218      */
 219     public Iterator<Class<?>> getCategories() {
 220         Set keySet = categoryMap.keySet();
 221         return keySet.iterator();
 222     }
 223 
 224     /**
 225      * Returns an Iterator containing the subregistries to which the
 226      * provider belongs.
 227      */
 228     private Iterator getSubRegistries(Object provider) {
 229         List l = new ArrayList();
 230         Iterator iter = categoryMap.keySet().iterator();
 231         while (iter.hasNext()) {
 232             Class c = (Class)iter.next();
 233             if (c.isAssignableFrom(provider.getClass())) {
 234                 l.add((SubRegistry)categoryMap.get(c));
 235             }
 236         }
 237         return l.iterator();
 238     }
 239 
 240     /**
 241      * Adds a service provider object to the registry.  The provider
 242      * is associated with the given category.
 243      *
 244      * <p> If <code>provider</code> implements the
 245      * <code>RegisterableService</code> interface, its
 246      * <code>onRegistration</code> method will be called.  Its
 247      * <code>onDeregistration</code> method will be called each time
 248      * it is deregistered from a category, for example if a
 249      * category is removed or the registry is garbage collected.
 250      *
 251      * @param provider the service provide object to be registered.
 252      * @param category the category under which to register the
 253      * provider.
 254      * @param <T> the type of the provider.
 255      *
 256      * @return true if no provider of the same class was previously
 257      * registered in the same category category.
 258      *
 259      * @exception IllegalArgumentException if <code>provider</code> is
 260      * <code>null</code>.
 261      * @exception IllegalArgumentException if there is no category
 262      * corresponding to <code>category</code>.
 263      * @exception ClassCastException if provider does not implement
 264      * the <code>Class</code> defined by <code>category</code>.
 265      */
 266     public <T> boolean registerServiceProvider(T provider,
 267                                                Class<T> category) {
 268         if (provider == null) {
 269             throw new IllegalArgumentException("provider == null!");
 270         }
 271         SubRegistry reg = (SubRegistry)categoryMap.get(category);
 272         if (reg == null) {
 273             throw new IllegalArgumentException("category unknown!");
 274         }
 275         if (!category.isAssignableFrom(provider.getClass())) {
 276             throw new ClassCastException();
 277         }
 278 
 279         return reg.registerServiceProvider(provider);
 280     }
 281 
 282     /**
 283      * Adds a service provider object to the registry.  The provider
 284      * is associated within each category present in the registry
 285      * whose <code>Class</code> it implements.
 286      *
 287      * <p> If <code>provider</code> implements the
 288      * <code>RegisterableService</code> interface, its
 289      * <code>onRegistration</code> method will be called once for each
 290      * category it is registered under.  Its
 291      * <code>onDeregistration</code> method will be called each time
 292      * it is deregistered from a category or when the registry is
 293      * finalized.
 294      *
 295      * @param provider the service provider object to be registered.
 296      *
 297      * @exception IllegalArgumentException if
 298      * <code>provider</code> is <code>null</code>.
 299      */
 300     public void registerServiceProvider(Object provider) {
 301         if (provider == null) {
 302             throw new IllegalArgumentException("provider == null!");
 303         }
 304         Iterator regs = getSubRegistries(provider);
 305         while (regs.hasNext()) {
 306             SubRegistry reg = (SubRegistry)regs.next();
 307             reg.registerServiceProvider(provider);
 308         }
 309     }
 310 
 311     /**
 312      * Adds a set of service provider objects, taken from an
 313      * <code>Iterator</code> to the registry.  Each provider is
 314      * associated within each category present in the registry whose
 315      * <code>Class</code> it implements.
 316      *
 317      * <p> For each entry of <code>providers</code> that implements
 318      * the <code>RegisterableService</code> interface, its
 319      * <code>onRegistration</code> method will be called once for each
 320      * category it is registered under.  Its
 321      * <code>onDeregistration</code> method will be called each time
 322      * it is deregistered from a category or when the registry is
 323      * finalized.
 324      *
 325      * @param providers an Iterator containing service provider
 326      * objects to be registered.


 354      * @param category the category from which to deregister the
 355      * provider.
 356      * @param <T> the type of the provider.
 357      *
 358      * @return <code>true</code> if the provider was previously
 359      * registered in the same category category,
 360      * <code>false</code> otherwise.
 361      *
 362      * @exception IllegalArgumentException if <code>provider</code> is
 363      * <code>null</code>.
 364      * @exception IllegalArgumentException if there is no category
 365      * corresponding to <code>category</code>.
 366      * @exception ClassCastException if provider does not implement
 367      * the class defined by <code>category</code>.
 368      */
 369     public <T> boolean deregisterServiceProvider(T provider,
 370                                                  Class<T> category) {
 371         if (provider == null) {
 372             throw new IllegalArgumentException("provider == null!");
 373         }
 374         SubRegistry reg = (SubRegistry)categoryMap.get(category);
 375         if (reg == null) {
 376             throw new IllegalArgumentException("category unknown!");
 377         }
 378         if (!category.isAssignableFrom(provider.getClass())) {
 379             throw new ClassCastException();
 380         }
 381         return reg.deregisterServiceProvider(provider);
 382     }
 383 
 384     /**
 385      * Removes a service provider object from all categories that
 386      * contain it.
 387      *
 388      * @param provider the service provider object to be deregistered.
 389      *
 390      * @exception IllegalArgumentException if <code>provider</code> is
 391      * <code>null</code>.
 392      */
 393     public void deregisterServiceProvider(Object provider) {
 394         if (provider == null) {
 395             throw new IllegalArgumentException("provider == null!");
 396         }
 397         Iterator regs = getSubRegistries(provider);
 398         while (regs.hasNext()) {
 399             SubRegistry reg = (SubRegistry)regs.next();
 400             reg.deregisterServiceProvider(provider);
 401         }
 402     }
 403 
 404     /**
 405      * Returns <code>true</code> if <code>provider</code> is currently
 406      * registered.
 407      *
 408      * @param provider the service provider object to be queried.
 409      *
 410      * @return <code>true</code> if the given provider has been
 411      * registered.
 412      *
 413      * @exception IllegalArgumentException if <code>provider</code> is
 414      * <code>null</code>.
 415      */
 416     public boolean contains(Object provider) {
 417         if (provider == null) {
 418             throw new IllegalArgumentException("provider == null!");
 419         }
 420         Iterator regs = getSubRegistries(provider);
 421         while (regs.hasNext()) {
 422             SubRegistry reg = (SubRegistry)regs.next();
 423             if (reg.contains(provider)) {
 424                 return true;
 425             }
 426         }
 427 
 428         return false;
 429     }
 430 
 431     /**
 432      * Returns an <code>Iterator</code> containing all registered
 433      * service providers in the given category.  If
 434      * <code>useOrdering</code> is <code>false</code>, the iterator
 435      * will return all of the server provider objects in an arbitrary
 436      * order.  Otherwise, the ordering will respect any pairwise
 437      * orderings that have been set.  If the graph of pairwise
 438      * orderings contains cycles, any providers that belong to a cycle
 439      * will not be returned.
 440      *
 441      * @param category the category to be retrieved from.
 442      * @param useOrdering <code>true</code> if pairwise orderings
 443      * should be taken account in ordering the returned objects.
 444      * @param <T> the type of the category.
 445      *
 446      * @return an <code>Iterator</code> containing service provider
 447      * objects from the given category, possibly in order.
 448      *
 449      * @exception IllegalArgumentException if there is no category
 450      * corresponding to <code>category</code>.
 451      */
 452     public <T> Iterator<T> getServiceProviders(Class<T> category,
 453                                                boolean useOrdering) {
 454         SubRegistry reg = (SubRegistry)categoryMap.get(category);
 455         if (reg == null) {
 456             throw new IllegalArgumentException("category unknown!");
 457         }
 458         return reg.getServiceProviders(useOrdering);


 459     }
 460 
 461     /**
 462      * A simple filter interface used by
 463      * <code>ServiceRegistry.getServiceProviders</code> to select
 464      * providers matching an arbitrary criterion.  Classes that
 465      * implement this interface should be defined in order to make use
 466      * of the <code>getServiceProviders</code> method of
 467      * <code>ServiceRegistry</code> that takes a <code>Filter</code>.
 468      *
 469      * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)
 470      */
 471     public interface Filter {
 472 
 473         /**
 474          * Returns <code>true</code> if the given
 475          * <code>provider</code> object matches the criterion defined
 476          * by this <code>Filter</code>.
 477          *
 478          * @param provider a service provider <code>Object</code>.


 491      * <p> The <code>useOrdering</code> argument controls the
 492      * ordering of the results using the same rules as
 493      * <code>getServiceProviders(Class, boolean)</code>.
 494      *
 495      * @param category the category to be retrieved from.
 496      * @param filter an instance of <code>ServiceRegistry.Filter</code>
 497      * whose <code>filter</code> method will be invoked.
 498      * @param useOrdering <code>true</code> if pairwise orderings
 499      * should be taken account in ordering the returned objects.
 500      * @param <T> the type of the category.
 501      *
 502      * @return an <code>Iterator</code> containing service provider
 503      * objects from the given category, possibly in order.
 504      *
 505      * @exception IllegalArgumentException if there is no category
 506      * corresponding to <code>category</code>.
 507      */
 508     public <T> Iterator<T> getServiceProviders(Class<T> category,
 509                                                Filter filter,
 510                                                boolean useOrdering) {
 511         SubRegistry reg = (SubRegistry)categoryMap.get(category);
 512         if (reg == null) {
 513             throw new IllegalArgumentException("category unknown!");
 514         }
 515         Iterator iter = getServiceProviders(category, useOrdering);
 516         return new FilterIterator(iter, filter);
 517     }
 518 
 519     /**
 520      * Returns the currently registered service provider object that
 521      * is of the given class type.  At most one object of a given
 522      * class is allowed to be registered at any given time.  If no
 523      * registered object has the desired class type, <code>null</code>
 524      * is returned.
 525      *
 526      * @param providerClass the <code>Class</code> of the desired
 527      * service provider object.
 528      * @param <T> the type of the provider.
 529      *
 530      * @return a currently registered service provider object with the
 531      * desired <code>Class</code>type, or <code>null</code> is none is
 532      * present.
 533      *
 534      * @exception IllegalArgumentException if <code>providerClass</code> is
 535      * <code>null</code>.
 536      */
 537     public <T> T getServiceProviderByClass(Class<T> providerClass) {
 538         if (providerClass == null) {
 539             throw new IllegalArgumentException("providerClass == null!");
 540         }
 541         Iterator iter = categoryMap.keySet().iterator();
 542         while (iter.hasNext()) {
 543             Class c = (Class)iter.next();
 544             if (c.isAssignableFrom(providerClass)) {
 545                 SubRegistry reg = (SubRegistry)categoryMap.get(c);
 546                 T provider = reg.getServiceProviderByClass(providerClass);
 547                 if (provider != null) {
 548                     return provider;
 549                 }
 550             }
 551         }
 552         return null;
 553     }
 554 
 555     /**
 556      * Sets a pairwise ordering between two service provider objects
 557      * within a given category.  If one or both objects are not
 558      * currently registered within the given category, or if the
 559      * desired ordering is already set, nothing happens and
 560      * <code>false</code> is returned.  If the providers previously
 561      * were ordered in the reverse direction, that ordering is
 562      * removed.
 563      *
 564      * <p> The ordering will be used by the
 565      * <code>getServiceProviders</code> methods when their


 572      * <code>firstProvider</code> is preferred.
 573      * @param <T> the type of the category.
 574      *
 575      * @return <code>true</code> if a previously unset ordering
 576      * was established.
 577      *
 578      * @exception IllegalArgumentException if either provider is
 579      * <code>null</code> or they are the same object.
 580      * @exception IllegalArgumentException if there is no category
 581      * corresponding to <code>category</code>.
 582      */
 583     public <T> boolean setOrdering(Class<T> category,
 584                                    T firstProvider,
 585                                    T secondProvider) {
 586         if (firstProvider == null || secondProvider == null) {
 587             throw new IllegalArgumentException("provider is null!");
 588         }
 589         if (firstProvider == secondProvider) {
 590             throw new IllegalArgumentException("providers are the same!");
 591         }
 592         SubRegistry reg = (SubRegistry)categoryMap.get(category);
 593         if (reg == null) {
 594             throw new IllegalArgumentException("category unknown!");
 595         }
 596         if (reg.contains(firstProvider) &&
 597             reg.contains(secondProvider)) {
 598             return reg.setOrdering(firstProvider, secondProvider);
 599         }
 600         return false;
 601     }
 602 
 603     /**
 604      * Sets a pairwise ordering between two service provider objects
 605      * within a given category.  If one or both objects are not
 606      * currently registered within the given category, or if no
 607      * ordering is currently set between them, nothing happens
 608      * and <code>false</code> is returned.
 609      *
 610      * <p> The ordering will be used by the
 611      * <code>getServiceProviders</code> methods when their
 612      * <code>useOrdering</code> argument is <code>true</code>.


 618      * <code>firstProvider</code> was formerly preferred.
 619      * @param <T> the type of the category.
 620      *
 621      * @return <code>true</code> if a previously set ordering was
 622      * disestablished.
 623      *
 624      * @exception IllegalArgumentException if either provider is
 625      * <code>null</code> or they are the same object.
 626      * @exception IllegalArgumentException if there is no category
 627      * corresponding to <code>category</code>.
 628      */
 629     public <T> boolean unsetOrdering(Class<T> category,
 630                                      T firstProvider,
 631                                      T secondProvider) {
 632         if (firstProvider == null || secondProvider == null) {
 633             throw new IllegalArgumentException("provider is null!");
 634         }
 635         if (firstProvider == secondProvider) {
 636             throw new IllegalArgumentException("providers are the same!");
 637         }
 638         SubRegistry reg = (SubRegistry)categoryMap.get(category);
 639         if (reg == null) {
 640             throw new IllegalArgumentException("category unknown!");
 641         }
 642         if (reg.contains(firstProvider) &&
 643             reg.contains(secondProvider)) {
 644             return reg.unsetOrdering(firstProvider, secondProvider);
 645         }
 646         return false;
 647     }
 648 
 649     /**
 650      * Deregisters all service provider object currently registered
 651      * under the given category.
 652      *
 653      * @param category the category to be emptied.
 654      *
 655      * @exception IllegalArgumentException if there is no category
 656      * corresponding to <code>category</code>.
 657      */
 658     public void deregisterAll(Class<?> category) {
 659         SubRegistry reg = (SubRegistry)categoryMap.get(category);
 660         if (reg == null) {
 661             throw new IllegalArgumentException("category unknown!");
 662         }
 663         reg.clear();
 664     }
 665 
 666     /**
 667      * Deregisters all currently registered service providers from all
 668      * categories.
 669      */
 670     public void deregisterAll() {
 671         Iterator iter = categoryMap.values().iterator();
 672         while (iter.hasNext()) {
 673             SubRegistry reg = (SubRegistry)iter.next();
 674             reg.clear();
 675         }
 676     }
 677 
 678     /**
 679      * Finalizes this object prior to garbage collection.  The
 680      * <code>deregisterAll</code> method is called to deregister all
 681      * currently registered service providers.  This method should not
 682      * be called from application code.
 683      *
 684      * @exception Throwable if an error occurs during superclass
 685      * finalization.
 686      */
 687     public void finalize() throws Throwable {
 688         deregisterAll();
 689         super.finalize();
 690     }
 691 }
 692 
 693 
 694 /**
 695  * A portion of a registry dealing with a single superclass or
 696  * interface.
 697  */
 698 class SubRegistry {
 699 
 700     ServiceRegistry registry;
 701 
 702     Class category;
 703 
 704     // Provider Objects organized by partial oridering
 705     PartiallyOrderedSet poset = new PartiallyOrderedSet();
 706 
 707     // Class -> Provider Object of that class
 708     Map<Class<?>,Object> map = new HashMap();


 709 
 710     public SubRegistry(ServiceRegistry registry, Class category) {
 711         this.registry = registry;
 712         this.category = category;
 713     }
 714 
 715     public boolean registerServiceProvider(Object provider) {
 716         Object oprovider = map.get(provider.getClass());
 717         boolean present =  oprovider != null;
 718 
 719         if (present) {
 720             deregisterServiceProvider(oprovider);
 721         }
 722         map.put(provider.getClass(), provider);
 723         poset.add(provider);
 724         if (provider instanceof RegisterableService) {
 725             RegisterableService rs = (RegisterableService)provider;
 726             rs.onRegistration(registry, category);
 727         }
 728 
 729         return !present;
 730     }


 748             return true;
 749         }
 750         return false;
 751     }
 752 
 753     public boolean contains(Object provider) {
 754         Object oprovider = map.get(provider.getClass());
 755         return oprovider == provider;
 756     }
 757 
 758     public boolean setOrdering(Object firstProvider,
 759                                Object secondProvider) {
 760         return poset.setOrdering(firstProvider, secondProvider);
 761     }
 762 
 763     public boolean unsetOrdering(Object firstProvider,
 764                                  Object secondProvider) {
 765         return poset.unsetOrdering(firstProvider, secondProvider);
 766     }
 767 
 768     public Iterator getServiceProviders(boolean useOrdering) {
 769         if (useOrdering) {
 770             return poset.iterator();
 771         } else {
 772             return map.values().iterator();
 773         }
 774     }
 775 

 776     public <T> T getServiceProviderByClass(Class<T> providerClass) {
 777         return (T)map.get(providerClass);
 778     }
 779 
 780     public void clear() {
 781         Iterator iter = map.values().iterator();
 782         while (iter.hasNext()) {
 783             Object provider = iter.next();
 784             iter.remove();
 785 
 786             if (provider instanceof RegisterableService) {
 787                 RegisterableService rs = (RegisterableService)provider;
 788                 rs.onDeregistration(registry, category);
 789             }
 790         }
 791         poset.clear();
 792     }
 793 
 794     public void finalize() {
 795         clear();
 796     }
 797 }
 798 
 799 
 800 /**
 801  * A class for wrapping <code>Iterators</code> with a filter function.
 802  * This provides an iterator for a subset without duplication.
 803  */
 804 class FilterIterator<T> implements Iterator<T> {
 805 
 806     private Iterator<T> iter;
 807     private ServiceRegistry.Filter filter;
 808 
 809     private T next = null;
 810 
 811     public FilterIterator(Iterator<T> iter,
 812                           ServiceRegistry.Filter filter) {
 813         this.iter = iter;
 814         this.filter = filter;
 815         advance();
 816     }
 817 
 818     private void advance() {
 819         while (iter.hasNext()) {
 820             T elt = iter.next();
 821             if (filter.filter(elt)) {
 822                 next = elt;
 823                 return;
 824             }
 825         }
 826 
 827         next = null;
 828     }
 829 
 830     public boolean hasNext() {
 831         return next != null;


  82  * <p> The service provider classes should be to be lightweight and
  83  * quick to load.  Implementations of these interfaces should avoid
  84  * complex dependencies on other classes and on native code. The usual
  85  * pattern for more complex services is to register a lightweight
  86  * proxy for the heavyweight service.
  87  *
  88  * <p> An application may customize the contents of a registry as it
  89  * sees fit, so long as it has the appropriate runtime permission.
  90  *
  91  * <p> For more details on declaring service providers, and the JAR
  92  * format in general, see the <a
  93  * href="../../../../technotes/guides/jar/jar.html">
  94  * JAR File Specification</a>.
  95  *
  96  * @see RegisterableService
  97  *
  98  */
  99 public class ServiceRegistry {
 100 
 101     // Class -> Registry
 102     private Map<Class<?>, SubRegistry> categoryMap = new HashMap<>();
 103 
 104     /**
 105      * Constructs a <code>ServiceRegistry</code> instance with a
 106      * set of categories taken from the <code>categories</code>
 107      * argument.
 108      *
 109      * @param categories an <code>Iterator</code> containing
 110      * <code>Class</code> objects to be used to define categories.
 111      *
 112      * @exception IllegalArgumentException if
 113      * <code>categories</code> is <code>null</code>.
 114      */
 115     public ServiceRegistry(Iterator<Class<?>> categories) {
 116         if (categories == null) {
 117             throw new IllegalArgumentException("categories == null!");
 118         }
 119         while (categories.hasNext()) {
 120             Class<?> category = categories.next();
 121             SubRegistry reg = new SubRegistry(this, category);
 122             categoryMap.put(category, reg);
 123         }
 124     }
 125 
 126     // The following two methods expose functionality from
 127     // sun.misc.Service.  If that class is made public, they may be
 128     // removed.
 129     //
 130     // The sun.misc.ServiceConfigurationError class may also be
 131     // exposed, in which case the references to 'an
 132     // <code>Error</code>' below should be changed to 'a
 133     // <code>ServiceConfigurationError</code>'.
 134 
 135     /**
 136      * Searches for implementations of a particular service class
 137      * using the given class loader.
 138      *
 139      * <p> This method transforms the name of the given service class
 140      * into a provider-configuration filename as described in the


 200      *
 201      * @exception IllegalArgumentException if
 202      * <code>providerClass</code> is <code>null</code>.
 203      */
 204     public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {
 205         if (providerClass == null) {
 206             throw new IllegalArgumentException("providerClass == null!");
 207         }
 208         return ServiceLoader.load(providerClass).iterator();
 209     }
 210 
 211     /**
 212      * Returns an <code>Iterator</code> of <code>Class</code> objects
 213      * indicating the current set of categories.  The iterator will be
 214      * empty if no categories exist.
 215      *
 216      * @return an <code>Iterator</code> containing
 217      * <code>Class</code>objects.
 218      */
 219     public Iterator<Class<?>> getCategories() {
 220         Set<Class<?>> keySet = categoryMap.keySet();
 221         return keySet.iterator();
 222     }
 223 
 224     /**
 225      * Returns an Iterator containing the subregistries to which the
 226      * provider belongs.
 227      */
 228     private Iterator<SubRegistry> getSubRegistries(Object provider) {
 229         List<SubRegistry> l = new ArrayList<>();
 230         Iterator<Class<?>> iter = categoryMap.keySet().iterator();
 231         while (iter.hasNext()) {
 232             Class<?> c = iter.next();
 233             if (c.isAssignableFrom(provider.getClass())) {
 234                 l.add(categoryMap.get(c));
 235             }
 236         }
 237         return l.iterator();
 238     }
 239 
 240     /**
 241      * Adds a service provider object to the registry.  The provider
 242      * is associated with the given category.
 243      *
 244      * <p> If <code>provider</code> implements the
 245      * <code>RegisterableService</code> interface, its
 246      * <code>onRegistration</code> method will be called.  Its
 247      * <code>onDeregistration</code> method will be called each time
 248      * it is deregistered from a category, for example if a
 249      * category is removed or the registry is garbage collected.
 250      *
 251      * @param provider the service provide object to be registered.
 252      * @param category the category under which to register the
 253      * provider.
 254      * @param <T> the type of the provider.
 255      *
 256      * @return true if no provider of the same class was previously
 257      * registered in the same category category.
 258      *
 259      * @exception IllegalArgumentException if <code>provider</code> is
 260      * <code>null</code>.
 261      * @exception IllegalArgumentException if there is no category
 262      * corresponding to <code>category</code>.
 263      * @exception ClassCastException if provider does not implement
 264      * the <code>Class</code> defined by <code>category</code>.
 265      */
 266     public <T> boolean registerServiceProvider(T provider,
 267                                                Class<T> category) {
 268         if (provider == null) {
 269             throw new IllegalArgumentException("provider == null!");
 270         }
 271         SubRegistry reg = categoryMap.get(category);
 272         if (reg == null) {
 273             throw new IllegalArgumentException("category unknown!");
 274         }
 275         if (!category.isAssignableFrom(provider.getClass())) {
 276             throw new ClassCastException();
 277         }
 278 
 279         return reg.registerServiceProvider(provider);
 280     }
 281 
 282     /**
 283      * Adds a service provider object to the registry.  The provider
 284      * is associated within each category present in the registry
 285      * whose <code>Class</code> it implements.
 286      *
 287      * <p> If <code>provider</code> implements the
 288      * <code>RegisterableService</code> interface, its
 289      * <code>onRegistration</code> method will be called once for each
 290      * category it is registered under.  Its
 291      * <code>onDeregistration</code> method will be called each time
 292      * it is deregistered from a category or when the registry is
 293      * finalized.
 294      *
 295      * @param provider the service provider object to be registered.
 296      *
 297      * @exception IllegalArgumentException if
 298      * <code>provider</code> is <code>null</code>.
 299      */
 300     public void registerServiceProvider(Object provider) {
 301         if (provider == null) {
 302             throw new IllegalArgumentException("provider == null!");
 303         }
 304         Iterator<SubRegistry> regs = getSubRegistries(provider);
 305         while (regs.hasNext()) {
 306             SubRegistry reg = regs.next();
 307             reg.registerServiceProvider(provider);
 308         }
 309     }
 310 
 311     /**
 312      * Adds a set of service provider objects, taken from an
 313      * <code>Iterator</code> to the registry.  Each provider is
 314      * associated within each category present in the registry whose
 315      * <code>Class</code> it implements.
 316      *
 317      * <p> For each entry of <code>providers</code> that implements
 318      * the <code>RegisterableService</code> interface, its
 319      * <code>onRegistration</code> method will be called once for each
 320      * category it is registered under.  Its
 321      * <code>onDeregistration</code> method will be called each time
 322      * it is deregistered from a category or when the registry is
 323      * finalized.
 324      *
 325      * @param providers an Iterator containing service provider
 326      * objects to be registered.


 354      * @param category the category from which to deregister the
 355      * provider.
 356      * @param <T> the type of the provider.
 357      *
 358      * @return <code>true</code> if the provider was previously
 359      * registered in the same category category,
 360      * <code>false</code> otherwise.
 361      *
 362      * @exception IllegalArgumentException if <code>provider</code> is
 363      * <code>null</code>.
 364      * @exception IllegalArgumentException if there is no category
 365      * corresponding to <code>category</code>.
 366      * @exception ClassCastException if provider does not implement
 367      * the class defined by <code>category</code>.
 368      */
 369     public <T> boolean deregisterServiceProvider(T provider,
 370                                                  Class<T> category) {
 371         if (provider == null) {
 372             throw new IllegalArgumentException("provider == null!");
 373         }
 374         SubRegistry reg = categoryMap.get(category);
 375         if (reg == null) {
 376             throw new IllegalArgumentException("category unknown!");
 377         }
 378         if (!category.isAssignableFrom(provider.getClass())) {
 379             throw new ClassCastException();
 380         }
 381         return reg.deregisterServiceProvider(provider);
 382     }
 383 
 384     /**
 385      * Removes a service provider object from all categories that
 386      * contain it.
 387      *
 388      * @param provider the service provider object to be deregistered.
 389      *
 390      * @exception IllegalArgumentException if <code>provider</code> is
 391      * <code>null</code>.
 392      */
 393     public void deregisterServiceProvider(Object provider) {
 394         if (provider == null) {
 395             throw new IllegalArgumentException("provider == null!");
 396         }
 397         Iterator<SubRegistry> regs = getSubRegistries(provider);
 398         while (regs.hasNext()) {
 399             SubRegistry reg = regs.next();
 400             reg.deregisterServiceProvider(provider);
 401         }
 402     }
 403 
 404     /**
 405      * Returns <code>true</code> if <code>provider</code> is currently
 406      * registered.
 407      *
 408      * @param provider the service provider object to be queried.
 409      *
 410      * @return <code>true</code> if the given provider has been
 411      * registered.
 412      *
 413      * @exception IllegalArgumentException if <code>provider</code> is
 414      * <code>null</code>.
 415      */
 416     public boolean contains(Object provider) {
 417         if (provider == null) {
 418             throw new IllegalArgumentException("provider == null!");
 419         }
 420         Iterator<SubRegistry> regs = getSubRegistries(provider);
 421         while (regs.hasNext()) {
 422             SubRegistry reg = regs.next();
 423             if (reg.contains(provider)) {
 424                 return true;
 425             }
 426         }
 427 
 428         return false;
 429     }
 430 
 431     /**
 432      * Returns an <code>Iterator</code> containing all registered
 433      * service providers in the given category.  If
 434      * <code>useOrdering</code> is <code>false</code>, the iterator
 435      * will return all of the server provider objects in an arbitrary
 436      * order.  Otherwise, the ordering will respect any pairwise
 437      * orderings that have been set.  If the graph of pairwise
 438      * orderings contains cycles, any providers that belong to a cycle
 439      * will not be returned.
 440      *
 441      * @param category the category to be retrieved from.
 442      * @param useOrdering <code>true</code> if pairwise orderings
 443      * should be taken account in ordering the returned objects.
 444      * @param <T> the type of the category.
 445      *
 446      * @return an <code>Iterator</code> containing service provider
 447      * objects from the given category, possibly in order.
 448      *
 449      * @exception IllegalArgumentException if there is no category
 450      * corresponding to <code>category</code>.
 451      */
 452     public <T> Iterator<T> getServiceProviders(Class<T> category,
 453                                                boolean useOrdering) {
 454         SubRegistry reg = categoryMap.get(category);
 455         if (reg == null) {
 456             throw new IllegalArgumentException("category unknown!");
 457         }
 458         @SuppressWarnings("unchecked")
 459         Iterator<T> it = (Iterator<T>)reg.getServiceProviders(useOrdering);
 460         return it;
 461     }
 462 
 463     /**
 464      * A simple filter interface used by
 465      * <code>ServiceRegistry.getServiceProviders</code> to select
 466      * providers matching an arbitrary criterion.  Classes that
 467      * implement this interface should be defined in order to make use
 468      * of the <code>getServiceProviders</code> method of
 469      * <code>ServiceRegistry</code> that takes a <code>Filter</code>.
 470      *
 471      * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)
 472      */
 473     public interface Filter {
 474 
 475         /**
 476          * Returns <code>true</code> if the given
 477          * <code>provider</code> object matches the criterion defined
 478          * by this <code>Filter</code>.
 479          *
 480          * @param provider a service provider <code>Object</code>.


 493      * <p> The <code>useOrdering</code> argument controls the
 494      * ordering of the results using the same rules as
 495      * <code>getServiceProviders(Class, boolean)</code>.
 496      *
 497      * @param category the category to be retrieved from.
 498      * @param filter an instance of <code>ServiceRegistry.Filter</code>
 499      * whose <code>filter</code> method will be invoked.
 500      * @param useOrdering <code>true</code> if pairwise orderings
 501      * should be taken account in ordering the returned objects.
 502      * @param <T> the type of the category.
 503      *
 504      * @return an <code>Iterator</code> containing service provider
 505      * objects from the given category, possibly in order.
 506      *
 507      * @exception IllegalArgumentException if there is no category
 508      * corresponding to <code>category</code>.
 509      */
 510     public <T> Iterator<T> getServiceProviders(Class<T> category,
 511                                                Filter filter,
 512                                                boolean useOrdering) {
 513         SubRegistry reg = categoryMap.get(category);
 514         if (reg == null) {
 515             throw new IllegalArgumentException("category unknown!");
 516         }
 517         Iterator<T> iter = getServiceProviders(category, useOrdering);
 518         return new FilterIterator<>(iter, filter);
 519     }
 520 
 521     /**
 522      * Returns the currently registered service provider object that
 523      * is of the given class type.  At most one object of a given
 524      * class is allowed to be registered at any given time.  If no
 525      * registered object has the desired class type, <code>null</code>
 526      * is returned.
 527      *
 528      * @param providerClass the <code>Class</code> of the desired
 529      * service provider object.
 530      * @param <T> the type of the provider.
 531      *
 532      * @return a currently registered service provider object with the
 533      * desired <code>Class</code>type, or <code>null</code> is none is
 534      * present.
 535      *
 536      * @exception IllegalArgumentException if <code>providerClass</code> is
 537      * <code>null</code>.
 538      */
 539     public <T> T getServiceProviderByClass(Class<T> providerClass) {
 540         if (providerClass == null) {
 541             throw new IllegalArgumentException("providerClass == null!");
 542         }
 543         Iterator<Class<?>> iter = categoryMap.keySet().iterator();
 544         while (iter.hasNext()) {
 545             Class<?> c = iter.next();
 546             if (c.isAssignableFrom(providerClass)) {
 547                 SubRegistry reg = categoryMap.get(c);
 548                 T provider = reg.getServiceProviderByClass(providerClass);
 549                 if (provider != null) {
 550                     return provider;
 551                 }
 552             }
 553         }
 554         return null;
 555     }
 556 
 557     /**
 558      * Sets a pairwise ordering between two service provider objects
 559      * within a given category.  If one or both objects are not
 560      * currently registered within the given category, or if the
 561      * desired ordering is already set, nothing happens and
 562      * <code>false</code> is returned.  If the providers previously
 563      * were ordered in the reverse direction, that ordering is
 564      * removed.
 565      *
 566      * <p> The ordering will be used by the
 567      * <code>getServiceProviders</code> methods when their


 574      * <code>firstProvider</code> is preferred.
 575      * @param <T> the type of the category.
 576      *
 577      * @return <code>true</code> if a previously unset ordering
 578      * was established.
 579      *
 580      * @exception IllegalArgumentException if either provider is
 581      * <code>null</code> or they are the same object.
 582      * @exception IllegalArgumentException if there is no category
 583      * corresponding to <code>category</code>.
 584      */
 585     public <T> boolean setOrdering(Class<T> category,
 586                                    T firstProvider,
 587                                    T secondProvider) {
 588         if (firstProvider == null || secondProvider == null) {
 589             throw new IllegalArgumentException("provider is null!");
 590         }
 591         if (firstProvider == secondProvider) {
 592             throw new IllegalArgumentException("providers are the same!");
 593         }
 594         SubRegistry reg = categoryMap.get(category);
 595         if (reg == null) {
 596             throw new IllegalArgumentException("category unknown!");
 597         }
 598         if (reg.contains(firstProvider) &&
 599             reg.contains(secondProvider)) {
 600             return reg.setOrdering(firstProvider, secondProvider);
 601         }
 602         return false;
 603     }
 604 
 605     /**
 606      * Sets a pairwise ordering between two service provider objects
 607      * within a given category.  If one or both objects are not
 608      * currently registered within the given category, or if no
 609      * ordering is currently set between them, nothing happens
 610      * and <code>false</code> is returned.
 611      *
 612      * <p> The ordering will be used by the
 613      * <code>getServiceProviders</code> methods when their
 614      * <code>useOrdering</code> argument is <code>true</code>.


 620      * <code>firstProvider</code> was formerly preferred.
 621      * @param <T> the type of the category.
 622      *
 623      * @return <code>true</code> if a previously set ordering was
 624      * disestablished.
 625      *
 626      * @exception IllegalArgumentException if either provider is
 627      * <code>null</code> or they are the same object.
 628      * @exception IllegalArgumentException if there is no category
 629      * corresponding to <code>category</code>.
 630      */
 631     public <T> boolean unsetOrdering(Class<T> category,
 632                                      T firstProvider,
 633                                      T secondProvider) {
 634         if (firstProvider == null || secondProvider == null) {
 635             throw new IllegalArgumentException("provider is null!");
 636         }
 637         if (firstProvider == secondProvider) {
 638             throw new IllegalArgumentException("providers are the same!");
 639         }
 640         SubRegistry reg = categoryMap.get(category);
 641         if (reg == null) {
 642             throw new IllegalArgumentException("category unknown!");
 643         }
 644         if (reg.contains(firstProvider) &&
 645             reg.contains(secondProvider)) {
 646             return reg.unsetOrdering(firstProvider, secondProvider);
 647         }
 648         return false;
 649     }
 650 
 651     /**
 652      * Deregisters all service provider object currently registered
 653      * under the given category.
 654      *
 655      * @param category the category to be emptied.
 656      *
 657      * @exception IllegalArgumentException if there is no category
 658      * corresponding to <code>category</code>.
 659      */
 660     public void deregisterAll(Class<?> category) {
 661         SubRegistry reg = categoryMap.get(category);
 662         if (reg == null) {
 663             throw new IllegalArgumentException("category unknown!");
 664         }
 665         reg.clear();
 666     }
 667 
 668     /**
 669      * Deregisters all currently registered service providers from all
 670      * categories.
 671      */
 672     public void deregisterAll() {
 673         Iterator<SubRegistry> iter = categoryMap.values().iterator();
 674         while (iter.hasNext()) {
 675             SubRegistry reg = iter.next();
 676             reg.clear();
 677         }
 678     }
 679 
 680     /**
 681      * Finalizes this object prior to garbage collection.  The
 682      * <code>deregisterAll</code> method is called to deregister all
 683      * currently registered service providers.  This method should not
 684      * be called from application code.
 685      *
 686      * @exception Throwable if an error occurs during superclass
 687      * finalization.
 688      */
 689     public void finalize() throws Throwable {
 690         deregisterAll();
 691         super.finalize();
 692     }
 693 }
 694 
 695 
 696 /**
 697  * A portion of a registry dealing with a single superclass or
 698  * interface.
 699  */
 700 class SubRegistry {
 701 
 702     ServiceRegistry registry;
 703 
 704     Class<?> category;
 705 
 706     // Provider Objects organized by partial oridering
 707     PartiallyOrderedSet<Object> poset = new PartiallyOrderedSet<>();
 708 
 709     // Class -> Provider Object of that class
 710     // No way to express heterogeneous map, we want
 711     // Map<Class<T>, T>, where T is ?
 712     Map<Class<?>, Object> map = new HashMap<>();
 713 
 714     public SubRegistry(ServiceRegistry registry, Class<?> category) {
 715         this.registry = registry;
 716         this.category = category;
 717     }
 718 
 719     public boolean registerServiceProvider(Object provider) {
 720         Object oprovider = map.get(provider.getClass());
 721         boolean present =  oprovider != null;
 722 
 723         if (present) {
 724             deregisterServiceProvider(oprovider);
 725         }
 726         map.put(provider.getClass(), provider);
 727         poset.add(provider);
 728         if (provider instanceof RegisterableService) {
 729             RegisterableService rs = (RegisterableService)provider;
 730             rs.onRegistration(registry, category);
 731         }
 732 
 733         return !present;
 734     }


 752             return true;
 753         }
 754         return false;
 755     }
 756 
 757     public boolean contains(Object provider) {
 758         Object oprovider = map.get(provider.getClass());
 759         return oprovider == provider;
 760     }
 761 
 762     public boolean setOrdering(Object firstProvider,
 763                                Object secondProvider) {
 764         return poset.setOrdering(firstProvider, secondProvider);
 765     }
 766 
 767     public boolean unsetOrdering(Object firstProvider,
 768                                  Object secondProvider) {
 769         return poset.unsetOrdering(firstProvider, secondProvider);
 770     }
 771 
 772     public Iterator<Object> getServiceProviders(boolean useOrdering) {
 773         if (useOrdering) {
 774             return poset.iterator();
 775         } else {
 776             return map.values().iterator();
 777         }
 778     }
 779 
 780     @SuppressWarnings("unchecked")
 781     public <T> T getServiceProviderByClass(Class<T> providerClass) {
 782         return (T)map.get(providerClass);
 783     }
 784 
 785     public void clear() {
 786         Iterator<Object> iter = map.values().iterator();
 787         while (iter.hasNext()) {
 788             Object provider = iter.next();
 789             iter.remove();
 790 
 791             if (provider instanceof RegisterableService) {
 792                 RegisterableService rs = (RegisterableService)provider;
 793                 rs.onDeregistration(registry, category);
 794             }
 795         }
 796         poset.clear();
 797     }
 798 
 799     public void finalize() {
 800         clear();
 801     }
 802 }
 803 
 804 
 805 /**
 806  * A class for wrapping <code>Iterators</code> with a filter function.
 807  * This provides an iterator for a subset without duplication.
 808  */
 809 class FilterIterator<T> implements Iterator<T> {
 810 
 811     private Iterator<? extends T> iter;
 812     private ServiceRegistry.Filter filter;
 813 
 814     private T next = null;
 815 
 816     public FilterIterator(Iterator<? extends T> iter,
 817                           ServiceRegistry.Filter filter) {
 818         this.iter = iter;
 819         this.filter = filter;
 820         advance();
 821     }
 822 
 823     private void advance() {
 824         while (iter.hasNext()) {
 825             T elt = iter.next();
 826             if (filter.filter(elt)) {
 827                 next = elt;
 828                 return;
 829             }
 830         }
 831 
 832         next = null;
 833     }
 834 
 835     public boolean hasNext() {
 836         return next != null;