001/* ServiceRegistry.java -- A simple registry for service providers. 002 Copyright (C) 2004, 2005 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.imageio.spi; 040 041import gnu.classpath.ServiceFactory; 042 043import java.util.ArrayList; 044import java.util.Collection; 045import java.util.Collections; 046import java.util.Comparator; 047import java.util.HashSet; 048import java.util.IdentityHashMap; 049import java.util.Iterator; 050import java.util.LinkedList; 051import java.util.Map; 052import java.util.NoSuchElementException; 053import java.util.Set; 054 055/** 056 * A registry for service providers. 057 * 058 * @since 1.4 059 * 060 * @author Michael Koch (konqueror@gmx.de) 061 * @author Sascha Brawer (brawer@dandelis.ch) 062 */ 063public class ServiceRegistry 064{ 065 // Package-private to avoid a trampoline. 066 /** 067 * The service categories of this registry. 068 * 069 * <p>Note that we expect that only very few categories will 070 * typically be used with a registry. The most common case will be 071 * one, it seems unlikely that any registry would contain more than 072 * five or six categories. Therefore, we intentionally avoid the 073 * overhead of a HashMap. 074 * 075 * @see #providers 076 */ 077 final Class[] categories; 078 079 080 /** 081 * The registered providers for each service category, indexed by 082 * the same index as the {@link #categories} array. If no provider 083 * is registered for a category, the array entry will be 084 * <code>null</code>. 085 * 086 * <p>Note that we expect that only very few providers will 087 * typically be registered for a category. The most common case will 088 * be one or two. Therefore, we intentionally avoid the overhead of 089 * a HashMap. 090 */ 091 private final LinkedList[] providers; 092 093 094 /** 095 * The ordring constaints for each service category, indexed by the 096 * same index as the {@link #categories} array. The constraints for 097 * a service category are stored as a <code>Map<Object, 098 * Set<Object>></code>, where the Map’s values are 099 * 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}<{@link java.lang.Class}>. 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’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’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}