1 /*
   2  * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.imageio.spi;
  27 
  28 import java.io.File;
  29 import java.util.ArrayList;
  30 import java.util.HashMap;
  31 import java.util.Iterator;
  32 import java.util.List;
  33 import java.util.Map;
  34 import java.util.NoSuchElementException;
  35 import java.util.Set;
  36 import java.util.ServiceLoader;
  37 
  38 /**
  39  * A registry for service provider instances.
  40  *
  41  * <p> A <i>service</i> is a well-known set of interfaces and (usually
  42  * abstract) classes.  A <i>service provider</i> is a specific
  43  * implementation of a service.  The classes in a provider typically
  44  * implement the interface or subclass the class defined by the
  45  * service itself.
  46  *
  47  * <p> Service providers are stored in one or more <i>categories</i>,
  48  * each of which is defined by a class of interface (described by a
  49  * <code>Class</code> object) that all of its members must implement.
  50  * The set of categories may be changed dynamically.
  51  *
  52  * <p> Only a single instance of a given leaf class (that is, the
  53  * actual class returned by <code>getClass()</code>, as opposed to any
  54  * inherited classes or interfaces) may be registered.  That is,
  55  * suppose that the
  56  * <code>com.mycompany.mypkg.GreenServiceProvider</code> class
  57  * implements the <code>com.mycompany.mypkg.MyService</code>
  58  * interface.  If a <code>GreenServiceProvider</code> instance is
  59  * registered, it will be stored in the category defined by the
  60  * <code>MyService</code> class.  If a new instance of
  61  * <code>GreenServiceProvider</code> is registered, it will replace
  62  * the previous instance.  In practice, service provider objects are
  63  * usually singletons so this behavior is appropriate.
  64  *
  65  * <p> To declare a service provider, a <code>services</code>
  66  * subdirectory is placed within the <code>META-INF</code> directory
  67  * that is present in every JAR file.  This directory contains a file
  68  * for each service provider interface that has one or more
  69  * implementation classes present in the JAR file.  For example, if
  70  * the JAR file contained a class named
  71  * <code>com.mycompany.mypkg.MyServiceImpl</code> which implements the
  72  * <code>javax.someapi.SomeService</code> interface, the JAR file
  73  * would contain a file named: <pre>
  74  * META-INF/services/javax.someapi.SomeService </pre>
  75  *
  76  * containing the line:
  77  *
  78  * <pre>
  79  * com.mycompany.mypkg.MyService
  80  * </pre>
  81  *
  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
 141      * class comment and then uses the <code>getResources</code>
 142      * method of the given class loader to find all available files
 143      * with that name.  These files are then read and parsed to
 144      * produce a list of provider-class names.  The iterator that is
 145      * returned uses the given class loader to look up and then
 146      * instantiate each element of the list.
 147      *
 148      * <p> Because it is possible for extensions to be installed into
 149      * a running Java virtual machine, this method may return
 150      * different results each time it is invoked.
 151      *
 152      * @param providerClass a <code>Class</code>object indicating the
 153      * class or interface of the service providers being detected.
 154      *
 155      * @param loader the class loader to be used to load
 156      * provider-configuration files and instantiate provider classes,
 157      * or <code>null</code> if the system class loader (or, failing that
 158      * the bootstrap class loader) is to be used.
 159      *
 160      * @param <T> the type of the providerClass.
 161      *
 162      * @return An <code>Iterator</code> that yields provider objects
 163      * for the given service, in some arbitrary order.  The iterator
 164      * will throw an <code>Error</code> if a provider-configuration
 165      * file violates the specified format or if a provider class
 166      * cannot be found and instantiated.
 167      *
 168      * @exception IllegalArgumentException if
 169      * <code>providerClass</code> is <code>null</code>.
 170      */
 171     public static <T> Iterator<T> lookupProviders(Class<T> providerClass,
 172                                                   ClassLoader loader)
 173     {
 174         if (providerClass == null) {
 175             throw new IllegalArgumentException("providerClass == null!");
 176         }
 177         return ServiceLoader.load(providerClass, loader).iterator();
 178     }
 179 
 180     /**
 181      * Locates and incrementally instantiates the available providers
 182      * of a given service using the context class loader.  This
 183      * convenience method is equivalent to:
 184      *
 185      * <pre>
 186      *   ClassLoader cl = Thread.currentThread().getContextClassLoader();
 187      *   return Service.providers(service, cl);
 188      * </pre>
 189      *
 190      * @param providerClass a <code>Class</code>object indicating the
 191      * class or interface of the service providers being detected.
 192      *
 193      * @param <T> the type of the providerClass.
 194      *
 195      * @return An <code>Iterator</code> that yields provider objects
 196      * for the given service, in some arbitrary order.  The iterator
 197      * will throw an <code>Error</code> if a provider-configuration
 198      * file violates the specified format or if a provider class
 199      * cannot be found and instantiated.
 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.
 327      *
 328      * @exception IllegalArgumentException if <code>providers</code>
 329      * is <code>null</code> or contains a <code>null</code> entry.
 330      */
 331     public void registerServiceProviders(Iterator<?> providers) {
 332         if (providers == null) {
 333             throw new IllegalArgumentException("provider == null!");
 334         }
 335         while (providers.hasNext()) {
 336             registerServiceProvider(providers.next());
 337         }
 338     }
 339 
 340     /**
 341      * Removes a service provider object from the given category.  If
 342      * the provider was not previously registered, nothing happens and
 343      * <code>false</code> is returned.  Otherwise, <code>true</code>
 344      * is returned.  If an object of the same class as
 345      * <code>provider</code> but not equal (using <code>==</code>) to
 346      * <code>provider</code> is registered, it will not be
 347      * deregistered.
 348      *
 349      * <p> If <code>provider</code> implements the
 350      * <code>RegisterableService</code> interface, its
 351      * <code>onDeregistration</code> method will be called.
 352      *
 353      * @param provider the service provider object to be deregistered.
 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>.
 479          *
 480          * @return true if the provider matches the criterion.
 481          */
 482         boolean filter(Object provider);
 483     }
 484 
 485     /**
 486      * Returns an <code>Iterator</code> containing service provider
 487      * objects within a given category that satisfy a criterion
 488      * imposed by the supplied <code>ServiceRegistry.Filter</code>
 489      * object's <code>filter</code> method.
 490      *
 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
 566      * <code>useOrdering</code> argument is <code>true</code>.
 567      *
 568      * @param category a <code>Class</code> object indicating the
 569      * category under which the preference is to be established.
 570      * @param firstProvider the preferred provider.
 571      * @param secondProvider the provider to which
 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>.
 613      *
 614      * @param category a <code>Class</code> object indicating the
 615      * category under which the preference is to be disestablished.
 616      * @param firstProvider the formerly preferred provider.
 617      * @param secondProvider the provider to which
 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     }
 731 
 732     /**
 733      * If the provider was not previously registered, do nothing.
 734      *
 735      * @return true if the provider was previously registered.
 736      */
 737     public boolean deregisterServiceProvider(Object provider) {
 738         Object oprovider = map.get(provider.getClass());
 739 
 740         if (provider == oprovider) {
 741             map.remove(provider.getClass());
 742             poset.remove(provider);
 743             if (provider instanceof RegisterableService) {
 744                 RegisterableService rs = (RegisterableService)provider;
 745                 rs.onDeregistration(registry, category);
 746             }
 747 
 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;
 832     }
 833 
 834     public T next() {
 835         if (next == null) {
 836             throw new NoSuchElementException();
 837         }
 838         T o = next;
 839         advance();
 840         return o;
 841     }
 842 
 843     public void remove() {
 844         throw new UnsupportedOperationException();
 845     }
 846 }