${database.allClassCopyright}package ${glPackageBaseCommon};

import java.util.Collection;
import java.util.Map;
import java.util.HashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.seasar.dbflute.BehaviorSelector;
import org.seasar.dbflute.bhv.BehaviorReadable;
import org.seasar.dbflute.dbmeta.DBMeta;
import org.seasar.dbflute.util.DfTraceViewUtil;

#if ($database.isTargetContainerSeasar())
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.ComponentNotFoundRuntimeException;
#end
#if ($database.isTargetContainerSpring())
import org.springframework.context.ApplicationContext;
#end
#if ($database.isTargetContainerLucy())
import org.t2framework.lucy.Lucy;
#end
#if ($database.isTargetContainerGuice())
import com.google.inject.Inject;
import com.google.inject.Injector;
#end
#if ($database.isTargetContainerSlim3())
import org.slim3.container.S3Container;
#end

/**
 * The implementation of behavior selector.
 * @author DBFlute(AutoGenerator)
 */
public class ${glImplementedBehaviorSelector} implements BehaviorSelector {

    // ===================================================================================
    //                                                                          Definition
    //                                                                          ==========
    /** Log instance. */
    private static final Log _log = LogFactory.getLog(${glImplementedBehaviorSelector}.class);

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    /** The cache of behavior. */
    protected Map<Class<? extends BehaviorReadable>, BehaviorReadable> _behaviorCache = newHashMap();

#if ($database.isTargetContainerSeasar())
    /** The container of Seasar. */
    protected S2Container _container;
#end
#if ($database.isTargetContainerSpring())
    /** The container of Spring. */
    protected ApplicationContext _container;
#end
#if ($database.isTargetContainerLucy())
    /** The container of Lucy. */
    protected Lucy _container;
#end
#if ($database.isTargetContainerGuice())
    /** The container of Guice. */
    protected Injector _container;
#end
#if ($database.isTargetContainerSlim3())
    /** The container of Slim3. */
    protected S3Container _container;
#end

    // ===================================================================================
    //                                                                           Component
    //                                                                           =========
#if ($database.isTargetContainerSeasar() || $database.isTargetContainerSpring() || $database.isTargetContainerLucy())
    @SuppressWarnings("unchecked")
#end
    public <COMPONENT> COMPONENT getComponent(Class<COMPONENT> componentType) {
        assertObjectNotNull("componentType", componentType);
        assertObjectNotNull("_container", _container);
#if ($database.isTargetContainerSeasar())
        try {
		    return (COMPONENT)_container.getComponent(componentType);
		} catch (ComponentNotFoundRuntimeException e) { // Normally it doesn't come.
		    final COMPONENT component;
		    try {
		        // for HotDeploy Mode
		        component = (COMPONENT)_container.getRoot().getComponent(componentType);
		    } catch (ComponentNotFoundRuntimeException ignored) {
		        throw e;
		    }
		    _container = _container.getRoot(); // Change container.
		    return component;
		}
#end
#if ($database.isTargetContainerSpring())
		return (COMPONENT)_container.getBean(initUncap(componentType.getSimpleName()));
#end
#if ($database.isTargetContainerLucy())
		return (COMPONENT)_container.get(initUncap(componentType.getSimpleName()));
#end
#if ($database.isTargetContainerGuice())
		return _container.getInstance(componentType);
#end
#if ($database.isTargetContainerSlim3())
		return _container.lookup(componentType.getSimpleName());
#end
    }

    // ===================================================================================
    //                                                                          Initialize
    //                                                                          ==========
    /**
     * Initialize condition-bean meta data. <br />
     */
    public void initializeConditionBeanMetaData() {
        final Map<String, DBMeta> dbmetaMap = ${glDBMetaInstanceHandler}.getUnmodifiableDBMetaMap();
        final Collection<DBMeta> dbmetas = dbmetaMap.values();
        long before = 0;
        if (_log.isInfoEnabled()) {
            before = System.currentTimeMillis();
            _log.info("/= = = = = = = = = = = = = = = = = initializeConditionBeanMetaData()");
        }
        for (DBMeta dbmeta : dbmetas) {
            final BehaviorReadable bhv = byName(dbmeta.getTableDbName());
            bhv.warmUpCommand();
        }
        if (_log.isInfoEnabled()) {
            long after = System.currentTimeMillis();
            _log.info("Initialized Count: " + dbmetas.size());
            _log.info("= = = = = = = = = =/ [" + DfTraceViewUtil.convertToPerformanceView(after - before) + "]");
        }
    }

