优化代码,修复不准确的注释,完善测试用例

This commit is contained in:
huangchengxing 2022-07-13 22:34:13 +08:00
parent 17b48024ad
commit eae76eb275
14 changed files with 743 additions and 54 deletions

View File

@ -1,5 +1,6 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjectUtil;
@ -42,6 +43,7 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation
final AnnotationAttribute aliasAttribute = aliasAnnotation.getAttributes().get(link.attribute());
SyntheticAnnotationUtil.checkLinkedAttributeNotNull(originalAttribute, aliasAttribute, link);
SyntheticAnnotationUtil.checkAttributeType(originalAttribute, aliasAttribute);
checkCircularDependency(originalAttribute, aliasAttribute);
// aliasFor
if (RelationType.ALIAS_FOR.equals(link.type())) {
@ -81,4 +83,23 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation
.ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, originalAttribute)));
}
/**
* 检查两个属性是否互为别名
*/
private void checkCircularDependency(AnnotationAttribute original, AnnotationAttribute alias) {
Link annotation = SyntheticAnnotationUtil.getLink(alias, RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR);
if (ObjectUtil.isNull(annotation)) {
return;
}
final Class<?> aliasAnnotationType = SyntheticAnnotationUtil.getLinkedAnnotationType(annotation, alias.getAnnotationType());
if (ObjectUtil.notEqual(aliasAnnotationType, original.getAnnotationType())) {
return;
}
Assert.notEquals(
annotation.attribute(), original.getAttributeName(),
"circular reference between the alias attribute [{}] and the original attribute [{}]",
alias.getAttribute(), original.getAttribute()
);
}
}

View File

