/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2005-2006, GeoTools Project Managment Committee (PMC)
 *    (C) 2005, Institut de Recherche pour le Développement
 *   
 *    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.
 *
 *    This package contains documentation from OpenGIS specifications.
 *    OpenGIS consortium's work is fully acknowledged here.
 */
package org.geotools.referencing.factory;

// J2SE dependencies and extensions
import java.util.Map;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.Collection;

// OpenGIS dependencies
import org.opengis.referencing.*;

// Geotools dependencies
import org.geotools.factory.Hints;


/**
 * The base class for authority factories that create referencing object directly. This is
 * in contrast with other factories like the {@linkplain AuthorityFactoryAdapter adapter}
 * or {@linkplain BufferedAuthorityFactory buffered} ones, which delegates their work to
 * an other factory.
 *
 * @since 2.3
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/factory/DirectAuthorityFactory.java $
 * @version $Id: DirectAuthorityFactory.java 25955 2007-06-20 17:07:52Z desruisseaux $
 * @author Martin Desruisseaux
 */
public abstract class DirectAuthorityFactory extends AbstractAuthorityFactory {

    // IMPLEMENTATION NOTE:  The reason why this class exist is that we don't want "indirect"
    // factories like BufferedAuthorityFactory to inherit the factories field.  If this field
    // existed in their super-class, then the super-class constructor could try to initialize
    // it while in fact BufferedAuthorityFactory don't need it.  Experience with Geotools 2.2
    // suggest that it can lead to tricky recursivity problems in FactoryFinder, because most
    // factories registered in META-INF/services are some kind of BufferedAuthorityFactory.

    /**
     * The underlying factories used for objects creation.
     */
    protected final ReferencingFactoryContainer factories;

    /**
     * Tells if {@link ReferencingFactoryContainer#hints} has been invoked. It must be
     * invoked exactly once, but can't be invoked in the constructor because it causes
     * a {@link StackOverflowError} in some situations.
     */
    private boolean hintsInitialized;

    /**
     * Constructs an instance using the specified set of factories.
     *
     * @param factories The low-level factories to use.
     * @param priority The priority for this factory, as a number between
     *        {@link #MINIMUM_PRIORITY MINIMUM_PRIORITY} and
     *        {@link #MAXIMUM_PRIORITY MAXIMUM_PRIORITY} inclusive.
     */
    protected DirectAuthorityFactory(final ReferencingFactoryContainer factories, final int priority) {
        super(priority);
        this.factories = factories;
        ensureNonNull("factories", factories);
    }

    /**
     * Constructs an instance using the specified hints. This constructor recognizes the
     * {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS}, {@link Hints#DATUM_FACTORY DATUM}
     * and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM} {@code FACTORY} hints.
     *
     * @param hints The hints, or {@code null} if none.
     * @param priority The priority for this factory, as a number between
     *        {@link #MINIMUM_PRIORITY MINIMUM_PRIORITY} and
     *        {@link #MAXIMUM_PRIORITY MAXIMUM_PRIORITY} inclusive.
     */
    protected DirectAuthorityFactory(final Hints hints, final int priority) {
        super(priority);
        factories = ReferencingFactoryContainer.instance(hints);
        // Do not copies the user-provided hints to this.hints, because
        // this is up to sub-classes to decide which hints are relevant.
    }

    /**
     * Returns the implementation hints for this factory. The returned map contains values for
     * {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS}, {@link Hints#DATUM_FACTORY DATUM}
     * and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM} {@code FACTORY} hints. Other values
     * may be provided as well, at implementation choice.
     */
    public Map getImplementationHints() {
        synchronized (hints) { // Note: avoid lock on public object.
            if (!hintsInitialized) {
                hintsInitialized = true;
                hints.putAll(factories.getImplementationHints());
            }
        }
        return super.getImplementationHints();
    }

    /**
     * Returns the direct {@linkplain Factory factory} dependencies.
     */
    //@Override
    Collection/*<?>*/ dependencies() {
        if (factories != null) {
            final Set dependencies = new LinkedHashSet(8);
            dependencies.add(factories.getCRSFactory());
            dependencies.add(factories.getCSFactory());
            dependencies.add(factories.getDatumFactory());
            return dependencies;
        }
        return super.dependencies();
    }
}
