/*
 *    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: http://svn.osgeo.org/geotools/branches/2.7.x/modules/library/metadata/src/main/java/org/geotools/factory/FactoryIteratorProviders.java $
 * @version $Id: FactoryIteratorProviders.java 30640 2008-06-12 17:34:32Z acuster $
 * @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);
    }
    /**
     * 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()]);
        }
    }
}
