/*
 * 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.util;

import java.util.List;
import java.util.Set;

import javax.persistence.spi.ClassTransformer;
import javax.persistence.spi.PersistenceUnitInfo;

import org.seasar.framework.jpa.PersistenceClassTransformer;
import org.seasar.framework.util.tiger.CollectionsUtil;

/**
 * 未ロードのクラスをトランスフォームし、トランスフォーム後のクラスのクラスを適切なクラスローダーにロードするクラスローダです。
 * 
 * @author taedium
 */
public class TransformClassLoader extends AbstractClassLoader {

    /** 永続クラスのトランスフォーマ */
    protected PersistenceClassTransformer persistenceClassTransformer;

    /** 永続ユニット情報 */
    protected PersistenceUnitInfo persistenceUnitInfo;

    /** クラストランスフォーマのリスト */
    protected List<ClassTransformer> transformers = CollectionsUtil
            .newArrayList();

    /**
     * インスタンスを構築します。
     * <p>
     * コンテキストクラスローダからロードされるクラス及び、<code>java.</code>または<code>javax.</code>で始まるクラスを除いて
     * 親クラスローダに委譲せず、{@link PersistenceClassTransformer#transform(PersistenceUnitInfo, String, byte[])}
     * を呼び出してロードするように構成します。
     * </p>
     * 
     */
    public TransformClassLoader() {
        this(Thread.currentThread().getContextClassLoader());
    }

    /**
     * インスタンスを構築します。
     * <p>
     * ブートストラップクラスローダからロードされるクラス及び、<code>java.</code>または<code>javax.</code>で始まるクラスを除いて
     * 親クラスローダに委譲せず、{@link PersistenceClassTransformer#transform(PersistenceUnitInfo, String, byte[])}
     * を呼び出してロードするように構成します。
     * </p>
     * 
     * @param parent
     *            親クラスローダ
     */
    public TransformClassLoader(ClassLoader parent) {
        super(parent);
    }

    /**
     * インスタンスを構築します。
     * <p>
     * <code>includedNames</code>に含まれる名前のクラスのみ、 親クラスローダに委譲せず、
     * {@link PersistenceClassTransformer#transform(PersistenceUnitInfo, String, byte[])}を呼び出してロードするように構成します。
     * ただし、ブートストラップクラスローダからロードされるクラス及び、 <code>java.</code>または<code>javax.</code>で始まるクラスを除きます。
     * </p>
     * 
     * @param parent
     *            親クラスローダ
     * @param includedNames
     *            親より先にロードする対象となるクラス名のセット
     */
    public TransformClassLoader(final ClassLoader parent,
            final Set<String> includedNames) {
        super(parent, includedNames);
    }

    /**
     * 永続クラスのトランスフォーマを登録します。
     * 
     * @param persistenceClassTransformer
     *            永続クラスのトランスフォーマ
     */
    public void registerPersistenceClassTransformer(
            final PersistenceClassTransformer persistenceClassTransformer) {
        this.persistenceClassTransformer = persistenceClassTransformer;
    }

    /**
     * 永続ユニット情報を登録します。
     * 
     * @param persistenceUnitInfo
     *            永続ユニット情報
     */
    public void registerPersistenceUnitInfo(
            final PersistenceUnitInfo persistenceUnitInfo) {
        this.persistenceUnitInfo = persistenceUnitInfo;
    }

    /**
     * クラストランスフォーマを登録します。
     * 
     * @param transformer
     *            クラストランスフォーマ
     */
    public void addTransformer(final ClassTransformer transformer) {
        transformers.add(transformer);
    }

    @Override
    protected Class<?> doDefineClass(String className, byte[] bytes) {
        return persistenceClassTransformer.transform(persistenceUnitInfo,
                className, bytes);
    }

}
