!702 合成注解相关功能重构,添加支持通过@Link及其子注解,实现注解中属性的互为镜像、可选覆盖以及强制覆盖三种模式的别名机制的新特性

Merge pull request !702 from Createsequence/feat-link-annotation-clean
This commit is contained in:
Looly
2022-07-17 13:59:18 +00:00
committed by Gitee
51 changed files with 4296 additions and 769 deletions

View File

@@ -0,0 +1,106 @@
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 AbstractWrappedAnnotationAttributeTest {
@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);
AbstractWrappedAnnotationAttribute nameWrapper = new TestWrappedAnnotationAttribute(nameAttribute, valueAttribute);
Assert.assertEquals(nameWrapper.getAnnotation(), annotation);
// 注解属性
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);
AbstractWrappedAnnotationAttribute wrapper1 = new TestWrappedAnnotationAttribute(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);
AbstractWrappedAnnotationAttribute wrapper2 = new TestWrappedAnnotationAttribute(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);
AbstractWrappedAnnotationAttribute wrapper3 = new TestWrappedAnnotationAttribute(value3Attribute, wrapper2);
Assert.assertEquals(value3Attribute, wrapper3.getNonWrappedOriginal());
Assert.assertEquals(CollUtil.newArrayList(value3Attribute, name1Attribute, value1Attribute, value2Attribute), wrapper3.getAllLinkedNonWrappedAttributes());
}
static class TestWrappedAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
protected TestWrappedAnnotationAttribute(AnnotationAttribute original, AnnotationAttribute linked) {
super(original, linked);
}
@Override
public Object getValue() {
return linked.getValue();
}
@Override
public boolean isValueEquivalentToDefaultValue() {
return getOriginal().isValueEquivalentToDefaultValue() && getLinked().isValueEquivalentToDefaultValue();
}
}
@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,195 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.UnaryOperator;
public class AliasAnnotationPostProcessorTest {
@Test
public void processTest() {
AliasAnnotationPostProcessor processor = new AliasAnnotationPostProcessor();
Map<Class<?>, SynthesizedAnnotation> annotationMap = new HashMap<>();
SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap);
AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class);
SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation);
annotationMap.put(annotation.annotationType(), synthesizedAnnotation);
processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator);
AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value"), valueAttribute.getAttribute());
Assert.assertTrue(valueAttribute.isWrapped());
Assert.assertEquals(ForceAliasedAnnotationAttribute.class, valueAttribute.getClass());
AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name"), nameAttribute.getAttribute());
Assert.assertFalse(nameAttribute.isWrapped());
Assert.assertEquals(CacheableAnnotationAttribute.class, nameAttribute.getClass());
Assert.assertEquals(nameAttribute, ((WrappedAnnotationAttribute)valueAttribute).getLinked());
}
@AnnotationForTest
static class ClassForTest {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
@Alias("name")
String value() default "";
String name() default "";
}
static class TestSynthesizedAggregateAnnotation implements SynthesizedAggregateAnnotation {
private final Map<Class<?>, SynthesizedAnnotation> annotationMap;
public TestSynthesizedAggregateAnnotation(Map<Class<?>, SynthesizedAnnotation> annotationMap) {
this.annotationMap = annotationMap;
}
@Override
public Object getSource() {
return null;
}
@Override
public SynthesizedAnnotationSelector getAnnotationSelector() {
return null;
}
@Override
public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() {
return null;
}
@Override
public Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors() {
return null;
}
@Override
public SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType) {
return annotationMap.get(annotationType);
}
@Override
public Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation() {
return null;
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return null;
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return false;
}
@Override
public Annotation[] getAnnotations() {
return new Annotation[0];
}
@Override
public <T extends Annotation> T synthesize(Class<T> annotationType) {
return null;
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
@Override
public Object getRoot() {
return null;
}
}
static class TestSynthesizedAnnotation implements SynthesizedAnnotation {
private final Annotation annotation;
private final SynthesizedAggregateAnnotation owner;
private final Map<String, AnnotationAttribute> attributeMap;
public TestSynthesizedAnnotation(SynthesizedAggregateAnnotation owner, Annotation annotation) {
this.owner = owner;
this.attributeMap = new HashMap<>();
this.annotation = annotation;
for (Method declaredMethod : annotation.annotationType().getDeclaredMethods()) {
attributeMap.put(declaredMethod.getName(), new CacheableAnnotationAttribute(annotation, declaredMethod));
}
}
@Override
public Object getRoot() {
return null;
}
@Override
public Annotation getAnnotation() {
return annotation;
}
@Override
public int getVerticalDistance() {
return 0;
}
@Override
public int getHorizontalDistance() {
return 0;
}
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return false;
}
@Override
public Map<String, AnnotationAttribute> getAttributes() {
return attributeMap;
}
@Override
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
attributeMap.put(attributeName, attribute);
}
@Override
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
AnnotationAttribute annotationAttribute = attributeMap.get(attributeName);
if (ObjectUtil.isNotNull(annotationAttribute)) {
attributeMap.put(attributeName, operator.apply(annotationAttribute));
}
}
@Override
public Object getAttributeValue(String attributeName) {
return null;
}
@Override
public Class<? extends Annotation> annotationType() {
return annotation.annotationType();
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
}
}

