1 /* 2 * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.imageio.spi; 27 28 import java.io.File; 29 import java.util.ArrayList; 30 import java.util.HashMap; 31 import java.util.Iterator; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.NoSuchElementException; 35 import java.util.Set; 36 import java.util.ServiceLoader; 37 38 /** 39 * A registry for service provider instances. 40 * 41 * <p> A <i>service</i> is a well-known set of interfaces and (usually 42 * abstract) classes. A <i>service provider</i> is a specific 43 * implementation of a service. The classes in a provider typically 44 * implement the interface or subclass the class defined by the 45 * service itself. 46 * 47 * <p> Service providers are stored in one or more <i>categories</i>, 48 * each of which is defined by a class of interface (described by a 49 * <code>Class</code> object) that all of its members must implement. 50 * The set of categories may be changed dynamically. 51 * 52 * <p> Only a single instance of a given leaf class (that is, the 53 * actual class returned by <code>getClass()</code>, as opposed to any 54 * inherited classes or interfaces) may be registered. That is, 55 * suppose that the 56 * <code>com.mycompany.mypkg.GreenServiceProvider</code> class 57 * implements the <code>com.mycompany.mypkg.MyService</code> 58 * interface. If a <code>GreenServiceProvider</code> instance is 59 * registered, it will be stored in the category defined by the 60 * <code>MyService</code> class. If a new instance of 61 * <code>GreenServiceProvider</code> is registered, it will replace 62 * the previous instance. In practice, service provider objects are 63 * usually singletons so this behavior is appropriate. 64 * 65 * <p> To declare a service provider, a <code>services</code> 66 * subdirectory is placed within the <code>META-INF</code> directory 67 * that is present in every JAR file. This directory contains a file 68 * for each service provider interface that has one or more 69 * implementation classes present in the JAR file. For example, if 70 * the JAR file contained a class named 71 * <code>com.mycompany.mypkg.MyServiceImpl</code> which implements the 72 * <code>javax.someapi.SomeService</code> interface, the JAR file 73 * would contain a file named: <pre> 74 * META-INF/services/javax.someapi.SomeService </pre> 75 * 76 * containing the line: 77 * 78 * <pre> 79 * com.mycompany.mypkg.MyService 80 * </pre> 81 * 82 * <p> The service provider classes should be to be lightweight and 83 * quick to load. Implementations of these interfaces should avoid 84 * complex dependencies on other classes and on native code. The usual 85 * pattern for more complex services is to register a lightweight 86 * proxy for the heavyweight service. 87 * 88 * <p> An application may customize the contents of a registry as it 89 * sees fit, so long as it has the appropriate runtime permission. 90 * 91 * <p> For more details on declaring service providers, and the JAR 92 * format in general, see the <a 93 * href="../../../../technotes/guides/jar/jar.html"> 94 * JAR File Specification</a>. 95 * 96 * @see RegisterableService 97 * 98 */ 99 public class ServiceRegistry { 100 101 // Class -> Registry 102 private Map<Class<?>, SubRegistry> categoryMap = new HashMap<>(); 103 104 /** 105 * Constructs a <code>ServiceRegistry</code> instance with a 106 * set of categories taken from the <code>categories</code> 107 * argument. 108 * 109 * @param categories an <code>Iterator</code> containing 110 * <code>Class</code> objects to be used to define categories. 111 * 112 * @exception IllegalArgumentException if 113 * <code>categories</code> is <code>null</code>. 114 */ 115 public ServiceRegistry(Iterator<Class<?>> categories) { 116 if (categories == null) { 117 throw new IllegalArgumentException("categories == null!"); 118 } 119 while (categories.hasNext()) { 120 Class<?> category = categories.next(); 121 SubRegistry reg = new SubRegistry(this, category); 122 categoryMap.put(category, reg); 123 } 124 } 125 126 // The following two methods expose functionality from 127 // sun.misc.Service. If that class is made public, they may be 128 // removed. 129 // 130 // The sun.misc.ServiceConfigurationError class may also be 131 // exposed, in which case the references to 'an 132 // <code>Error</code>' below should be changed to 'a 133 // <code>ServiceConfigurationError</code>'. 134 135 /** 136 * Searches for implementations of a particular service class 137 * using the given class loader. 138 * 139 * <p> This method transforms the name of the given service class 140 * into a provider-configuration filename as described in the 141 * class comment and then uses the <code>getResources</code> 142 * method of the given class loader to find all available files 143 * with that name. These files are then read and parsed to 144 * produce a list of provider-class names. The iterator that is 145 * returned uses the given class loader to look up and then 146 * instantiate each element of the list. 147 * 148 * <p> Because it is possible for extensions to be installed into 149 * a running Java virtual machine, this method may return 150 * different results each time it is invoked. 151 * 152 * @param providerClass a <code>Class</code>object indicating the 153 * class or interface of the service providers being detected. 154 * 155 * @param loader the class loader to be used to load 156 * provider-configuration files and instantiate provider classes, 157 * or <code>null</code> if the system class loader (or, failing that 158 * the bootstrap class loader) is to be used. 159 * 160 * @param <T> the type of the providerClass. 161 * 162 * @return An <code>Iterator</code> that yields provider objects 163 * for the given service, in some arbitrary order. The iterator 164 * will throw an <code>Error</code> if a provider-configuration 165 * file violates the specified format or if a provider class 166 * cannot be found and instantiated. 167 * 168 * @exception IllegalArgumentException if 169 * <code>providerClass</code> is <code>null</code>. 170 */ 171 public static <T> Iterator<T> lookupProviders(Class<T> providerClass, 172 ClassLoader loader) 173 { 174 if (providerClass == null) { 175 throw new IllegalArgumentException("providerClass == null!"); 176 } 177 return ServiceLoader.load(providerClass, loader).iterator(); 178 } 179 180 /** 181 * Locates and incrementally instantiates the available providers 182 * of a given service using the context class loader. This 183 * convenience method is equivalent to: 184 * 185 * <pre> 186 * ClassLoader cl = Thread.currentThread().getContextClassLoader(); 187 * return Service.providers(service, cl); 188 * </pre> 189 * 190 * @param providerClass a <code>Class</code>object indicating the 191 * class or interface of the service providers being detected. 192 * 193 * @param <T> the type of the providerClass. 194 * 195 * @return An <code>Iterator</code> that yields provider objects 196 * for the given service, in some arbitrary order. The iterator 197 * will throw an <code>Error</code> if a provider-configuration 198 * file violates the specified format or if a provider class 199 * cannot be found and instantiated. 200 * 201 * @exception IllegalArgumentException if 202 * <code>providerClass</code> is <code>null</code>. 203 */ 204 public static <T> Iterator<T> lookupProviders(Class<T> providerClass) { 205 if (providerClass == null) { 206 throw new IllegalArgumentException("providerClass == null!"); 207 } 208 return ServiceLoader.load(providerClass).iterator(); 209 } 210 211 /** 212 * Returns an <code>Iterator</code> of <code>Class</code> objects 213 * indicating the current set of categories. The iterator will be 214 * empty if no categories exist. 215 * 216 * @return an <code>Iterator</code> containing 217 * <code>Class</code>objects. 218 */ 219 public Iterator<Class<?>> getCategories() { 220 Set<Class<?>> keySet = categoryMap.keySet(); 221 return keySet.iterator(); 222 } 223 224 /** 225 * Returns an Iterator containing the subregistries to which the 226 * provider belongs. 227 */ 228 private Iterator<SubRegistry> getSubRegistries(Object provider) { 229 List<SubRegistry> l = new ArrayList<>(); 230 Iterator<Class<?>> iter = categoryMap.keySet().iterator(); 231 while (iter.hasNext()) { 232 Class<?> c = iter.next(); 233 if (c.isAssignableFrom(provider.getClass())) { 234 l.add(categoryMap.get(c)); 235 } 236 } 237 return l.iterator(); 238 } 239 240 /** 241 * Adds a service provider object to the registry. The provider 242 * is associated with the given category. 243 * 244 * <p> If <code>provider</code> implements the 245 * <code>RegisterableService</code> interface, its 246 * <code>onRegistration</code> method will be called. Its 247 * <code>onDeregistration</code> method will be called each time 248 * it is deregistered from a category, for example if a 249 * category is removed or the registry is garbage collected. 250 * 251 * @param provider the service provide object to be registered. 252 * @param category the category under which to register the 253 * provider. 254 * @param <T> the type of the provider. 255 * 256 * @return true if no provider of the same class was previously 257 * registered in the same category category. 258 * 259 * @exception IllegalArgumentException if <code>provider</code> is 260 * <code>null</code>. 261 * @exception IllegalArgumentException if there is no category 262 * corresponding to <code>category</code>. 263 * @exception ClassCastException if provider does not implement 264 * the <code>Class</code> defined by <code>category</code>. 265 */ 266 public <T> boolean registerServiceProvider(T provider, 267 Class<T> category) { 268 if (provider == null) { 269 throw new IllegalArgumentException("provider == null!"); 270 } 271 SubRegistry reg = categoryMap.get(category); 272 if (reg == null) { 273 throw new IllegalArgumentException("category unknown!"); 274 } 275 if (!category.isAssignableFrom(provider.getClass())) { 276 throw new ClassCastException(); 277 } 278 279 return reg.registerServiceProvider(provider); 280 } 281 282 /** 283 * Adds a service provider object to the registry. The provider 284 * is associated within each category present in the registry 285 * whose <code>Class</code> it implements. 286 * 287 * <p> If <code>provider</code> implements the 288 * <code>RegisterableService</code> interface, its 289 * <code>onRegistration</code> method will be called once for each 290 * category it is registered under. Its 291 * <code>onDeregistration</code> method will be called each time 292 * it is deregistered from a category or when the registry is 293 * finalized. 294 * 295 * @param provider the service provider object to be registered. 296 * 297 * @exception IllegalArgumentException if 298 * <code>provider</code> is <code>null</code>. 299 */ 300 public void registerServiceProvider(Object provider) { 301 if (provider == null) { 302 throw new IllegalArgumentException("provider == null!"); 303 } 304 Iterator<SubRegistry> regs = getSubRegistries(provider); 305 while (regs.hasNext()) { 306 SubRegistry reg = regs.next(); 307 reg.registerServiceProvider(provider); 308 } 309 } 310 311 /** 312 * Adds a set of service provider objects, taken from an 313 * <code>Iterator</code> to the registry. Each provider is 314 * associated within each category present in the registry whose 315 * <code>Class</code> it implements. 316 * 317 * <p> For each entry of <code>providers</code> that implements 318 * the <code>RegisterableService</code> interface, its 319 * <code>onRegistration</code> method will be called once for each 320 * category it is registered under. Its 321 * <code>onDeregistration</code> method will be called each time 322 * it is deregistered from a category or when the registry is 323 * finalized. 324 * 325 * @param providers an Iterator containing service provider 326 * objects to be registered. 327 * 328 * @exception IllegalArgumentException if <code>providers</code> 329 * is <code>null</code> or contains a <code>null</code> entry. 330 */ 331 public void registerServiceProviders(Iterator<?> providers) { 332 if (providers == null) { 333 throw new IllegalArgumentException("provider == null!"); 334 } 335 while (providers.hasNext()) { 336 registerServiceProvider(providers.next()); 337 } 338 } 339 340 /** 341 * Removes a service provider object from the given category. If 342 * the provider was not previously registered, nothing happens and 343 * <code>false</code> is returned. Otherwise, <code>true</code> 344 * is returned. If an object of the same class as 345 * <code>provider</code> but not equal (using <code>==</code>) to 346 * <code>provider</code> is registered, it will not be 347 * deregistered. 348 * 349 * <p> If <code>provider</code> implements the 350 * <code>RegisterableService</code> interface, its 351 * <code>onDeregistration</code> method will be called. 352 * 353 * @param provider the service provider object to be deregistered. 354 * @param category the category from which to deregister the 355 * provider. 356 * @param <T> the type of the provider. 357 * 358 * @return <code>true</code> if the provider was previously 359 * registered in the same category category, 360 * <code>false</code> otherwise. 361 * 362 * @exception IllegalArgumentException if <code>provider</code> is 363 * <code>null</code>. 364 * @exception IllegalArgumentException if there is no category 365 * corresponding to <code>category</code>. 366 * @exception ClassCastException if provider does not implement 367 * the class defined by <code>category</code>. 368 */ 369 public <T> boolean deregisterServiceProvider(T provider, 370 Class<T> category) { 371 if (provider == null) { 372 throw new IllegalArgumentException("provider == null!"); 373 } 374 SubRegistry reg = categoryMap.get(category); 375 if (reg == null) { 376 throw new IllegalArgumentException("category unknown!"); 377 } 378 if (!category.isAssignableFrom(provider.getClass())) { 379 throw new ClassCastException(); 380 } 381 return reg.deregisterServiceProvider(provider); 382 } 383 384 /** 385 * Removes a service provider object from all categories that 386 * contain it. 387 * 388 * @param provider the service provider object to be deregistered. 389 * 390 * @exception IllegalArgumentException if <code>provider</code> is 391 * <code>null</code>. 392 */ 393 public void deregisterServiceProvider(Object provider) { 394 if (provider == null) { 395 throw new IllegalArgumentException("provider == null!"); 396 } 397 Iterator<SubRegistry> regs = getSubRegistries(provider); 398 while (regs.hasNext()) { 399 SubRegistry reg = regs.next(); 400 reg.deregisterServiceProvider(provider); 401 } 402 } 403 404 /** 405 * Returns <code>true</code> if <code>provider</code> is currently 406 * registered. 407 * 408 * @param provider the service provider object to be queried. 409 * 410 * @return <code>true</code> if the given provider has been 411 * registered. 412 * 413 * @exception IllegalArgumentException if <code>provider</code> is 414 * <code>null</code>. 415 */ 416 public boolean contains(Object provider) { 417 if (provider == null) { 418 throw new IllegalArgumentException("provider == null!"); 419 } 420 Iterator<SubRegistry> regs = getSubRegistries(provider); 421 while (regs.hasNext()) { 422 SubRegistry reg = regs.next(); 423 if (reg.contains(provider)) { 424 return true; 425 } 426 } 427 428 return false; 429 } 430 431 /** 432 * Returns an <code>Iterator</code> containing all registered 433 * service providers in the given category. If 434 * <code>useOrdering</code> is <code>false</code>, the iterator 435 * will return all of the server provider objects in an arbitrary 436 * order. Otherwise, the ordering will respect any pairwise 437 * orderings that have been set. If the graph of pairwise 438 * orderings contains cycles, any providers that belong to a cycle 439 * will not be returned. 440 * 441 * @param category the category to be retrieved from. 442 * @param useOrdering <code>true</code> if pairwise orderings 443 * should be taken account in ordering the returned objects. 444 * @param <T> the type of the category. 445 * 446 * @return an <code>Iterator</code> containing service provider 447 * objects from the given category, possibly in order. 448 * 449 * @exception IllegalArgumentException if there is no category 450 * corresponding to <code>category</code>. 451 */ 452 public <T> Iterator<T> getServiceProviders(Class<T> category, 453 boolean useOrdering) { 454 SubRegistry reg = categoryMap.get(category); 455 if (reg == null) { 456 throw new IllegalArgumentException("category unknown!"); 457 } 458 @SuppressWarnings("unchecked") 459 Iterator<T> it = (Iterator<T>)reg.getServiceProviders(useOrdering); 460 return it; 461 } 462 463 /** 464 * A simple filter interface used by 465 * <code>ServiceRegistry.getServiceProviders</code> to select 466 * providers matching an arbitrary criterion. Classes that 467 * implement this interface should be defined in order to make use 468 * of the <code>getServiceProviders</code> method of 469 * <code>ServiceRegistry</code> that takes a <code>Filter</code>. 470 * 471 * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean) 472 */ 473 public interface Filter { 474 475 /** 476 * Returns <code>true</code> if the given 477 * <code>provider</code> object matches the criterion defined 478 * by this <code>Filter</code>. 479 * 480 * @param provider a service provider <code>Object</code>. 481 * 482 * @return true if the provider matches the criterion. 483 */ 484 boolean filter(Object provider); 485 } 486 487 /** 488 * Returns an <code>Iterator</code> containing service provider 489 * objects within a given category that satisfy a criterion 490 * imposed by the supplied <code>ServiceRegistry.Filter</code> 491 * object's <code>filter</code> method. 492 * 493 * <p> The <code>useOrdering</code> argument controls the 494 * ordering of the results using the same rules as 495 * <code>getServiceProviders(Class, boolean)</code>. 496 * 497 * @param category the category to be retrieved from. 498 * @param filter an instance of <code>ServiceRegistry.Filter</code> 499 * whose <code>filter</code> method will be invoked. 500 * @param useOrdering <code>true</code> if pairwise orderings 501 * should be taken account in ordering the returned objects. 502 * @param <T> the type of the category. 503 * 504 * @return an <code>Iterator</code> containing service provider 505 * objects from the given category, possibly in order. 506 * 507 * @exception IllegalArgumentException if there is no category 508 * corresponding to <code>category</code>. 509 */ 510 public <T> Iterator<T> getServiceProviders(Class<T> category, 511 Filter filter, 512 boolean useOrdering) { 513 SubRegistry reg = categoryMap.get(category); 514 if (reg == null) { 515 throw new IllegalArgumentException("category unknown!"); 516 } 517 Iterator<T> iter = getServiceProviders(category, useOrdering); 518 return new FilterIterator<>(iter, filter); 519 } 520 521 /** 522 * Returns the currently registered service provider object that 523 * is of the given class type. At most one object of a given 524 * class is allowed to be registered at any given time. If no 525 * registered object has the desired class type, <code>null</code> 526 * is returned. 527 * 528 * @param providerClass the <code>Class</code> of the desired 529 * service provider object. 530 * @param <T> the type of the provider. 531 * 532 * @return a currently registered service provider object with the 533 * desired <code>Class</code>type, or <code>null</code> is none is 534 * present. 535 * 536 * @exception IllegalArgumentException if <code>providerClass</code> is 537 * <code>null</code>. 538 */ 539 public <T> T getServiceProviderByClass(Class<T> providerClass) { 540 if (providerClass == null) { 541 throw new IllegalArgumentException("providerClass == null!"); 542 } 543 Iterator<Class<?>> iter = categoryMap.keySet().iterator(); 544 while (iter.hasNext()) { 545 Class<?> c = iter.next(); 546 if (c.isAssignableFrom(providerClass)) { 547 SubRegistry reg = categoryMap.get(c); 548 T provider = reg.getServiceProviderByClass(providerClass); 549 if (provider != null) { 550 return provider; 551 } 552 } 553 } 554 return null; 555 } 556 557 /** 558 * Sets a pairwise ordering between two service provider objects 559 * within a given category. If one or both objects are not 560 * currently registered within the given category, or if the 561 * desired ordering is already set, nothing happens and 562 * <code>false</code> is returned. If the providers previously 563 * were ordered in the reverse direction, that ordering is 564 * removed. 565 * 566 * <p> The ordering will be used by the 567 * <code>getServiceProviders</code> methods when their 568 * <code>useOrdering</code> argument is <code>true</code>. 569 * 570 * @param category a <code>Class</code> object indicating the 571 * category under which the preference is to be established. 572 * @param firstProvider the preferred provider. 573 * @param secondProvider the provider to which 574 * <code>firstProvider</code> is preferred. 575 * @param <T> the type of the category. 576 * 577 * @return <code>true</code> if a previously unset ordering 578 * was established. 579 * 580 * @exception IllegalArgumentException if either provider is 581 * <code>null</code> or they are the same object. 582 * @exception IllegalArgumentException if there is no category 583 * corresponding to <code>category</code>. 584 */ 585 public <T> boolean setOrdering(Class<T> category, 586 T firstProvider, 587 T secondProvider) { 588 if (firstProvider == null || secondProvider == null) { 589 throw new IllegalArgumentException("provider is null!"); 590 } 591 if (firstProvider == secondProvider) { 592 throw new IllegalArgumentException("providers are the same!"); 593 } 594 SubRegistry reg = categoryMap.get(category); 595 if (reg == null) { 596 throw new IllegalArgumentException("category unknown!"); 597 } 598 if (reg.contains(firstProvider) && 599 reg.contains(secondProvider)) { 600 return reg.setOrdering(firstProvider, secondProvider); 601 } 602 return false; 603 } 604 605 /** 606 * Sets a pairwise ordering between two service provider objects 607 * within a given category. If one or both objects are not 608 * currently registered within the given category, or if no 609 * ordering is currently set between them, nothing happens 610 * and <code>false</code> is returned. 611 * 612 * <p> The ordering will be used by the 613 * <code>getServiceProviders</code> methods when their 614 * <code>useOrdering</code> argument is <code>true</code>. 615 * 616 * @param category a <code>Class</code> object indicating the 617 * category under which the preference is to be disestablished. 618 * @param firstProvider the formerly preferred provider. 619 * @param secondProvider the provider to which 620 * <code>firstProvider</code> was formerly preferred. 621 * @param <T> the type of the category. 622 * 623 * @return <code>true</code> if a previously set ordering was 624 * disestablished. 625 * 626 * @exception IllegalArgumentException if either provider is 627 * <code>null</code> or they are the same object. 628 * @exception IllegalArgumentException if there is no category 629 * corresponding to <code>category</code>. 630 */ 631 public <T> boolean unsetOrdering(Class<T> category, 632 T firstProvider, 633 T secondProvider) { 634 if (firstProvider == null || secondProvider == null) { 635 throw new IllegalArgumentException("provider is null!"); 636 } 637 if (firstProvider == secondProvider) { 638 throw new IllegalArgumentException("providers are the same!"); 639 } 640 SubRegistry reg = categoryMap.get(category); 641 if (reg == null) { 642 throw new IllegalArgumentException("category unknown!"); 643 } 644 if (reg.contains(firstProvider) && 645 reg.contains(secondProvider)) { 646 return reg.unsetOrdering(firstProvider, secondProvider); 647 } 648 return false; 649 } 650 651 /** 652 * Deregisters all service provider object currently registered 653 * under the given category. 654 * 655 * @param category the category to be emptied. 656 * 657 * @exception IllegalArgumentException if there is no category 658 * corresponding to <code>category</code>. 659 */ 660 public void deregisterAll(Class<?> category) { 661 SubRegistry reg = categoryMap.get(category); 662 if (reg == null) { 663 throw new IllegalArgumentException("category unknown!"); 664 } 665 reg.clear(); 666 } 667 668 /** 669 * Deregisters all currently registered service providers from all 670 * categories. 671 */ 672 public void deregisterAll() { 673 Iterator<SubRegistry> iter = categoryMap.values().iterator(); 674 while (iter.hasNext()) { 675 SubRegistry reg = iter.next(); 676 reg.clear(); 677 } 678 } 679 680 /** 681 * Finalizes this object prior to garbage collection. The 682 * <code>deregisterAll</code> method is called to deregister all 683 * currently registered service providers. This method should not 684 * be called from application code. 685 * 686 * @exception Throwable if an error occurs during superclass 687 * finalization. 688 */ 689 public void finalize() throws Throwable { 690 deregisterAll(); 691 super.finalize(); 692 } 693 } 694 695 696 /** 697 * A portion of a registry dealing with a single superclass or 698 * interface. 699 */ 700 class SubRegistry { 701 702 ServiceRegistry registry; 703 704 Class<?> category; 705 706 // Provider Objects organized by partial oridering 707 PartiallyOrderedSet<Object> poset = new PartiallyOrderedSet<>(); 708 709 // Class -> Provider Object of that class 710 // No way to express heterogeneous map, we want 711 // Map<Class<T>, T>, where T is ? 712 Map<Class<?>, Object> map = new HashMap<>(); 713 714 public SubRegistry(ServiceRegistry registry, Class<?> category) { 715 this.registry = registry; 716 this.category = category; 717 } 718 719 public boolean registerServiceProvider(Object provider) { 720 Object oprovider = map.get(provider.getClass()); 721 boolean present = oprovider != null; 722 723 if (present) { 724 deregisterServiceProvider(oprovider); 725 } 726 map.put(provider.getClass(), provider); 727 poset.add(provider); 728 if (provider instanceof RegisterableService) { 729 RegisterableService rs = (RegisterableService)provider; 730 rs.onRegistration(registry, category); 731 } 732 733 return !present; 734 } 735 736 /** 737 * If the provider was not previously registered, do nothing. 738 * 739 * @return true if the provider was previously registered. 740 */ 741 public boolean deregisterServiceProvider(Object provider) { 742 Object oprovider = map.get(provider.getClass()); 743 744 if (provider == oprovider) { 745 map.remove(provider.getClass()); 746 poset.remove(provider); 747 if (provider instanceof RegisterableService) { 748 RegisterableService rs = (RegisterableService)provider; 749 rs.onDeregistration(registry, category); 750 } 751 752 return true; 753 } 754 return false; 755 } 756 757 public boolean contains(Object provider) { 758 Object oprovider = map.get(provider.getClass()); 759 return oprovider == provider; 760 } 761 762 public boolean setOrdering(Object firstProvider, 763 Object secondProvider) { 764 return poset.setOrdering(firstProvider, secondProvider); 765 } 766 767 public boolean unsetOrdering(Object firstProvider, 768 Object secondProvider) { 769 return poset.unsetOrdering(firstProvider, secondProvider); 770 } 771 772 public Iterator<Object> getServiceProviders(boolean useOrdering) { 773 if (useOrdering) { 774 return poset.iterator(); 775 } else { 776 return map.values().iterator(); 777 } 778 } 779 780 @SuppressWarnings("unchecked") 781 public <T> T getServiceProviderByClass(Class<T> providerClass) { 782 return (T)map.get(providerClass); 783 } 784 785 public void clear() { 786 Iterator<Object> iter = map.values().iterator(); 787 while (iter.hasNext()) { 788 Object provider = iter.next(); 789 iter.remove(); 790 791 if (provider instanceof RegisterableService) { 792 RegisterableService rs = (RegisterableService)provider; 793 rs.onDeregistration(registry, category); 794 } 795 } 796 poset.clear(); 797 } 798 799 public void finalize() { 800 clear(); 801 } 802 } 803 804 805 /** 806 * A class for wrapping <code>Iterators</code> with a filter function. 807 * This provides an iterator for a subset without duplication. 808 */ 809 class FilterIterator<T> implements Iterator<T> { 810 811 private Iterator<? extends T> iter; 812 private ServiceRegistry.Filter filter; 813 814 private T next = null; 815 816 public FilterIterator(Iterator<? extends T> iter, 817 ServiceRegistry.Filter filter) { 818 this.iter = iter; 819 this.filter = filter; 820 advance(); 821 } 822 823 private void advance() { 824 while (iter.hasNext()) { 825 T elt = iter.next(); 826 if (filter.filter(elt)) { 827 next = elt; 828 return; 829 } 830 } 831 832 next = null; 833 } 834 835 public boolean hasNext() { 836 return next != null; 837 } 838 839 public T next() { 840 if (next == null) { 841 throw new NoSuchElementException(); 842 } 843 T o = next; 844 advance(); 845 return o; 846 } 847 848 public void remove() { 849 throw new UnsupportedOperationException(); 850 } 851 }