Path: blob/master/src/java.desktop/share/classes/javax/imageio/spi/ServiceRegistry.java
41155 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.imageio.spi;2627import java.io.File;28import java.security.AccessControlContext;29import java.security.AccessController;30import java.security.PrivilegedAction;31import java.util.ArrayList;32import java.util.HashMap;33import java.util.Iterator;34import java.util.List;35import java.util.Map;36import java.util.NoSuchElementException;37import java.util.Set;38import java.util.ServiceLoader;3940/**41* A registry for service provider instances for Image I/O service types.42*43* <p> Service providers are stored in one or more <i>categories</i>,44* each of which is defined by a class or interface (described by a45* {@code Class} object) that all of its members must implement.46*47* <p>The set of categories supported by this class is limited48* to the following standard Image I/O service types:49*50* <ul>51* <li>{@link ImageInputStreamSpi}52* <li>{@link ImageOutputStreamSpi}53* <li>{@link ImageReaderSpi}54* <li>{@link ImageTranscoderSpi}55* <li>{@link ImageWriterSpi}56* </ul>57*58* <p>An attempt to load a provider that is not a subtype of one of the59* above types will result in {@code IllegalArgumentException}.60* <p> For the general mechanism to load service providers, see61* {@link java.util.ServiceLoader ServiceLoader}, which is62* the underlying standard mechanism used by this class.63*64* <p> Only a single instance of a given leaf class (that is, the65* actual class returned by {@code getClass()}, as opposed to any66* inherited classes or interfaces) may be registered. That is,67* suppose that the68* {@code com.mycompany.mypkg.GreenImageReaderProvider} class69* is a subclass of {@code javax.imageio.spi.ImageReaderSpi}.70* If a {@code GreenImageReaderProvider} instance is71* registered, it will be stored in the category defined by the72* {@code ImageReaderSpi} class. If a new instance of73* {@code GreenImageReaderProvider} is registered, it will replace74* the previous instance. In practice, service provider objects are75* usually singletons so this behavior is appropriate.76*77* <p> The service provider classes should be lightweight and78* quick to load. Implementations of these interfaces should avoid79* complex dependencies on other classes and on native code. The usual80* pattern for more complex services is to register a lightweight81* proxy for the heavyweight service.82*83* <p> An application may customize the contents of a registry as it84* sees fit, so long as it has the appropriate runtime permission.85*86* <p> For information on how to create and deploy service providers,87* refer to the documentation on {@link java.util.ServiceLoader ServiceLoader}88*89* @see RegisterableService90* @see java.util.ServiceLoader91*/92public class ServiceRegistry {9394// Class -> Registry95private Map<Class<?>, SubRegistry> categoryMap = new HashMap<>();9697/**98* Constructs a {@code ServiceRegistry} instance with a99* set of categories taken from the {@code categories}100* argument. The categories must all be members of the set101* of service types listed in the class specification.102*103* @param categories an {@code Iterator} containing104* {@code Class} objects to be used to define categories.105*106* @exception IllegalArgumentException if107* {@code categories} is {@code null}, or if108* one of the categories is not an allowed service type.109*/110public ServiceRegistry(Iterator<Class<?>> categories) {111if (categories == null) {112throw new IllegalArgumentException("categories == null!");113}114while (categories.hasNext()) {115Class<?> category = categories.next();116checkClassAllowed(category);117SubRegistry reg = new SubRegistry(this, category);118categoryMap.put(category, reg);119}120}121122/**123* Searches for implementations of a particular service class124* using the given class loader.125*126* <p>The service class must be one of the service types listed127* in the class specification. If it is not, {@code IllegalArgumentException}128* will be thrown.129*130* <p> This method transforms the name of the given service class131* into a provider-configuration filename as described in the132* class comment and then uses the {@code getResources}133* method of the given class loader to find all available files134* with that name. These files are then read and parsed to135* produce a list of provider-class names. The iterator that is136* returned uses the given class loader to look up and then137* instantiate each element of the list.138*139* <p> Because it is possible for extensions to be installed into140* a running Java virtual machine, this method may return141* different results each time it is invoked.142*143* @param providerClass a {@code Class} object indicating the144* class or interface of the service providers being detected.145*146* @param loader the class loader to be used to load147* provider-configuration files and instantiate provider classes,148* or {@code null} if the system class loader (or, failing that149* the bootstrap class loader) is to be used.150*151* @param <T> the type of the providerClass.152*153* @return An {@code Iterator} that yields provider objects154* for the given service, in some arbitrary order. The iterator155* will throw an {@code Error} if a provider-configuration156* file violates the specified format or if a provider class157* cannot be found and instantiated.158*159* @exception IllegalArgumentException if160* {@code providerClass} is {@code null}, or if it is161* not one of the allowed service types.162*/163public static <T> Iterator<T> lookupProviders(Class<T> providerClass,164ClassLoader loader)165{166if (providerClass == null) {167throw new IllegalArgumentException("providerClass == null!");168}169checkClassAllowed(providerClass);170return ServiceLoader.load(providerClass, loader).iterator();171}172173/**174* Locates and incrementally instantiates the available providers175* of a given service using the context class loader. This176* convenience method is equivalent to:177*178* <pre>179* ClassLoader cl = Thread.currentThread().getContextClassLoader();180* return Service.providers(service, cl);181* </pre>182*183* <p>The service class must be one of the service types listed184* in the class specification. If it is not, {@code IllegalArgumentException}185* will be thrown.186*187* @param providerClass a {@code Class} object indicating the188* class or interface of the service providers being detected.189*190* @param <T> the type of the providerClass.191*192* @return An {@code Iterator} that yields provider objects193* for the given service, in some arbitrary order. The iterator194* will throw an {@code Error} if a provider-configuration195* file violates the specified format or if a provider class196* cannot be found and instantiated.197*198* @exception IllegalArgumentException if199* {@code providerClass} is {@code null}, or if it is200* not one of the allowed service types.201*/202public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {203if (providerClass == null) {204throw new IllegalArgumentException("providerClass == null!");205}206checkClassAllowed(providerClass);207return ServiceLoader.load(providerClass).iterator();208}209210/**211* Returns an {@code Iterator} of {@code Class} objects212* indicating the current set of categories. The iterator will be213* empty if no categories exist.214*215* @return an {@code Iterator} containing216* {@code Class} objects.217*/218public Iterator<Class<?>> getCategories() {219Set<Class<?>> keySet = categoryMap.keySet();220return keySet.iterator();221}222223/**224* Returns an Iterator containing the subregistries to which the225* provider belongs.226*/227private Iterator<SubRegistry> getSubRegistries(Object provider) {228List<SubRegistry> l = new ArrayList<>();229for (Class<?> c : categoryMap.keySet()) {230if (c.isAssignableFrom(provider.getClass())) {231l.add(categoryMap.get(c));232}233}234return l.iterator();235}236237/**238* Adds a service provider object to the registry. The provider239* is associated with the given category.240*241* <p> If {@code provider} implements the242* {@code RegisterableService} interface, its243* {@code onRegistration} method will be called. Its244* {@code onDeregistration} method will be called each time245* it is deregistered from a category, for example if a246* category is removed or the registry is garbage collected.247*248* @param provider the service provide object to be registered.249* @param category the category under which to register the250* provider.251* @param <T> the type of the provider.252*253* @return true if no provider of the same class was previously254* registered in the same category category.255*256* @exception IllegalArgumentException if {@code provider} is257* {@code null}.258* @exception IllegalArgumentException if there is no category259* corresponding to {@code category}.260* @exception ClassCastException if provider does not implement261* the {@code Class} defined by {@code category}.262*/263public <T> boolean registerServiceProvider(T provider,264Class<T> category) {265if (provider == null) {266throw new IllegalArgumentException("provider == null!");267}268SubRegistry reg = categoryMap.get(category);269if (reg == null) {270throw new IllegalArgumentException("category unknown!");271}272if (!category.isAssignableFrom(provider.getClass())) {273throw new ClassCastException();274}275276return reg.registerServiceProvider(provider);277}278279/**280* Adds a service provider object to the registry. The provider281* is associated within each category present in the registry282* whose {@code Class} it implements.283*284* <p> If {@code provider} implements the285* {@code RegisterableService} interface, its286* {@code onRegistration} method will be called once for each287* category it is registered under. Its288* {@code onDeregistration} method will be called each time289* it is deregistered from a category or when the registry is290* finalized.291*292* @param provider the service provider object to be registered.293*294* @exception IllegalArgumentException if295* {@code provider} is {@code null}.296*/297public void registerServiceProvider(Object provider) {298if (provider == null) {299throw new IllegalArgumentException("provider == null!");300}301Iterator<SubRegistry> regs = getSubRegistries(provider);302while (regs.hasNext()) {303SubRegistry reg = regs.next();304reg.registerServiceProvider(provider);305}306}307308/**309* Adds a set of service provider objects, taken from an310* {@code Iterator} to the registry. Each provider is311* associated within each category present in the registry whose312* {@code Class} it implements.313*314* <p> For each entry of {@code providers} that implements315* the {@code RegisterableService} interface, its316* {@code onRegistration} method will be called once for each317* category it is registered under. Its318* {@code onDeregistration} method will be called each time319* it is deregistered from a category or when the registry is320* finalized.321*322* @param providers an Iterator containing service provider323* objects to be registered.324*325* @exception IllegalArgumentException if {@code providers}326* is {@code null} or contains a {@code null} entry.327*/328public void registerServiceProviders(Iterator<?> providers) {329if (providers == null) {330throw new IllegalArgumentException("provider == null!");331}332while (providers.hasNext()) {333registerServiceProvider(providers.next());334}335}336337/**338* Removes a service provider object from the given category. If339* the provider was not previously registered, nothing happens and340* {@code false} is returned. Otherwise, {@code true}341* is returned. If an object of the same class as342* {@code provider} but not equal (using {@code ==}) to343* {@code provider} is registered, it will not be344* deregistered.345*346* <p> If {@code provider} implements the347* {@code RegisterableService} interface, its348* {@code onDeregistration} method will be called.349*350* @param provider the service provider object to be deregistered.351* @param category the category from which to deregister the352* provider.353* @param <T> the type of the provider.354*355* @return {@code true} if the provider was previously356* registered in the same category category,357* {@code false} otherwise.358*359* @exception IllegalArgumentException if {@code provider} is360* {@code null}.361* @exception IllegalArgumentException if there is no category362* corresponding to {@code category}.363* @exception ClassCastException if provider does not implement364* the class defined by {@code category}.365*/366public <T> boolean deregisterServiceProvider(T provider,367Class<T> category) {368if (provider == null) {369throw new IllegalArgumentException("provider == null!");370}371SubRegistry reg = categoryMap.get(category);372if (reg == null) {373throw new IllegalArgumentException("category unknown!");374}375if (!category.isAssignableFrom(provider.getClass())) {376throw new ClassCastException();377}378return reg.deregisterServiceProvider(provider);379}380381/**382* Removes a service provider object from all categories that383* contain it.384*385* @param provider the service provider object to be deregistered.386*387* @exception IllegalArgumentException if {@code provider} is388* {@code null}.389*/390public void deregisterServiceProvider(Object provider) {391if (provider == null) {392throw new IllegalArgumentException("provider == null!");393}394Iterator<SubRegistry> regs = getSubRegistries(provider);395while (regs.hasNext()) {396SubRegistry reg = regs.next();397reg.deregisterServiceProvider(provider);398}399}400401/**402* Returns {@code true} if {@code provider} is currently403* registered.404*405* @param provider the service provider object to be queried.406*407* @return {@code true} if the given provider has been408* registered.409*410* @exception IllegalArgumentException if {@code provider} is411* {@code null}.412*/413public boolean contains(Object provider) {414if (provider == null) {415throw new IllegalArgumentException("provider == null!");416}417Iterator<SubRegistry> regs = getSubRegistries(provider);418while (regs.hasNext()) {419SubRegistry reg = regs.next();420if (reg.contains(provider)) {421return true;422}423}424425return false;426}427428/**429* Returns an {@code Iterator} containing all registered430* service providers in the given category. If431* {@code useOrdering} is {@code false}, the iterator432* will return all of the server provider objects in an arbitrary433* order. Otherwise, the ordering will respect any pairwise434* orderings that have been set. If the graph of pairwise435* orderings contains cycles, any providers that belong to a cycle436* will not be returned.437*438* @param category the category to be retrieved from.439* @param useOrdering {@code true} if pairwise orderings440* should be taken account in ordering the returned objects.441* @param <T> the type of the category.442*443* @return an {@code Iterator} containing service provider444* objects from the given category, possibly in order.445*446* @exception IllegalArgumentException if there is no category447* corresponding to {@code category}.448*/449public <T> Iterator<T> getServiceProviders(Class<T> category,450boolean useOrdering) {451SubRegistry reg = categoryMap.get(category);452if (reg == null) {453throw new IllegalArgumentException("category unknown!");454}455@SuppressWarnings("unchecked")456Iterator<T> it = (Iterator<T>)reg.getServiceProviders(useOrdering);457return it;458}459460/**461* A simple filter interface used by462* {@code ServiceRegistry.getServiceProviders} to select463* providers matching an arbitrary criterion. Classes that464* implement this interface should be defined in order to make use465* of the {@code getServiceProviders} method of466* {@code ServiceRegistry} that takes a {@code Filter}.467*468* @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)469*/470public interface Filter {471472/**473* Returns {@code true} if the given474* {@code provider} object matches the criterion defined475* by this {@code Filter}.476*477* @param provider a service provider {@code Object}.478*479* @return true if the provider matches the criterion.480*/481boolean filter(Object provider);482}483484/**485* Returns an {@code Iterator} containing service provider486* objects within a given category that satisfy a criterion487* imposed by the supplied {@code ServiceRegistry.Filter}488* object's {@code filter} method.489*490* <p> The {@code useOrdering} argument controls the491* ordering of the results using the same rules as492* {@code getServiceProviders(Class, boolean)}.493*494* @param category the category to be retrieved from.495* @param filter an instance of {@code ServiceRegistry.Filter}496* whose {@code filter} method will be invoked.497* @param useOrdering {@code true} if pairwise orderings498* should be taken account in ordering the returned objects.499* @param <T> the type of the category.500*501* @return an {@code Iterator} containing service provider502* objects from the given category, possibly in order.503*504* @exception IllegalArgumentException if there is no category505* corresponding to {@code category}.506*/507public <T> Iterator<T> getServiceProviders(Class<T> category,508Filter filter,509boolean useOrdering) {510SubRegistry reg = categoryMap.get(category);511if (reg == null) {512throw new IllegalArgumentException("category unknown!");513}514Iterator<T> iter = getServiceProviders(category, useOrdering);515return new FilterIterator<>(iter, filter);516}517518/**519* Returns the currently registered service provider object that520* is of the given class type. At most one object of a given521* class is allowed to be registered at any given time. If no522* registered object has the desired class type, {@code null}523* is returned.524*525* @param providerClass the {@code Class} of the desired526* service provider object.527* @param <T> the type of the provider.528*529* @return a currently registered service provider object with the530* desired {@code Class} type, or {@code null} is none is531* present.532*533* @exception IllegalArgumentException if {@code providerClass} is534* {@code null}.535*/536public <T> T getServiceProviderByClass(Class<T> providerClass) {537if (providerClass == null) {538throw new IllegalArgumentException("providerClass == null!");539}540for (Class<?> c : categoryMap.keySet()) {541if (c.isAssignableFrom(providerClass)) {542SubRegistry reg = categoryMap.get(c);543T provider = reg.getServiceProviderByClass(providerClass);544if (provider != null) {545return provider;546}547}548}549return null;550}551552/**553* Sets a pairwise ordering between two service provider objects554* within a given category. If one or both objects are not555* currently registered within the given category, or if the556* desired ordering is already set, nothing happens and557* {@code false} is returned. If the providers previously558* were ordered in the reverse direction, that ordering is559* removed.560*561* <p> The ordering will be used by the562* {@code getServiceProviders} methods when their563* {@code useOrdering} argument is {@code true}.564*565* @param category a {@code Class} object indicating the566* category under which the preference is to be established.567* @param firstProvider the preferred provider.568* @param secondProvider the provider to which569* {@code firstProvider} is preferred.570* @param <T> the type of the category.571*572* @return {@code true} if a previously unset ordering573* was established.574*575* @exception IllegalArgumentException if either provider is576* {@code null} or they are the same object.577* @exception IllegalArgumentException if there is no category578* corresponding to {@code category}.579*/580public <T> boolean setOrdering(Class<T> category,581T firstProvider,582T secondProvider) {583if (firstProvider == null || secondProvider == null) {584throw new IllegalArgumentException("provider is null!");585}586if (firstProvider == secondProvider) {587throw new IllegalArgumentException("providers are the same!");588}589SubRegistry reg = categoryMap.get(category);590if (reg == null) {591throw new IllegalArgumentException("category unknown!");592}593if (reg.contains(firstProvider) &&594reg.contains(secondProvider)) {595return reg.setOrdering(firstProvider, secondProvider);596}597return false;598}599600/**601* Sets a pairwise ordering between two service provider objects602* within a given category. If one or both objects are not603* currently registered within the given category, or if no604* ordering is currently set between them, nothing happens605* and {@code false} is returned.606*607* <p> The ordering will be used by the608* {@code getServiceProviders} methods when their609* {@code useOrdering} argument is {@code true}.610*611* @param category a {@code Class} object indicating the612* category under which the preference is to be disestablished.613* @param firstProvider the formerly preferred provider.614* @param secondProvider the provider to which615* {@code firstProvider} was formerly preferred.616* @param <T> the type of the category.617*618* @return {@code true} if a previously set ordering was619* disestablished.620*621* @exception IllegalArgumentException if either provider is622* {@code null} or they are the same object.623* @exception IllegalArgumentException if there is no category624* corresponding to {@code category}.625*/626public <T> boolean unsetOrdering(Class<T> category,627T firstProvider,628T secondProvider) {629if (firstProvider == null || secondProvider == null) {630throw new IllegalArgumentException("provider is null!");631}632if (firstProvider == secondProvider) {633throw new IllegalArgumentException("providers are the same!");634}635SubRegistry reg = categoryMap.get(category);636if (reg == null) {637throw new IllegalArgumentException("category unknown!");638}639if (reg.contains(firstProvider) &&640reg.contains(secondProvider)) {641return reg.unsetOrdering(firstProvider, secondProvider);642}643return false;644}645646/**647* Deregisters all service provider object currently registered648* under the given category.649*650* @param category the category to be emptied.651*652* @exception IllegalArgumentException if there is no category653* corresponding to {@code category}.654*/655public void deregisterAll(Class<?> category) {656SubRegistry reg = categoryMap.get(category);657if (reg == null) {658throw new IllegalArgumentException("category unknown!");659}660reg.clear();661}662663/**664* Deregisters all currently registered service providers from all665* categories.666*/667public void deregisterAll() {668for (SubRegistry reg : categoryMap.values()) {669reg.clear();670}671}672673/**674* Finalizes this object prior to garbage collection. The675* {@code deregisterAll} method is called to deregister all676* currently registered service providers. This method should not677* be called from application code.678*679* @exception Throwable if an error occurs during superclass680* finalization.681*682* @deprecated The {@code finalize} method has been deprecated.683* Subclasses that override {@code finalize} in order to perform cleanup684* should be modified to use alternative cleanup mechanisms and685* to remove the overriding {@code finalize} method.686* When overriding the {@code finalize} method, its implementation must explicitly687* ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.688* See the specification for {@link Object#finalize()} for further689* information about migration options.690*/691@Deprecated(since="9")692public void finalize() throws Throwable {693deregisterAll();694super.finalize();695}696697/**698* Checks whether the provided class is one of the allowed699* ImageIO service provider classes. If it is, returns normally.700* If it is not, throws IllegalArgumentException.701*702* @param clazz703* @throws IllegalArgumentException if clazz is null or is not one of the allowed set704*/705private static void checkClassAllowed(Class<?> clazz) {706if (clazz == null) {707throw new IllegalArgumentException("class must not be null");708}709710if ( clazz != ImageInputStreamSpi.class711&& clazz != ImageOutputStreamSpi.class712&& clazz != ImageReaderSpi.class713&& clazz != ImageTranscoderSpi.class714&& clazz != ImageWriterSpi.class) {715throw new IllegalArgumentException(clazz.getName() + " is not an ImageIO SPI class");716}717}718}719720721/**722* A portion of a registry dealing with a single superclass or723* interface.724*/725class SubRegistry {726727ServiceRegistry registry;728729Class<?> category;730731// Provider Objects organized by partial ordering732final PartiallyOrderedSet<Object> poset = new PartiallyOrderedSet<>();733734// Class -> Provider Object of that class735// No way to express heterogeneous map, we want736// Map<Class<T>, T>, where T is ?737final Map<Class<?>, Object> map = new HashMap<>();738@SuppressWarnings("removal")739final Map<Class<?>, AccessControlContext> accMap = new HashMap<>();740741public SubRegistry(ServiceRegistry registry, Class<?> category) {742this.registry = registry;743this.category = category;744}745746@SuppressWarnings("removal")747public synchronized boolean registerServiceProvider(Object provider) {748Object oprovider = map.get(provider.getClass());749boolean present = oprovider != null;750751if (present) {752deregisterServiceProvider(oprovider);753}754map.put(provider.getClass(), provider);755accMap.put(provider.getClass(), AccessController.getContext());756poset.add(provider);757if (provider instanceof RegisterableService) {758RegisterableService rs = (RegisterableService)provider;759try {760rs.onRegistration(registry, category);761} catch (Throwable t) {762System.err.println("Caught and handled this exception :");763t.printStackTrace();764}765}766767return !present;768}769770/**771* If the provider was not previously registered, do nothing.772*773* @return true if the provider was previously registered.774*/775public synchronized boolean deregisterServiceProvider(Object provider) {776Object oprovider = map.get(provider.getClass());777778if (provider == oprovider) {779map.remove(provider.getClass());780accMap.remove(provider.getClass());781poset.remove(provider);782if (provider instanceof RegisterableService) {783RegisterableService rs = (RegisterableService)provider;784rs.onDeregistration(registry, category);785}786787return true;788}789return false;790}791792public synchronized boolean contains(Object provider) {793Object oprovider = map.get(provider.getClass());794return oprovider == provider;795}796797public synchronized boolean setOrdering(Object firstProvider,798Object secondProvider) {799return poset.setOrdering(firstProvider, secondProvider);800}801802public synchronized boolean unsetOrdering(Object firstProvider,803Object secondProvider) {804return poset.unsetOrdering(firstProvider, secondProvider);805}806807public synchronized Iterator<Object> getServiceProviders808(boolean useOrdering) {809if (useOrdering) {810return poset.iterator();811} else {812return map.values().iterator();813}814}815816@SuppressWarnings("unchecked")817public synchronized <T> T getServiceProviderByClass818(Class<T> providerClass) {819return (T)map.get(providerClass);820}821822@SuppressWarnings("removal")823public synchronized void clear() {824Iterator<Object> iter = map.values().iterator();825while (iter.hasNext()) {826Object provider = iter.next();827iter.remove();828829if (provider instanceof RegisterableService) {830RegisterableService rs = (RegisterableService)provider;831AccessControlContext acc = accMap.get(provider.getClass());832if (acc != null || System.getSecurityManager() == null) {833AccessController.doPrivileged((PrivilegedAction<Void>) () -> {834rs.onDeregistration(registry, category);835return null;836}, acc);837}838}839}840poset.clear();841accMap.clear();842}843844@SuppressWarnings("deprecation")845public synchronized void finalize() {846clear();847}848}849850851/**852* A class for wrapping {@code Iterators} with a filter function.853* This provides an iterator for a subset without duplication.854*/855class FilterIterator<T> implements Iterator<T> {856857private Iterator<? extends T> iter;858private ServiceRegistry.Filter filter;859860private T next = null;861862public FilterIterator(Iterator<? extends T> iter,863ServiceRegistry.Filter filter) {864this.iter = iter;865this.filter = filter;866advance();867}868869private void advance() {870while (iter.hasNext()) {871T elt = iter.next();872if (filter.filter(elt)) {873next = elt;874return;875}876}877878next = null;879}880881public boolean hasNext() {882return next != null;883}884885public T next() {886if (next == null) {887throw new NoSuchElementException();888}889T o = next;890advance();891return o;892}893894public void remove() {895throw new UnsupportedOperationException();896}897}898899900