/*
 * Copyright 2004-2015 the Seasar Foundation and the Others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.seasar.framework.jpa.metadata;

import java.util.concurrent.ConcurrentMap;

import org.seasar.framework.jpa.PersistenceUnitManager;
import org.seasar.framework.jpa.PersistenceUnitManagerLocater;
import org.seasar.framework.jpa.PersistenceUnitProvider;
import org.seasar.framework.util.Disposable;
import org.seasar.framework.util.DisposableUtil;
import org.seasar.framework.util.tiger.CollectionsUtil;

/**
 * {@link EntityDesc}のファクトリです。
 * 
 * @author koichik
 */
public class EntityDescFactory {

    /** クラスに対応する{@link EntityDesc}が存在しないことを示すオブジェクト */
    protected static final EntityDesc NOT_FOUND = new NotFound();

    /** 初期化されているかどうかを表すフラグ */
    protected static boolean initialized;

    /** エンティティクラスをキー、エンティティ定義を値とするマップ */
    protected static final ConcurrentMap<Class<?>, EntityDesc> entityDescs = CollectionsUtil
            .newConcurrentHashMap();

    /**
     * このクラスを初期化します。
     * 
     */
    public static void initialize() {
        if (initialized) {
            return;
        }
        DisposableUtil.add(new Disposable() {

            public void dispose() {
                clear();
            }
        });
        initialized = true;
    }

    /**
     * このクラスがキャッシュしている値を消去し、初期化以前の状態にします。
     * 
     */
    public static void clear() {
        entityDescs.clear();
        initialized = false;
    }

    /**
     * エンティティクラスを表現する{@link EntityDesc}を返します。
     * 
     * @param entityClass
     *            エンティティクラス
     * @return エンティティクラスを表現する{@link EntityDesc}
     */
    public static EntityDesc getEntityDesc(final Class<?> entityClass) {
        initialize();
        final EntityDesc entityDesc = entityDescs.get(entityClass);
        if (entityDesc != null) {
            return entityDesc == NOT_FOUND ? null : entityDesc;
        }
        return createEntityDesc(entityClass);
    }

    /**
     * エンティティクラスを表現する{@link EntityDesc}を作成します。
     * 
     * @param entityClass
     *            エンティティクラス
     * @return エンティティクラスを表現する{@link EntityDesc}
     */
    protected static EntityDesc createEntityDesc(final Class<?> entityClass) {
        final PersistenceUnitManager manager = PersistenceUnitManagerLocater
                .getInstance();
        final PersistenceUnitProvider unitProvider = manager
                .getPersistenceUnitProvider(manager
                        .getConcretePersistenceUnitName(entityClass));
        final EntityDesc entityDesc = unitProvider.getEntityDescProvider()
                .createEntityDesc(unitProvider.getEntityManagerFactory(),
                        entityClass);
        if (entityDesc == null) {
            CollectionsUtil.putIfAbsent(entityDescs, entityClass, NOT_FOUND);
            return null;
        }
        return CollectionsUtil
                .putIfAbsent(entityDescs, entityClass, entityDesc);
    }

    private static class NotFound implements EntityDesc {

        public AttributeDesc getAttributeDesc(String attributeName) {
            return null;
        }

        public AttributeDesc[] getAttributeDescs() {
            return null;
        }

        public String[] getAttributeNames() {
            return null;
        }

        public Class<?> getEntityClass() {
            return null;
        }

        public String getEntityName() {
            return null;
        }

        public AttributeDesc getIdAttributeDesc() {
            return null;
        }

    }

}
