Frames | No Frames |
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<Object, 98: * Set<Object>></code>, where the Map’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}<{@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: }