/* * 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.extension.dxo.annotation.impl; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Map; import org.seasar.extension.dxo.annotation.AnnotationReader; import org.seasar.extension.dxo.annotation.ConversionRule; import org.seasar.extension.dxo.annotation.DatePattern; import org.seasar.extension.dxo.annotation.DestPrefix; import org.seasar.extension.dxo.annotation.DxoConverter; import org.seasar.extension.dxo.annotation.ExcludeNull; import org.seasar.extension.dxo.annotation.ExcludeWhitespace; import org.seasar.extension.dxo.annotation.SourcePrefix; import org.seasar.extension.dxo.annotation.TimePattern; import org.seasar.extension.dxo.annotation.TimestampPattern; import org.seasar.extension.dxo.converter.Converter; import org.seasar.framework.beans.BeanDesc; import org.seasar.framework.beans.PropertyDesc; import org.seasar.framework.beans.factory.BeanDescFactory; import org.seasar.framework.beans.util.BeanUtil; import org.seasar.framework.container.S2Container; import org.seasar.framework.util.tiger.AnnotationUtil; import org.seasar.framework.util.tiger.CollectionsUtil; /** * DxoインタフェースまたはクラスやそのメソッドからTigerアノテーションを読み取るクラスです。 * * @author koichik */ public class TigerAnnotationReader implements AnnotationReader { /** S2コンテナ */ protected S2Container container; /** 後続の{@link AnnotationReader} */ protected AnnotationReader next; /** コンバータのキャッシュ */ protected Map<Class<?>, Map<String, Converter>> convertersCache = CollectionsUtil .newConcurrentHashMap(); /** * インスタンスを構築します。 * * @param container * S2コンテナ */ public TigerAnnotationReader(final S2Container container) { this(container, null); } /** * インスタンスを構築します。 * * @param container * S2コンテナ * @param next * 後続の{@link AnnotationReader} */ public TigerAnnotationReader(final S2Container container, final AnnotationReader next) { this.container = container.getRoot(); this.next = next; } @SuppressWarnings("unchecked") public String getDatePattern(final Class dxoClass, final Method method) { final DatePattern datePattern = getAnnotation(dxoClass, method, DatePattern.class); if (datePattern != null) { return datePattern.value(); } if (next != null) { return next.getDatePattern(dxoClass, method); } return null; } @SuppressWarnings("unchecked") public String getTimePattern(final Class dxoClass, final Method method) { final TimePattern timePattern = getAnnotation(dxoClass, method, TimePattern.class); if (timePattern != null) { return timePattern.value(); } if (next != null) { return next.getTimePattern(dxoClass, method); } return null; } @SuppressWarnings("unchecked") public String getTimestampPattern(final Class dxoClass, final Method method) { final TimestampPattern timestampPattern = getAnnotation(dxoClass, method, TimestampPattern.class); if (timestampPattern != null) { return timestampPattern.value(); } if (next != null) { return next.getTimestampPattern(dxoClass, method); } return null; } @SuppressWarnings("unchecked") public String getConversionRule(final Class dxoClass, final Method method) { final ConversionRule mapConversion = method .getAnnotation(ConversionRule.class); if (mapConversion != null) { return mapConversion.value(); } if (next != null) { return next.getConversionRule(dxoClass, method); } return null; } @SuppressWarnings("unchecked") public boolean isExcludeNull(final Class dxoClass, final Method method) { final ExcludeNull excludeNull = getAnnotation(dxoClass, method, ExcludeNull.class); if (excludeNull != null) { return true; } if (next != null) { return next.isExcludeNull(dxoClass, method); } return false; } @SuppressWarnings("unchecked") public boolean isExcludeWhitespace(final Class dxoClass, final Method method) { final ExcludeWhitespace excludeWhitespace = getAnnotation(dxoClass, method, ExcludeWhitespace.class); if (excludeWhitespace != null) { return true; } if (next != null) { return next.isExcludeWhitespace(dxoClass, method); } return false; } @SuppressWarnings("unchecked") public String getSourcePrefix(Class dxoClass, Method method) { final SourcePrefix sourcePrefix = getAnnotation(dxoClass, method, SourcePrefix.class); if (sourcePrefix != null) { return sourcePrefix.value(); } if (next != null) { return next.getSourcePrefix(dxoClass, method); } return null; } @SuppressWarnings("unchecked") public String getDestPrefix(Class dxoClass, Method method) { final DestPrefix destPrefix = getAnnotation(dxoClass, method, DestPrefix.class); if (destPrefix != null) { return destPrefix.value(); } if (next != null) { return next.getDestPrefix(dxoClass, method); } return null; } @SuppressWarnings("unchecked") public Map getConverters(final Class destClass) { final Map<String, Converter> converters = convertersCache .get(destClass); if (converters != null) { return converters; } return createConverters(destClass); } /** * 指定アノテーションを取得して返します。 * <p> * メソッドに指定のアノテーションが付与されていればそれを返します。 * メソッドに指定のアノテーションが付与されていなければ、クラスに付与されているアノテーションを返します。 * メソッドにもクラスに指定アノテーションが付与されていなければ<code>null</code>を返します。 * </p> * * @param <T> * アノテーションの型 * @param dxoClass * Dxoクラス * @param method * Dxoメソッド * @param annotationType * アノテーションの型 * @return メソッドまたはクラスに付けられたアノテーション */ protected <T extends Annotation> T getAnnotation(final Class<?> dxoClass, final Method method, final Class<T> annotationType) { final T annotation = method.getAnnotation(annotationType); if (annotation != null) { return annotation; } return dxoClass.getAnnotation(annotationType); } /** * クラスに指定されたコンバータの{@link Map}を返します。 * <p> * 指定されたクラスのコンバータの指定されたプロパティについて、プロパティ名をキー、コンバータを値とする{@link Map}を作成します。 * </p> * * @param destClass * 変換先のクラス * @return クラスに指定されたコンバータの{@link Map} */ protected Map<String, Converter> createConverters(final Class<?> destClass) { final Map<String, Converter> converters = CollectionsUtil.newHashMap(); final BeanDesc beanDesc = BeanDescFactory.getBeanDesc(destClass); for (int i = 0; i < beanDesc.getPropertyDescSize(); ++i) { final PropertyDesc propertyDesc = beanDesc.getPropertyDesc(i); if (!propertyDesc.isWritable()) { continue; } final Annotation[] annotations = (propertyDesc.hasWriteMethod()) ? propertyDesc .getWriteMethod().getDeclaredAnnotations() : propertyDesc.getField().getDeclaredAnnotations(); final Converter converter = detectConverter(annotations); if (converter != null) { converters.put(propertyDesc.getPropertyName(), converter); } } convertersCache.put(destClass, converters); return converters; } /** * アノテーションの配列に{@link DxoConverter}メタアノテーションで注釈されたアノテーションが含まれていれば、 * そのアノテーションに従い{@link Converter}を作成して返します。 * <p> * アノテーションの配列に{@link DxoConverter}メタアノテーションで注釈されたアノテーションが含まれていない場合は * <code>null</code>を返します。 * </p> * * @param annotations * プロパティのsetterメソッドまたはpublicフィールドに指定されたアノテーションの配列 * @return {@link DxoConverter}メタアノテーションで注釈されたアノテーションに従い作成された{@link Converter} */ protected Converter detectConverter(final Annotation[] annotations) { for (final Annotation annotation : annotations) { final Class<? extends Annotation> annotationType = annotation .annotationType(); final Annotation metaAnnotation = annotationType .getAnnotation(DxoConverter.class); if (metaAnnotation == null) { continue; } final DxoConverter dxoConverterAnnotation = DxoConverter.class .cast(metaAnnotation); final String converterName = dxoConverterAnnotation.value(); final Converter converter = Converter.class.cast(container .getComponent(converterName)); final Map<?, ?> props = AnnotationUtil.getProperties(annotation); BeanUtil.copyProperties(props, converter); return converter; } return null; } }