/* * 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); } }