< prev index next >

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

Print this page




  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  *
  51  * <p>The set of categories supported is limited
  52  * to the following standard Image I/O service types:
  53  *
  54  * <ul>
  55  * <li>{@link ImageInputStreamSpi}
  56  * <li>{@link ImageOutputStreamSpi}
  57  * <li>{@link ImageReaderSpi}
  58  * <li>{@link ImageTranscoderSpi}
  59  * <li>{@link ImageWriterSpi}
  60  * </ul>
  61  *
  62  * <p>An attempt to load a provider that is not a subtype of one of the
  63  * above types will result in {@code IllegalArgumentException}. For
  64  * a general mechanism to load service providers, see
  65  * {@link java.util.ServiceLoader ServiceLoader}.
  66  *
  67  * <p> Only a single instance of a given leaf class (that is, the
  68  * actual class returned by <code>getClass()</code>, as opposed to any
  69  * inherited classes or interfaces) may be registered.  That is,
  70  * suppose that the
  71  * <code>com.mycompany.mypkg.GreenImageReaderProvider</code> class
  72  * is a subclass of <code>javax.imageio.spi.ImageReaderSpi</code>.
  73  * If a <code>GreenImageReaderProvider</code> instance is
  74  * registered, it will be stored in the category defined by the
  75  * <code>ImageReaderSpi</code> class.  If a new instance of
  76  * <code>GreenImageReaderProvider</code> is registered, it will replace
  77  * the previous instance.  In practice, service provider objects are
  78  * usually singletons so this behavior is appropriate.
  79  *
  80  * <p> To declare a service provider, a <code>services</code>
  81  * subdirectory is placed within the <code>META-INF</code> directory
  82  * that is present in every JAR file.  This directory contains a file
  83  * for each service provider interface that has one or more
  84  * implementation classes present in the JAR file.  For example, if
  85  * the JAR file contained a class named
  86  * <code>com.mycompany.mypkg.GreenImageReaderProvider</code> which implements the
  87  * <code>javax.imageio.spi.ImageReaderSpi</code> interface, the JAR file
  88  * would contain a file named: <pre>
  89  * META-INF/services/javax.imageio.spi.ImageReaderSpi</pre>
  90  *
  91  * containing the line:
  92  *
  93  * <pre>
  94  * com.mycompany.mypkg.GreenImageReaderProvider
  95  * </pre>
  96  *
  97  * <p> The service provider classes should be to be lightweight and
  98  * quick to load.  Implementations of these interfaces should avoid
  99  * complex dependencies on other classes and on native code. The usual
 100  * pattern for more complex services is to register a lightweight
 101  * proxy for the heavyweight service.
 102  *
 103  * <p> An application may customize the contents of a registry as it
 104  * sees fit, so long as it has the appropriate runtime permission.
 105  *
 106  * <p> For more details on declaring service providers, and the JAR
 107  * format in general, see the <a
 108  * href="../../../../technotes/guides/jar/jar.html">
 109  * JAR File Specification</a>.
 110  *
 111  * @see RegisterableService
 112  * @see java.util.ServiceLoader
 113  */
 114 public class ServiceRegistry {
 115 
 116     // Class -> Registry
 117     private Map<Class<?>, SubRegistry> categoryMap = new HashMap<>();
 118 
 119     /**
 120      * Constructs a <code>ServiceRegistry</code> instance with a
 121      * set of categories taken from the <code>categories</code>
 122      * argument. The categories must all be members of the set
 123      * of service types listed in the class specification.
 124      *
 125      * @param categories an <code>Iterator</code> containing
 126      * <code>Class</code> objects to be used to define categories.
 127      *
 128      * @exception IllegalArgumentException if
 129      * <code>categories</code> is <code>null</code>, or if
 130      * one of the categories is not an allowed service type.
 131      */
 132     public ServiceRegistry(Iterator<Class<?>> categories) {
 133         if (categories == null) {
 134             throw new IllegalArgumentException("categories == null!");
 135         }
 136         while (categories.hasNext()) {
 137             Class<?> category = categories.next();
 138             checkClassAllowed(category);
 139             SubRegistry reg = new SubRegistry(this, category);
 140             categoryMap.put(category, reg);
 141         }
 142     }
 143 
 144     /**
 145      * Searches for implementations of a particular service class
 146      * using the given class loader.
 147      *
 148      * <p>The service class must be one of the service types listed
 149      * in the class specification. If it is not, {@code IllegalArgumentException}
 150      * will be thrown.
 151      *
 152      * <p> This method transforms the name of the given service class
 153      * into a provider-configuration filename as described in the
 154      * class comment and then uses the <code>getResources</code>
 155      * method of the given class loader to find all available files
 156      * with that name.  These files are then read and parsed to
 157      * produce a list of provider-class names.  The iterator that is
 158      * returned uses the given class loader to look up and then
 159      * instantiate each element of the list.
 160      *
 161      * <p> Because it is possible for extensions to be installed into
 162      * a running Java virtual machine, this method may return
 163      * different results each time it is invoked.
 164      *
 165      * @param providerClass a <code>Class</code>object indicating the
 166      * class or interface of the service providers being detected.
 167      *
 168      * @param loader the class loader to be used to load
 169      * provider-configuration files and instantiate provider classes,
 170      * or <code>null</code> if the system class loader (or, failing that
 171      * the bootstrap class loader) is to be used.
 172      *
 173      * @param <T> the type of the providerClass.
 174      *
 175      * @return An <code>Iterator</code> that yields provider objects
 176      * for the given service, in some arbitrary order.  The iterator
 177      * will throw an <code>Error</code> if a provider-configuration
 178      * file violates the specified format or if a provider class
 179      * cannot be found and instantiated.
 180      *
 181      * @exception IllegalArgumentException if
 182      * <code>providerClass</code> is <code>null</code>, or if it is
 183      * not one of the allowed service types.
 184      */
 185     public static <T> Iterator<T> lookupProviders(Class<T> providerClass,
 186                                                   ClassLoader loader)
 187     {
 188         if (providerClass == null) {
 189             throw new IllegalArgumentException("providerClass == null!");
 190         }
 191         checkClassAllowed(providerClass);
 192         return ServiceLoader.load(providerClass, loader).iterator();
 193     }
 194 
 195     /**
 196      * Locates and incrementally instantiates the available providers
 197      * of a given service using the context class loader.  This
 198      * convenience method is equivalent to:
 199      *
 200      * <pre>
 201      *   ClassLoader cl = Thread.currentThread().getContextClassLoader();
 202      *   return Service.providers(service, cl);
 203      * </pre>
 204      *
 205      * <p>The service class must be one of the service types listed
 206      * in the class specification. If it is not, {@code IllegalArgumentException}
 207      * will be thrown.
 208      *
 209      * @param providerClass a <code>Class</code>object indicating the
 210      * class or interface of the service providers being detected.
 211      *
 212      * @param <T> the type of the providerClass.
 213      *
 214      * @return An <code>Iterator</code> that yields provider objects
 215      * for the given service, in some arbitrary order.  The iterator
 216      * will throw an <code>Error</code> if a provider-configuration
 217      * file violates the specified format or if a provider class
 218      * cannot be found and instantiated.
 219      *
 220      * @exception IllegalArgumentException if
 221      * <code>providerClass</code> is <code>null</code>, or if it is
 222      * not one of the allowed service types.
 223      */
 224     public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {
 225         if (providerClass == null) {
 226             throw new IllegalArgumentException("providerClass == null!");
 227         }
 228         checkClassAllowed(providerClass);
 229         return ServiceLoader.load(providerClass).iterator();
 230     }
 231 
 232     /**
 233      * Returns an <code>Iterator</code> of <code>Class</code> objects
 234      * indicating the current set of categories.  The iterator will be
 235      * empty if no categories exist.
 236      *
 237      * @return an <code>Iterator</code> containing
 238      * <code>Class</code>objects.
 239      */
 240     public Iterator<Class<?>> getCategories() {
 241         Set<Class<?>> keySet = categoryMap.keySet();
 242         return keySet.iterator();
 243     }
 244 
 245     /**
 246      * Returns an Iterator containing the subregistries to which the
 247      * provider belongs.
 248      */
 249     private Iterator<SubRegistry> getSubRegistries(Object provider) {
 250         List<SubRegistry> l = new ArrayList<>();
 251         Iterator<Class<?>> iter = categoryMap.keySet().iterator();
 252         while (iter.hasNext()) {
 253             Class<?> c = iter.next();
 254             if (c.isAssignableFrom(provider.getClass())) {
 255                 l.add(categoryMap.get(c));
 256             }
 257         }
 258         return l.iterator();
 259     }
 260 
 261     /**
 262      * Adds a service provider object to the registry.  The provider
 263      * is associated with the given category.
 264      *
 265      * <p> If <code>provider</code> implements the
 266      * <code>RegisterableService</code> interface, its
 267      * <code>onRegistration</code> method will be called.  Its
 268      * <code>onDeregistration</code> method will be called each time
 269      * it is deregistered from a category, for example if a
 270      * category is removed or the registry is garbage collected.
 271      *
 272      * @param provider the service provide object to be registered.
 273      * @param category the category under which to register the
 274      * provider.
 275      * @param <T> the type of the provider.
 276      *
 277      * @return true if no provider of the same class was previously
 278      * registered in the same category category.
 279      *
 280      * @exception IllegalArgumentException if <code>provider</code> is
 281      * <code>null</code>.
 282      * @exception IllegalArgumentException if there is no category
 283      * corresponding to <code>category</code>.
 284      * @exception ClassCastException if provider does not implement
 285      * the <code>Class</code> defined by <code>category</code>.
 286      */
 287     public <T> boolean registerServiceProvider(T provider,
 288                                                Class<T> category) {
 289         if (provider == null) {
 290             throw new IllegalArgumentException("provider == null!");
 291         }
 292         SubRegistry reg = categoryMap.get(category);
 293         if (reg == null) {
 294             throw new IllegalArgumentException("category unknown!");
 295         }
 296         if (!category.isAssignableFrom(provider.getClass())) {
 297             throw new ClassCastException();
 298         }
 299 
 300         return reg.registerServiceProvider(provider);
 301     }
 302 
 303     /**
 304      * Adds a service provider object to the registry.  The provider
 305      * is associated within each category present in the registry
 306      * whose <code>Class</code> it implements.
 307      *
 308      * <p> If <code>provider</code> implements the
 309      * <code>RegisterableService</code> interface, its
 310      * <code>onRegistration</code> method will be called once for each
 311      * category it is registered under.  Its
 312      * <code>onDeregistration</code> method will be called each time
 313      * it is deregistered from a category or when the registry is
 314      * finalized.
 315      *
 316      * @param provider the service provider object to be registered.
 317      *
 318      * @exception IllegalArgumentException if
 319      * <code>provider</code> is <code>null</code>.
 320      */
 321     public void registerServiceProvider(Object provider) {
 322         if (provider == null) {
 323             throw new IllegalArgumentException("provider == null!");
 324         }
 325         Iterator<SubRegistry> regs = getSubRegistries(provider);
 326         while (regs.hasNext()) {
 327             SubRegistry reg = regs.next();
 328             reg.registerServiceProvider(provider);
 329         }
 330     }
 331 
 332     /**
 333      * Adds a set of service provider objects, taken from an
 334      * <code>Iterator</code> to the registry.  Each provider is
 335      * associated within each category present in the registry whose
 336      * <code>Class</code> it implements.
 337      *
 338      * <p> For each entry of <code>providers</code> that implements
 339      * the <code>RegisterableService</code> interface, its
 340      * <code>onRegistration</code> method will be called once for each
 341      * category it is registered under.  Its
 342      * <code>onDeregistration</code> method will be called each time
 343      * it is deregistered from a category or when the registry is
 344      * finalized.
 345      *
 346      * @param providers an Iterator containing service provider
 347      * objects to be registered.
 348      *
 349      * @exception IllegalArgumentException if <code>providers</code>
 350      * is <code>null</code> or contains a <code>null</code> entry.
 351      */
 352     public void registerServiceProviders(Iterator<?> providers) {
 353         if (providers == null) {
 354             throw new IllegalArgumentException("provider == null!");
 355         }
 356         while (providers.hasNext()) {
 357             registerServiceProvider(providers.next());
 358         }
 359     }
 360 
 361     /**
 362      * Removes a service provider object from the given category.  If
 363      * the provider was not previously registered, nothing happens and
 364      * <code>false</code> is returned.  Otherwise, <code>true</code>
 365      * is returned.  If an object of the same class as
 366      * <code>provider</code> but not equal (using <code>==</code>) to
 367      * <code>provider</code> is registered, it will not be
 368      * deregistered.
 369      *
 370      * <p> If <code>provider</code> implements the
 371      * <code>RegisterableService</code> interface, its
 372      * <code>onDeregistration</code> method will be called.
 373      *
 374      * @param provider the service provider object to be deregistered.
 375      * @param category the category from which to deregister the
 376      * provider.
 377      * @param <T> the type of the provider.
 378      *
 379      * @return <code>true</code> if the provider was previously
 380      * registered in the same category category,
 381      * <code>false</code> otherwise.
 382      *
 383      * @exception IllegalArgumentException if <code>provider</code> is
 384      * <code>null</code>.
 385      * @exception IllegalArgumentException if there is no category
 386      * corresponding to <code>category</code>.
 387      * @exception ClassCastException if provider does not implement
 388      * the class defined by <code>category</code>.
 389      */
 390     public <T> boolean deregisterServiceProvider(T provider,
 391                                                  Class<T> category) {
 392         if (provider == null) {
 393             throw new IllegalArgumentException("provider == null!");
 394         }
 395         SubRegistry reg = categoryMap.get(category);
 396         if (reg == null) {
 397             throw new IllegalArgumentException("category unknown!");
 398         }
 399         if (!category.isAssignableFrom(provider.getClass())) {
 400             throw new ClassCastException();
 401         }
 402         return reg.deregisterServiceProvider(provider);
 403     }
 404 
 405     /**
 406      * Removes a service provider object from all categories that
 407      * contain it.
 408      *
 409      * @param provider the service provider object to be deregistered.
 410      *
 411      * @exception IllegalArgumentException if <code>provider</code> is
 412      * <code>null</code>.
 413      */
 414     public void deregisterServiceProvider(Object provider) {
 415         if (provider == null) {
 416             throw new IllegalArgumentException("provider == null!");
 417         }
 418         Iterator<SubRegistry> regs = getSubRegistries(provider);
 419         while (regs.hasNext()) {
 420             SubRegistry reg = regs.next();
 421             reg.deregisterServiceProvider(provider);
 422         }
 423     }
 424 
 425     /**
 426      * Returns <code>true</code> if <code>provider</code> is currently
 427      * registered.
 428      *
 429      * @param provider the service provider object to be queried.
 430      *
 431      * @return <code>true</code> if the given provider has been
 432      * registered.
 433      *
 434      * @exception IllegalArgumentException if <code>provider</code> is
 435      * <code>null</code>.
 436      */
 437     public boolean contains(Object provider) {
 438         if (provider == null) {
 439             throw new IllegalArgumentException("provider == null!");
 440         }
 441         Iterator<SubRegistry> regs = getSubRegistries(provider);
 442         while (regs.hasNext()) {
 443             SubRegistry reg = regs.next();
 444             if (reg.contains(provider)) {
 445                 return true;
 446             }
 447         }
 448 
 449         return false;
 450     }
 451 
 452     /**
 453      * Returns an <code>Iterator</code> containing all registered
 454      * service providers in the given category.  If
 455      * <code>useOrdering</code> is <code>false</code>, the iterator
 456      * will return all of the server provider objects in an arbitrary
 457      * order.  Otherwise, the ordering will respect any pairwise
 458      * orderings that have been set.  If the graph of pairwise
 459      * orderings contains cycles, any providers that belong to a cycle
 460      * will not be returned.
 461      *
 462      * @param category the category to be retrieved from.
 463      * @param useOrdering <code>true</code> if pairwise orderings
 464      * should be taken account in ordering the returned objects.
 465      * @param <T> the type of the category.
 466      *
 467      * @return an <code>Iterator</code> containing service provider
 468      * objects from the given category, possibly in order.
 469      *
 470      * @exception IllegalArgumentException if there is no category
 471      * corresponding to <code>category</code>.
 472      */
 473     public <T> Iterator<T> getServiceProviders(Class<T> category,
 474                                                boolean useOrdering) {
 475         SubRegistry reg = categoryMap.get(category);
 476         if (reg == null) {
 477             throw new IllegalArgumentException("category unknown!");
 478         }
 479         @SuppressWarnings("unchecked")
 480         Iterator<T> it = (Iterator<T>)reg.getServiceProviders(useOrdering);
 481         return it;
 482     }
 483 
 484     /**
 485      * A simple filter interface used by
 486      * <code>ServiceRegistry.getServiceProviders</code> to select
 487      * providers matching an arbitrary criterion.  Classes that
 488      * implement this interface should be defined in order to make use
 489      * of the <code>getServiceProviders</code> method of
 490      * <code>ServiceRegistry</code> that takes a <code>Filter</code>.
 491      *
 492      * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)
 493      */
 494     public interface Filter {
 495 
 496         /**
 497          * Returns <code>true</code> if the given
 498          * <code>provider</code> object matches the criterion defined
 499          * by this <code>Filter</code>.
 500          *
 501          * @param provider a service provider <code>Object</code>.
 502          *
 503          * @return true if the provider matches the criterion.
 504          */
 505         boolean filter(Object provider);
 506     }
 507 
 508     /**
 509      * Returns an <code>Iterator</code> containing service provider
 510      * objects within a given category that satisfy a criterion
 511      * imposed by the supplied <code>ServiceRegistry.Filter</code>
 512      * object's <code>filter</code> method.
 513      *
 514      * <p> The <code>useOrdering</code> argument controls the
 515      * ordering of the results using the same rules as
 516      * <code>getServiceProviders(Class, boolean)</code>.
 517      *
 518      * @param category the category to be retrieved from.
 519      * @param filter an instance of <code>ServiceRegistry.Filter</code>
 520      * whose <code>filter</code> method will be invoked.
 521      * @param useOrdering <code>true</code> if pairwise orderings
 522      * should be taken account in ordering the returned objects.
 523      * @param <T> the type of the category.
 524      *
 525      * @return an <code>Iterator</code> containing service provider
 526      * objects from the given category, possibly in order.
 527      *
 528      * @exception IllegalArgumentException if there is no category
 529      * corresponding to <code>category</code>.
 530      */
 531     public <T> Iterator<T> getServiceProviders(Class<T> category,
 532                                                Filter filter,
 533                                                boolean useOrdering) {
 534         SubRegistry reg = categoryMap.get(category);
 535         if (reg == null) {
 536             throw new IllegalArgumentException("category unknown!");
 537         }
 538         Iterator<T> iter = getServiceProviders(category, useOrdering);
 539         return new FilterIterator<>(iter, filter);
 540     }
 541 
 542     /**
 543      * Returns the currently registered service provider object that
 544      * is of the given class type.  At most one object of a given
 545      * class is allowed to be registered at any given time.  If no
 546      * registered object has the desired class type, <code>null</code>
 547      * is returned.
 548      *
 549      * @param providerClass the <code>Class</code> of the desired
 550      * service provider object.
 551      * @param <T> the type of the provider.
 552      *
 553      * @return a currently registered service provider object with the
 554      * desired <code>Class</code>type, or <code>null</code> is none is
 555      * present.
 556      *
 557      * @exception IllegalArgumentException if <code>providerClass</code> is
 558      * <code>null</code>.
 559      */
 560     public <T> T getServiceProviderByClass(Class<T> providerClass) {
 561         if (providerClass == null) {
 562             throw new IllegalArgumentException("providerClass == null!");
 563         }
 564         Iterator<Class<?>> iter = categoryMap.keySet().iterator();
 565         while (iter.hasNext()) {
 566             Class<?> c = iter.next();
 567             if (c.isAssignableFrom(providerClass)) {
 568                 SubRegistry reg = categoryMap.get(c);
 569                 T provider = reg.getServiceProviderByClass(providerClass);
 570                 if (provider != null) {
 571                     return provider;
 572                 }
 573             }
 574         }
 575         return null;
 576     }
 577 
 578     /**
 579      * Sets a pairwise ordering between two service provider objects
 580      * within a given category.  If one or both objects are not
 581      * currently registered within the given category, or if the
 582      * desired ordering is already set, nothing happens and
 583      * <code>false</code> is returned.  If the providers previously
 584      * were ordered in the reverse direction, that ordering is
 585      * removed.
 586      *
 587      * <p> The ordering will be used by the
 588      * <code>getServiceProviders</code> methods when their
 589      * <code>useOrdering</code> argument is <code>true</code>.
 590      *
 591      * @param category a <code>Class</code> object indicating the
 592      * category under which the preference is to be established.
 593      * @param firstProvider the preferred provider.
 594      * @param secondProvider the provider to which
 595      * <code>firstProvider</code> is preferred.
 596      * @param <T> the type of the category.
 597      *
 598      * @return <code>true</code> if a previously unset ordering
 599      * was established.
 600      *
 601      * @exception IllegalArgumentException if either provider is
 602      * <code>null</code> or they are the same object.
 603      * @exception IllegalArgumentException if there is no category
 604      * corresponding to <code>category</code>.
 605      */
 606     public <T> boolean setOrdering(Class<T> category,
 607                                    T firstProvider,
 608                                    T secondProvider) {
 609         if (firstProvider == null || secondProvider == null) {
 610             throw new IllegalArgumentException("provider is null!");
 611         }
 612         if (firstProvider == secondProvider) {
 613             throw new IllegalArgumentException("providers are the same!");
 614         }
 615         SubRegistry reg = categoryMap.get(category);
 616         if (reg == null) {
 617             throw new IllegalArgumentException("category unknown!");
 618         }
 619         if (reg.contains(firstProvider) &&
 620             reg.contains(secondProvider)) {
 621             return reg.setOrdering(firstProvider, secondProvider);
 622         }
 623         return false;
 624     }
 625 
 626     /**
 627      * Sets a pairwise ordering between two service provider objects
 628      * within a given category.  If one or both objects are not
 629      * currently registered within the given category, or if no
 630      * ordering is currently set between them, nothing happens
 631      * and <code>false</code> is returned.
 632      *
 633      * <p> The ordering will be used by the
 634      * <code>getServiceProviders</code> methods when their
 635      * <code>useOrdering</code> argument is <code>true</code>.
 636      *
 637      * @param category a <code>Class</code> object indicating the
 638      * category under which the preference is to be disestablished.
 639      * @param firstProvider the formerly preferred provider.
 640      * @param secondProvider the provider to which
 641      * <code>firstProvider</code> was formerly preferred.
 642      * @param <T> the type of the category.
 643      *
 644      * @return <code>true</code> if a previously set ordering was
 645      * disestablished.
 646      *
 647      * @exception IllegalArgumentException if either provider is
 648      * <code>null</code> or they are the same object.
 649      * @exception IllegalArgumentException if there is no category
 650      * corresponding to <code>category</code>.
 651      */
 652     public <T> boolean unsetOrdering(Class<T> category,
 653                                      T firstProvider,
 654                                      T secondProvider) {
 655         if (firstProvider == null || secondProvider == null) {
 656             throw new IllegalArgumentException("provider is null!");
 657         }
 658         if (firstProvider == secondProvider) {
 659             throw new IllegalArgumentException("providers are the same!");
 660         }
 661         SubRegistry reg = categoryMap.get(category);
 662         if (reg == null) {
 663             throw new IllegalArgumentException("category unknown!");
 664         }
 665         if (reg.contains(firstProvider) &&
 666             reg.contains(secondProvider)) {
 667             return reg.unsetOrdering(firstProvider, secondProvider);
 668         }
 669         return false;
 670     }
 671 
 672     /**
 673      * Deregisters all service provider object currently registered
 674      * under the given category.
 675      *
 676      * @param category the category to be emptied.
 677      *
 678      * @exception IllegalArgumentException if there is no category
 679      * corresponding to <code>category</code>.
 680      */
 681     public void deregisterAll(Class<?> category) {
 682         SubRegistry reg = categoryMap.get(category);
 683         if (reg == null) {
 684             throw new IllegalArgumentException("category unknown!");
 685         }
 686         reg.clear();
 687     }
 688 
 689     /**
 690      * Deregisters all currently registered service providers from all
 691      * categories.
 692      */
 693     public void deregisterAll() {
 694         Iterator<SubRegistry> iter = categoryMap.values().iterator();
 695         while (iter.hasNext()) {
 696             SubRegistry reg = iter.next();
 697             reg.clear();
 698         }
 699     }
 700 
 701     /**
 702      * Finalizes this object prior to garbage collection.  The
 703      * <code>deregisterAll</code> method is called to deregister all
 704      * currently registered service providers.  This method should not
 705      * be called from application code.
 706      *
 707      * @exception Throwable if an error occurs during superclass
 708      * finalization.
 709      */
 710     public void finalize() throws Throwable {
 711         deregisterAll();
 712         super.finalize();
 713     }
 714 
 715     /**
 716      * Checks whether the provided class is one of the allowed
 717      * ImageIO service provider classes. If it is, returns normally.
 718      * If it is not, throws IllegalArgumentException.
 719      *
 720      * @param clazz
 721      * @throws IllegalArgumentException if clazz is null or is not one of the allowed set
 722      */
 723     private static void checkClassAllowed(Class<?> clazz) {


 829         Iterator<Object> iter = map.values().iterator();
 830         while (iter.hasNext()) {
 831             Object provider = iter.next();
 832             iter.remove();
 833 
 834             if (provider instanceof RegisterableService) {
 835                 RegisterableService rs = (RegisterableService)provider;
 836                 rs.onDeregistration(registry, category);
 837             }
 838         }
 839         poset.clear();
 840     }
 841 
 842     public void finalize() {
 843         clear();
 844     }
 845 }
 846 
 847 
 848 /**
 849  * A class for wrapping <code>Iterators</code> with a filter function.
 850  * This provides an iterator for a subset without duplication.
 851  */
 852 class FilterIterator<T> implements Iterator<T> {
 853 
 854     private Iterator<? extends T> iter;
 855     private ServiceRegistry.Filter filter;
 856 
 857     private T next = null;
 858 
 859     public FilterIterator(Iterator<? extends T> iter,
 860                           ServiceRegistry.Filter filter) {
 861         this.iter = iter;
 862         this.filter = filter;
 863         advance();
 864     }
 865 
 866     private void advance() {
 867         while (iter.hasNext()) {
 868             T elt = iter.next();
 869             if (filter.filter(elt)) {




  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} object) that all of its members must implement.
  50  *
  51  * <p>The set of categories supported is limited
  52  * to the following standard Image I/O service types:
  53  *
  54  * <ul>
  55  * <li>{@link ImageInputStreamSpi}
  56  * <li>{@link ImageOutputStreamSpi}
  57  * <li>{@link ImageReaderSpi}
  58  * <li>{@link ImageTranscoderSpi}
  59  * <li>{@link ImageWriterSpi}
  60  * </ul>
  61  *
  62  * <p>An attempt to load a provider that is not a subtype of one of the
  63  * above types will result in {@code IllegalArgumentException}. For
  64  * a general mechanism to load service providers, see
  65  * {@link java.util.ServiceLoader ServiceLoader}.
  66  *
  67  * <p> Only a single instance of a given leaf class (that is, the
  68  * actual class returned by {@code getClass()}, as opposed to any
  69  * inherited classes or interfaces) may be registered.  That is,
  70  * suppose that the
  71  * {@code com.mycompany.mypkg.GreenImageReaderProvider} class
  72  * is a subclass of {@code javax.imageio.spi.ImageReaderSpi}.
  73  * If a {@code GreenImageReaderProvider} instance is
  74  * registered, it will be stored in the category defined by the
  75  * {@code ImageReaderSpi} class.  If a new instance of
  76  * {@code GreenImageReaderProvider} is registered, it will replace
  77  * the previous instance.  In practice, service provider objects are
  78  * usually singletons so this behavior is appropriate.
  79  *
  80  * <p> To declare a service provider, a {@code services}
  81  * subdirectory is placed within the {@code META-INF} directory
  82  * that is present in every JAR file.  This directory contains a file
  83  * for each service provider interface that has one or more
  84  * implementation classes present in the JAR file.  For example, if
  85  * the JAR file contained a class named
  86  * {@code com.mycompany.mypkg.GreenImageReaderProvider} which implements the
  87  * {@code javax.imageio.spi.ImageReaderSpi} interface, the JAR file
  88  * would contain a file named: <pre>
  89  * META-INF/services/javax.imageio.spi.ImageReaderSpi</pre>
  90  *
  91  * containing the line:
  92  *
  93  * <pre>
  94  * com.mycompany.mypkg.GreenImageReaderProvider
  95  * </pre>
  96  *
  97  * <p> The service provider classes should be to be lightweight and
  98  * quick to load.  Implementations of these interfaces should avoid
  99  * complex dependencies on other classes and on native code. The usual
 100  * pattern for more complex services is to register a lightweight
 101  * proxy for the heavyweight service.
 102  *
 103  * <p> An application may customize the contents of a registry as it
 104  * sees fit, so long as it has the appropriate runtime permission.
 105  *
 106  * <p> For more details on declaring service providers, and the JAR
 107  * format in general, see the <a
 108  * href="../../../../technotes/guides/jar/jar.html">
 109  * JAR File Specification</a>.
 110  *
 111  * @see RegisterableService
 112  * @see java.util.ServiceLoader
 113  */
 114 public class ServiceRegistry {
 115 
 116     // Class -> Registry
 117     private Map<Class<?>, SubRegistry> categoryMap = new HashMap<>();
 118 
 119     /**
 120      * Constructs a {@code ServiceRegistry} instance with a
 121      * set of categories taken from the {@code categories}
 122      * argument. The categories must all be members of the set
 123      * of service types listed in the class specification.
 124      *
 125      * @param categories an {@code Iterator} containing
 126      * {@code Class} objects to be used to define categories.
 127      *
 128      * @exception IllegalArgumentException if
 129      * {@code categories} is {@code null}, or if
 130      * one of the categories is not an allowed service type.
 131      */
 132     public ServiceRegistry(Iterator<Class<?>> categories) {
 133         if (categories == null) {
 134             throw new IllegalArgumentException("categories == null!");
 135         }
 136         while (categories.hasNext()) {
 137             Class<?> category = categories.next();
 138             checkClassAllowed(category);
 139             SubRegistry reg = new SubRegistry(this, category);
 140             categoryMap.put(category, reg);
 141         }
 142     }
 143 
 144     /**
 145      * Searches for implementations of a particular service class
 146      * using the given class loader.
 147      *
 148      * <p>The service class must be one of the service types listed
 149      * in the class specification. If it is not, {@code IllegalArgumentException}
 150      * will be thrown.
 151      *
 152      * <p> This method transforms the name of the given service class
 153      * into a provider-configuration filename as described in the
 154      * class comment and then uses the {@code getResources}
 155      * method of the given class loader to find all available files
 156      * with that name.  These files are then read and parsed to
 157      * produce a list of provider-class names.  The iterator that is
 158      * returned uses the given class loader to look up and then
 159      * instantiate each element of the list.
 160      *
 161      * <p> Because it is possible for extensions to be installed into
 162      * a running Java virtual machine, this method may return
 163      * different results each time it is invoked.
 164      *
 165      * @param providerClass a {@code Class} object indicating the
 166      * class or interface of the service providers being detected.
 167      *
 168      * @param loader the class loader to be used to load
 169      * provider-configuration files and instantiate provider classes,
 170      * or {@code null} if the system class loader (or, failing that
 171      * the bootstrap class loader) is to be used.
 172      *
 173      * @param <T> the type of the providerClass.
 174      *
 175      * @return An {@code Iterator} that yields provider objects
 176      * for the given service, in some arbitrary order.  The iterator
 177      * will throw an {@code Error} if a provider-configuration
 178      * file violates the specified format or if a provider class
 179      * cannot be found and instantiated.
 180      *
 181      * @exception IllegalArgumentException if
 182      * {@code providerClass} is {@code null}, or if it is
 183      * not one of the allowed service types.
 184      */
 185     public static <T> Iterator<T> lookupProviders(Class<T> providerClass,
 186                                                   ClassLoader loader)
 187     {
 188         if (providerClass == null) {
 189             throw new IllegalArgumentException("providerClass == null!");
 190         }
 191         checkClassAllowed(providerClass);
 192         return ServiceLoader.load(providerClass, loader).iterator();
 193     }
 194 
 195     /**
 196      * Locates and incrementally instantiates the available providers
 197      * of a given service using the context class loader.  This
 198      * convenience method is equivalent to:
 199      *
 200      * <pre>
 201      *   ClassLoader cl = Thread.currentThread().getContextClassLoader();
 202      *   return Service.providers(service, cl);
 203      * </pre>
 204      *
 205      * <p>The service class must be one of the service types listed
 206      * in the class specification. If it is not, {@code IllegalArgumentException}
 207      * will be thrown.
 208      *
 209      * @param providerClass a {@code Class} object indicating the
 210      * class or interface of the service providers being detected.
 211      *
 212      * @param <T> the type of the providerClass.
 213      *
 214      * @return An {@code Iterator} that yields provider objects
 215      * for the given service, in some arbitrary order.  The iterator
 216      * will throw an {@code Error} if a provider-configuration
 217      * file violates the specified format or if a provider class
 218      * cannot be found and instantiated.
 219      *
 220      * @exception IllegalArgumentException if
 221      * {@code providerClass} is {@code null}, or if it is
 222      * not one of the allowed service types.
 223      */
 224     public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {
 225         if (providerClass == null) {
 226             throw new IllegalArgumentException("providerClass == null!");
 227         }
 228         checkClassAllowed(providerClass);
 229         return ServiceLoader.load(providerClass).iterator();
 230     }
 231 
 232     /**
 233      * Returns an {@code Iterator} of {@code Class} objects
 234      * indicating the current set of categories.  The iterator will be
 235      * empty if no categories exist.
 236      *
 237      * @return an {@code Iterator} containing
 238      * {@code Class} objects.
 239      */
 240     public Iterator<Class<?>> getCategories() {
 241         Set<Class<?>> keySet = categoryMap.keySet();
 242         return keySet.iterator();
 243     }
 244 
 245     /**
 246      * Returns an Iterator containing the subregistries to which the
 247      * provider belongs.
 248      */
 249     private Iterator<SubRegistry> getSubRegistries(Object provider) {
 250         List<SubRegistry> l = new ArrayList<>();
 251         Iterator<Class<?>> iter = categoryMap.keySet().iterator();
 252         while (iter.hasNext()) {
 253             Class<?> c = iter.next();
 254             if (c.isAssignableFrom(provider.getClass())) {
 255                 l.add(categoryMap.get(c));
 256             }
 257         }
 258         return l.iterator();
 259     }
 260 
 261     /**
 262      * Adds a service provider object to the registry.  The provider
 263      * is associated with the given category.
 264      *
 265      * <p> If {@code provider} implements the
 266      * {@code RegisterableService} interface, its
 267      * {@code onRegistration} method will be called.  Its
 268      * {@code onDeregistration} method will be called each time
 269      * it is deregistered from a category, for example if a
 270      * category is removed or the registry is garbage collected.
 271      *
 272      * @param provider the service provide object to be registered.
 273      * @param category the category under which to register the
 274      * provider.
 275      * @param <T> the type of the provider.
 276      *
 277      * @return true if no provider of the same class was previously
 278      * registered in the same category category.
 279      *
 280      * @exception IllegalArgumentException if {@code provider} is
 281      * {@code null}.
 282      * @exception IllegalArgumentException if there is no category
 283      * corresponding to {@code category}.
 284      * @exception ClassCastException if provider does not implement
 285      * the {@code Class} defined by {@code category}.
 286      */
 287     public <T> boolean registerServiceProvider(T provider,
 288                                                Class<T> category) {
 289         if (provider == null) {
 290             throw new IllegalArgumentException("provider == null!");
 291         }
 292         SubRegistry reg = categoryMap.get(category);
 293         if (reg == null) {
 294             throw new IllegalArgumentException("category unknown!");
 295         }
 296         if (!category.isAssignableFrom(provider.getClass())) {
 297             throw new ClassCastException();
 298         }
 299 
 300         return reg.registerServiceProvider(provider);
 301     }
 302 
 303     /**
 304      * Adds a service provider object to the registry.  The provider
 305      * is associated within each category present in the registry
 306      * whose {@code Class} it implements.
 307      *
 308      * <p> If {@code provider} implements the
 309      * {@code RegisterableService} interface, its
 310      * {@code onRegistration} method will be called once for each
 311      * category it is registered under.  Its
 312      * {@code onDeregistration} method will be called each time
 313      * it is deregistered from a category or when the registry is
 314      * finalized.
 315      *
 316      * @param provider the service provider object to be registered.
 317      *
 318      * @exception IllegalArgumentException if
 319      * {@code provider} is {@code null}.
 320      */
 321     public void registerServiceProvider(Object provider) {
 322         if (provider == null) {
 323             throw new IllegalArgumentException("provider == null!");
 324         }
 325         Iterator<SubRegistry> regs = getSubRegistries(provider);
 326         while (regs.hasNext()) {
 327             SubRegistry reg = regs.next();
 328             reg.registerServiceProvider(provider);
 329         }
 330     }
 331 
 332     /**
 333      * Adds a set of service provider objects, taken from an
 334      * {@code Iterator} to the registry.  Each provider is
 335      * associated within each category present in the registry whose
 336      * {@code Class} it implements.
 337      *
 338      * <p> For each entry of {@code providers} that implements
 339      * the {@code RegisterableService} interface, its
 340      * {@code onRegistration} method will be called once for each
 341      * category it is registered under.  Its
 342      * {@code onDeregistration} method will be called each time
 343      * it is deregistered from a category or when the registry is
 344      * finalized.
 345      *
 346      * @param providers an Iterator containing service provider
 347      * objects to be registered.
 348      *
 349      * @exception IllegalArgumentException if {@code providers}
 350      * is {@code null} or contains a {@code null} entry.
 351      */
 352     public void registerServiceProviders(Iterator<?> providers) {
 353         if (providers == null) {
 354             throw new IllegalArgumentException("provider == null!");
 355         }
 356         while (providers.hasNext()) {
 357             registerServiceProvider(providers.next());
 358         }
 359     }
 360 
 361     /**
 362      * Removes a service provider object from the given category.  If
 363      * the provider was not previously registered, nothing happens and
 364      * {@code false} is returned.  Otherwise, {@code true}
 365      * is returned.  If an object of the same class as
 366      * {@code provider} but not equal (using {@code ==}) to
 367      * {@code provider} is registered, it will not be
 368      * deregistered.
 369      *
 370      * <p> If {@code provider} implements the
 371      * {@code RegisterableService} interface, its
 372      * {@code onDeregistration} method will be called.
 373      *
 374      * @param provider the service provider object to be deregistered.
 375      * @param category the category from which to deregister the
 376      * provider.
 377      * @param <T> the type of the provider.
 378      *
 379      * @return {@code true} if the provider was previously
 380      * registered in the same category category,
 381      * {@code false} otherwise.
 382      *
 383      * @exception IllegalArgumentException if {@code provider} is
 384      * {@code null}.
 385      * @exception IllegalArgumentException if there is no category
 386      * corresponding to {@code category}.
 387      * @exception ClassCastException if provider does not implement
 388      * the class defined by {@code category}.
 389      */
 390     public <T> boolean deregisterServiceProvider(T provider,
 391                                                  Class<T> category) {
 392         if (provider == null) {
 393             throw new IllegalArgumentException("provider == null!");
 394         }
 395         SubRegistry reg = categoryMap.get(category);
 396         if (reg == null) {
 397             throw new IllegalArgumentException("category unknown!");
 398         }
 399         if (!category.isAssignableFrom(provider.getClass())) {
 400             throw new ClassCastException();
 401         }
 402         return reg.deregisterServiceProvider(provider);
 403     }
 404 
 405     /**
 406      * Removes a service provider object from all categories that
 407      * contain it.
 408      *
 409      * @param provider the service provider object to be deregistered.
 410      *
 411      * @exception IllegalArgumentException if {@code provider} is
 412      * {@code null}.
 413      */
 414     public void deregisterServiceProvider(Object provider) {
 415         if (provider == null) {
 416             throw new IllegalArgumentException("provider == null!");
 417         }
 418         Iterator<SubRegistry> regs = getSubRegistries(provider);
 419         while (regs.hasNext()) {
 420             SubRegistry reg = regs.next();
 421             reg.deregisterServiceProvider(provider);
 422         }
 423     }
 424 
 425     /**
 426      * Returns {@code true} if {@code provider} is currently
 427      * registered.
 428      *
 429      * @param provider the service provider object to be queried.
 430      *
 431      * @return {@code true} if the given provider has been
 432      * registered.
 433      *
 434      * @exception IllegalArgumentException if {@code provider} is
 435      * {@code null}.
 436      */
 437     public boolean contains(Object provider) {
 438         if (provider == null) {
 439             throw new IllegalArgumentException("provider == null!");
 440         }
 441         Iterator<SubRegistry> regs = getSubRegistries(provider);
 442         while (regs.hasNext()) {
 443             SubRegistry reg = regs.next();
 444             if (reg.contains(provider)) {
 445                 return true;
 446             }
 447         }
 448 
 449         return false;
 450     }
 451 
 452     /**
 453      * Returns an {@code Iterator} containing all registered
 454      * service providers in the given category.  If
 455      * {@code useOrdering} is {@code false}, the iterator
 456      * will return all of the server provider objects in an arbitrary
 457      * order.  Otherwise, the ordering will respect any pairwise
 458      * orderings that have been set.  If the graph of pairwise
 459      * orderings contains cycles, any providers that belong to a cycle
 460      * will not be returned.
 461      *
 462      * @param category the category to be retrieved from.
 463      * @param useOrdering {@code true} if pairwise orderings
 464      * should be taken account in ordering the returned objects.
 465      * @param <T> the type of the category.
 466      *
 467      * @return an {@code Iterator} containing service provider
 468      * objects from the given category, possibly in order.
 469      *
 470      * @exception IllegalArgumentException if there is no category
 471      * corresponding to {@code category}.
 472      */
 473     public <T> Iterator<T> getServiceProviders(Class<T> category,
 474                                                boolean useOrdering) {
 475         SubRegistry reg = categoryMap.get(category);
 476         if (reg == null) {
 477             throw new IllegalArgumentException("category unknown!");
 478         }
 479         @SuppressWarnings("unchecked")
 480         Iterator<T> it = (Iterator<T>)reg.getServiceProviders(useOrdering);
 481         return it;
 482     }
 483 
 484     /**
 485      * A simple filter interface used by
 486      * {@code ServiceRegistry.getServiceProviders} to select
 487      * providers matching an arbitrary criterion.  Classes that
 488      * implement this interface should be defined in order to make use
 489      * of the {@code getServiceProviders} method of
 490      * {@code ServiceRegistry} that takes a {@code Filter}.
 491      *
 492      * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)
 493      */
 494     public interface Filter {
 495 
 496         /**
 497          * Returns {@code true} if the given
 498          * {@code provider} object matches the criterion defined
 499          * by this {@code Filter}.
 500          *
 501          * @param provider a service provider {@code Object}.
 502          *
 503          * @return true if the provider matches the criterion.
 504          */
 505         boolean filter(Object provider);
 506     }
 507 
 508     /**
 509      * Returns an {@code Iterator} containing service provider
 510      * objects within a given category that satisfy a criterion
 511      * imposed by the supplied {@code ServiceRegistry.Filter}
 512      * object's {@code filter} method.
 513      *
 514      * <p> The {@code useOrdering} argument controls the
 515      * ordering of the results using the same rules as
 516      * {@code getServiceProviders(Class, boolean)}.
 517      *
 518      * @param category the category to be retrieved from.
 519      * @param filter an instance of {@code ServiceRegistry.Filter}
 520      * whose {@code filter} method will be invoked.
 521      * @param useOrdering {@code true} if pairwise orderings
 522      * should be taken account in ordering the returned objects.
 523      * @param <T> the type of the category.
 524      *
 525      * @return an {@code Iterator} containing service provider
 526      * objects from the given category, possibly in order.
 527      *
 528      * @exception IllegalArgumentException if there is no category
 529      * corresponding to {@code category}.
 530      */
 531     public <T> Iterator<T> getServiceProviders(Class<T> category,
 532                                                Filter filter,
 533                                                boolean useOrdering) {
 534         SubRegistry reg = categoryMap.get(category);
 535         if (reg == null) {
 536             throw new IllegalArgumentException("category unknown!");
 537         }
 538         Iterator<T> iter = getServiceProviders(category, useOrdering);
 539         return new FilterIterator<>(iter, filter);
 540     }
 541 
 542     /**
 543      * Returns the currently registered service provider object that
 544      * is of the given class type.  At most one object of a given
 545      * class is allowed to be registered at any given time.  If no
 546      * registered object has the desired class type, {@code null}
 547      * is returned.
 548      *
 549      * @param providerClass the {@code Class} of the desired
 550      * service provider object.
 551      * @param <T> the type of the provider.
 552      *
 553      * @return a currently registered service provider object with the
 554      * desired {@code Class} type, or {@code null} is none is
 555      * present.
 556      *
 557      * @exception IllegalArgumentException if {@code providerClass} is
 558      * {@code null}.
 559      */
 560     public <T> T getServiceProviderByClass(Class<T> providerClass) {
 561         if (providerClass == null) {
 562             throw new IllegalArgumentException("providerClass == null!");
 563         }
 564         Iterator<Class<?>> iter = categoryMap.keySet().iterator();
 565         while (iter.hasNext()) {
 566             Class<?> c = iter.next();
 567             if (c.isAssignableFrom(providerClass)) {
 568                 SubRegistry reg = categoryMap.get(c);
 569                 T provider = reg.getServiceProviderByClass(providerClass);
 570                 if (provider != null) {
 571                     return provider;
 572                 }
 573             }
 574         }
 575         return null;
 576     }
 577 
 578     /**
 579      * Sets a pairwise ordering between two service provider objects
 580      * within a given category.  If one or both objects are not
 581      * currently registered within the given category, or if the
 582      * desired ordering is already set, nothing happens and
 583      * {@code false} is returned.  If the providers previously
 584      * were ordered in the reverse direction, that ordering is
 585      * removed.
 586      *
 587      * <p> The ordering will be used by the
 588      * {@code getServiceProviders} methods when their
 589      * {@code useOrdering} argument is {@code true}.
 590      *
 591      * @param category a {@code Class} object indicating the
 592      * category under which the preference is to be established.
 593      * @param firstProvider the preferred provider.
 594      * @param secondProvider the provider to which
 595      * {@code firstProvider} is preferred.
 596      * @param <T> the type of the category.
 597      *
 598      * @return {@code true} if a previously unset ordering
 599      * was established.
 600      *
 601      * @exception IllegalArgumentException if either provider is
 602      * {@code null} or they are the same object.
 603      * @exception IllegalArgumentException if there is no category
 604      * corresponding to {@code category}.
 605      */
 606     public <T> boolean setOrdering(Class<T> category,
 607                                    T firstProvider,
 608                                    T secondProvider) {
 609         if (firstProvider == null || secondProvider == null) {
 610             throw new IllegalArgumentException("provider is null!");
 611         }
 612         if (firstProvider == secondProvider) {
 613             throw new IllegalArgumentException("providers are the same!");
 614         }
 615         SubRegistry reg = categoryMap.get(category);
 616         if (reg == null) {
 617             throw new IllegalArgumentException("category unknown!");
 618         }
 619         if (reg.contains(firstProvider) &&
 620             reg.contains(secondProvider)) {
 621             return reg.setOrdering(firstProvider, secondProvider);
 622         }
 623         return false;
 624     }
 625 
 626     /**
 627      * Sets a pairwise ordering between two service provider objects
 628      * within a given category.  If one or both objects are not
 629      * currently registered within the given category, or if no
 630      * ordering is currently set between them, nothing happens
 631      * and {@code false} is returned.
 632      *
 633      * <p> The ordering will be used by the
 634      * {@code getServiceProviders} methods when their
 635      * {@code useOrdering} argument is {@code true}.
 636      *
 637      * @param category a {@code Class} object indicating the
 638      * category under which the preference is to be disestablished.
 639      * @param firstProvider the formerly preferred provider.
 640      * @param secondProvider the provider to which
 641      * {@code firstProvider} was formerly preferred.
 642      * @param <T> the type of the category.
 643      *
 644      * @return {@code true} if a previously set ordering was
 645      * disestablished.
 646      *
 647      * @exception IllegalArgumentException if either provider is
 648      * {@code null} or they are the same object.
 649      * @exception IllegalArgumentException if there is no category
 650      * corresponding to {@code category}.
 651      */
 652     public <T> boolean unsetOrdering(Class<T> category,
 653                                      T firstProvider,
 654                                      T secondProvider) {
 655         if (firstProvider == null || secondProvider == null) {
 656             throw new IllegalArgumentException("provider is null!");
 657         }
 658         if (firstProvider == secondProvider) {
 659             throw new IllegalArgumentException("providers are the same!");
 660         }
 661         SubRegistry reg = categoryMap.get(category);
 662         if (reg == null) {
 663             throw new IllegalArgumentException("category unknown!");
 664         }
 665         if (reg.contains(firstProvider) &&
 666             reg.contains(secondProvider)) {
 667             return reg.unsetOrdering(firstProvider, secondProvider);
 668         }
 669         return false;
 670     }
 671 
 672     /**
 673      * Deregisters all service provider object currently registered
 674      * under the given category.
 675      *
 676      * @param category the category to be emptied.
 677      *
 678      * @exception IllegalArgumentException if there is no category
 679      * corresponding to {@code category}.
 680      */
 681     public void deregisterAll(Class<?> category) {
 682         SubRegistry reg = categoryMap.get(category);
 683         if (reg == null) {
 684             throw new IllegalArgumentException("category unknown!");
 685         }
 686         reg.clear();
 687     }
 688 
 689     /**
 690      * Deregisters all currently registered service providers from all
 691      * categories.
 692      */
 693     public void deregisterAll() {
 694         Iterator<SubRegistry> iter = categoryMap.values().iterator();
 695         while (iter.hasNext()) {
 696             SubRegistry reg = iter.next();
 697             reg.clear();
 698         }
 699     }
 700 
 701     /**
 702      * Finalizes this object prior to garbage collection.  The
 703      * {@code deregisterAll} method is called to deregister all
 704      * currently registered service providers.  This method should not
 705      * be called from application code.
 706      *
 707      * @exception Throwable if an error occurs during superclass
 708      * finalization.
 709      */
 710     public void finalize() throws Throwable {
 711         deregisterAll();
 712         super.finalize();
 713     }
 714 
 715     /**
 716      * Checks whether the provided class is one of the allowed
 717      * ImageIO service provider classes. If it is, returns normally.
 718      * If it is not, throws IllegalArgumentException.
 719      *
 720      * @param clazz
 721      * @throws IllegalArgumentException if clazz is null or is not one of the allowed set
 722      */
 723     private static void checkClassAllowed(Class<?> clazz) {


 829         Iterator<Object> iter = map.values().iterator();
 830         while (iter.hasNext()) {
 831             Object provider = iter.next();
 832             iter.remove();
 833 
 834             if (provider instanceof RegisterableService) {
 835                 RegisterableService rs = (RegisterableService)provider;
 836                 rs.onDeregistration(registry, category);
 837             }
 838         }
 839         poset.clear();
 840     }
 841 
 842     public void finalize() {
 843         clear();
 844     }
 845 }
 846 
 847 
 848 /**
 849  * A class for wrapping {@code Iterators} with a filter function.
 850  * This provides an iterator for a subset without duplication.
 851  */
 852 class FilterIterator<T> implements Iterator<T> {
 853 
 854     private Iterator<? extends T> iter;
 855     private ServiceRegistry.Filter filter;
 856 
 857     private T next = null;
 858 
 859     public FilterIterator(Iterator<? extends T> iter,
 860                           ServiceRegistry.Filter filter) {
 861         this.iter = iter;
 862         this.filter = filter;
 863         advance();
 864     }
 865 
 866     private void advance() {
 867         while (iter.hasNext()) {
 868             T elt = iter.next();
 869             if (filter.filter(elt)) {


< prev index next >