/*
 Copyright (c) 2007 Eric J. Feminella  <eric@ericfeminella.com>
 All rights reserved.

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is furnished
 to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 @internal
 */

package com.ericfeminella.utils
{
    import flash.utils.Dictionary;
    import flash.utils.getQualifiedClassName;
    import flash.errors.IllegalOperationError;

    /**
     *
     * All static class which provides an API which enforces classes
     * which are to follow the Singleton pattern are only ever have a
     * single instance instantiated per application, by monitoring the
     * instantiation of derived classes this requirement is ensured,
     * thus elliminating the Singleton implementation code from the
     * actual class implementation.
     *
     * <p>
     * Upon instantiation of a Singleton class, the class constructor
     * invokes <code>SingletonManager.validateInstance</code> and simply
     * passes a reference to the instance.
     * </p>
     *
     * @example this can be seen in the following
     *
     * <listing version="3.0">
     *
     * SingletonManager.validateInstance( this );
     *
     * </listing>
     *
     * <p>
     * The fully qualified class name is then used as the unique key in
     * the HashMap instance (<code>singletons</code>). The class name /
     * key is then assigned a Boolean value of true. When a subsequent
     * instantiation of the Singleton class occurs the Singleton class
     * repeats the process of invoking SingletonManager.validateInstance
     * to see if the Singleton class has been mapped previously, and if
     * so an exception is thrown.
     * </p>
     *
     * <p>
     * Classes must invoke <code>SingletonManager.validateInstance</code>
     * from within their constructor in order to garuntee they are only to
     * have single instance created, hence the term Singleton.
     * </p>
     *
     * @example The following example demonstrates how a class can invoke
     * <code>SingletonManager.validateInstance</code> in order to simplify
     * the Singleton construction process by delegating Singleton Management
     * to a specific object rather than the class implementation itself
     *
     * <listing version="3.0">
     *
     * package com.examples
     * {
     *     public class SomeSingleton
     *     {
     *         // Defines the singleton instance of SomeSingleton
     *         public static const instance:SomeSingleton = new SomeSingleton();
     *
     *         // Singelton class passes in a reference of the instance,
     *         // SingletonManager determines the fully qualified name
     *         // of the class and validates the instance
     *         public function SomeSingleton()
     *         {
     *             SingletonManager.validateInstance( this );
     *         }
     *     }
     * }
     *
     * </listing>
     *
     * <p>
     * The above example guarantees that only one instance of the example class
     * "SomeSingleton" can ever be instantiated.
     * </p>
     *
     */
    public final class SingletonManager
    {
        /**
         *
         * Defines a static HashMap which contains a reference to all
         * Singleton class types.
         *
         * <p>
         * A unique key based on the Singleton class name is added to the
         * map for each type which is to only contain a single instance
         * </p>
         *
         */
        private static const singletons:Dictionary = new Dictionary();

        /**
         *
         * Validates a new Singleton class instance to determine if the
         * class has previously been instantiated, if so, an Exception
         * is thrown, thus indicating that the class is a Singleton and
         * is only intended to have a single instance instantiated
         *
         * @param Singleton class instance which has been instantiated
         *
         */
        public static function validateInstance(instance:*) : void
        {
            var className:String = getQualifiedClassName( instance );

            if ( singletons[ className ] )
            {
                 throw new IllegalOperationError( "Singleton Exception. Only one instance is allowed for type: " + className );
            }
            singletons[ className ] = true;
        }
    }
}