@ -1,5 +1,6 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.multi.RowKeyTable;
import cn.hutool.core.map.multi.Table;
import cn.hutool.core.util.ObjectUtil;
@ -12,6 +13,10 @@ import java.util.Comparator;
* 构建时需要传入比较器获取属性值时将根据比较器对合成注解进行排序
* 然后选择具有所需属性的排序最靠前的注解用于获取属性值
*
* <p>通过该处理器获取合成注解属性值时会出现隐式别名
* 即子注解和元注解中同时存在类型和名称皆相同的属性时元注解中属性总是会被该属性覆盖
* 并且该覆盖关系并不会通过{@link Alias}{@link Link}被传递到关联的属性中
*
* @author huangchengxing
*/
public class CacheableSynthesizedAnnotationAttributeProcessor implements SynthesizedAnnotationAttributeProcessor {
@ -25,9 +30,19 @@ public class CacheableSynthesizedAnnotationAttributeProcessor implements Synthes
* @param annotationComparator 注解比较器排序更靠前的注解将被优先用于获取值
*/
public CacheableSynthesizedAnnotationAttributeProcessor(Comparator<SynthesizedAnnotation> annotationComparator) {
Assert.notNull(annotationComparator, "annotationComparator must not null");
this.annotationComparator = annotationComparator;
}
/**
* 创建一个带缓存的注解值选择器
* 默认按{@link SynthesizedAnnotation#getVerticalDistance()}{@link SynthesizedAnnotation#getHorizontalDistance()}排序
* 越靠前的越优先被取值
*/
public CacheableSynthesizedAnnotationAttributeProcessor() {
this(SyntheticAnnotationUtil.getChildPriorityAnnotationCompare());
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAttributeValue(String attributeName, Class<T> attributeType, Collection<? extends SynthesizedAnnotation> synthesizedAnnotations) {

View File

@ -7,12 +7,16 @@ import java.util.Map;
import java.util.function.UnaryOperator;
/**
* 用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象
* <p>用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象<br>
* 当对多个合成注解排序时默认先按{@link #getVerticalDistance()}排序
* 再按{@link #getHorizontalDistance()}排序
* 该顺序应当保证当合成注解与其他注解存在层级关系时
* 离根对象最接近的注解被排在靠前的位置
*
* @author huangchengxing
* @see SyntheticAnnotation
*/
public interface SynthesizedAnnotation extends Annotation {
public interface SynthesizedAnnotation extends Annotation, Comparable<SynthesizedAnnotation> {
/**
* 获取所属的合成注解
@ -111,4 +115,15 @@ public interface SynthesizedAnnotation extends Annotation {
*/
Object getAttributeValue(String attributeName);
/**
* {@link #getVerticalDistance()}{@link #getHorizontalDistance()}排序
*
* @param o {@link SynthesizedAnnotation}对象
* @return 比较值
*/
@Override
default int compareTo(SynthesizedAnnotation o) {
return SyntheticAnnotationUtil.getChildPriorityAnnotationCompare().compare(this, o);
}
}

View File

@ -10,7 +10,18 @@ import java.util.Comparator;
* 当多个{@link SynthesizedAnnotationPostProcessor}需要一起执行时将按照{@link #order()}的返回值进行排序
* 该值更小的处理器将被优先执行
*
* <p>该接口存在多个实现类调用者应当保证在任何时候对一批后置处理器的调用顺序都符合
* <ul>
* <li>{@link AliasAttributePostProcessor}</li>
* <li>{@link MirrorLinkAttributePostProcessor}</li>
* <li>{@link AliasForLinkAttributePostProcessor}</li>
* <li>其他后置处理器</li>
* </ul>
*
* @author huangchengxing
* @see AliasAttributePostProcessor
* @see MirrorLinkAttributePostProcessor
* @see AliasForLinkAttributePostProcessor
*/
public interface SynthesizedAnnotationPostProcessor extends Comparable<SynthesizedAnnotationPostProcessor> {

View File

@ -70,6 +70,13 @@ public interface SyntheticAnnotation extends Annotation, AnnotatedElement {
*/
SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType);
/**
* 获取全部的合成注解
*
* @return 合成注解
*/
Collection<SynthesizedAnnotation> getAllSyntheticAnnotations();
/**
* 获取当前的注解类型
*

View File

@ -6,6 +6,7 @@ import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.util.Comparator;
/**
* {@link SyntheticAnnotation}相关工具用于内部使用
@ -36,11 +37,23 @@ class SyntheticAnnotationUtil {
*/
static SynthesizedAnnotation getLinkedAnnotation(
Link annotation, SyntheticAnnotation syntheticAnnotation, Class<? extends Annotation> defaultType) {
final Class<? extends Annotation> targetAnnotationType = ObjectUtil.equals(annotation.annotation(), Annotation.class) ?
defaultType : annotation.annotation();
final Class<?> targetAnnotationType = getLinkedAnnotationType(annotation, defaultType);
return syntheticAnnotation.getSynthesizedAnnotation(targetAnnotationType);
}
/**
* {@link Link#annotation()}获取的类型{@link Annotation#getClass()}则返回{@code defaultType}
* 否则返回{@link Link#annotation()}指定的类型
*
* @param annotation {@link Link}注解
* @param defaultType 默认注解类型
* @return 注解类型
*/
static Class<?> getLinkedAnnotationType(Link annotation, Class<?> defaultType) {
return ObjectUtil.equals(annotation.annotation(), Annotation.class) ?
defaultType : annotation.annotation();
}
/**
* 校验两个注解属性的返回值类型是否一致
*
@ -64,8 +77,19 @@ class SyntheticAnnotationUtil {
*/
static void checkLinkedAttributeNotNull(AnnotationAttribute original, AnnotationAttribute linkedAttribute, Link annotation) {
Assert.notNull(linkedAttribute, "cannot find linked attribute [{}] of original [{}] in [{}]",
original.getAttribute(), annotation.attribute(), annotation.annotation()
original.getAttribute(), annotation.attribute(),
getLinkedAnnotationType(annotation, original.getAnnotationType())
);
}
/**
* 获取按{@link SynthesizedAnnotation#getVerticalDistance()}{@link SynthesizedAnnotation#getHorizontalDistance()}排序的比较器
*
* @return 比较值
*/
static Comparator<SynthesizedAnnotation> getChildPriorityAnnotationCompare() {
return Comparator.comparing(SynthesizedAnnotation::getVerticalDistance)
.thenComparing(SynthesizedAnnotation::getHorizontalDistance);
}
}

View File

@ -15,29 +15,36 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 表示一个根注解与根注解上的多层元注解合成的注解
* {@link SyntheticAnnotation}的基本实现表示一个根注解与根注解上的多层元注解合成的注解
*
* <p>假设现有注解AA上存在元注解BB上存在元注解C则对A解析得到的合成注解X则CBA都是X的元注解X为根注解<br>
* 通过{@link #isAnnotationPresent(Class)}可确定指定类型是注解是否是该合成注解的元注解即是否为当前实例的父类
* 若指定注解是当前实例的元注解则通过{@link #getAnnotation(Class)}可获得动态代理生成的对应的注解实例<br>
* 需要注意的是由于认为合并注解X以最初的根注解A作为元注解因此{@link #getAnnotations()}{@link #getDeclaredAnnotations()}
* 都将只能获得A
* <p>假设现有注解AA上存在元注解BB上存在元注解C则对注解A进行解析
* 将得到包含根注解A以及其元注解BC在内的合成元注解{@link SyntheticMetaAnnotation}
* {@link AnnotatedElement}的角度来说得到的合成注解是一个同时承载有ABC三个注解对象的被注解元素
* 因此通过调用{@link AnnotatedElement}的相关方法将返回对应符合语义的注解对象
*
* <p>若认为该合成注解X在第0层则根注解A在第1层B在第2层......以此类推,
* 则相同或不同的层级中可能会出现类型相同的注解对象此时将通过{@link SynthesizedAnnotationSelector}选择出最合适的注解对象
* 该注解对象将在合成注解中作为唯一有效的元注解用于进行相关操作<br>
* 默认情况下选择{@link SynthesizedAnnotationSelector#NEAREST_AND_OLDEST_PRIORITY}选择器实例
* 即层级越低的注解离根注解距离近则该注解优先级越高即遵循就近原则
* <p>在扫描指定根注解及其元注解时若在不同的层级出现了类型相同的注解实例
* 将会根据实例化时指定的{@link SynthesizedAnnotationSelector}选择最优的注解
* 完成对根注解及其元注解的扫描后合成注解中每种类型的注解对象都将有且仅有一个<br>
* 默认情况下使用{@link SynthesizedAnnotationSelector#NEAREST_AND_OLDEST_PRIORITY}作为选择器
* 此时若出现扫描时得到了多个同类型的注解对象有且仅有最接近根注解的注解对象会被作为有效注解
*
* <p>合成注解中获取到的注解中可能会具有一些同名且同类型的属性
* 此时将根据{@link SynthesizedAnnotationAttributeProcessor}决定如何从这些注解的相同属性中获取属性值<br>
* 默认情况下将选择{@link CacheableSynthesizedAnnotationAttributeProcessor}用于获取属性
* 该处理器将选择距离根注解最近的注解中的属性用于获取属性值{@link #getAnnotation(Class)}获得的代理类实例的属性值遵循该规则<br>
* 举个例子若CBA同时存在属性y则将X视为CB或者A时获得的y属性的值都与最底层元注解A的值保持一致
* 若两相同注解处于同一层级则按照从其上一级子注解{@link AnnotatedElement#getAnnotations()}的调用顺序排序
* <p>当扫描的注解对象经过{@link SynthesizedAnnotationSelector}处理后
* 将会被转为{@link MetaAnnotation}并使用在实例化时指定的{@link AliasAttributePostProcessor}
* 进行后置处理<br>
* 默认情况下将注册以下后置处理器以对{@link Alias}{@link Link}和其扩展注解提供支持
* <ul>
* <li>{@link AliasAttributePostProcessor}</li>
* <li>{@link MirrorLinkAttributePostProcessor}</li>
* <li>{@link AliasForLinkAttributePostProcessor}</li>
* </ul>
* 若用户需要自行扩展则需要保证上述三个处理器被正确注入当前实例
*
* <p>别名在合成注解中仍然有效若注解X中任意属性上存在{@link Alias}注解{@link Alias#value()}指定的属性值将会覆盖注解属性的本身的值<br>
* {@link Alias}注解仅能指定注解X中存在的属性作为别名不允许指定元注解或子类注解的属性
* <p>{@link SyntheticMetaAnnotation}支持通过{@link #getAttribute(String, Class)}
* 或通过{@link #syntheticAnnotation(Class)}获得注解代理对象后获取指定类型的注解属性值
* 返回的属性值将根据合成注解中对应原始注解属性上的{@link Alias}{@link Link}注解而有所变化
* 通过当前实例获取属性值时将经过{@link SynthesizedAnnotationAttributeProcessor}的处理<br>
* 默认情况下实例将会注册{@link CacheableSynthesizedAnnotationAttributeProcessor}
* 该处理器将令元注解中与子注解类型与名称皆一致的属性被子注解的属性覆盖并且缓存最终获取到的属性值
*
* @author huangchengxing
* @see AnnotationUtil
@ -80,10 +87,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
public SyntheticMetaAnnotation(Annotation source) {
this(
source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY,
new CacheableSynthesizedAnnotationAttributeProcessor(
Comparator.comparing(SynthesizedAnnotation::getVerticalDistance)
.thenComparing(SynthesizedAnnotation::getHorizontalDistance)
),
new CacheableSynthesizedAnnotationAttributeProcessor(),
Arrays.asList(
new AliasAttributePostProcessor(),
new MirrorLinkAttributePostProcessor(),
@ -118,7 +122,6 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
this.metaAnnotationMap = new LinkedHashMap<>();
// 初始化元注解信息并进行后置处理
// TODO 缓存元注解信息避免重复解析
loadMetaAnnotations();
annotationPostProcessors.forEach(processor ->
metaAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this))
@ -185,13 +188,13 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
}
/**
* 获取根注解类型
* 获取全部的已合成注解
*
* @return 注解类型
* @return 合成注解
*/
@Override
public Class<? extends Annotation> annotationType() {
return this.getClass();
public Collection<SynthesizedAnnotation> getAllSyntheticAnnotations() {
return metaAnnotationMap.values();
}
/**
@ -208,7 +211,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
}
/**
* 获取合成的注解
* 获取合成注解中包含指定注解
*
* @param annotationType 注解类型
* @param <T> 注解类型
@ -235,13 +238,15 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
}
/**
* 获取全部注解
* 获取合成注解中包含的全部注解
*
* @return 注解对象
*/
@Override
public Annotation[] getAnnotations() {
return getMetaAnnotationMap().values().toArray(new MetaAnnotation[0]);
return metaAnnotationMap.values().stream()
.map(SynthesizedAnnotation::getAnnotation)
.toArray(Annotation[]::new);
}
/**

View File

@ -0,0 +1,99 @@
package cn.hutool.core.annotation;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
public class AbstractAnnotationAttributeWrapperTest {
@Test
public void workTest() {
Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest1.class);
Method valueMethod = ReflectUtil.getMethod(AnnotationForTest1.class, "value1");
CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
Method nameMethod = ReflectUtil.getMethod(AnnotationForTest1.class, "name1");
CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
TestAnnotationAttributeWrapper nameWrapper = new TestAnnotationAttributeWrapper(nameAttribute, valueAttribute);
// 注解属性
Assert.assertEquals(annotation, nameWrapper.getAnnotation());
Assert.assertEquals(annotation.annotationType(), nameWrapper.getAnnotationType());
Assert.assertEquals(nameAttribute, nameWrapper.getOriginal());
Assert.assertEquals(valueAttribute, nameWrapper.getLinked());
// 方法属性
Assert.assertEquals(nameMethod.getName(), nameWrapper.getAttributeName());
Assert.assertEquals(nameMethod.getReturnType(), nameWrapper.getAttributeType());
Assert.assertTrue(nameWrapper.isWrapped());
Assert.assertEquals("value1", nameWrapper.getValue());
}
@Test
public void multiWrapperTest() {
// 包装第一层 name1 + value1
Annotation annotation1 = ClassForTest1.class.getAnnotation(AnnotationForTest1.class);
Method value1Method = ReflectUtil.getMethod(AnnotationForTest1.class, "value1");
CacheableAnnotationAttribute value1Attribute = new CacheableAnnotationAttribute(annotation1, value1Method);
Method name1Method = ReflectUtil.getMethod(AnnotationForTest1.class, "name1");
CacheableAnnotationAttribute name1Attribute = new CacheableAnnotationAttribute(annotation1, name1Method);
TestAnnotationAttributeWrapper wrapper1 = new TestAnnotationAttributeWrapper(name1Attribute, value1Attribute);
Assert.assertEquals(name1Attribute, wrapper1.getNonWrappedOriginal());
Assert.assertEquals(CollUtil.newArrayList(name1Attribute, value1Attribute), wrapper1.getAllLinkedNonWrappedAttributes());
// 包装第二层( name1 + value1 ) + value2
Annotation annotation2 = ClassForTest1.class.getAnnotation(AnnotationForTest2.class);
Method value2Method = ReflectUtil.getMethod(AnnotationForTest2.class, "value2");
CacheableAnnotationAttribute value2Attribute = new CacheableAnnotationAttribute(annotation2, value2Method);
TestAnnotationAttributeWrapper wrapper2 = new TestAnnotationAttributeWrapper(wrapper1, value2Attribute);
Assert.assertEquals(name1Attribute, wrapper2.getNonWrappedOriginal());
Assert.assertEquals(CollUtil.newArrayList(name1Attribute, value1Attribute, value2Attribute), wrapper2.getAllLinkedNonWrappedAttributes());
// 包装第二层value3 + ( ( name1 + value1 ) + value2 )
Annotation annotation3 = ClassForTest1.class.getAnnotation(AnnotationForTest3.class);
Method value3Method = ReflectUtil.getMethod(AnnotationForTest3.class, "value3");
CacheableAnnotationAttribute value3Attribute = new CacheableAnnotationAttribute(annotation3, value3Method);
TestAnnotationAttributeWrapper wrapper3 = new TestAnnotationAttributeWrapper(value3Attribute, wrapper2);
Assert.assertEquals(value3Attribute, wrapper3.getNonWrappedOriginal());
Assert.assertEquals(CollUtil.newArrayList(value3Attribute, name1Attribute, value1Attribute, value2Attribute), wrapper3.getAllLinkedNonWrappedAttributes());
}
static class TestAnnotationAttributeWrapper extends AbstractAnnotationAttributeWrapper {
protected TestAnnotationAttributeWrapper(AnnotationAttribute original, AnnotationAttribute linked) {
super(original, linked);
}
@Override
public Object getValue() {
return linked.getValue();
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest1 {
String value1() default "";
String name1() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest2 {
String value2() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest3 {
String value3() default "";
}
@AnnotationForTest1(name1 = "name1", value1 = "value1")
@AnnotationForTest2(value2 = "value2")
@AnnotationForTest3(value3 = "value3")
static class ClassForTest1 {}
}

View File

@ -0,0 +1,76 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
public class AliasedAnnotationAttributeTest {
@Test
public void baseInfoTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final AliasedAnnotationAttribute annotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 注解属性
Assert.assertEquals(annotation, annotationAttribute.getAnnotation());
Assert.assertEquals(annotation.annotationType(), annotationAttribute.getAnnotationType());
// 方法属性
Assert.assertEquals(valueMethod.getName(), annotationAttribute.getAttributeName());
Assert.assertEquals(nameMethod.getReturnType(), annotationAttribute.getAttributeType());
}
@Test
public void workWhenValueDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final AliasedAnnotationAttribute annotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 值处理
Assert.assertEquals("name", annotationAttribute.getValue());
Assert.assertFalse(annotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(annotationAttribute.isWrapped());
}
@Test
public void workWhenValueNonDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final AliasedAnnotationAttribute annotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 值处理
Assert.assertEquals("value", annotationAttribute.getValue());
Assert.assertFalse(annotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(annotationAttribute.isWrapped());
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
String value() default "";
String name() default "";
}
@AnnotationForTest(name = "name", value = "value")
static class ClassForTest1 {}
@AnnotationForTest(value = "value")
static class ClassForTest2 {}
}

View File

@ -0,0 +1,61 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
public class CacheableAnnotationAttributeTest {
@Test
public void baseInfoTest() {
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method attribute = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute annotationAttribute = new CacheableAnnotationAttribute(annotation, attribute);
// 注解属性
Assert.assertEquals(annotation, annotationAttribute.getAnnotation());
Assert.assertEquals(annotation.annotationType(), annotationAttribute.getAnnotationType());
// 方法属性
Assert.assertEquals(attribute.getName(), annotationAttribute.getAttributeName());
Assert.assertEquals(attribute.getReturnType(), annotationAttribute.getAttributeType());
}
@Test
public void workWhenValueDefaultTest() {
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method attribute = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute annotationAttribute = new CacheableAnnotationAttribute(annotation, attribute);
// 值处理
Assert.assertEquals("", annotationAttribute.getValue());
Assert.assertTrue(annotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertFalse(annotationAttribute.isWrapped());
}
@Test
public void workWhenValueNonDefaultTest() {
final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class);
final Method attribute = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute annotationAttribute = new CacheableAnnotationAttribute(annotation, attribute);
// 值处理
Assert.assertEquals("test", annotationAttribute.getValue());
Assert.assertFalse(annotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertFalse(annotationAttribute.isWrapped());
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
String value() default "";
}
@AnnotationForTest("")
static class ClassForTest1 {}
@AnnotationForTest("test")
static class ClassForTest2 {}
}

View File

@ -0,0 +1,76 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
public class ForceAliasedAnnotationAttributeTest {
@Test
public void baseInfoTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final ForceAliasedAnnotationAttribute valueAnnotationAttribute = new ForceAliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 注解属性
Assert.assertEquals(annotation, valueAnnotationAttribute.getAnnotation());
Assert.assertEquals(annotation.annotationType(), valueAnnotationAttribute.getAnnotationType());
// 方法属性
Assert.assertEquals(valueMethod.getName(), valueAnnotationAttribute.getAttributeName());
Assert.assertEquals(valueMethod.getReturnType(), valueAnnotationAttribute.getAttributeType());
}
@Test
public void workWhenValueDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final AliasedAnnotationAttribute valueAnnotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 值处理
Assert.assertEquals("name", valueAnnotationAttribute.getValue());
Assert.assertFalse(valueAnnotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(valueAnnotationAttribute.isWrapped());
}
@Test
public void workWhenValueNonDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final ForceAliasedAnnotationAttribute valueAnnotationAttribute = new ForceAliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 值处理
Assert.assertEquals("", valueAnnotationAttribute.getValue());
Assert.assertTrue(valueAnnotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(valueAnnotationAttribute.isWrapped());
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
String value() default "";
String name() default "";
}
@AnnotationForTest(name = "name", value = "value")
static class ClassForTest1 {}
@AnnotationForTest(value = "value")
static class ClassForTest2 {}
}

View File

@ -0,0 +1,76 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
public class MirroredAnnotationAttributeTest {
@Test
public void baseInfoTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final MirroredAnnotationAttribute nameAnnotationAttribute = new MirroredAnnotationAttribute(nameAttribute, valueAttribute);
// 注解属性
Assert.assertEquals(annotation, nameAnnotationAttribute.getAnnotation());
Assert.assertEquals(annotation.annotationType(), nameAnnotationAttribute.getAnnotationType());
// 方法属性
Assert.assertEquals(nameMethod.getName(), nameAnnotationAttribute.getAttributeName());
Assert.assertEquals(nameMethod.getReturnType(), nameAnnotationAttribute.getAttributeType());
}
@Test
public void workWhenValueDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final MirroredAnnotationAttribute nameAnnotationAttribute = new MirroredAnnotationAttribute(nameAttribute, valueAttribute);
// 值处理
Assert.assertEquals("", nameAnnotationAttribute.getValue());
Assert.assertTrue(nameAnnotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(nameAnnotationAttribute.isWrapped());
}
@Test
public void workWhenValueNonDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final MirroredAnnotationAttribute nameAnnotationAttribute = new MirroredAnnotationAttribute(nameAttribute, valueAttribute);
// 值处理
Assert.assertEquals("name", nameAnnotationAttribute.getValue());
Assert.assertFalse(nameAnnotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(nameAnnotationAttribute.isWrapped());
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
String value() default "";
String name() default "";
}
@AnnotationForTest(value = "name")
static class ClassForTest1 {}
@AnnotationForTest
static class ClassForTest2 {}
}

View File

@ -0,0 +1,146 @@
package cn.hutool.core.annotation;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.function.UnaryOperator;
public class SynthesizedAnnotationSelectorTest {
@Test
public void nearestAndOldestPriorityTest() {
final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY;
TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0);
TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 1);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(1, 0);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
}
@Test
public void nearestAndNewestPriorityTest() {
final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.NEAREST_AND_NEWEST_PRIORITY;
TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0);
TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 1);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 0);
annotation2 = new TestSynthesizedAnnotation(1, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
}
@Test
public void farthestAndOldestPriorityTest() {
final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.FARTHEST_AND_OLDEST_PRIORITY;
TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0);
TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 1);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 0);
annotation2 = new TestSynthesizedAnnotation(1, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
}
@Test
public void farthestAndNewestPriorityTest() {
final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.FARTHEST_AND_NEWEST_PRIORITY;
TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0);
TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 1);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(1, 0);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
}
static class TestSynthesizedAnnotation implements SynthesizedAnnotation {
private final int verticalDistance;
private final int horizontalDistance;
public TestSynthesizedAnnotation(int verticalDistance, int horizontalDistance) {
this.verticalDistance = verticalDistance;
this.horizontalDistance = horizontalDistance;
}
@Override
public SyntheticAnnotation getOwner() {
return null;
}
@Override
public Object getRoot() {
return null;
}
@Override
public Annotation getAnnotation() {
return null;
}
@Override
public int getVerticalDistance() {
return this.verticalDistance;
}
@Override
public int getHorizontalDistance() {
return this.horizontalDistance;
}
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return false;
}
@Override
public Map<String, AnnotationAttribute> getAttributes() {
return null;
}
@Override
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
}
@Override
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
}
@Override
public Object getAttributeValue(String attributeName) {
return null;
}
@Override
public Class<? extends Annotation> annotationType() {
return null;
}
}
}

View File

@ -4,11 +4,9 @@ import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 合成注解{@link SyntheticMetaAnnotation}的测试用例
@ -18,8 +16,38 @@ import java.lang.reflect.Method;
public class SyntheticMetaAnnotationTest {
@Test
public void testSynthesisAnnotation() {
ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
public void baseSynthesisAnnotationWorkTest() {
// AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation
// -> @GrandParentAnnotation
final GrandParentAnnotation grandParentAnnotation = ChildAnnotation.class.getAnnotation(GrandParentAnnotation.class);
final ParentAnnotation parentAnnotation = ChildAnnotation.class.getAnnotation(ParentAnnotation.class);
final ChildAnnotation childAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
final SyntheticMetaAnnotation syntheticMetaAnnotation = new SyntheticMetaAnnotation(childAnnotation);
// Annotation & AnnotatedElement
Assert.assertEquals(SyntheticMetaAnnotation.class, syntheticMetaAnnotation.annotationType());
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class));
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ParentAnnotation.class));
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class));
Assert.assertEquals(grandParentAnnotation, syntheticMetaAnnotation.getAnnotation(GrandParentAnnotation.class));
Assert.assertEquals(parentAnnotation, syntheticMetaAnnotation.getAnnotation(ParentAnnotation.class));
Assert.assertEquals(childAnnotation, syntheticMetaAnnotation.getAnnotation(ChildAnnotation.class));
Assert.assertEquals(
Arrays.asList(childAnnotation, grandParentAnnotation, parentAnnotation),
Arrays.asList(syntheticMetaAnnotation.getAnnotations())
);
Assert.assertArrayEquals(new Annotation[]{ childAnnotation }, syntheticMetaAnnotation.getDeclaredAnnotations());
// 扩展方法
Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class));
Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(ParentAnnotation.class));
Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(ChildAnnotation.class));
Assert.assertEquals(3, syntheticMetaAnnotation.getAllSyntheticAnnotations().size());
}
@Test
public void synthesisAnnotationAttributeTest() {
final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
SyntheticMetaAnnotation syntheticMetaAnnotation = new SyntheticMetaAnnotation(rootAnnotation);
Assert.assertEquals(syntheticMetaAnnotation.getSource(), rootAnnotation);
Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SyntheticMetaAnnotation.class);
@ -32,7 +60,7 @@ public class SyntheticMetaAnnotationTest {
Assert.assertEquals("Child's Parent!", syntheticMetaAnnotation.getAttribute("parentValue", String.class));
Assert.assertEquals("Child's GrandParent!", syntheticMetaAnnotation.getAttribute("grandParentValue", String.class));
ChildAnnotation childAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ChildAnnotation.class);
final ChildAnnotation childAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ChildAnnotation.class);
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class));
Assert.assertNotNull(childAnnotation);
Assert.assertEquals("Child!", childAnnotation.childValue());
@ -40,14 +68,14 @@ public class SyntheticMetaAnnotationTest {
Assert.assertEquals(childAnnotation.grandParentType(), Integer.class);
Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(childAnnotation));
ParentAnnotation parentAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ParentAnnotation.class);
final ParentAnnotation parentAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ParentAnnotation.class);
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ParentAnnotation.class));
Assert.assertNotNull(parentAnnotation);
Assert.assertEquals("Child's Parent!", parentAnnotation.parentValue());
Assert.assertEquals("java.lang.Void", parentAnnotation.grandParentType());
Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(parentAnnotation));
GrandParentAnnotation grandParentAnnotation = syntheticMetaAnnotation.syntheticAnnotation(GrandParentAnnotation.class);
final GrandParentAnnotation grandParentAnnotation = syntheticMetaAnnotation.syntheticAnnotation(GrandParentAnnotation.class);
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class));
Assert.assertNotNull(grandParentAnnotation);
Assert.assertEquals("Child's GrandParent!", grandParentAnnotation.grandParentValue());
@ -57,9 +85,9 @@ public class SyntheticMetaAnnotationTest {
@Test
public void linkTest() {
Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value");
SyntheticAnnotation syntheticAnnotation = new SyntheticMetaAnnotation(method.getAnnotation(AliasFor.class));
Link link = syntheticAnnotation.syntheticAnnotation(Link.class);
final Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value");
final SyntheticAnnotation syntheticAnnotation = new SyntheticMetaAnnotation(method.getAnnotation(AliasFor.class));
final Link link = syntheticAnnotation.syntheticAnnotation(Link.class);
Assert.assertEquals(AnnotationForLinkTest.class, link.annotation());
Assert.assertEquals("name", link.attribute());
}
@ -82,7 +110,7 @@ public class SyntheticMetaAnnotationTest {
synthetic = new SyntheticMetaAnnotation(annotation);
syntheticAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorTest.class);
AnnotationForMirrorTest finalSyntheticAnnotation = syntheticAnnotation;
Assert.assertThrows(IllegalArgumentException.class, () -> finalSyntheticAnnotation.name());
Assert.assertThrows(IllegalArgumentException.class, finalSyntheticAnnotation::name);
}
@Test
@ -132,18 +160,30 @@ public class SyntheticMetaAnnotationTest {
@Test
public void multiAliasForTest() {
AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class);
SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation);
final AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class);
final SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation);
MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest1.class);
final MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest1.class);
Assert.assertEquals("test", metaAnnotation1.name());
Assert.assertEquals("test", metaAnnotation1.value1());
MetaAnnotationForMultiAliasForTest2 metaAnnotation2 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest2.class);
final MetaAnnotationForMultiAliasForTest2 metaAnnotation2 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest2.class);
Assert.assertEquals("test", metaAnnotation2.value2());
AnnotationForMultiAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForMultiAliasForTest.class);
final AnnotationForMultiAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForMultiAliasForTest.class);
Assert.assertEquals("test", childAnnotation.value3());
}
@Test
public void implicitAliasTest() {
final AnnotationForImplicitAliasTest annotation = ClassForImplicitAliasTest.class.getAnnotation(AnnotationForImplicitAliasTest.class);
final SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation);
final MetaAnnotationForImplicitAliasTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForImplicitAliasTest.class);
Assert.assertEquals("Meta", metaAnnotation.name());
Assert.assertEquals("Foo", metaAnnotation.value());
final AnnotationForImplicitAliasTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForImplicitAliasTest.class);
Assert.assertEquals("Foo", childAnnotation.value());
}
// 注解结构如下
// AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation
// -> @GrandParentAnnotation
@ -284,4 +324,21 @@ public class SyntheticMetaAnnotationTest {
@AnnotationForMultiAliasForTest(value3 = "test")
static class ClassForMultiAliasForTest{}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForImplicitAliasTest {
@MirrorFor(attribute = "value")
String name() default "";
@MirrorFor(attribute = "name")
String value() default "";
}
@MetaAnnotationForImplicitAliasTest("Meta")
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForImplicitAliasTest {
String value() default "";
}
@AnnotationForImplicitAliasTest("Foo")
static class ClassForImplicitAliasTest {}
}