Source for javax.imageio.spi.ServiceRegistry

   1: /* ServiceRegistry.java -- A simple registry for service providers.
   2:    Copyright (C) 2004, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.imageio.spi;
  40: 
  41: import gnu.classpath.ServiceFactory;
  42: 
  43: import java.util.ArrayList;
  44: import java.util.Collection;
  45: import java.util.Collections;
  46: import java.util.Comparator;
  47: import java.util.HashSet;
  48: import java.util.IdentityHashMap;
  49: import java.util.Iterator;
  50: import java.util.LinkedList;
  51: import java.util.Map;
  52: import java.util.NoSuchElementException;
  53: import java.util.Set;
  54: 
  55: /**
  56:  * A registry for service providers.
  57:  *
  58:  * @since 1.4
  59:  *
  60:  * @author Michael Koch (konqueror@gmx.de)
  61:  * @author Sascha Brawer (brawer@dandelis.ch)
  62:  */
  63: public class ServiceRegistry
  64: {
  65:   // Package-private to avoid a trampoline.
  66:   /**
  67:    * The service categories of this registry.
  68:    *
  69:    * <p>Note that we expect that only very few categories will
  70:    * typically be used with a registry. The most common case will be
  71:    * one, it seems unlikely that any registry would contain more than
  72:    * five or six categories. Therefore, we intentionally avoid the
  73:    * overhead of a HashMap.
  74:    *
  75:    * @see #providers
  76:    */
  77:   final Class[] categories;
  78: 
  79: 
  80:   /**
  81:    * The registered providers for each service category, indexed by
  82:    * the same index as the {@link #categories} array. If no provider
  83:    * is registered for a category, the array entry will be
  84:    * <code>null</code>.
  85:    *
  86:    * <p>Note that we expect that only very few providers will
  87:    * typically be registered for a category. The most common case will
  88:    * be one or two. Therefore, we intentionally avoid the overhead of
  89:    * a HashMap.
  90:    */
  91:   private final LinkedList[] providers;
  92: 
  93: 
  94:   /**
  95:    * The ordring constaints for each service category, indexed by the
  96:    * same index as the {@link #categories} array. The constraints for
  97:    * a service category are stored as a <code>Map&lt;Object,
  98:    * Set&lt;Object&gt;&gt;</code>, where the Map&#x2019;s values are
  99:    * those providers that need to come after the key.  If no
 100:    * constraints are imposed on the providers of a category, the array
 101:    * entry will be <code>null</code>. If no constraints have been set
 102:    * whatsoever, <code>constraints</code> will be <code>null</code>.
 103:    *
 104:    * <p>Note that we expect that only very few constraints will
 105:    * typically be imposed on a category. The most common case will
 106:    * be zero.
 107:    */
 108:   private IdentityHashMap[] constraints;
 109: 
 110: 
 111:   /**
 112:    * Constructs a <code>ServiceRegistry</code> for the specified
 113:    * service categories.
 114:    *
 115:    * @param categories the categories to support
 116:    *
 117:    * @throws IllegalArgumentException if <code>categories</code> is
 118:    * <code>null</code>, or if its {@link Iterator#next()} method
 119:    * returns <code>null</code>.
 120:    *
 121:    * @throws ClassCastException if <code>categories</code> does not
 122:    * iterate over instances of {@link java.lang.Class}.
 123:    */
 124:   public ServiceRegistry(Iterator<Class<?>> categories)
 125:   {
 126:     ArrayList cats = new ArrayList(/* expected size */ 10);
 127: 
 128:     if (categories == null)
 129:       throw new IllegalArgumentException();
 130: 
 131:     while (categories.hasNext())
 132:       {
 133:         Class cat = (Class) categories.next();
 134:         if (cat == null)
 135:           throw new IllegalArgumentException();
 136:         cats.add(cat);
 137:       }
 138: 
 139:     int numCats = cats.size();
 140:     this.categories = (Class[]) cats.toArray(new Class[numCats]);
 141:     this.providers = new LinkedList[numCats];
 142:   }
 143: 
 144: 
 145:   /**
 146:    * Finds service providers that are implementing the specified
 147:    * Service Provider Interface.
 148:    *
 149:    * <p><b>On-demand loading:</b> Loading and initializing service
 150:    * providers is delayed as much as possible. The rationale is that
 151:    * typical clients will iterate through the set of installed service
 152:    * providers until one is found that matches some criteria (like
 153:    * supported formats, or quality of service). In such scenarios, it
 154:    * might make sense to install only the frequently needed service
 155:    * providers on the local machine. More exotic providers can be put
 156:    * onto a server; the server will only be contacted when no suitable
 157:    * service could be found locally.</p>
 158:    *
 159:    * <p><b>Security considerations:</b> Any loaded service providers
 160:    * are loaded through the specified ClassLoader, or the system
 161:    * ClassLoader if <code>classLoader</code> is
 162:    * <code>null</code>. When <code>lookupProviders</code> is called,
 163:    * the current {@link java.security.AccessControlContext} gets
 164:    * recorded. This captured security context will determine the
 165:    * permissions when services get loaded via the <code>next()</code>
 166:    * method of the returned <code>Iterator</code>.</p>
 167:    *
 168:    * @param spi the service provider interface which must be
 169:    * implemented by any loaded service providers.
 170:    *
 171:    * @param loader the class loader that will be used to load the
 172:    * service providers, or <code>null</code> for the system class
 173:    * loader. For using the context class loader, see {@link
 174:    * #lookupProviders(Class)}.
 175:    *
 176:    * @return an iterator over instances of <code>spi</code>.
 177:    *
 178:    * @throws IllegalArgumentException if <code>spi</code> is
 179:    * <code>null</code>.
 180:    */
 181:   public static <T> Iterator<T> lookupProviders(Class<T> spi,
 182:                                                 ClassLoader loader)
 183:   {
 184:     return ServiceFactory.lookupProviders(spi, loader);
 185:   }
 186: 
 187: 
 188:   /**
 189:    * Finds service providers that are implementing the specified
 190:    * Service Provider Interface, using the context class loader
 191:    * for loading providers.
 192:    *
 193:    * @param spi the service provider interface which must be
 194:    * implemented by any loaded service providers.
 195:    *
 196:    * @return an iterator over instances of <code>spi</code>.
 197:    *
 198:    * @throws IllegalArgumentException if <code>spi</code> is
 199:    * <code>null</code>.
 200:    *
 201:    * @see #lookupProviders(Class, ClassLoader)
 202:    */
 203:   public static <T> Iterator<T> lookupProviders(Class<T> spi)
 204:   {
 205:     return ServiceFactory.lookupProviders(spi);
 206:   }
 207: 
 208: 
 209:   /**
 210:    * Returns an iterator over all service categories.
 211:    *
 212:    * @return an unmodifiable {@link
 213:    * java.util.Iterator}&lt;{@link java.lang.Class}&gt;.
 214:    */
 215:   public Iterator<Class<?>> getCategories()
 216:   {
 217:     return new Iterator()
 218:       {
 219:         int index = -1;
 220: 
 221:         public boolean hasNext()
 222:         {
 223:           return index < categories.length - 1;
 224:         }
 225: 
 226:         public Object next()
 227:         {
 228:           if (!hasNext())
 229:             throw new NoSuchElementException();
 230: 
 231:           return categories[++index];
 232:         }
 233: 
 234:         public void remove()
 235:         {
 236:           throw new UnsupportedOperationException();
 237:         }
 238:       };
 239:   }
 240: 
 241: 
 242:   /**
 243:    * Registers a provider for a service category which is specified by
 244:    * the class-internal category ID.
 245:    *
 246:    * @param provider the service provider to be registered.
 247:    *
 248:    * @param cat the service category, which is identified by an index
 249:    * into the {@link #categories} array.
 250:    *
 251:    * @return <code>true</code> if <code>provider</code> is the first
 252:    * provider that gets registered for the specified service category;
 253:    * <code>false</code> if other providers have already been
 254:    * registered for the same servide category.
 255:    *
 256:    * @throws IllegalArgumentException if <code>provider</code> is
 257:    * <code>null</code>.
 258:    *
 259:    * @throws ClassCastException if <code>provider</code> does not
 260:    * implement the specified service provider interface.
 261:    */
 262:   private synchronized boolean registerServiceProvider(Object provider,
 263:                                                        int cat)
 264:   {
 265:     LinkedList provs;
 266:     boolean result;
 267:     Class category;
 268: 
 269:     if (provider == null)
 270:       throw new IllegalArgumentException();
 271: 
 272:     category = categories[cat];
 273:     if (!category.isInstance(provider))
 274:       throw new ClassCastException(category.getName());
 275: 
 276:     provs = providers[cat];
 277:     if (provs == null)
 278:     {
 279:       result = true;
 280:       provs = providers[cat] = new LinkedList();
 281:     }
 282:     else
 283:       result = false;
 284: 
 285:     provs.add(provider);
 286:     if (provider instanceof RegisterableService)
 287:       ((RegisterableService) provider).onRegistration(this, category);
 288: 
 289:     return result;
 290:   }
 291: 
 292: 
 293:   /**
 294:    * Registers a provider for the specified service category.
 295:    *
 296:    * <p>If <code>provider</code> implements the {@link
 297:    * RegisterableService} interface, its {@link
 298:    * RegisterableService#onRegistration onRegistration} method is
 299:    * invoked in order to inform the provider about the addition to
 300:    * this registry.
 301:    *
 302:    * @param provider the service provider to be registered.
 303:    *
 304:    * @param category the service category under which
 305:    * <code>provider</code> shall be registered.
 306:    *
 307:    * @return <code>true</code> if <code>provider</code> is the first
 308:    * provider that gets registered for the specified service category;
 309:    * <code>false</code> if other providers have already been
 310:    * registered for the same servide category.
 311:    *
 312:    * @throws IllegalArgumentException if <code>provider</code> is
 313:    * <code>null</code>, or if <code>category</code> is not among the
 314:    * categories passed to the {@linkplain #ServiceRegistry(Iterator)
 315:    * constructor} of this ServiceRegistry.
 316:    *
 317:    * @throws ClassCastException if <code>provider</code> does not
 318:    * implement <code>category</code>.
 319:    */
 320:   public synchronized <T> boolean registerServiceProvider(T provider,
 321:                                                           Class<T> category)
 322:   {
 323:     for (int i = 0; i < categories.length; i++)
 324:       if (categories[i] == category)
 325:         return registerServiceProvider(provider, i);
 326:     throw new IllegalArgumentException();
 327:   }
 328: 
 329: 
 330:   /**
 331:    * Registers a provider under all service categories it
 332:    * implements.
 333:    *
 334:    * <p>If <code>provider</code> implements the {@link
 335:    * RegisterableService} interface, its {@link
 336:    * RegisterableService#onRegistration onRegistration} method is
 337:    * invoked in order to inform the provider about the addition to
 338:    * this registry. If <code>provider</code> implements several
 339:    * service categories, <code>onRegistration</code> gets called
 340:    * multiple times.
 341:    *
 342:    * @param provider the service provider to be registered.
 343:    *
 344:    * @throws IllegalArgumentException if <code>provider</code> is
 345:    * <code>null</code>, or if <code>provider</code> does not implement
 346:    * any of the service categories passed to the {@linkplain
 347:    * #ServiceRegistry(Iterator) constructor} of this ServiceRegistry.
 348:    */
 349:   public synchronized void registerServiceProvider(Object provider)
 350:   {
 351:     boolean ok = false;
 352: 
 353:     if (provider == null)
 354:       throw new IllegalArgumentException();
 355: 
 356:     for (int i = 0; i < categories.length; i++)
 357:       if (categories[i].isInstance(provider))
 358:         {
 359:           ok = true;
 360:           registerServiceProvider(provider, i);
 361:         }
 362: 
 363:     if (!ok)
 364:       throw new IllegalArgumentException();
 365:   }
 366: 
 367: 
 368:   /**
 369:    * Registers a number of providers under all service categories they
 370:    * implement.
 371:    *
 372:    * <p>If a provider implements the {@link RegisterableService}
 373:    * interface, its {@link RegisterableService#onRegistration
 374:    * onRegistration} method is invoked in order to inform the provider
 375:    * about the addition to this registry. If <code>provider</code>
 376:    * implements several service categories,
 377:    * <code>onRegistration</code> gets called multiple times.
 378:    *
 379:    * @throws IllegalArgumentException if <code>providers</code> is
 380:    * <code>null</code>, if any iterated provider is <code>null</code>,
 381:    * or if some iterated provider does not implement any of the
 382:    * service categories passed to the {@linkplain
 383:    * #ServiceRegistry(Iterator) constructor} of this
 384:    * <code>ServiceRegistry</code>.
 385:    */
 386:   public synchronized void registerServiceProviders(Iterator<?> providers)
 387:   {
 388:     if (providers == null)
 389:       throw new IllegalArgumentException();
 390: 
 391:     while (providers.hasNext())
 392:       registerServiceProvider(providers.next());
 393:   }
 394: 
 395: 
 396:   /**
 397:    * De-registers a provider for a service category which is specified
 398:    * by the class-internal category ID.
 399:    *
 400:    * @param provider the service provider to be registered.
 401:    *
 402:    * @param cat the service category, which is identified by an index
 403:    * into the {@link #categories} array.
 404:    *
 405:    * @return <code>true</code> if <code>provider</code> was previously
 406:    * registered for the specified service category; <code>false</code>
 407:    * if if the provider had not been registered.
 408:    *
 409:    * @throws IllegalArgumentException if <code>provider</code> is
 410:    * <code>null</code>.
 411:    *
 412:    * @throws ClassCastException if <code>provider</code> does not
 413:    * implement the specified service provider interface.
 414:    */
 415:   private synchronized boolean deregisterServiceProvider(Object provider,
 416:                                                          int cat)
 417:   {
 418:     LinkedList provs;
 419:     boolean result;
 420:     Class category;
 421: 
 422:     if (provider == null)
 423:       throw new IllegalArgumentException();
 424: 
 425:     category = categories[cat];
 426:     if (!category.isInstance(provider))
 427:       throw new ClassCastException(category.getName());
 428: 
 429:     provs = providers[cat];
 430:     if (provs == null)
 431:       return false;
 432: 
 433:     result = provs.remove(provider);
 434:     if (provs.isEmpty())
 435:       providers[cat] = null;
 436: 
 437:     if (result && (provider instanceof RegisterableService))
 438:       ((RegisterableService) provider).onDeregistration(this, category);
 439: 
 440:     return result;
 441:   }
 442: 
 443: 
 444:   /**
 445:    * De-registers a provider for the specified service category.
 446:    *
 447:    * <p>If <code>provider</code> implements the {@link
 448:    * RegisterableService} interface, its {@link
 449:    * RegisterableService#onDeregistration onDeregistration} method is
 450:    * invoked in order to inform the provider about the removal from
 451:    * this registry.
 452:    *
 453:    * @param provider the service provider to be de-registered.
 454:    *
 455:    * @param category the service category from which
 456:    * <code>provider</code> shall be de-registered.
 457:    *
 458:    * @return <code>true</code> if <code>provider</code> was previously
 459:    * registered for the specified service category; <code>false</code>
 460:    * if if the provider had not been registered.
 461:    *
 462:    * @throws IllegalArgumentException if <code>provider</code> is
 463:    * <code>null</code>, or if <code>category</code> is not among the
 464:    * categories passed to the {@linkplain #ServiceRegistry(Iterator)
 465:    * constructor} of this ServiceRegistry.
 466:    *
 467:    * @throws ClassCastException if <code>provider</code> does not
 468:    * implement <code>category</code>.
 469:    */
 470:   public synchronized <T> boolean deregisterServiceProvider(T provider,
 471:                                                             Class<T> category)
 472:   {
 473:     for (int i = 0; i < categories.length; i++)
 474:       if (categories[i] == category)
 475:         return deregisterServiceProvider(provider, i);
 476:     throw new IllegalArgumentException();
 477:   }
 478: 
 479: 
 480:   /**
 481:    * De-registers a provider from all service categories it
 482:    * implements.
 483:    *
 484:    * <p>If <code>provider</code> implements the {@link
 485:    * RegisterableService} interface, its {@link
 486:    * RegisterableService#onDeregistration onDeregistration} method is
 487:    * invoked in order to inform the provider about the removal from
 488:    * this registry. If <code>provider</code> implements several
 489:    * service categories, <code>onDeregistration</code> gets called
 490:    * multiple times.</p>
 491:    *
 492:    * @param provider the service provider to be de-registered.
 493:    *
 494:    * @throws IllegalArgumentException if <code>provider</code> is
 495:    * <code>null</code>, or if <code>provider</code> does not implement
 496:    * any of the service categories passed to the {@linkplain
 497:    * #ServiceRegistry(Iterator) constructor} of this
 498:    * <code>ServiceRegistry</code>.
 499:    */
 500:   public synchronized void deregisterServiceProvider(Object provider)
 501:   {
 502:     boolean ok = false;
 503: 
 504:     if (provider == null)
 505:       throw new IllegalArgumentException();
 506: 
 507:     for (int i = 0; i < categories.length; i++)
 508:       if (categories[i].isInstance(provider))
 509:         {
 510:           ok = true;
 511:           deregisterServiceProvider(provider, i);
 512:         }
 513: 
 514:     if (!ok)
 515:       throw new IllegalArgumentException();
 516:   }
 517: 
 518: 
 519:   /**
 520:    * De-registers all providers which have been registered for the
 521:    * specified service category.
 522:    *
 523:    * <p>If a provider implements the {@link RegisterableService}
 524:    * interface, its {@link RegisterableService#onDeregistration
 525:    * onDeregistration} method is invoked in order to inform the
 526:    * provider about the removal from this registry. If the provider
 527:    * implements several service categories,
 528:    * <code>onDeregistration</code> gets called multiple times.
 529:    *
 530:    * @param category the category whose registered providers will be
 531:    * de-registered.
 532:    *
 533:    * @throws IllegalArgumentException if <code>category</code> is not
 534:    * among the categories passed to the {@linkplain
 535:    * #ServiceRegistry(Iterator) constructor} of this
 536:    * <code>ServiceRegistry</code>.
 537:    */
 538:   public synchronized void deregisterAll(Class<?> category)
 539:   {
 540:     boolean ok = false;
 541: 
 542:     for (int i = 0; i < categories.length; i++)
 543:       {
 544:         if (categories[i] != category)
 545:           continue;
 546: 
 547:         ok = true;
 548:         while (providers[i] != null)
 549:           deregisterServiceProvider(providers[i].get(0), i);
 550:       }
 551: 
 552:     if (!ok)
 553:       throw new IllegalArgumentException();
 554:   }
 555: 
 556: 
 557:   /**
 558:    * De-registers all service providers.
 559:    *
 560:    * <p>If a provider implements the {@link RegisterableService}
 561:    * interface, its {@link RegisterableService#onDeregistration
 562:    * onDeregistration} method is invoked in order to inform the
 563:    * provider about the removal from this registry. If the provider
 564:    * implements several service categories,
 565:    * <code>onDeregistration</code> gets called multiple times.
 566:    */
 567:   public synchronized void deregisterAll()
 568:   {
 569:     for (int i = 0; i < categories.length; i++)
 570:       while (providers[i] != null)
 571:         deregisterServiceProvider(providers[i].get(0), i);
 572:   }
 573: 
 574: 
 575:   /**
 576:    * Called by the Virtual Machine when it detects that this
 577:    * <code>ServiceRegistry</code> has become garbage. De-registers all
 578:    * service providers, which will cause those that implement {@link
 579:    * RegisterableService} to receive a {@link
 580:    * RegisterableService#onDeregistration onDeregistration}
 581:    * notification.
 582:    */
 583:   public void finalize()
 584:     throws Throwable
 585:   {
 586:     super.finalize();
 587:     deregisterAll();
 588:   }
 589: 
 590: 
 591:   /**
 592:    * Determines whether a provider has been registered with this
 593:    * registry.
 594:    *
 595:    * @return <code>true</code> if <code>provider</code> has been
 596:    * registered under any service category; <code>false</code> if
 597:    * it is not registered.
 598:    *
 599:    * @throws IllegalArgumentException if <code>provider</code> is
 600:    * <code>null</code>.
 601:    */
 602:   public synchronized boolean contains(Object provider)
 603:   {
 604:     if (provider == null)
 605:       throw new IllegalArgumentException();
 606: 
 607:     // Note that contains is rather unlikely to be ever called,
 608:     // so it would be wasteful to keep a special data structure
 609:     // (such as a HashSet) for making it a fast operation.
 610:     for (int i = 0; i < providers.length; i++)
 611:       {
 612:         // If provider does not implement categories[i],
 613:         // it would not have been possible to register it there.
 614:         // In that case, it would be pointless to look there.
 615:         if (!categories[i].isInstance(provider))
 616:           continue;
 617: 
 618:         // But if the list of registered providers contains provider,
 619:         // we have found it.
 620:         LinkedList p = providers[i];
 621:         if (p != null && p.contains(provider))
 622:           return true;
 623:       }
 624: 
 625:     return false;
 626:   }
 627: 
 628: 
 629:   /**
 630:    * Returns the index in {@link #categories} occupied by the
 631:    * specified service category.
 632:    *
 633:    * @throws IllegalArgumentException if <code>category</code> is not
 634:    * among the categories passed to the {@linkplain
 635:    * #ServiceRegistry(Iterator) constructor} of this ServiceRegistry.
 636:    */
 637:   private int getCategoryID(Class category)
 638:   {
 639:     for (int i = 0; i < categories.length; i++)
 640:       if (categories[i] == category)
 641:         return i;
 642: 
 643:     throw new IllegalArgumentException();
 644:   }
 645: 
 646: 
 647:   /**
 648:    * Retrieves all providers that have been registered for the
 649:    * specified service category.
 650:    *
 651:    * @param category the service category whose providers are
 652:    * to be retrieved.
 653:    *
 654:    * @param useOrdering <code>true</code> in order to retrieve the
 655:    * providers in an order imposed by the {@linkplain #setOrdering
 656:    * ordering constraints}; <code>false</code> in order to retrieve
 657:    * the providers in any order.
 658:    *
 659:    * @throws IllegalArgumentException if <code>category</code> is not
 660:    * among the categories passed to the {@linkplain
 661:    * #ServiceRegistry(Iterator) constructor} of this
 662:    * <code>ServiceRegistry</code>.
 663:    *
 664:    * @see #getServiceProviders(Class, Filter, boolean)
 665:    */
 666:   public <T> Iterator<T> getServiceProviders(Class<T> category,
 667:                                              boolean useOrdering)
 668:   {
 669:     return getServiceProviders(category, null, useOrdering);
 670:   }
 671: 
 672: 
 673:   /**
 674:    * Retrieves all providers that have been registered for the
 675:    * specified service category and that satisfy the criteria
 676:    * of a custom filter.
 677:    *
 678:    * @param category the service category whose providers are
 679:    * to be retrieved.
 680:    *
 681:    * @param filter a custom filter, or <code>null</code> to
 682:    * retrieve all registered providers for the specified
 683:    * category.
 684:    *
 685:    * @param useOrdering <code>true</code> in order to retrieve the
 686:    * providers in an order imposed by the {@linkplain #setOrdering
 687:    * ordering constraints}; <code>false</code> in order to retrieve
 688:    * the providers in any order.
 689:    *
 690:    * @throws IllegalArgumentException if <code>category</code> is not
 691:    * among the categories passed to the {@linkplain
 692:    * #ServiceRegistry(Iterator) constructor} of this
 693:    * <code>ServiceRegistry</code>.
 694:    */
 695:   public synchronized <T> Iterator<T> getServiceProviders(Class<T> category,
 696:                                                           Filter filter,
 697:                                                           boolean useOrdering)
 698:   {
 699:     int catid;
 700:     LinkedList provs;
 701:     ArrayList result;
 702: 
 703:     catid = getCategoryID(category);
 704:     provs = providers[catid];
 705:     if (provs == null)
 706:       return Collections.EMPTY_LIST.iterator();
 707: 
 708:     result = new ArrayList(provs.size());
 709:     for (Iterator iter = provs.iterator(); iter.hasNext();)
 710:       {
 711:         Object provider = iter.next();
 712:         if (filter == null || filter.filter(provider))
 713:           result.add(provider);
 714:       }
 715: 
 716:     // If we are supposed to obey ordering constraints, and
 717:     // if any constraints have been imposed on the specified
 718:     // service category, sort the result.
 719:     if (useOrdering && constraints != null)
 720:       {
 721:         final Map cons = constraints[catid];
 722:         if (cons != null)
 723:           Collections.sort(result, new Comparator()
 724:             {
 725:               public int compare(Object o1, Object o2)
 726:               {
 727:                 Set s;
 728: 
 729:                 if (o1 == o2)
 730:                   return 0;
 731: 
 732:                 s = (Set) cons.get(o1);
 733:                 if (s != null && s.contains(o2))
 734:                   return -1;  // o1 < o2
 735: 
 736:                 s = (Set) cons.get(o2);
 737:                 if (s != null && s.contains(o1))
 738:                   return 1;  // o1 > o2
 739: 
 740:                 return 0; // o1 == o2
 741:               }
 742:             });
 743:       }
 744: 
 745:     return result.iterator();
 746:   }
 747: 
 748: 
 749:   /**
 750:    * Returns one of the service providers that is a subclass of the
 751:    * specified class.
 752:    *
 753:    * @param providerClass a class to search for.
 754:    */
 755:   public synchronized <T> T getServiceProviderByClass(Class<T> providerClass)
 756:   {
 757:     if (providerClass == null)
 758:       throw new IllegalArgumentException();
 759: 
 760:     // Note that the method getServiceProviderByClass is rather
 761:     // unlikely to be ever called, so it would be wasteful to keep a
 762:     // special data structure for making it a fast operation.
 763:     for (int cat = 0; cat < categories.length; cat++)
 764:       {
 765:         if (!categories[cat].isAssignableFrom(providerClass))
 766:           continue;
 767: 
 768:         LinkedList provs = providers[cat];
 769:         if (provs == null)
 770:           continue;
 771: 
 772:         for (Iterator iter = provs.iterator(); iter.hasNext();)
 773:           {
 774:             Object provider = iter.next();
 775:             if (providerClass.isInstance(provider))
 776:               return (T) provider;
 777:           }
 778:       }
 779: 
 780:     return null;
 781:   }
 782: 
 783: 
 784:   /**
 785:    * Adds an ordering constraint on service providers.
 786:    *
 787:    * @param category the service category to which an ordering
 788:    * constraint is to be added.
 789:    *
 790:    * @param firstProvider the provider which is supposed to come before
 791:    * <code>second</code>.
 792:    *
 793:    * @param secondProvider the provider which is supposed to come after
 794:    * <code>first</code>.
 795:    *
 796:    * @throws IllegalArgumentException if <code>first</code> and
 797:    * <code>second</code> are referring to the same object, or if one
 798:    * of them is <code>null</code>.
 799:    *
 800:    * @see #unsetOrdering
 801:    * @see #getServiceProviders(Class, Filter, boolean)
 802:    */
 803:   public synchronized <T> boolean setOrdering(Class<T> category,
 804:                                               T firstProvider,
 805:                                               T secondProvider)
 806:   {
 807:     return addConstraint(getCategoryID(category), firstProvider,
 808:                          secondProvider);
 809:   }
 810: 
 811: 
 812:   /**
 813:    * Removes an ordering constraint on service providers.
 814:    *
 815:    * @param category the service category from which an ordering
 816:    * constraint is to be removed.
 817:    *
 818:    * @param firstProvider the provider which is supposed to come before
 819:    * <code>second</code>.
 820:    *
 821:    * @param secondProvider the provider which is supposed to come after
 822:    * <code>first</code>.
 823:    *
 824:    * @throws IllegalArgumentException if <code>first</code> and
 825:    * <code>second</code> are referring to the same object, or if one
 826:    * of them is <code>null</code>.
 827:    *
 828:    * @see #setOrdering
 829:    */
 830:   public synchronized <T> boolean unsetOrdering(Class<T> category,
 831:                                                 T firstProvider,
 832:                                                 T secondProvider)
 833:   {
 834:     return removeConstraint(getCategoryID(category),
 835:                             firstProvider, secondProvider);
 836:   }
 837: 
 838: 
 839:   /**
 840:    * Adds an ordering constraint on service providers.
 841:    *
 842:    * @param catid the service category ID, which is the
 843:    * category&#x2019;s index into the {@link #categories} array.
 844:    *
 845:    * @param first the provider which is supposed to come before
 846:    * <code>second</code>.
 847:    *
 848:    * @param second the provider which is supposed to come after
 849:    * <code>first</code>.
 850:    *
 851:    * @throws IllegalArgumentException if <code>first</code> and
 852:    * <code>second</code> are referring to the same object, or if one
 853:    * of them is <code>null</code>.
 854:    */
 855:   private boolean addConstraint(int catid, Object first, Object second)
 856:   {
 857:     Set s;
 858:     IdentityHashMap cons;
 859: 
 860:     // Also checks argument validity.
 861:     removeConstraint(catid, second, first);
 862: 
 863:     if (constraints == null)
 864:       constraints = new IdentityHashMap[categories.length];
 865:     cons = constraints[catid];
 866:     if (cons == null)
 867:       cons = constraints[catid] = new IdentityHashMap();
 868: 
 869:     s = (Set) cons.get(first);
 870:     if (s == null)
 871:       cons.put(first, s = new HashSet());
 872:     return s.add(second);
 873:   }
 874: 
 875: 
 876:   /**
 877:    * Removes an ordering constraint on service providers.
 878:    *
 879:    * @param catid the service category ID, which is the
 880:    * category&#x2019;s index into the {@link #categories} array.
 881:    *
 882:    * @param first the provider which is supposed to come before
 883:    * <code>second</code>.
 884:    *
 885:    * @param second the provider which is supposed to come after
 886:    * <code>first</code>.
 887:    *
 888:    * @throws IllegalArgumentException if <code>first</code> and
 889:    * <code>second</code> are referring to the same object, or if one
 890:    * of them is <code>null</code>.
 891:    */
 892:   private boolean removeConstraint(int catid, Object first, Object second)
 893:   {
 894:     Collection s;
 895:     IdentityHashMap cons;
 896: 
 897:     if (first == null || second == null || first == second)
 898:       throw new IllegalArgumentException();
 899: 
 900:     if (constraints == null)
 901:       return false;
 902: 
 903:     cons = constraints[catid];
 904:     if (cons == null)
 905:       return false;
 906: 
 907:     s = (Collection) cons.get(first);
 908:     if (s == null)
 909:       return false;
 910: 
 911:     if (!s.remove(second))
 912:       return false;
 913: 
 914:     // If we removed the last constraint for a service category,
 915:     // we can get free some memory.
 916:     if (cons.isEmpty())
 917:       {
 918:         constraints[catid] = null;
 919:         boolean anyConstraints = false;
 920:         for (int i = 0; i < constraints.length; i++)
 921:           {
 922:             if (constraints[i] != null)
 923:               {
 924:                 anyConstraints = true;
 925:                 break;
 926:               }
 927:           }
 928:         if (!anyConstraints)
 929:           constraints = null;
 930:       }
 931: 
 932:     return true;
 933:   }
 934: 
 935: 
 936:   /**
 937:    * A filter for selecting service providers that match custom
 938:    * criteria.
 939:    *
 940:    * @see ServiceRegistry#getServiceProviders(Class, Filter,
 941:    * boolean)
 942:    *
 943:    * @since 1.4
 944:    *
 945:    * @author Michael Koch (konqueror@gmx.de)
 946:    * @author Sascha Brawer (brawer@dandelis.ch)
 947:    */
 948:   public static interface Filter
 949:   {
 950:     /**
 951:      * Checks whether the specified service provider matches the
 952:      * constraints of this Filter.
 953:      *
 954:      * @param provider the service provider in question.
 955:      *
 956:      * @return <code>true</code> if <code>provider</code> matches the
 957:      * criteria; <code>false</code> if it does not match.
 958:      */
 959:     boolean filter(Object provider);
 960:   }
 961: }