| /* ServiceRegistry.java -- A simple registry for service providers. |
| Copyright (C) 2004, 2005 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.imageio.spi; |
| |
| import gnu.classpath.ServiceFactory; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.IdentityHashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| |
| /** |
| * A registry for service providers. |
| * |
| * @since 1.4 |
| * |
| * @author Michael Koch (konqueror@gmx.de) |
| * @author Sascha Brawer (brawer@dandelis.ch) |
| */ |
| public class ServiceRegistry |
| { |
| // Package-private to avoid a trampoline. |
| /** |
| * The service categories of this registry. |
| * |
| * <p>Note that we expect that only very few categories will |
| * typically be used with a registry. The most common case will be |
| * one, it seems unlikely that any registry would contain more than |
| * five or six categories. Therefore, we intentionally avoid the |
| * overhead of a HashMap. |
| * |
| * @see #providers |
| */ |
| final Class[] categories; |
| |
| |
| /** |
| * The registered providers for each service category, indexed by |
| * the same index as the {@link #categories} array. If no provider |
| * is registered for a category, the array entry will be |
| * <code>null</code>. |
| * |
| * <p>Note that we expect that only very few providers will |
| * typically be registered for a category. The most common case will |
| * be one or two. Therefore, we intentionally avoid the overhead of |
| * a HashMap. |
| */ |
| private final LinkedList[] providers; |
| |
| |
| /** |
| * The ordring constaints for each service category, indexed by the |
| * same index as the {@link #categories} array. The constraints for |
| * a service category are stored as a <code>Map<Object, |
| * Set<Object>></code>, where the Map’s values are |
| * those providers that need to come after the key. If no |
| * constraints are imposed on the providers of a category, the array |
| * entry will be <code>null</code>. If no constraints have been set |
| * whatsoever, <code>constraints</code> will be <code>null</code>. |
| * |
| * <p>Note that we expect that only very few constraints will |
| * typically be imposed on a category. The most common case will |
| * be zero. |
| */ |
| private IdentityHashMap[] constraints; |
| |
| |
| /** |
| * Constructs a <code>ServiceRegistry</code> for the specified |
| * service categories. |
| * |
| * @param categories the categories to support |
| * |
| * @throws IllegalArgumentException if <code>categories</code> is |
| * <code>null</code>, or if its {@link Iterator#next()} method |
| * returns <code>null</code>. |
| * |
| * @throws ClassCastException if <code>categories</code> does not |
| * iterate over instances of {@link java.lang.Class}. |
| */ |
| public ServiceRegistry(Iterator categories) |
| { |
| ArrayList cats = new ArrayList(/* expected size */ 10); |
| |
| if (categories == null) |
| throw new IllegalArgumentException(); |
| |
| while (categories.hasNext()) |
| { |
| Class cat = (Class) categories.next(); |
| if (cat == null) |
| throw new IllegalArgumentException(); |
| cats.add(cat); |
| } |
| |
| int numCats = cats.size(); |
| this.categories = (Class[]) cats.toArray(new Class[numCats]); |
| this.providers = new LinkedList[numCats]; |
| } |
| |
| |
| /** |
| * Finds service providers that are implementing the specified |
| * Service Provider Interface. |
| * |
| * <p><b>On-demand loading:</b> Loading and initializing service |
| * providers is delayed as much as possible. The rationale is that |
| * typical clients will iterate through the set of installed service |
| * providers until one is found that matches some criteria (like |
| * supported formats, or quality of service). In such scenarios, it |
| * might make sense to install only the frequently needed service |
| * providers on the local machine. More exotic providers can be put |
| * onto a server; the server will only be contacted when no suitable |
| * service could be found locally.</p> |
| * |
| * <p><b>Security considerations:</b> Any loaded service providers |
| * are loaded through the specified ClassLoader, or the system |
| * ClassLoader if <code>classLoader</code> is |
| * <code>null</code>. When <code>lookupProviders</code> is called, |
| * the current {@link java.security.AccessControlContext} gets |
| * recorded. This captured security context will determine the |
| * permissions when services get loaded via the <code>next()</code> |
| * method of the returned <code>Iterator</code>.</p> |
| * |
| * @param spi the service provider interface which must be |
| * implemented by any loaded service providers. |
| * |
| * @param loader the class loader that will be used to load the |
| * service providers, or <code>null</code> for the system class |
| * loader. For using the context class loader, see {@link |
| * #lookupProviders(Class)}. |
| * |
| * @return an iterator over instances of <code>spi</code>. |
| * |
| * @throws IllegalArgumentException if <code>spi</code> is |
| * <code>null</code>. |
| */ |
| public static Iterator lookupProviders(Class spi, |
| ClassLoader loader) |
| { |
| return ServiceFactory.lookupProviders(spi, loader); |
| } |
| |
| |
| /** |
| * Finds service providers that are implementing the specified |
| * Service Provider Interface, using the context class loader |
| * for loading providers. |
| * |
| * @param spi the service provider interface which must be |
| * implemented by any loaded service providers. |
| * |
| * @return an iterator over instances of <code>spi</code>. |
| * |
| * @throws IllegalArgumentException if <code>spi</code> is |
| * <code>null</code>. |
| * |
| * @see #lookupProviders(Class, ClassLoader) |
| */ |
| public static Iterator lookupProviders(Class spi) |
| { |
| return ServiceFactory.lookupProviders(spi); |
| } |
| |
| |
| /** |
| * Returns an iterator over all service categories. |
| * |
| * @return an unmodifiable {@link |
| * java.util.Iterator}<{@link java.lang.Class}>. |
| */ |
| public Iterator getCategories() |
| { |
| return new Iterator() |
| { |
| int index = -1; |
| |
| public boolean hasNext() |
| { |
| return index < categories.length - 1; |
| } |
| |
| public Object next() |
| { |
| if (!hasNext()) |
| throw new NoSuchElementException(); |
| |
| return categories[++index]; |
| } |
| |
| public void remove() |
| { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| |
| |
| /** |
| * Registers a provider for a service category which is specified by |
| * the class-internal category ID. |
| * |
| * @param provider the service provider to be registered. |
| * |
| * @param cat the service category, which is identified by an index |
| * into the {@link #categories} array. |
| * |
| * @return <code>true</code> if <code>provider</code> is the first |
| * provider that gets registered for the specified service category; |
| * <code>false</code> if other providers have already been |
| * registered for the same servide category. |
| * |
| * @throws IllegalArgumentException if <code>provider</code> is |
| * <code>null</code>. |
| * |
| * @throws ClassCastException if <code>provider</code> does not |
| * implement the specified service provider interface. |
| */ |
| private synchronized boolean registerServiceProvider(Object provider, |
| int cat) |
| { |
| LinkedList provs; |
| boolean result; |
| Class category; |
| |
| if (provider == null) |
| throw new IllegalArgumentException(); |
| |
| category = categories[cat]; |
| if (!category.isInstance(provider)) |
| throw new ClassCastException(category.getName()); |
| |
| provs = providers[cat]; |
| if (provs == null) |
| { |
| result = true; |
| provs = providers[cat] = new LinkedList(); |
| } |
| else |
| result = false; |
| |
| provs.add(provider); |
| if (provider instanceof RegisterableService) |
| ((RegisterableService) provider).onRegistration(this, category); |
| |
| return result; |
| } |
| |
| |
| /** |
| * Registers a provider for the specified service category. |
| * |
| * <p>If <code>provider</code> implements the {@link |
| * RegisterableService} interface, its {@link |
| * RegisterableService#onRegistration onRegistration} method is |
| * invoked in order to inform the provider about the addition to |
| * this registry. |
| * |
| * @param provider the service provider to be registered. |
| * |
| * @param category the service category under which |
| * <code>provider</code> shall be registered. |
| * |
| * @return <code>true</code> if <code>provider</code> is the first |
| * provider that gets registered for the specified service category; |
| * <code>false</code> if other providers have already been |
| * registered for the same servide category. |
| * |
| * @throws IllegalArgumentException if <code>provider</code> is |
| * <code>null</code>, or if <code>category</code> is not among the |
| * categories passed to the {@linkplain #ServiceRegistry(Iterator) |
| * constructor} of this ServiceRegistry. |
| * |
| * @throws ClassCastException if <code>provider</code> does not |
| * implement <code>category</code>. |
| */ |
| public synchronized boolean registerServiceProvider(Object provider, |
| Class category) |
| { |
| for (int i = 0; i < categories.length; i++) |
| if (categories[i] == category) |
| return registerServiceProvider(provider, i); |
| throw new IllegalArgumentException(); |
| } |
| |
| |
| /** |
| * Registers a provider under all service categories it |
| * implements. |
| * |
| * <p>If <code>provider</code> implements the {@link |
| * RegisterableService} interface, its {@link |
| * RegisterableService#onRegistration onRegistration} method is |
| * invoked in order to inform the provider about the addition to |
| * this registry. If <code>provider</code> implements several |
| * service categories, <code>onRegistration</code> gets called |
| * multiple times. |
| * |
| * @param provider the service provider to be registered. |
| * |
| * @throws IllegalArgumentException if <code>provider</code> is |
| * <code>null</code>, or if <code>provider</code> does not implement |
| * any of the service categories passed to the {@linkplain |
| * #ServiceRegistry(Iterator) constructor} of this ServiceRegistry. |
| */ |
| public synchronized void registerServiceProvider(Object provider) |
| { |
| boolean ok = false; |
| |
| if (provider == null) |
| throw new IllegalArgumentException(); |
| |
| for (int i = 0; i < categories.length; i++) |
| if (categories[i].isInstance(provider)) |
| { |
| ok = true; |
| registerServiceProvider(provider, i); |
| } |
| |
| if (!ok) |
| throw new IllegalArgumentException(); |
| } |
| |
| |
| /** |
| * Registers a number of providers under all service categories they |
| * implement. |
| * |
| * <p>If a provider implements the {@link RegisterableService} |
| * interface, its {@link RegisterableService#onRegistration |
| * onRegistration} method is invoked in order to inform the provider |
| * about the addition to this registry. If <code>provider</code> |
| * implements several service categories, |
| * <code>onRegistration</code> gets called multiple times. |
| * |
| * @throws IllegalArgumentException if <code>providers</code> is |
| * <code>null</code>, if any iterated provider is <code>null</code>, |
| * or if some iterated provider does not implement any of the |
| * service categories passed to the {@linkplain |
| * #ServiceRegistry(Iterator) constructor} of this |
| * <code>ServiceRegistry</code>. |
| */ |
| public synchronized void registerServiceProviders(Iterator providers) |
| { |
| if (providers == null) |
| throw new IllegalArgumentException(); |
| |
| while (providers.hasNext()) |
| registerServiceProvider(providers.next()); |
| } |
| |
| |
| /** |
| * De-registers a provider for a service category which is specified |
| * by the class-internal category ID. |
| * |
| * @param provider the service provider to be registered. |
| * |
| * @param cat the service category, which is identified by an index |
| * into the {@link #categories} array. |
| * |
| * @return <code>true</code> if <code>provider</code> was previously |
| * registered for the specified service category; <code>false</code> |
| * if if the provider had not been registered. |
| * |
| * @throws IllegalArgumentException if <code>provider</code> is |
| * <code>null</code>. |
| * |
| * @throws ClassCastException if <code>provider</code> does not |
| * implement the specified service provider interface. |
| */ |
| private synchronized boolean deregisterServiceProvider(Object provider, |
| int cat) |
| { |
| LinkedList provs; |
| boolean result; |
| Class category; |
| |
| if (provider == null) |
| throw new IllegalArgumentException(); |
| |
| category = categories[cat]; |
| if (!category.isInstance(provider)) |
| throw new ClassCastException(category.getName()); |
| |
| provs = providers[cat]; |
| if (provs == null) |
| return false; |
| |
| result = provs.remove(provider); |
| if (provs.isEmpty()) |
| providers[cat] = null; |
| |
| if (result && (provider instanceof RegisterableService)) |
| ((RegisterableService) provider).onDeregistration(this, category); |
| |
| return result; |
| } |
| |
| |
| /** |
| * De-registers a provider for the specified service category. |
| * |
| * <p>If <code>provider</code> implements the {@link |
| * RegisterableService} interface, its {@link |
| * RegisterableService#onDeregistration onDeregistration} method is |
| * invoked in order to inform the provider about the removal from |
| * this registry. |
| * |
| * @param provider the service provider to be de-registered. |
| * |
| * @param category the service category from which |
| * <code>provider</code> shall be de-registered. |
| * |
| * @return <code>true</code> if <code>provider</code> was previously |
| * registered for the specified service category; <code>false</code> |
| * if if the provider had not been registered. |
| * |
| * @throws IllegalArgumentException if <code>provider</code> is |
| * <code>null</code>, or if <code>category</code> is not among the |
| * categories passed to the {@linkplain #ServiceRegistry(Iterator) |
| * constructor} of this ServiceRegistry. |
| * |
| * @throws ClassCastException if <code>provider</code> does not |
| * implement <code>category</code>. |
| */ |
| public synchronized boolean deregisterServiceProvider(Object provider, |
| Class category) |
| { |
| for (int i = 0; i < categories.length; i++) |
| if (categories[i] == category) |
| return deregisterServiceProvider(provider, i); |
| throw new IllegalArgumentException(); |
| } |
| |
| |
| /** |
| * De-registers a provider from all service categories it |
| * implements. |
| * |
| * <p>If <code>provider</code> implements the {@link |
| * RegisterableService} interface, its {@link |
| * RegisterableService#onDeregistration onDeregistration} method is |
| * invoked in order to inform the provider about the removal from |
| * this registry. If <code>provider</code> implements several |
| * service categories, <code>onDeregistration</code> gets called |
| * multiple times.</p> |
| * |
| * @param provider the service provider to be de-registered. |
| * |
| * @throws IllegalArgumentException if <code>provider</code> is |
| * <code>null</code>, or if <code>provider</code> does not implement |
| * any of the service categories passed to the {@linkplain |
| * #ServiceRegistry(Iterator) constructor} of this |
| * <code>ServiceRegistry</code>. |
| */ |
| public synchronized void deregisterServiceProvider(Object provider) |
| { |
| boolean ok = false; |
| |
| if (provider == null) |
| throw new IllegalArgumentException(); |
| |
| for (int i = 0; i < categories.length; i++) |
| if (categories[i].isInstance(provider)) |
| { |
| ok = true; |
| deregisterServiceProvider(provider, i); |
| } |
| |
| if (!ok) |
| throw new IllegalArgumentException(); |
| } |
| |
| |
| /** |
| * De-registers all providers which have been registered for the |
| * specified service category. |
| * |
| * <p>If a provider implements the {@link RegisterableService} |
| * interface, its {@link RegisterableService#onDeregistration |
| * onDeregistration} method is invoked in order to inform the |
| * provider about the removal from this registry. If the provider |
| * implements several service categories, |
| * <code>onDeregistration</code> gets called multiple times. |
| * |
| * @param category the category whose registered providers will be |
| * de-registered. |
| * |
| * @throws IllegalArgumentException if <code>category</code> is not |
| * among the categories passed to the {@linkplain |
| * #ServiceRegistry(Iterator) constructor} of this |
| * <code>ServiceRegistry</code>. |
| */ |
| public synchronized void deregisterAll(Class category) |
| { |
| boolean ok = false; |
| |
| for (int i = 0; i < categories.length; i++) |
| { |
| if (categories[i] != category) |
| continue; |
| |
| ok = true; |
| while (providers[i] != null) |
| deregisterServiceProvider(providers[i].get(0), i); |
| } |
| |
| if (!ok) |
| throw new IllegalArgumentException(); |
| } |
| |
| |
| /** |
| * De-registers all service providers. |
| * |
| * <p>If a provider implements the {@link RegisterableService} |
| * interface, its {@link RegisterableService#onDeregistration |
| * onDeregistration} method is invoked in order to inform the |
| * provider about the removal from this registry. If the provider |
| * implements several service categories, |
| * <code>onDeregistration</code> gets called multiple times. |
| */ |
| public synchronized void deregisterAll() |
| { |
| for (int i = 0; i < categories.length; i++) |
| while (providers[i] != null) |
| deregisterServiceProvider(providers[i].get(0), i); |
| } |
| |
| |
| /** |
| * Called by the Virtual Machine when it detects that this |
| * <code>ServiceRegistry</code> has become garbage. De-registers all |
| * service providers, which will cause those that implement {@link |
| * RegisterableService} to receive a {@link |
| * RegisterableService#onDeregistration onDeregistration} |
| * notification. |
| */ |
| public void finalize() |
| throws Throwable |
| { |
| super.finalize(); |
| deregisterAll(); |
| } |
| |
| |
| /** |
| * Determines whether a provider has been registered with this |
| * registry. |
| * |
| * @return <code>true</code> if <code>provider</code> has been |
| * registered under any service category; <code>false</code> if |
| * it is not registered. |
| * |
| * @throws IllegalArgumentException if <code>provider</code> is |
| * <code>null</code>. |
| */ |
| public synchronized boolean contains(Object provider) |
| { |
| if (provider == null) |
| throw new IllegalArgumentException(); |
| |
| // Note that contains is rather unlikely to be ever called, |
| // so it would be wasteful to keep a special data structure |
| // (such as a HashSet) for making it a fast operation. |
| for (int i = 0; i < providers.length; i++) |
| { |
| // If provider does not implement categories[i], |
| // it would not have been possible to register it there. |
| // In that case, it would be pointless to look there. |
| if (!categories[i].isInstance(provider)) |
| continue; |
| |
| // But if the list of registered providers contains provider, |
| // we have found it. |
| LinkedList p = providers[i]; |
| if (p != null && p.contains(provider)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| |
| /** |
| * Returns the index in {@link #categories} occupied by the |
| * specified service category. |
| * |
| * @throws IllegalArgumentException if <code>category</code> is not |
| * among the categories passed to the {@linkplain |
| * #ServiceRegistry(Iterator) constructor} of this ServiceRegistry. |
| */ |
| private int getCategoryID(Class category) |
| { |
| for (int i = 0; i < categories.length; i++) |
| if (categories[i] == category) |
| return i; |
| |
| throw new IllegalArgumentException(); |
| } |
| |
| |
| /** |
| * Retrieves all providers that have been registered for the |
| * specified service category. |
| * |
| * @param category the service category whose providers are |
| * to be retrieved. |
| * |
| * @param useOrdering <code>true</code> in order to retrieve the |
| * providers in an order imposed by the {@linkplain #setOrdering |
| * ordering constraints}; <code>false</code> in order to retrieve |
| * the providers in any order. |
| * |
| * @throws IllegalArgumentException if <code>category</code> is not |
| * among the categories passed to the {@linkplain |
| * #ServiceRegistry(Iterator) constructor} of this |
| * <code>ServiceRegistry</code>. |
| * |
| * @see #getServiceProviders(Class, Filter, boolean) |
| */ |
| public Iterator getServiceProviders(Class category, boolean useOrdering) |
| { |
| return getServiceProviders(category, null, useOrdering); |
| } |
| |
| |
| /** |
| * Retrieves all providers that have been registered for the |
| * specified service category and that satisfy the criteria |
| * of a custom filter. |
| * |
| * @param category the service category whose providers are |
| * to be retrieved. |
| * |
| * @param filter a custom filter, or <code>null</code> to |
| * retrieve all registered providers for the specified |
| * category. |
| * |
| * @param useOrdering <code>true</code> in order to retrieve the |
| * providers in an order imposed by the {@linkplain #setOrdering |
| * ordering constraints}; <code>false</code> in order to retrieve |
| * the providers in any order. |
| * |
| * @throws IllegalArgumentException if <code>category</code> is not |
| * among the categories passed to the {@linkplain |
| * #ServiceRegistry(Iterator) constructor} of this |
| * <code>ServiceRegistry</code>. |
| */ |
| public synchronized Iterator getServiceProviders(Class category, |
| Filter filter, |
| boolean useOrdering) |
| { |
| int catid; |
| LinkedList provs; |
| ArrayList result; |
| |
| catid = getCategoryID(category); |
| provs = providers[catid]; |
| if (provs == null) |
| return Collections.EMPTY_LIST.iterator(); |
| |
| result = new ArrayList(provs.size()); |
| for (Iterator iter = provs.iterator(); iter.hasNext();) |
| { |
| Object provider = iter.next(); |
| if (filter == null || filter.filter(provider)) |
| result.add(provider); |
| } |
| |
| // If we are supposed to obey ordering constraints, and |
| // if any constraints have been imposed on the specified |
| // service category, sort the result. |
| if (useOrdering && constraints != null) |
| { |
| final Map cons = constraints[catid]; |
| if (cons != null) |
| Collections.sort(result, new Comparator() |
| { |
| public int compare(Object o1, Object o2) |
| { |
| Set s; |
| |
| if (o1 == o2) |
| return 0; |
| |
| s = (Set) cons.get(o1); |
| if (s != null && s.contains(o2)) |
| return -1; // o1 < o2 |
| |
| s = (Set) cons.get(o2); |
| if (s != null && s.contains(o1)) |
| return 1; // o1 > o2 |
| |
| return 0; // o1 == o2 |
| } |
| }); |
| } |
| |
| return result.iterator(); |
| } |
| |
| |
| /** |
| * Returns one of the service providers that is a subclass of the |
| * specified class. |
| * |
| * @param providerClass a class to search for. |
| */ |
| public synchronized Object getServiceProviderByClass(Class providerClass) |
| { |
| if (providerClass == null) |
| throw new IllegalArgumentException(); |
| |
| // Note that the method getServiceProviderByClass is rather |
| // unlikely to be ever called, so it would be wasteful to keep a |
| // special data structure for making it a fast operation. |
| for (int cat = 0; cat < categories.length; cat++) |
| { |
| if (!categories[cat].isAssignableFrom(providerClass)) |
| continue; |
| |
| LinkedList provs = providers[cat]; |
| if (provs == null) |
| continue; |
| |
| for (Iterator iter = provs.iterator(); iter.hasNext();) |
| { |
| Object provider = iter.next(); |
| if (providerClass.isInstance(provider)) |
| return provider; |
| } |
| } |
| |
| return null; |
| } |
| |
| |
| /** |
| * Adds an ordering constraint on service providers. |
| * |
| * @param category the service category to which an ordering |
| * constraint is to be added. |
| * |
| * @param first the provider which is supposed to come before |
| * <code>second</code>. |
| * |
| * @param second the provider which is supposed to come after |
| * <code>first</code>. |
| * |
| * @throws IllegalArgumentException if <code>first</code> and |
| * <code>second</code> are referring to the same object, or if one |
| * of them is <code>null</code>. |
| * |
| * @see #unsetOrdering |
| * @see #getServiceProviders(Class, Filter, boolean) |
| */ |
| public synchronized boolean setOrdering(Class category, |
| Object firstProvider, |
| Object secondProvider) |
| { |
| return addConstraint(getCategoryID(category), firstProvider, |
| secondProvider); |
| } |
| |
| |
| /** |
| * Removes an ordering constraint on service providers. |
| * |
| * @param category the service category from which an ordering |
| * constraint is to be removed. |
| * |
| * @param first the provider which is supposed to come before |
| * <code>second</code>. |
| * |
| * @param second the provider which is supposed to come after |
| * <code>first</code>. |
| * |
| * @throws IllegalArgumentException if <code>first</code> and |
| * <code>second</code> are referring to the same object, or if one |
| * of them is <code>null</code>. |
| * |
| * @see #setOrdering |
| */ |
| public synchronized boolean unsetOrdering(Class category, |
| Object firstProvider, |
| Object secondProvider) |
| { |
| return removeConstraint(getCategoryID(category), |
| firstProvider, secondProvider); |
| } |
| |
| |
| /** |
| * Adds an ordering constraint on service providers. |
| * |
| * @param catid the service category ID, which is the |
| * category’s index into the {@link #categories} array. |
| * |
| * @param first the provider which is supposed to come before |
| * <code>second</code>. |
| * |
| * @param second the provider which is supposed to come after |
| * <code>first</code>. |
| * |
| * @throws IllegalArgumentException if <code>first</code> and |
| * <code>second</code> are referring to the same object, or if one |
| * of them is <code>null</code>. |
| */ |
| private boolean addConstraint(int catid, Object first, Object second) |
| { |
| Set s; |
| IdentityHashMap cons; |
| |
| // Also checks argument validity. |
| removeConstraint(catid, second, first); |
| |
| if (constraints == null) |
| constraints = new IdentityHashMap[categories.length]; |
| cons = constraints[catid]; |
| if (cons == null) |
| cons = constraints[catid] = new IdentityHashMap(); |
| |
| s = (Set) cons.get(first); |
| if (s == null) |
| cons.put(first, s = new HashSet()); |
| return s.add(second); |
| } |
| |
| |
| /** |
| * Removes an ordering constraint on service providers. |
| * |
| * @param catid the service category ID, which is the |
| * category’s index into the {@link #categories} array. |
| * |
| * @param first the provider which is supposed to come before |
| * <code>second</code>. |
| * |
| * @param second the provider which is supposed to come after |
| * <code>first</code>. |
| * |
| * @throws IllegalArgumentException if <code>first</code> and |
| * <code>second</code> are referring to the same object, or if one |
| * of them is <code>null</code>. |
| */ |
| private boolean removeConstraint(int catid, Object first, Object second) |
| { |
| Collection s; |
| IdentityHashMap cons; |
| |
| if (first == null || second == null || first == second) |
| throw new IllegalArgumentException(); |
| |
| if (constraints == null) |
| return false; |
| |
| cons = constraints[catid]; |
| if (cons == null) |
| return false; |
| |
| s = (Collection) cons.get(first); |
| if (s == null) |
| return false; |
| |
| if (!s.remove(second)) |
| return false; |
| |
| // If we removed the last constraint for a service category, |
| // we can get free some memory. |
| if (cons.isEmpty()) |
| { |
| constraints[catid] = null; |
| boolean anyConstraints = false; |
| for (int i = 0; i < constraints.length; i++) |
| { |
| if (constraints[i] != null) |
| { |
| anyConstraints = true; |
| break; |
| } |
| } |
| if (!anyConstraints) |
| constraints = null; |
| } |
| |
| return true; |
| } |
| |
| |
| /** |
| * A filter for selecting service providers that match custom |
| * criteria. |
| * |
| * @see ServiceRegistry#getServiceProviders(Class, Filter, |
| * boolean) |
| * |
| * @since 1.4 |
| * |
| * @author Michael Koch (konqueror@gmx.de) |
| * @author Sascha Brawer (brawer@dandelis.ch) |
| */ |
| public static interface Filter |
| { |
| /** |
| * Checks whether the specified service provider matches the |
| * constraints of this Filter. |
| * |
| * @param provider the service provider in question. |
| * |
| * @return <code>true</code> if <code>provider</code> matches the |
| * criteria; <code>false</code> if it does not match. |
| */ |
| boolean filter(Object provider); |
| }; |
| } |
| |