View File

@@ -0,0 +1,223 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.UnaryOperator;
public class AliasLinkAnnotationPostProcessorTest {
@Test
public void processForceAliasForTest() {
AliasLinkAnnotationPostProcessor processor = new AliasLinkAnnotationPostProcessor();
Map<Class<?>, SynthesizedAnnotation> annotationMap = new HashMap<>();
SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap);
AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class);
SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation);
annotationMap.put(annotation.annotationType(), synthesizedAnnotation);
processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator);
AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value"), valueAttribute.getAttribute());
Assert.assertFalse(valueAttribute.isWrapped());
Assert.assertEquals(CacheableAnnotationAttribute.class, valueAttribute.getClass());
AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name"), nameAttribute.getAttribute());
Assert.assertTrue(nameAttribute.isWrapped());
Assert.assertEquals(ForceAliasedAnnotationAttribute.class, nameAttribute.getClass());
Assert.assertEquals(valueAttribute, ((WrappedAnnotationAttribute)nameAttribute).getLinked());
}
@Test
public void processAliasForTest() {
AliasLinkAnnotationPostProcessor processor = new AliasLinkAnnotationPostProcessor();
Map<Class<?>, SynthesizedAnnotation> annotationMap = new HashMap<>();
SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap);
AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class);
SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation);
annotationMap.put(annotation.annotationType(), synthesizedAnnotation);
processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator);
AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value2");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value2"), valueAttribute.getAttribute());
Assert.assertFalse(valueAttribute.isWrapped());
Assert.assertEquals(CacheableAnnotationAttribute.class, valueAttribute.getClass());
AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name2");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name2"), nameAttribute.getAttribute());
Assert.assertTrue(nameAttribute.isWrapped());
Assert.assertEquals(AliasedAnnotationAttribute.class, nameAttribute.getClass());
Assert.assertEquals(valueAttribute, ((WrappedAnnotationAttribute)nameAttribute).getLinked());
}
@AnnotationForTest
static class ClassForTest {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
@Link(attribute = "name", type = RelationType.FORCE_ALIAS_FOR)
String value() default "";
String name() default "";
@Link(attribute = "name2", type = RelationType.ALIAS_FOR)
String value2() default "";
String name2() default "";
}
static class TestSynthesizedAggregateAnnotation implements SynthesizedAggregateAnnotation {
private final Map<Class<?>, SynthesizedAnnotation> annotationMap;
public TestSynthesizedAggregateAnnotation(Map<Class<?>, SynthesizedAnnotation> annotationMap) {
this.annotationMap = annotationMap;
}
@Override
public Object getSource() {
return null;
}
@Override
public SynthesizedAnnotationSelector getAnnotationSelector() {
return null;
}
@Override
public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() {
return null;
}
@Override
public Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors() {
return null;
}
@Override
public SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType) {
return annotationMap.get(annotationType);
}
@Override
public Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation() {
return null;
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return null;
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return false;
}
@Override
public Annotation[] getAnnotations() {
return new Annotation[0];
}
@Override
public <T extends Annotation> T synthesize(Class<T> annotationType) {
return null;
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
@Override
public Object getRoot() {
return null;
}
}
static class TestSynthesizedAnnotation implements SynthesizedAnnotation {
private final Annotation annotation;
private final SynthesizedAggregateAnnotation owner;
private final Map<String, AnnotationAttribute> attributeMap;
public TestSynthesizedAnnotation(SynthesizedAggregateAnnotation owner, Annotation annotation) {
this.owner = owner;
this.attributeMap = new HashMap<>();
this.annotation = annotation;
for (Method declaredMethod : annotation.annotationType().getDeclaredMethods()) {
attributeMap.put(declaredMethod.getName(), new CacheableAnnotationAttribute(annotation, declaredMethod));
}
}
@Override
public Object getRoot() {
return null;
}
@Override
public Annotation getAnnotation() {
return annotation;
}
@Override
public int getVerticalDistance() {
return 0;
}
@Override
public int getHorizontalDistance() {
return 0;
}
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return false;
}
@Override
public Map<String, AnnotationAttribute> getAttributes() {
return attributeMap;
}
@Override
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
attributeMap.put(attributeName, attribute);
}
@Override
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
AnnotationAttribute annotationAttribute = attributeMap.get(attributeName);
if (ObjectUtil.isNotNull(annotationAttribute)) {
attributeMap.put(attributeName, operator.apply(annotationAttribute));
}
}
@Override
public Object getAttributeValue(String attributeName) {
return null;
}
@Override
public Class<? extends Annotation> annotationType() {
return annotation.annotationType();
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
}
}

