/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 * 
 *    (C) 2003-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.data;

import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.geotools.factory.FactoryCreator;
import org.geotools.factory.FactoryRegistry;
import org.opengis.feature.Feature;
import org.opengis.feature.type.FeatureType;


/**
 * Enable programs to find all available datastore implementations.
 *
 * <p>
 * In order to be located by this finder datasources must provide an
 * implementation of the {@link org.geotools.data.DataStoreFactorySpi} interface.
 * </p>
 *
 * <p>
 * In addition to implementing this interface datasouces should have a services
 * file:<br/><code>META-INF/services/org.geotools.data.DataStoreFactorySpi</code>
 * </p>
 *
 * <p>
 * The file should contain a single line which gives the full name of the
 * implementing class.
 * </p>
 *
 * <p>
 * Example:<br/><code>org.geotools.data.mytype.MyTypeDataStoreFacotry</code>
 * </p>
 *
 *
 * @source $URL$
 */
public final class DataStoreFinder {
	/** The logger for the filter module. */
    protected static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.data");

	/**
	 * The service registry for this manager. Will be initialized only when
	 * first needed.
	 */
	private static FactoryRegistry registry;

	//Singleton pattern
    private DataStoreFinder() {
    }

    /**
     * Checks each available datasource implementation in turn and returns the
     * first one which claims to support the resource identified by the params
     * object.
     *
     * @param params A Map object which contains a defenition of the resource
     *        to connect to. for file based resources the property 'url'
     *        should be set within this Map.
     *
     * @return The first datasource which claims to process the required
     *         resource, returns null if none can be found.
     *
     * @throws java.io.IOException If a suitable loader can be found, but it can not be
     *         attached to the specified resource without errors.
     */
    public static synchronized DataStore getDataStore(Map params) throws IOException {
        Iterator<DataStoreFactorySpi> ps = getAvailableDataStores();
        DataAccess<? extends FeatureType, ? extends Feature> dataStore;
        dataStore = DataAccessFinder.getDataStore(params, ps);
        return (DataStore) dataStore;
    }

    /**
     * Finds all implemtaions of DataStoreFactory which have registered using
     * the services mechanism, regardless weather it has the appropriate
     * libraries on the classpath.
     *
     * @return An iterator over all discovered datastores which have registered
     *         factories
     */
    public static synchronized Iterator<DataStoreFactorySpi> getAllDataStores() {
        return DataAccessFinder.getAllDataStores(getServiceRegistry(), DataStoreFactorySpi.class);
    }

    /**
     * Finds all implemtaions of DataStoreFactory which have registered using
     * the services mechanism, and that have the appropriate libraries on the
     * classpath.
     *
     * @return An iterator over all discovered datastores which have registered
     *         factories, and whose available method returns true.
     */
    public static synchronized Iterator<DataStoreFactorySpi> getAvailableDataStores() {
        Set<DataStoreFactorySpi> availableDS;
        FactoryRegistry serviceRegistry = getServiceRegistry();
        availableDS = DataAccessFinder.getAvailableDataStores(serviceRegistry, DataStoreFactorySpi.class);
        return availableDS.iterator();
    }

	/**
	 * Returns the service registry. The registry will be created the first time
	 * this method is invoked.
	 */
	private static FactoryRegistry getServiceRegistry() {
		assert Thread.holdsLock(DataStoreFinder.class);
		if (registry == null) {
			registry = new FactoryCreator(Arrays
					.asList(new Class<?>[] { DataStoreFactorySpi.class }));
		}
		return registry;
	}

	/**
	 * Scans for factory plug-ins on the application class path. This method is
	 * needed because the application class path can theoretically change, or
	 * additional plug-ins may become available. Rather than re-scanning the
	 * classpath on every invocation of the API, the class path is scanned
	 * automatically only on the first invocation. Clients can call this method
	 * to prompt a re-scan. Thus this method need only be invoked by
	 * sophisticated applications which dynamically make new plug-ins available
	 * at runtime.
	 */
	public static synchronized void scanForPlugins() {

		getServiceRegistry().scanForPlugins();

	}
	
	/**
	     * Resets the factory finder and prepares for a new full scan of the SPI subsystems
	     */
	    public static void reset() {
	        FactoryRegistry copy = registry;
	        registry = null;
	        if(copy != null) {
	            copy.deregisterAll();
	        }
	    }
    
}
