/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2007-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.factory;
import java.util.Iterator;
import java.util.Set;
import java.util.LinkedHashSet;
import org.geotools.resources.XArray;
/**
* The list of registered {@linkplain FactoryIteratorProvider factory iterator providers}.
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux
*
* @todo Consider removing {@link FactoryRegistry#globalConfiguration} and use listeners instead.
*/
final class FactoryIteratorProviders {
/**
* The system-wide configuration. This is the instance configured by
* the public static methods provided in this class.
*/
private static final FactoryIteratorProviders GLOBAL = new FactoryIteratorProviders();
/**
* Incremented every time a modification is performed.
*/
private int modifications = 0;
/**
* Alternative scanning methods used by {@link FactoryRegistry#scanForPlugins(Collection,Class)}
* in addition of the default lookup mechanism. Will be created only when first needed.
*/
private Set<FactoryIteratorProvider> iteratorProviders;
/**
* Creates an initially empty set of factories.
*/
FactoryIteratorProviders() {
}
/**
* Synchronizes the content of the {@link #iteratorProviders} map with the {@linkplain #GLOBAL
* global} one. New providers are returned for later {@linkplain FactoryRegistry#register
* registration}. Note that this method is typically invoked in a different thread than
* {@link FactoryIteratorProviders} public static method calls.
*
* @return The new iterators providers {@linkplain #addFactoryIteratorProvider added} since
* the last time this method was invoked, or {@code null} if none.
*/
final FactoryIteratorProvider[] synchronizeIteratorProviders() {
FactoryIteratorProvider[] newProviders = null;
int count = 0;
synchronized (GLOBAL) {
if (modifications == GLOBAL.modifications) {
return null;
}
modifications = GLOBAL.modifications;
if (GLOBAL.iteratorProviders == null) {
/*
* Should never happen. If GLOBAL.iteratorProviders was null, then every
* 'modifications' count should be 0 and this method should have returned 'null'.
*/
throw new AssertionError(modifications);
}
/*
* If 'removeFactoryIteratorProvider(...)' has been invoked since the last time
* this method was run, then synchronize 'iteratorProviders' accordingly. Current
* implementation do not unregister the factories that were created by those iterators.
*/
if (iteratorProviders != null) {
iteratorProviders.retainAll(GLOBAL.iteratorProviders);
} else if (!GLOBAL.iteratorProviders.isEmpty()) {
iteratorProviders = new LinkedHashSet<FactoryIteratorProvider>();
}
/*
* If 'addFactoryIteratorProvider(...)' has been invoked since the last time
* this method was run, then synchronize 'iteratorProviders' accordingly. We
* keep trace of new providers in order to allow 'FactoryRegistry' to use them
* for a immediate scanning.
*/
int remaining = GLOBAL.iteratorProviders.size();
for (final Iterator it=GLOBAL.iteratorProviders.iterator(); it.hasNext();) {
final FactoryIteratorProvider candidate = (FactoryIteratorProvider) it.next();
if (iteratorProviders.add(candidate)) {
if (newProviders == null) {
newProviders = new FactoryIteratorProvider[remaining];
}
newProviders[count++] = candidate;
}
remaining--;
}
}
// Note: newProviders may be null.
return XArray.resize(newProviders, count);
}
/**
* Adds an alternative way to search for factory implementations. {@link FactoryRegistry} has
* a default mechanism bundled in it, which uses the content of all {@code META-INF/services}
* directories found on the classpath. This {@code addFactoryIteratorProvider} method allows
* to specify additional discovery algorithms. It may be useful in the context of some
* frameworks that use the <cite>constructor injection</cite> pattern, like the
* <a href="http://www.springframework.org/">Spring framework</a>.
*/
public static void addFactoryIteratorProvider(FactoryIteratorProvider provider) {
synchronized (GLOBAL) {
if (GLOBAL.iteratorProviders == null) {
GLOBAL.iteratorProviders = new LinkedHashSet<FactoryIteratorProvider>();
}
if (GLOBAL.iteratorProviders.add(provider)) {
GLOBAL.modifications++;
}
}
}
/**
* Removes a provider that was previously {@linkplain #addFactoryIteratorProvider added}.
* Note that factories already obtained from the specified provider will not be
* {@linkplain FactoryRegistry#deregisterServiceProvider deregistered} by this method.
*/
public static void removeFactoryIteratorProvider(FactoryIteratorProvider provider) {
synchronized (GLOBAL) {
if (GLOBAL.iteratorProviders != null) {
if (GLOBAL.iteratorProviders.remove(provider)) {
GLOBAL.modifications++;
}
}
}
}
/**
* Returns all iterator providers. This method do not returns any live collection
* since the array will be used outside the synchronized block.
*/
static FactoryIteratorProvider[] getIteratorProviders() {
synchronized (GLOBAL) {
if (GLOBAL.iteratorProviders == null) {
return new FactoryIteratorProvider[0];
}
return GLOBAL.iteratorProviders.toArray(
new FactoryIteratorProvider[GLOBAL.iteratorProviders.size()]);
}
}
}