/*
* 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.autodetector;
import java.lang.annotation.Annotation;
import java.util.List;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import org.seasar.framework.autodetector.impl.AbstractClassAutoDetector;
import org.seasar.framework.container.annotation.tiger.Binding;
import org.seasar.framework.container.annotation.tiger.BindingType;
import org.seasar.framework.container.annotation.tiger.Component;
import org.seasar.framework.container.annotation.tiger.InitMethod;
import org.seasar.framework.convention.NamingConvention;
import org.seasar.framework.util.ClassUtil;
import org.seasar.framework.util.ResourcesUtil;
import org.seasar.framework.util.ClassTraversal.ClassHandler;
import org.seasar.framework.util.ResourcesUtil.Resources;
import org.seasar.framework.util.tiger.CollectionsUtil;
import org.seasar.framework.util.tiger.ReflectionUtil;
/**
* 規約を利用してJPAで管理すべき永続クラスを自動検出するクラスです。
* <p>
* このインスタンスが自動検出を実行するには{@link #namingConvention}に値が設定されていることが必須です。
* デフォルトで次の条件に合致するクラスを検出します。
* </p>
* <ul>
* <li>クラスが{@link NamingConvention#getEntityPackageName()}で決定されるパッケージの階層に含まれる</li>
* <li>クラスに{@link Entity}、{@link MappedSuperclass}、{@link Embeddable}
* のいずれかのアノテーションが指定されている</li>
* </ul>
*
* @author taedium
*/
@Component
public class PersistenceClassAutoDetector extends AbstractClassAutoDetector {
/** アノテーションのリスト */
protected final List<Class<? extends Annotation>> annotations = CollectionsUtil
.newArrayList();
/** 命名規約 */
protected NamingConvention namingConvention;
/** エンティティをロードするためのクラスローダ */
protected ClassLoader classLoader;
/**
* インスタンスを構築します。
*
*/
public PersistenceClassAutoDetector() {
annotations.add(Entity.class);
annotations.add(MappedSuperclass.class);
annotations.add(Embeddable.class);
}
/**
* 命名規約を設定します。
*
* @param namingConvention
* 命名規約
*/
@Binding(bindingType = BindingType.MAY)
public void setNamingConvention(final NamingConvention namingConvention) {
this.namingConvention = namingConvention;
}
/**
* クラスローダを設定します。
*
* @param classLoader
* クラスローダ
*/
@Binding(bindingType = BindingType.MAY)
public void setClassLoader(final ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* このインスタンスを初期化します。
*
*/
@InitMethod
public void init() {
if (namingConvention != null) {
final String entityPackageName = namingConvention
.getEntityPackageName();
for (final String rootPackageName : namingConvention
.getRootPackageNames()) {
final String packageName = ClassUtil.concatName(
rootPackageName, entityPackageName);
addTargetPackageName(packageName);
}
}
}
/**
* 検出の条件として使用するアノテーションを追加します。
*
* @param annotation
* アノテーション
*/
public void addAnnotation(final Class<? extends Annotation> annotation) {
annotations.add(annotation);
}
@SuppressWarnings("unchecked")
public void detect(final ClassHandler handler) {
for (int i = 0; i < getTargetPackageNameSize(); i++) {
final String targetPackageName = getTargetPackageName(i);
for (final Resources resources : ResourcesUtil
.getResourcesTypes(targetPackageName)) {
try {
resources.forEach(new ClassHandler() {
public void processClass(final String packageName,
final String shortClassName) {
if ((packageName.equals(targetPackageName) || packageName
.startsWith(targetPackageName + "."))
&& isEntity(packageName, shortClassName)) {
handler.processClass(packageName,
shortClassName);
}
}
});
} finally {
resources.close();
}
}
}
}
/**
* 指定されたクラスが永続クラスである場合{@code true}を返します。
*
* @param packageName
* パッケージ名
* @param shortClassName
* クラス名
* @return 指定されたクラスが永続クラスである場合{@code true}、永続クラスでない場合{@code false}
*/
protected boolean isEntity(final String packageName,
final String shortClassName) {
final String name = ClassUtil.concatName(packageName, shortClassName);
final Class<?> clazz = getClass(name);
for (final Annotation ann : clazz.getAnnotations()) {
if (annotations.contains(ann.annotationType())) {
return true;
}
}
return false;
}
/**
* 名前から解決してクラスを返します。
*
* @param className
* クラス名
* @return クラス
*/
protected Class<?> getClass(final String className) {
if (classLoader != null) {
return ReflectionUtil.forName(className, classLoader);
}
return ReflectionUtil.forNameNoException(className);
}
}