    // ===================================================================================
    //                                                                            Selector
    //                                                                            ========
    /**
     * Select behavior.
     * @param <BEHAVIOR> The type of behavior.
     * @param behaviorType Behavior type. (NotNull)
     * @return Behavior. (NotNull)
     */
    @SuppressWarnings("unchecked")
    public <BEHAVIOR extends BehaviorReadable> BEHAVIOR select(Class<BEHAVIOR> behaviorType) {
        BEHAVIOR bhv = (BEHAVIOR) _behaviorCache.get(behaviorType);
        if (bhv != null) {
            return bhv;
        }
        synchronized (_behaviorCache) {
            bhv = (BEHAVIOR) _behaviorCache.get(behaviorType);
            if (bhv != null) {
                return bhv;
            }
            bhv = (BEHAVIOR) getComponent(behaviorType);
            _behaviorCache.put(behaviorType, bhv);
            return bhv;
        }
    }

    /**
     * Select behavior-readable by name.
     * @param tableFlexibleName Table flexible-name. (NotNull)
     * @return Behavior-readable. (NotNull)
     */
    public BehaviorReadable byName(String tableFlexibleName) {
        assertStringNotNullAndNotTrimmedEmpty("tableFlexibleName", tableFlexibleName);
        final DBMeta dbmeta = ${glDBMetaInstanceHandler}.findDBMeta(tableFlexibleName);
        return select(getBehaviorType(dbmeta));
    }

    /**
     * Get behavior-type by DB meta.
     * @param dbmeta DB meta. (NotNull)
     * @return Behavior-type. (NotNull)
     */
    @SuppressWarnings("unchecked")
    protected Class<BehaviorReadable> getBehaviorType(DBMeta dbmeta) {
        final String behaviorTypeName = dbmeta.getBehaviorTypeName();
        if (behaviorTypeName == null) {
            String msg = "The dbmeta.getBehaviorTypeName() should not return null: dbmeta=" + dbmeta;
            throw new IllegalStateException(msg);
        }
        final Class<BehaviorReadable> behaviorType;
        try {
            behaviorType = (Class<BehaviorReadable>) Class.forName(behaviorTypeName);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("The class does not exist: " + behaviorTypeName, e);
        }
        return behaviorType;
    }

    // ===================================================================================
    //                                                                      General Helper
    //                                                                      ==============
    protected String initUncap(String str) {
        return str.substring(0, 1).toLowerCase() + str.substring(1);
    }

    protected <KEY, VALUE> HashMap<KEY, VALUE> newHashMap() {
        return new HashMap<KEY, VALUE>();
    }

    // ===================================================================================
    //                                                                              Assert
    //                                                                              ======
    // -----------------------------------------------------
    //                                         Assert Object
    //                                         -------------
    /**
     * Assert that the object is not null.
     * @param variableName Variable name. (NotNull)
     * @param value Value. (NotNull)
     * @exception IllegalArgumentException
     */
    protected void assertObjectNotNull(String variableName, Object value) {
        if (variableName == null) {
            String msg = "The value should not be null: variableName=null value=" + value;
            throw new IllegalArgumentException(msg);
        }
        if (value == null) {
            String msg = "The value should not be null: variableName=" + variableName;
            throw new IllegalArgumentException(msg);
        }
    }

    // -----------------------------------------------------
    //                                         Assert String
    //                                         -------------
    /**
     * Assert that the entity is not null and not trimmed empty.
     * @param variableName Variable name. (NotNull)
     * @param value Value. (NotNull)
     */
    protected void assertStringNotNullAndNotTrimmedEmpty(String variableName, String value) {
        assertObjectNotNull("variableName", variableName);
        assertObjectNotNull("value", value);
        if (value.trim().length() == 0) {
            String msg = "The value should not be empty: variableName=" + variableName + " value=" + value;
            throw new IllegalArgumentException(msg);
        }
    }

    // ===================================================================================
    //                                                                            Accessor
    //                                                                            ========
#if ($database.isTargetContainerSeasar())
    public void setContainer(S2Container container) {
        this._container = container;
    }
#end
#if ($database.isTargetContainerSpring())
    public void setContainer(ApplicationContext container) {
        this._container = container;
    }
#end
#if ($database.isTargetContainerLucy())
    public void setContainer(Lucy container) {
        this._container = container;
    }
#end
#if ($database.isTargetContainerGuice())
    @Inject
    public void setContainer(Injector container) {
        this._container = container;
    }
#end
#if ($database.isTargetContainerSlim3())
    public void setContainer(S3Container container) {
        this._container = container;
    }
#end
}