View File

@@ -0,0 +1,78 @@
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 valueAnnotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 注解属性
Assert.assertEquals(annotation, valueAnnotationAttribute.getAnnotation());
Assert.assertEquals(annotation.annotationType(), valueAnnotationAttribute.getAnnotationType());
// 方法属性
Assert.assertEquals(valueMethod.getAnnotation(Alias.class), valueAnnotationAttribute.getAnnotation(Alias.class));
Assert.assertEquals(valueMethod.getName(), valueAnnotationAttribute.getAttributeName());
Assert.assertEquals(nameMethod.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 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 {
@Alias("value")
String value() default "";
String name() default "";
}
@AnnotationForTest(name = "name", value = "value")
static class ClassForTest1 {}
@AnnotationForTest(value = "value")
static class ClassForTest2 {}
}

View File

@@ -42,6 +42,7 @@ public class AnnotationUtilTest {
// 加别名适配
final AnnotationForTest annotation = AnnotationUtil.getAnnotationAlias(ClassWithAnnotation.class, AnnotationForTest.class);
Assert.assertEquals("测试", annotation.retry());
Assert.assertTrue(AnnotationUtil.isSynthesizedAnnotation(annotation));
}
@AnnotationForTest("测试")

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,99 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.util.ClassUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Map;
import java.util.function.UnaryOperator;
public class CacheableSynthesizedAnnotationAttributeProcessorTest {
@Test
public void getAttributeValueTest() {
CacheableSynthesizedAnnotationAttributeProcessor processor = new CacheableSynthesizedAnnotationAttributeProcessor();
Map<String, Object> values1 = MapBuilder.<String, Object> create().put("name", "name1").put("value", 111).build();
SynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(1, 0, values1);
Map<String, Object> values2 = MapBuilder.<String, Object> create().put("name", "name2").put("value", "value2").build();
SynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0, values2);
Assert.assertEquals("name2", processor.getAttributeValue("name", String.class, Arrays.asList(annotation1, annotation2)));
Assert.assertEquals(Integer.valueOf(111), processor.getAttributeValue("value", Integer.class, Arrays.asList(annotation1, annotation2)));
}
static class TestSynthesizedAnnotation implements SynthesizedAnnotation {
private final int verticalDistance;
private final int horizontalDistance;
private final Map<String, Object> value;
public TestSynthesizedAnnotation(int verticalDistance, int horizontalDistance, Map<String, Object> value) {
this.verticalDistance = verticalDistance;
this.horizontalDistance = horizontalDistance;
this.value = value;
}
@Override
public Object getRoot() {
return null;
}
@Override
public Annotation getAnnotation() {
return null;
}
@Override
public int getVerticalDistance() {
return verticalDistance;
}
@Override
public int getHorizontalDistance() {
return horizontalDistance;
}
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return Opt.ofNullable(value.get(attributeName))
.map(t -> ClassUtil.isAssignable(returnType, t.getClass()))
.orElse(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 value.get(attributeName);
}
@Override
public Class<? extends Annotation> annotationType() {
return null;
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
}
}

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,370 @@
package cn.hutool.core.annotation;
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.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
/**
* 合成注解{@link GenericSynthesizedAggregateAnnotation}的测试用例
*
* @author huangchengxing
*/
public class GenericSynthesizedAggregateAnnotationTest {
@Test
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 GenericSynthesizedAggregateAnnotation syntheticMetaAnnotation = new GenericSynthesizedAggregateAnnotation(childAnnotation);
// Annotation & AnnotatedElement
Assert.assertEquals(GenericSynthesizedAggregateAnnotation.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.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class));
Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(ParentAnnotation.class));
Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(ChildAnnotation.class));
Assert.assertEquals(3, syntheticMetaAnnotation.getAllSynthesizedAnnotation().size());
// 属性
Assert.assertEquals(SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, syntheticMetaAnnotation.getAnnotationSelector());
Assert.assertEquals(CacheableSynthesizedAnnotationAttributeProcessor.class, syntheticMetaAnnotation.getAnnotationAttributeProcessor().getClass());
Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotationPostProcessors().size());
}
@Test
public void synthesisAnnotationAttributeTest() {
final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
GenericSynthesizedAggregateAnnotation syntheticMetaAnnotation = new GenericSynthesizedAggregateAnnotation(rootAnnotation);
Assert.assertEquals(syntheticMetaAnnotation.getSource(), Collections.singletonList(rootAnnotation));
Assert.assertEquals(syntheticMetaAnnotation.annotationType(), GenericSynthesizedAggregateAnnotation.class);
Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotations().length);
Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttributeValue("childValue", String.class));
Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttributeValue("childValueAlias", String.class));
Assert.assertEquals("Child's Parent!", syntheticMetaAnnotation.getAttributeValue("parentValue", String.class));
Assert.assertEquals("Child's GrandParent!", syntheticMetaAnnotation.getAttributeValue("grandParentValue", String.class));
}
@Test
public void syntheticAnnotationTest() {
final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
GenericSynthesizedAggregateAnnotation syntheticMetaAnnotation = new GenericSynthesizedAggregateAnnotation(rootAnnotation);
final ChildAnnotation childAnnotation = syntheticMetaAnnotation.synthesize(ChildAnnotation.class);
SynthesizedAnnotation childSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(ChildAnnotation.class);
Assert.assertNotNull(childSyntheticAnnotation);
Assert.assertTrue(childSyntheticAnnotation.hasAttribute("childValue", String.class));
Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), childSyntheticAnnotation.getRoot());
Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), childSyntheticAnnotation.getAnnotation());
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class));
Assert.assertNotNull(childAnnotation);
Assert.assertEquals("Child!", childAnnotation.childValue());
Assert.assertEquals("Child!", childAnnotation.childValueAlias());
Assert.assertEquals(childAnnotation.grandParentType(), Integer.class);
Assert.assertThrows(IllegalArgumentException.class, () -> new GenericSynthesizedAggregateAnnotation(childAnnotation));
final ParentAnnotation parentAnnotation = syntheticMetaAnnotation.synthesize(ParentAnnotation.class);
SynthesizedAnnotation parentSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(ParentAnnotation.class);
Assert.assertNotNull(parentSyntheticAnnotation);
Assert.assertTrue(parentSyntheticAnnotation.hasAttribute("parentValue", String.class));
Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), parentSyntheticAnnotation.getRoot());
Assert.assertEquals(ChildAnnotation.class.getAnnotation(ParentAnnotation.class), parentSyntheticAnnotation.getAnnotation());
Assert.assertNotNull(parentAnnotation);
Assert.assertEquals("Child's Parent!", parentAnnotation.parentValue());
Assert.assertEquals("java.lang.Void", parentAnnotation.grandParentType());
Assert.assertThrows(IllegalArgumentException.class, () -> new GenericSynthesizedAggregateAnnotation(parentAnnotation));
final GrandParentAnnotation grandParentAnnotation = syntheticMetaAnnotation.synthesize(GrandParentAnnotation.class);
SynthesizedAnnotation grandParentSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class);
Assert.assertNotNull(grandParentSyntheticAnnotation);
Assert.assertTrue(grandParentSyntheticAnnotation.hasAttribute("grandParentType", Class.class));
Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), grandParentSyntheticAnnotation.getRoot());
Assert.assertEquals(ChildAnnotation.class.getAnnotation(GrandParentAnnotation.class), grandParentSyntheticAnnotation.getAnnotation());
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class));
Assert.assertNotNull(grandParentAnnotation);
Assert.assertEquals("Child's GrandParent!", grandParentAnnotation.grandParentValue());
Assert.assertEquals(grandParentAnnotation.grandParentType(), Integer.class);
Assert.assertThrows(IllegalArgumentException.class, () -> new GenericSynthesizedAggregateAnnotation(grandParentAnnotation));
}
@Test
public void linkTest() {
final Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value");
final SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new GenericSynthesizedAggregateAnnotation(method.getAnnotation(AliasFor.class));
final Link link = synthesizedAnnotationAggregator.synthesize(Link.class);
Assert.assertEquals(AnnotationForLinkTest.class, link.annotation());
Assert.assertEquals("name", link.attribute());
}
@Test
public void mirrorAttributeTest() {
AnnotationForMirrorTest annotation = ClassForMirrorTest.class.getAnnotation(AnnotationForMirrorTest.class);
SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
AnnotationForMirrorTest syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class);
Assert.assertEquals("Foo", syntheticAnnotation.name());
Assert.assertEquals("Foo", syntheticAnnotation.value());
annotation = ClassForMirrorTest2.class.getAnnotation(AnnotationForMirrorTest.class);
synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class);
Assert.assertEquals("Foo", syntheticAnnotation.name());
Assert.assertEquals("Foo", syntheticAnnotation.value());
annotation = ClassForMirrorTest3.class.getAnnotation(AnnotationForMirrorTest.class);
synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class);
AnnotationForMirrorTest finalSyntheticAnnotation = syntheticAnnotation;
Assert.assertThrows(IllegalArgumentException.class, finalSyntheticAnnotation::name);
}
@Test
public void aliasForTest() {
AnnotationForAliasForTest annotation = ClassForAliasForTest.class.getAnnotation(AnnotationForAliasForTest.class);
SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
MetaAnnotationForAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForAliasForTest.class);
Assert.assertEquals("Meta", metaAnnotation.name());
AnnotationForAliasForTest childAnnotation = synthetic.synthesize(AnnotationForAliasForTest.class);
Assert.assertEquals("", childAnnotation.value());
annotation = ClassForAliasForTest2.class.getAnnotation(AnnotationForAliasForTest.class);
synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
metaAnnotation = synthetic.synthesize(MetaAnnotationForAliasForTest.class);
Assert.assertEquals("Foo", metaAnnotation.name());
childAnnotation = synthetic.synthesize(AnnotationForAliasForTest.class);
Assert.assertEquals("Foo", childAnnotation.value());
}
@Test
public void forceAliasForTest() {
AnnotationForceForAliasForTest annotation = ClassForForceAliasForTest.class.getAnnotation(AnnotationForceForAliasForTest.class);
SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
MetaAnnotationForForceAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForForceAliasForTest.class);
Assert.assertEquals("", metaAnnotation.name());
AnnotationForceForAliasForTest childAnnotation = synthetic.synthesize(AnnotationForceForAliasForTest.class);
Assert.assertEquals("", childAnnotation.value());
annotation = ClassForForceAliasForTest2.class.getAnnotation(AnnotationForceForAliasForTest.class);
synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
metaAnnotation = synthetic.synthesize(MetaAnnotationForForceAliasForTest.class);
Assert.assertEquals("Foo", metaAnnotation.name());
childAnnotation = synthetic.synthesize(AnnotationForceForAliasForTest.class);
Assert.assertEquals("Foo", childAnnotation.value());
}
@Test
public void aliasForAndMirrorTest() {
AnnotationForMirrorThenAliasForTest annotation = ClassForAliasForAndMirrorTest.class.getAnnotation(AnnotationForMirrorThenAliasForTest.class);
SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
MetaAnnotationForMirrorThenAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForMirrorThenAliasForTest.class);
Assert.assertEquals("test", metaAnnotation.name());
Assert.assertEquals("test", metaAnnotation.value());
AnnotationForMirrorThenAliasForTest childAnnotation = synthetic.synthesize(AnnotationForMirrorThenAliasForTest.class);
Assert.assertEquals("test", childAnnotation.childValue());
}
@Test
public void multiAliasForTest() {
final AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class);
final SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
final MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.synthesize(MetaAnnotationForMultiAliasForTest1.class);
Assert.assertEquals("test", metaAnnotation1.name());
Assert.assertEquals("test", metaAnnotation1.value1());
final MetaAnnotationForMultiAliasForTest2 metaAnnotation2 = synthetic.synthesize(MetaAnnotationForMultiAliasForTest2.class);
Assert.assertEquals("test", metaAnnotation2.value2());
final AnnotationForMultiAliasForTest childAnnotation = synthetic.synthesize(AnnotationForMultiAliasForTest.class);
Assert.assertEquals("test", childAnnotation.value3());
}
@Test
public void implicitAliasTest() {
final AnnotationForImplicitAliasTest annotation = ClassForImplicitAliasTest.class.getAnnotation(AnnotationForImplicitAliasTest.class);
final SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
final MetaAnnotationForImplicitAliasTest metaAnnotation = synthetic.synthesize(MetaAnnotationForImplicitAliasTest.class);
Assert.assertEquals("Meta", metaAnnotation.name());
Assert.assertEquals("Foo", metaAnnotation.value());
final AnnotationForImplicitAliasTest childAnnotation = synthetic.synthesize(AnnotationForImplicitAliasTest.class);
Assert.assertEquals("Foo", childAnnotation.value());
}
// 注解结构如下:
// AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation
// -> @GrandParentAnnotation
@ChildAnnotation(childValueAlias = "Child!", grandParentType = Integer.class)
static class AnnotatedClass {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE })
@interface GrandParentAnnotation {
String grandParentValue() default "";
Class<?> grandParentType() default Void.class;
}
@GrandParentAnnotation(grandParentValue = "Parent's GrandParent!") // 覆盖元注解@GrandParentAnnotation的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@interface ParentAnnotation {
String parentValue() default "";
String grandParentType() default "java.lang.Void";
}
@GrandParentAnnotation(grandParentValue = "Child's GrandParent!") // 重复的元注解,靠近根注解的优先级高
@ParentAnnotation(parentValue = "Child's Parent!") // 覆盖元注解@ParentAnnotation的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface ChildAnnotation {
String childValueAlias() default "";
@Alias("childValueAlias")
String childValue() default "";
Class<?> grandParentType() default Void.class;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForMirrorTest {
//@Link(attribute = "name")
@MirrorFor(attribute = "name")
String value() default "";
//@Link(attribute = "value")
@MirrorFor(attribute = "value")
String name() default "";
}
@AnnotationForMirrorTest("Foo")
static class ClassForMirrorTest {}
@AnnotationForMirrorTest(name = "Foo")
static class ClassForMirrorTest2 {}
@AnnotationForMirrorTest(value = "Aoo", name = "Foo")
static class ClassForMirrorTest3 {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForAliasForTest {
String name() default "";
}
@MetaAnnotationForAliasForTest(name = "Meta")
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForAliasForTest {
@AliasFor(
annotation = MetaAnnotationForAliasForTest.class,
attribute = "name"
)
String value() default "";
}
@AnnotationForAliasForTest
static class ClassForAliasForTest {}
@AnnotationForAliasForTest("Foo")
static class ClassForAliasForTest2 {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForForceAliasForTest {
String name() default "";
}
@MetaAnnotationForForceAliasForTest(name = "Meta")
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForceForAliasForTest {
//@Link(
// annotation = MetaAnnotationForForceAliasForTest.class,
// attribute = "name",
// type = RelationType.FORCE_ALIAS_FOR
//)
@ForceAliasFor(annotation = MetaAnnotationForForceAliasForTest.class, attribute = "name")
String value() default "";
}
@AnnotationForceForAliasForTest
static class ClassForForceAliasForTest {}
@AnnotationForceForAliasForTest("Foo")
static class ClassForForceAliasForTest2 {}
@interface AnnotationForLinkTest {
@AliasFor(attribute = "name", annotation = AnnotationForLinkTest.class)
String value() default "value";
String name() default "name";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForMirrorThenAliasForTest {
@MirrorFor(attribute = "value")
String name() default "";
@MirrorFor(attribute = "name")
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@MetaAnnotationForMirrorThenAliasForTest("Meta")
@interface AnnotationForMirrorThenAliasForTest {
@AliasFor(attribute = "name", annotation = MetaAnnotationForMirrorThenAliasForTest.class)
String childValue() default "value";
}
@AnnotationForMirrorThenAliasForTest(childValue = "test")
static class ClassForAliasForAndMirrorTest{}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForMultiAliasForTest1 {
@MirrorFor(attribute = "value1")
String name() default "";
@MirrorFor(attribute = "name")
String value1() default "";
}
@MetaAnnotationForMultiAliasForTest1
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForMultiAliasForTest2 {
@AliasFor(attribute = "name", annotation = MetaAnnotationForMultiAliasForTest1.class)
String value2() default "";
}
@MetaAnnotationForMultiAliasForTest2
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForMultiAliasForTest {
@AliasFor(attribute = "value2", annotation = MetaAnnotationForMultiAliasForTest2.class)
String value3() default "value";
}
@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 {}
}

View File

@@ -0,0 +1,198 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.UnaryOperator;
public class MirrorLinkAnnotationPostProcessorTest {
@Test
public void processTest() {
MirrorLinkAnnotationPostProcessor processor = new MirrorLinkAnnotationPostProcessor();
Map<Class<?>, SynthesizedAnnotation> annotationMap = new HashMap<>();
SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap);
AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class);
SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation);
annotationMap.put(annotation.annotationType(), synthesizedAnnotation);
processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator);
AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value"), valueAttribute.getAttribute());
Assert.assertTrue(valueAttribute.isWrapped());
Assert.assertEquals(MirroredAnnotationAttribute.class, valueAttribute.getClass());
AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name"), nameAttribute.getAttribute());
Assert.assertTrue(nameAttribute.isWrapped());
Assert.assertEquals(MirroredAnnotationAttribute.class, nameAttribute.getClass());
Assert.assertEquals(((WrappedAnnotationAttribute)nameAttribute).getLinked(), ((WrappedAnnotationAttribute)valueAttribute).getOriginal());
Assert.assertEquals(((WrappedAnnotationAttribute)nameAttribute).getOriginal(), ((WrappedAnnotationAttribute)valueAttribute).getLinked());
}
@AnnotationForTest
static class ClassForTest {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
@Link(attribute = "name", type = RelationType.MIRROR_FOR)
String value() default "";
@Link(attribute = "value", type = RelationType.MIRROR_FOR)
String name() default "";
}
static class TestSynthesizedAggregateAnnotation implements SynthesizedAggregateAnnotation {
private final Map<Class<?>, SynthesizedAnnotation> annotationMap;
public TestSynthesizedAggregateAnnotation(Map<Class<?>, SynthesizedAnnotation> annotationMap) {
this.annotationMap = annotationMap;
}
@Override
public Object getSource() {
return null;
}
@Override
public SynthesizedAnnotationSelector getAnnotationSelector() {
return null;
}
@Override
public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() {
return null;
}
@Override
public Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors() {
return null;
}
@Override
public SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType) {
return annotationMap.get(annotationType);
}
@Override
public Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation() {
return null;
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return null;
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return false;
}
@Override
public Annotation[] getAnnotations() {
return new Annotation[0];
}
@Override
public <T extends Annotation> T synthesize(Class<T> annotationType) {
return null;
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
@Override
public Object getRoot() {
return null;
}
}
static class TestSynthesizedAnnotation implements SynthesizedAnnotation {
private final Annotation annotation;
private final SynthesizedAggregateAnnotation owner;
private final Map<String, AnnotationAttribute> attributeMap;
public TestSynthesizedAnnotation(SynthesizedAggregateAnnotation owner, Annotation annotation) {
this.owner = owner;
this.attributeMap = new HashMap<>();
this.annotation = annotation;
for (Method declaredMethod : annotation.annotationType().getDeclaredMethods()) {
attributeMap.put(declaredMethod.getName(), new CacheableAnnotationAttribute(annotation, declaredMethod));
}
}
@Override
public Object getRoot() {
return null;
}
@Override
public Annotation getAnnotation() {
return annotation;
}
@Override
public int getVerticalDistance() {
return 0;
}
@Override
public int getHorizontalDistance() {
return 0;
}
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return false;
}
@Override
public Map<String, AnnotationAttribute> getAttributes() {
return attributeMap;
}
@Override
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
attributeMap.put(attributeName, attribute);
}
@Override
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
AnnotationAttribute annotationAttribute = attributeMap.get(attributeName);
if (ObjectUtil.isNotNull(annotationAttribute)) {
attributeMap.put(attributeName, operator.apply(annotationAttribute));
}
}
@Override
public Object getAttributeValue(String attributeName) {
return null;
}
@Override
public Class<? extends Annotation> annotationType() {
return annotation.annotationType();
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
}
}

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 chooseTest() {
final SynthesizedAnnotationSelector.NearestAndOldestPrioritySelector selector = (SynthesizedAnnotationSelector.NearestAndOldestPrioritySelector)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.NearestAndNewestPrioritySelector selector = (SynthesizedAnnotationSelector.NearestAndNewestPrioritySelector)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.FarthestAndOldestPrioritySelector selector = (SynthesizedAnnotationSelector.FarthestAndOldestPrioritySelector)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.FarthestAndNewestPrioritySelector selector = (SynthesizedAnnotationSelector.FarthestAndNewestPrioritySelector)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 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;
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
}
}

