From b64a807051ff9632c0a880d20081d5496ecbdcc2 Mon Sep 17 00:00:00 2001 From: richdouglasevans Date: Wed, 25 Nov 2015 20:55:48 +0000 Subject: [PATCH] Add support for custom @Rule annotations --- .../core/RuleDefinitionValidator.java | 2 +- .../java/org/easyrules/core/RuleProxy.java | 2 +- .../main/java/org/easyrules/util/Utils.java | 27 +++++++ .../AnnotatedRuleWithMetaRuleAnnotation.java | 14 ++++ .../org/easyrules/annotation/MetaRule.java | 12 ++++ .../core/RuleDefinitionValidatorTest.java | 5 ++ .../org/easyrules/core/RuleProxyTest.java | 20 ++++++ .../java/org/easyrules/util/UtilsTest.java | 72 +++++++++++++++++++ 8 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 easyrules-core/src/test/java/org/easyrules/annotation/AnnotatedRuleWithMetaRuleAnnotation.java create mode 100644 easyrules-core/src/test/java/org/easyrules/annotation/MetaRule.java create mode 100644 easyrules-core/src/test/java/org/easyrules/core/RuleProxyTest.java create mode 100644 easyrules-core/src/test/java/org/easyrules/util/UtilsTest.java diff --git a/easyrules-core/src/main/java/org/easyrules/core/RuleDefinitionValidator.java b/easyrules-core/src/main/java/org/easyrules/core/RuleDefinitionValidator.java index 788279c..ca9e336 100644 --- a/easyrules-core/src/main/java/org/easyrules/core/RuleDefinitionValidator.java +++ b/easyrules-core/src/main/java/org/easyrules/core/RuleDefinitionValidator.java @@ -93,7 +93,7 @@ class RuleDefinitionValidator { } private boolean isRuleClassWellDefined(final Object rule) { - return rule.getClass().isAnnotationPresent(Rule.class); + return Utils.isAnnotationPresent(Rule.class, rule.getClass()); } private boolean isConditionMethodWellDefined(final Method method) { diff --git a/easyrules-core/src/main/java/org/easyrules/core/RuleProxy.java b/easyrules-core/src/main/java/org/easyrules/core/RuleProxy.java index 4901bca..dc07910 100644 --- a/easyrules-core/src/main/java/org/easyrules/core/RuleProxy.java +++ b/easyrules-core/src/main/java/org/easyrules/core/RuleProxy.java @@ -146,7 +146,7 @@ class RuleProxy implements InvocationHandler { } private Rule getRuleAnnotation() { - return getTargetClass().getAnnotation(Rule.class); + return Utils.findAnnotation(Rule.class, getTargetClass()); } private String getRuleName() { diff --git a/easyrules-core/src/main/java/org/easyrules/util/Utils.java b/easyrules-core/src/main/java/org/easyrules/util/Utils.java index 12a4d49..2feeb23 100644 --- a/easyrules-core/src/main/java/org/easyrules/util/Utils.java +++ b/easyrules-core/src/main/java/org/easyrules/util/Utils.java @@ -1,5 +1,6 @@ package org.easyrules.util; +import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @@ -74,6 +75,32 @@ public final class Utils { return interfaces; } + @SuppressWarnings("unchecked") + public static A findAnnotation( + final Class targetAnnotation, final Class annotatedType) { + + checkNotNull(targetAnnotation, "targetAnnotation"); + checkNotNull(annotatedType, "annotatedType"); + + Annotation foundAnnotation = annotatedType.getAnnotation(targetAnnotation); + if (foundAnnotation == null) { + for (Annotation annotation : annotatedType.getAnnotations()) { + Class annotationType = annotation.annotationType(); + if (annotationType.isAnnotationPresent(targetAnnotation)) { + foundAnnotation = annotationType.getAnnotation(targetAnnotation); + break; + } + } + } + return (A) foundAnnotation; + } + + public static boolean isAnnotationPresent( + final Class targetAnnotation, final Class annotatedType) { + + return findAnnotation(targetAnnotation, annotatedType) != null; + } + public static void checkNotNull(final Object argument, final String argumentName) { if (argument == null) { throw new IllegalArgumentException(format("The %s must not be null", argumentName)); diff --git a/easyrules-core/src/test/java/org/easyrules/annotation/AnnotatedRuleWithMetaRuleAnnotation.java b/easyrules-core/src/test/java/org/easyrules/annotation/AnnotatedRuleWithMetaRuleAnnotation.java new file mode 100644 index 0000000..0378f46 --- /dev/null +++ b/easyrules-core/src/test/java/org/easyrules/annotation/AnnotatedRuleWithMetaRuleAnnotation.java @@ -0,0 +1,14 @@ +package org.easyrules.annotation; + +@MetaRule +public class AnnotatedRuleWithMetaRuleAnnotation { + + @Condition + public boolean when() { + return true; + } + + @Action + public void then() throws Exception { + } +} diff --git a/easyrules-core/src/test/java/org/easyrules/annotation/MetaRule.java b/easyrules-core/src/test/java/org/easyrules/annotation/MetaRule.java new file mode 100644 index 0000000..8fab0ea --- /dev/null +++ b/easyrules-core/src/test/java/org/easyrules/annotation/MetaRule.java @@ -0,0 +1,12 @@ +package org.easyrules.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Rule +public @interface MetaRule { +} diff --git a/easyrules-core/src/test/java/org/easyrules/core/RuleDefinitionValidatorTest.java b/easyrules-core/src/test/java/org/easyrules/core/RuleDefinitionValidatorTest.java index 6904f81..188c831 100644 --- a/easyrules-core/src/test/java/org/easyrules/core/RuleDefinitionValidatorTest.java +++ b/easyrules-core/src/test/java/org/easyrules/core/RuleDefinitionValidatorTest.java @@ -22,6 +22,11 @@ public class RuleDefinitionValidatorTest { ruleDefinitionValidator.validateRuleDefinition(new Object()); } + @Test + public void withCustomAnnotationThatIsItselfAnnotatedWithTheRuleAnnotation() throws Throwable { + ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithMetaRuleAnnotation()); + } + /* * Conditions methods tests */ diff --git a/easyrules-core/src/test/java/org/easyrules/core/RuleProxyTest.java b/easyrules-core/src/test/java/org/easyrules/core/RuleProxyTest.java new file mode 100644 index 0000000..73b0f98 --- /dev/null +++ b/easyrules-core/src/test/java/org/easyrules/core/RuleProxyTest.java @@ -0,0 +1,20 @@ +package org.easyrules.core; + +import org.easyrules.annotation.AnnotatedRuleWithMetaRuleAnnotation; +import org.easyrules.api.Rule; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class RuleProxyTest { + + @Test + public void proxyingHappensEvenWhenRuleIsAnnotatedWithMetaRuleAnnotation() { + AnnotatedRuleWithMetaRuleAnnotation rule1 = new AnnotatedRuleWithMetaRuleAnnotation(); + + Rule rule = RuleProxy.asRule(rule1); + + assertNotNull(rule.getDescription()); + assertNotNull(rule.getName()); + } +} diff --git a/easyrules-core/src/test/java/org/easyrules/util/UtilsTest.java b/easyrules-core/src/test/java/org/easyrules/util/UtilsTest.java new file mode 100644 index 0000000..4df9b05 --- /dev/null +++ b/easyrules-core/src/test/java/org/easyrules/util/UtilsTest.java @@ -0,0 +1,72 @@ +package org.easyrules.util; + +import org.junit.Test; + +import java.lang.annotation.*; + +import static org.junit.Assert.*; + +public class UtilsTest { + + @Test + public void findAnnotationWithClassWhereAnnotationIsPresent() { + Annotation foo = Utils.findAnnotation(Foo.class, AnnotationIsPresent.class); + + assertCorrectAnnotationIsFound(Foo.class, foo); + } + + @Test + public void findAnnotationWithClassWhereAnnotationIsPresentViaMetaAnnotation() { + Annotation foo = Utils.findAnnotation(Foo.class, AnnotationIsPresentViaMetaAnnotation.class); + + assertCorrectAnnotationIsFound(Foo.class, foo); + } + + @Test + public void findAnnotationWithClassWhereAnnotationIsNotPresent() { + Annotation foo = Utils.findAnnotation(Foo.class, Object.class); + + assertNull(foo); + } + + @Test + public void isAnnotationPresentWithClassWhereAnnotationIsPresent() { + assertTrue(Utils.isAnnotationPresent(Foo.class, AnnotationIsPresent.class)); + } + + @Test + public void isAnnotationPresentWithClassWhereAnnotationIsPresentViaMetaAnnotation() { + assertTrue(Utils.isAnnotationPresent(Foo.class, AnnotationIsPresentViaMetaAnnotation.class)); + } + + @Test + public void isAnnotationPresentWithClassWhereAnnotationIsNotPresent() { + assertFalse(Utils.isAnnotationPresent(Foo.class, Object.class)); + } + + private static void assertCorrectAnnotationIsFound( + Class expectedAnnotationType, Annotation actualAnnotation) { + + assertNotNull(actualAnnotation); + assertEquals(expectedAnnotationType, actualAnnotation.annotationType()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + private @interface Foo { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + @Foo + private @interface MetaFoo { + } + + @Foo + private static final class AnnotationIsPresent { + } + + @MetaFoo + private static final class AnnotationIsPresentViaMetaAnnotation { + } +}