You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2019/10/03 21:18:26 UTC
[isis] branch v2 updated: ISIS-2158: refinements to _Annotations to
support attribute inheritance
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/v2 by this push:
new 03e666f ISIS-2158: refinements to _Annotations to support attribute inheritance
03e666f is described below
commit 03e666f7f145c225ff031b2e00b51fe9c14d569b
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Oct 3 23:18:17 2019 +0200
ISIS-2158: refinements to _Annotations to support attribute inheritance
---
.../commons/internal/reflection/_Annotations.java | 108 +++++++++++++++++++--
.../Annotations_getAnnotations_on_Class_Test.java | 78 ++++++++++++---
2 files changed, 162 insertions(+), 24 deletions(-)
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Annotations.java b/core/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Annotations.java
index 1c6bb27..a74241a 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Annotations.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Annotations.java
@@ -25,6 +25,8 @@ import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
+import org.apache.isis.commons.internal.base._Strings;
+
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.val;
@@ -42,6 +44,9 @@ public final class _Annotations {
/**
* Optionally create a type-safe synthesized version of this annotation based on presence.
+ * <p>
+ * Does not support attribute inheritance.
+ *
* @param <A>
* @param annotatedElement
* @param annotationType
@@ -52,25 +57,112 @@ public final class _Annotations {
Class<A> annotationType) {
val synthesized = _Annotations
- .merge(annotatedElement, annotationType)
- .synthesize(ma->ma.isPresent());
+ .collect(annotatedElement)
+ .get(annotationType)
+ .synthesize(MergedAnnotation::isPresent);
return synthesized;
}
+
+ // -- ATTRIBUTE FETCHERS
+
+ /**
+ * Optionally create a String from attribute values based on presence.
+ * @param <A>
+ * @param attributeName
+ * @param annotatedElement
+ * @param annotationType
+ * @param attributeAcceptStrategy
+ * @return non-null
+ */
+ public static <A extends Annotation> Optional<String> getString(
+ String attributeName,
+ Class<?> annotatedElement,
+ Class<A> annotationType,
+ AttributeAcceptStrategy attributeAcceptStrategy) {
+
+ val value = _Annotations
+ .collect(annotatedElement)
+ .stream(annotationType)
+ .map(ma->ma.getString(attributeName))
+ .filter(attributeAcceptStrategy::acceptString)
+ .findFirst();
+
+ return value;
+ }
+ /**
+ * Optionally create an Enum from attribute values based on presence.
+ * @param <A>
+ * @param <E>
+ * @param attributeName
+ * @param annotatedElement
+ * @param annotationType
+ * @param enumType
+ * @param attributeAcceptStrategy
+ * @return non-null
+ */
+ public static <A extends Annotation, E extends Enum<E>> Optional<E> getEnum(
+ String attributeName,
+ Class<?> annotatedElement,
+ Class<A> annotationType,
+ Class<E> enumType,
+ AttributeAcceptStrategy attributeAcceptStrategy) {
+
+ val value = _Annotations
+ .collect(annotatedElement)
+ .stream(annotationType)
+ .map(ma->ma.getEnum(attributeName, enumType))
+ .filter(attributeAcceptStrategy::acceptEnum)
+ .findFirst();
+
+ return value;
+ }
+
+ // -- SHORTCUTS
+
+ public static <A extends Annotation> Optional<String> getString(
+ String attributeName,
+ Class<?> annotatedElement,
+ Class<A> annotationType) {
+ return getString(attributeName, annotatedElement, annotationType, DEFAULT_ATTRIBUTE_ACCEPT_STRATEGY);
+ }
+
+ public static <A extends Annotation, E extends Enum<E>> Optional<E> getEnum(
+ String attributeName,
+ Class<?> annotatedElement,
+ Class<A> annotationType,
+ Class<E> enumType) {
+ return getEnum(attributeName, annotatedElement, annotationType, enumType, DEFAULT_ATTRIBUTE_ACCEPT_STRATEGY);
+ }
+
+ // -- BEHAVIOR
+
+ public final static AttributeAcceptStrategy DEFAULT_ATTRIBUTE_ACCEPT_STRATEGY =
+ new AttributeAcceptStrategy() {};
+
+ static interface AttributeAcceptStrategy {
+
+ default boolean acceptString(String value) {
+ return !_Strings.isNullOrEmpty(value);
+ }
+
+ default boolean acceptEnum(Enum<?> value) {
+ return value != null && !value.name().equals("NOT_SPECIFIED");
+ }
+
+ }
+
// -- HELPER
/**
* @apiNote don't expose Spring's MergedAnnotation
*/
- private static <A extends Annotation> MergedAnnotation<A> merge(
- Class<?> annotatedElement,
- Class<A> annotationType) {
+ private static MergedAnnotations collect(Class<?> annotatedElement) {
//TODO use cache
- val merged = MergedAnnotations.from(annotatedElement, SearchStrategy.SUPERCLASS);
-
- return merged.get(annotationType);
+ val collected = MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS);
+ return collected;
}
diff --git a/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/Annotations_getAnnotations_on_Class_Test.java b/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/Annotations_getAnnotations_on_Class_Test.java
index 1987051..f60646b 100644
--- a/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/Annotations_getAnnotations_on_Class_Test.java
+++ b/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/Annotations_getAnnotations_on_Class_Test.java
@@ -24,12 +24,13 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import org.junit.Assert;
-import org.junit.Test;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
import org.apache.isis.commons.internal.reflection._Annotations;
import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
import lombok.val;
@@ -46,6 +47,7 @@ public class Annotations_getAnnotations_on_Class_Test {
NOT_SPECIFIED
}
Publishng publishng() default Publishng.NOT_SPECIFIED;
+ String name() default "";
}
@@ -64,6 +66,13 @@ public class Annotations_getAnnotations_on_Class_Test {
@Retention(RetentionPolicy.RUNTIME)
@interface NotPublished {
}
+
+ @DomainObj(name = "foo")
+ @Inherited
+ @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface NamedFoo {
+ }
//@Meta
@Published
@@ -82,8 +91,8 @@ public class Annotations_getAnnotations_on_Class_Test {
val synthesized = _Annotations
.synthesize(SomeDomainObject.class, DomainObj.class);
- Assert.assertThat(synthesized.isPresent(), is(true));
- Assert.assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.YES));
+ assertThat(synthesized.isPresent(), is(true));
+ assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.YES));
}
@Test
@@ -95,8 +104,8 @@ public class Annotations_getAnnotations_on_Class_Test {
val synthesized = _Annotations
.synthesize(SomeDomainObject.class, DomainObj.class);
- Assert.assertThat(synthesized.isPresent(), is(true));
- Assert.assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.YES));
+ assertThat(synthesized.isPresent(), is(true));
+ assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.YES));
}
@Test
@@ -108,8 +117,8 @@ public class Annotations_getAnnotations_on_Class_Test {
val synthesized = _Annotations
.synthesize(SomeDomainObject.class, DomainObj.class);
- Assert.assertThat(synthesized.isPresent(), is(true));
- Assert.assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.YES));
+ assertThat(synthesized.isPresent(), is(true));
+ assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.YES));
}
@Test
@@ -122,8 +131,8 @@ public class Annotations_getAnnotations_on_Class_Test {
val synthesized = _Annotations
.synthesize(SomeDomainObject.class, DomainObj.class);
- Assert.assertThat(synthesized.isPresent(), is(true));
- Assert.assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.YES));
+ assertThat(synthesized.isPresent(), is(true));
+ assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.YES));
}
@Test
@@ -136,8 +145,8 @@ public class Annotations_getAnnotations_on_Class_Test {
val synthesized = _Annotations
.synthesize(SomeDomainObject.class, DomainObj.class);
- Assert.assertThat(synthesized.isPresent(), is(true));
- Assert.assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.NO));
+ assertThat(synthesized.isPresent(), is(true));
+ assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.NO));
}
@Test
@@ -151,8 +160,8 @@ public class Annotations_getAnnotations_on_Class_Test {
val synthesized = _Annotations
.synthesize(SomeDomainObject.class, DomainObj.class);
- Assert.assertThat(synthesized.isPresent(), is(true));
- Assert.assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.NO));
+ assertThat(synthesized.isPresent(), is(true));
+ assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.NO));
}
@@ -167,8 +176,45 @@ public class Annotations_getAnnotations_on_Class_Test {
val synthesized = _Annotations
.synthesize(SomeDomainObject.class, DomainObj.class);
- Assert.assertThat(synthesized.isPresent(), is(true));
- Assert.assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.YES));
+ assertThat(synthesized.isPresent(), is(true));
+ assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.YES));
+ }
+
+ @Test
+ public void merge_only_valid_attributes() throws Exception {
+
+ @NamedFoo // <-- should provide name=foo even if the below matches first on publishng=YES
+ @DomainObj(publishng = DomainObj.Publishng.YES) // <-- should provide YES
+ class SomeDomainObject {}
+
+ val publishng = _Annotations
+ .getEnum("publishng", SomeDomainObject.class, DomainObj.class, DomainObj.Publishng.class);
+
+ val name = _Annotations
+ .getString("name", SomeDomainObject.class, DomainObj.class);
+
+ assertThat(publishng.isPresent(), is(true));
+ assertThat(name.isPresent(), is(true));
+
+ assertThat(publishng.get(), is(DomainObj.Publishng.YES));
+ assertThat(name.get(), is("foo"));
+ }
+
+ @Test @Disabled("unfortunately there is no easy way to plug into synthesizing, "
+ + "to get the desired behavior")
+ public void ignore_not_specified() throws Exception {
+
+ @MetaPublished
+ @NotPublished // <-- should win over the above
+ @DomainObj(publishng = DomainObj.Publishng.NOT_SPECIFIED) // <-- should be ignored
+ class SomeDomainObject {}
+
+ val synthesized = _Annotations
+ .synthesize(SomeDomainObject.class, DomainObj.class);
+
+ assertThat(synthesized.isPresent(), is(true));
+ assertThat(synthesized.get().publishng(), is(DomainObj.Publishng.NO));
}
+
}
\ No newline at end of file