/*
* 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.container.factory.aspect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.seasar.framework.aop.annotation.Interceptor;
import org.seasar.framework.aop.impl.PointcutImpl;
import org.seasar.framework.beans.BeanDesc;
import org.seasar.framework.beans.PropertyDesc;
import org.seasar.framework.beans.factory.BeanDescFactory;
import org.seasar.framework.container.AspectDef;
import org.seasar.framework.container.ComponentDef;
import org.seasar.framework.container.Expression;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.AnnotationHandler;
import org.seasar.framework.container.factory.AspectDefFactory;
import org.seasar.framework.container.impl.AspectDefImpl;
import org.seasar.framework.util.MethodUtil;
import org.seasar.framework.util.StringUtil;
/**
* メタアノテーションで注釈されたアノテーションを読み取り{@link AspectDef}を作成するコンポーネントの実装クラスです。
*
* @author koichik
*/
public class MetaAnnotationAspectDefBuilder extends AbstractAspectDefBuilder {
/** メタアノテーションの型 */
protected Class<? extends Annotation> metaAnnotationType;
/** インターセプタの名前空間 */
protected String interceptorNamespace;
/** インターセプタの接尾辞 */
protected String interceptorSuffix;
/**
* インスタンスを構築します。
*/
public MetaAnnotationAspectDefBuilder() {
}
/**
* インスタンスを構築します。
*
* @param metaAnnotationType
* メタアノテーションの型
* @param interceptorSuffix
* インターセプタの接尾辞
*/
public MetaAnnotationAspectDefBuilder(
final Class<? extends Annotation> metaAnnotationType,
final String interceptorSuffix) {
this.metaAnnotationType = metaAnnotationType;
this.interceptorNamespace = null;
this.interceptorSuffix = interceptorSuffix;
}
/**
* インスタンスを構築します。
*
* @param metaAnnotationType
* メタアノテーションの型
* @param interceptorNamespace
* インターセプタの名前空間
* @param interceptorSuffix
* インターセプタの接尾辞
*/
public MetaAnnotationAspectDefBuilder(
final Class<? extends Annotation> metaAnnotationType,
final String interceptorNamespace, final String interceptorSuffix) {
this.metaAnnotationType = metaAnnotationType;
this.interceptorNamespace = interceptorNamespace;
this.interceptorSuffix = interceptorSuffix;
}
/**
* メタアノテーションの型を返します。
*
* @return メタアノテーションの型
*/
public Class<? extends Annotation> getMetaAnnotationType() {
return metaAnnotationType;
}
/**
* メタアノテーションの型を設定します。
*
* @param metaAnnotationType
* メタアノテーションの型
*/
public void setMetaAnnotationType(
final Class<? extends Annotation> metaAnnotationType) {
this.metaAnnotationType = metaAnnotationType;
}
/**
* インターセプタの名前空間を返します。
*
* @return インターセプタの名前空間
*/
public String getInterceptorNamespace() {
return interceptorNamespace;
}
/**
* インターセプタの名前空間を設定します。
*
* @param interceptorNamespace
* インターセプタの名前空間
*/
public void setInterceptorNamespace(final String interceptorNamespace) {
this.interceptorNamespace = interceptorNamespace;
}
/**
* インターセプタの接尾辞を返します。
*
* @return インターセプタの接尾辞
*/
public String getInterceptorSuffix() {
return interceptorSuffix;
}
/**
* インターセプタの接尾辞を設定します。
*
* @param interceptorSuffix
* インターセプタの接尾辞
*/
public void setInterceptorSuffix(final String interceptorSuffix) {
this.interceptorSuffix = interceptorSuffix;
}
public void appendAspectDef(final AnnotationHandler annotationHandler,
final ComponentDef componentDef) {
final Class<?> componentClass = componentDef.getComponentClass();
if (componentClass == null) {
return;
}
processClass(componentDef, componentClass);
processMethod(componentDef, componentClass);
}
/**
* クラスに付けられたメタアノテーションで注釈されたアノテーションを読み取り{@link AspectDef アスペクト定義}を作成して{@link ComponentDef コンポーネント定義}に追加します。
*
* @param componentDef
* コンポーネント定義
* @param componentClass
* コンポーネントの型
*/
protected void processClass(final ComponentDef componentDef,
final Class<?> componentClass) {
for (final Annotation annotation : componentClass.getAnnotations()) {
final Class<? extends Annotation> annotationType = annotation
.annotationType();
final Annotation metaAnnotation = annotationType
.getAnnotation(getMetaAnnotationType());
if (metaAnnotation == null) {
continue;
}
final String pointcut = getPointcut(annotation);
final AspectDef aspectDef = new AspectDefImpl();
if (!StringUtil.isEmpty(pointcut)) {
aspectDef
.setPointcut(AspectDefFactory.createPointcut(pointcut));
}
aspectDef.setExpression(new ExpressionImpl(annotation));
componentDef.addAspectDef(aspectDef);
}
}
/**
* メソッドに付けられたメタアノテーションで注釈されたアノテーションを読み取り{@link AspectDef アスペクト定義}を作成して{@link ComponentDef コンポーネント定義}に追加します。
*
* @param componentDef
* コンポーネント定義
* @param componentClass
* コンポーネントの型
*/
protected void processMethod(final ComponentDef componentDef,
final Class<?> componentClass) {
for (final Method method : componentClass.getMethods()) {
if (method.isBridge() || method.isSynthetic()) {
continue;
}
final int modifiers = method.getModifiers();
if (!Modifier.isPublic(modifiers) || Modifier.isStatic(modifiers)
|| Modifier.isFinal(modifiers)) {
continue;
}
for (final Annotation annotation : method.getAnnotations()) {
final Class<? extends Annotation> annotationType = annotation
.annotationType();
final Annotation metaAnnotation = annotationType
.getAnnotation(getMetaAnnotationType());
if (metaAnnotation == null) {
continue;
}
final AspectDef aspectDef = new AspectDefImpl(new PointcutImpl(
method));
aspectDef.setExpression(new ExpressionImpl(annotation));
componentDef.addAspectDef(aspectDef);
}
}
}
/**
* アノテーションに指定されているポイントカットを返します。
*
* @param annotation
* アノテーション
* @return アノテーションに指定されているポイントカット
*/
protected String getPointcut(final Annotation annotation) {
for (final Method method : annotation.getClass().getMethods()) {
if (method.isBridge() || method.isSynthetic()) {
continue;
}
if ("pointcut".equals(method.getName())
&& method.getReturnType() == String.class) {
return String.class.cast(MethodUtil.invoke(method, annotation,
null));
}
}
return null;
}
/**
* アノテーションに指定されているインターセプタのコンポーネント名を返します。
*
* @param annotation
* アノテーション
* @return インターセプタのコンポーネント名
*/
protected String getInterceptorName(final Annotation annotation) {
final Class<? extends Annotation> annotationType = annotation
.annotationType();
final Interceptor interceptor = annotationType
.getAnnotation(Interceptor.class);
final String value = interceptor.value();
if (!StringUtil.isEmpty(value)) {
return value;
}
final String namespace = getInterceptorNamespace();
final String interceptorName = StringUtil.decapitalize(annotationType
.getSimpleName());
final String suffix = getInterceptorSuffix();
if (namespace != null) {
return namespace + "." + interceptorName + suffix;
}
return interceptorName + suffix;
}
/**
* 評価されると指定されたアノテーションで指定された名前のコンポーネント (インターセプタ) をコンテナから取得して返す{@link Expression}の実装です。
* <p>
* コンテナから取得したインターセプタのインスタンスにアノテーションの要素と名前の一致するプロパティがあれば、
* アノテーションの要素の値がインターセプタのプロパティにコピーされます。
* </p>
*
* @author koichik
*/
public class ExpressionImpl implements Expression {
/** アノテーション */
protected Annotation annotation;
/**
* インスタンスを構築します。
*
* @param annotation
* アノテーション
*/
public ExpressionImpl(final Annotation annotation) {
this.annotation = annotation;
}
@SuppressWarnings("unchecked")
public Object evaluate(final S2Container container, final Map context) {
final MethodInterceptor interceptor = MethodInterceptor.class
.cast(container
.getComponent(getInterceptorName(annotation)));
final BeanDesc beanDesc = BeanDescFactory.getBeanDesc(interceptor
.getClass());
for (final Method method : annotation.annotationType().getMethods()) {
if (method.isBridge() || method.isSynthetic()) {
continue;
}
final String propertyName = method.getName();
if ("pointcut".equals(propertyName)
|| !beanDesc.hasPropertyDesc(propertyName)) {
continue;
}
final PropertyDesc propertyDesc = beanDesc
.getPropertyDesc(propertyName);
if (!propertyDesc.isWritable()) {
continue;
}
propertyDesc.setValue(interceptor, MethodUtil.invoke(method,
annotation, null));
}
return interceptor;
}
}
}