View File

@@ -1,88 +0,0 @@
package cn.hutool.core.annotation;
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;
/**
* 合成注解{@link SyntheticMetaAnnotation}的测试用例
*
* @author huangchengxing
*/
public class SyntheticMetaAnnotationTest {
@Test
public void testSynthesisAnnotation() {
ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
SyntheticMetaAnnotation syntheticMetaAnnotation = new SyntheticMetaAnnotation(rootAnnotation);
Assert.assertEquals(syntheticMetaAnnotation.getSource(), rootAnnotation);
Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SyntheticMetaAnnotation.class);
Assert.assertEquals(1, syntheticMetaAnnotation.getDeclaredAnnotations().length);
Assert.assertEquals(syntheticMetaAnnotation.getDeclaredAnnotations()[0], rootAnnotation);
Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotations().length);
Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttribute("childValue", String.class));
Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttribute("childValueAlias", String.class));
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);
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class));
Assert.assertNotNull(childAnnotation);
Assert.assertEquals("Child!", childAnnotation.childValue());
Assert.assertEquals("Child!", childAnnotation.childValueAlias());
Assert.assertEquals(childAnnotation.grandParentType(), Integer.class);
Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(childAnnotation));
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);
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class));
Assert.assertNotNull(grandParentAnnotation);
Assert.assertEquals("Child's GrandParent!", grandParentAnnotation.grandParentValue());
Assert.assertEquals(grandParentAnnotation.grandParentType(), Integer.class);
Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(grandParentAnnotation));
}
// 注解结构如下:
// AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation
// -> @GrandParentAnnotation
@ChildAnnotation(childValueAlias = "Child!", grandParentType = Integer.class)
static class AnnotatedClass {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE })
@interface GrandParentAnnotation {
String grandParentValue() default "";
Class<?> grandParentType() default Void.class;
}
@GrandParentAnnotation(grandParentValue = "Parent's GrandParent!") // 覆盖元注解@GrandParentAnnotation的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@interface ParentAnnotation {
String parentValue() default "";
String grandParentType() default "java.lang.Void";
}
@GrandParentAnnotation(grandParentValue = "Child's GrandParent!") // 重复的元注解,靠近根注解的优先级高
@ParentAnnotation(parentValue = "Child's Parent!") // 覆盖元注解@ParentAnnotation的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface ChildAnnotation {
String childValueAlias() default "";
@Alias("childValueAlias")
String childValue() default "";
Class<?> grandParentType() default Void.class;
}
}

View File

@@ -36,7 +36,7 @@ public class TypeAnnotationScannerTest {
annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
// 不查找父类
scanner = new TypeAnnotationScanner().setIncludeSupperClass(false);
scanner = new TypeAnnotationScanner().setIncludeSuperClass(false);
annotations = scanner.getAnnotations(Example.class);
Assert.assertEquals(1, annotations.size());
annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
@@ -88,7 +88,7 @@ public class TypeAnnotationScannerTest {
// 不查找父类
map.clear();
new TypeAnnotationScanner()
.setIncludeSupperClass(false)
.setIncludeSuperClass(false)
.scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
Example.class, null