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 2021/09/05 04:53:40 UTC
[isis] branch master updated: ISIS-2774: meta-annotation support
for @Title and 'navigable parent'
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 16cb432 ISIS-2774: meta-annotation support for @Title and 'navigable parent'
16cb432 is described below
commit 16cb43202d46727fea597238317c58dc9c698b1c
Author: andi-huber <ah...@apache.org>
AuthorDate: Sun Sep 5 06:53:27 2021 +0200
ISIS-2774: meta-annotation support for @Title and 'navigable parent'
makes Evaluator more generic
---
.../isis/core/metamodel/facets/Evaluators.java | 86 +++++++++-------------
.../NavigableParentAnnotationFacetFactory.java | 36 ++++-----
.../annotation/TitleFacetViaTitleAnnotation.java | 65 +++++++++-------
.../TitleAnnotationFacetFactoryTest.java | 14 ++--
4 files changed, 99 insertions(+), 102 deletions(-)
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/Evaluators.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/Evaluators.java
index 7f5f3bf..af7ef63 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/Evaluators.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/Evaluators.java
@@ -22,12 +22,15 @@ package org.apache.isis.core.metamodel.facets;
import java.beans.IntrospectionException;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Optional;
+import java.util.function.Predicate;
import java.util.stream.Stream;
import org.apache.isis.applib.exceptions.unrecoverable.MetaModelException;
+import org.apache.isis.commons.internal.base._NullSafe;
import org.apache.isis.commons.internal.reflection._ClassCache;
import org.apache.isis.commons.internal.reflection._Reflect;
import org.apache.isis.commons.internal.reflection._Reflect.InterfacePolicy;
@@ -37,6 +40,7 @@ import org.apache.isis.core.metamodel.commons.ThrowableExtensions;
import lombok.Getter;
import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
import lombok.val;
import lombok.extern.log4j.Log4j2;
@@ -47,35 +51,35 @@ public final class Evaluators {
* Streams all fields and no-arg methods having a specified annotationType,
* each wrapped with an {@link Evaluator} object.
*/
- public static <T extends Annotation> Stream<Evaluator<T>> streamEvaluators(
+ public static <T extends Annotation> Stream<Evaluator> streamEvaluators(
final @NonNull Class<?> cls,
- final @NonNull Class<T> annotationType,
+ final @NonNull Predicate<AnnotatedElement> memberFilter,
final @NonNull TypeHierarchyPolicy typeHierarchyPolicy,
final @NonNull InterfacePolicy interfacePolicy) {
return typeHierarchyPolicy.isIncludeTypeHierarchy()
? _Reflect
.streamTypeHierarchy(cls, interfacePolicy)
- .flatMap(type->streamAnnotatedMemberEvaluators(type, annotationType))
- : streamAnnotatedMemberEvaluators(cls, annotationType);
+ .flatMap(type->streamAnnotatedMemberEvaluators(type, memberFilter))
+ : streamAnnotatedMemberEvaluators(cls, memberFilter);
}
// -- HELPER
- private static <T extends Annotation> Stream<Evaluator<T>> streamAnnotatedMemberEvaluators(
+ private static <T extends Annotation> Stream<Evaluator> streamAnnotatedMemberEvaluators(
final Class<?> cls,
- final Class<T> annotationType) {
+ final Predicate<AnnotatedElement> memberFilter) {
val classCache = _ClassCache.getInstance();
return Stream.concat(
- streamMethodEvaluators(cls, annotationType, classCache),
- streamFieldEvaluators(cls, annotationType, classCache));
+ streamMethodEvaluators(cls, memberFilter, classCache),
+ streamFieldEvaluators(cls, memberFilter, classCache));
}
- private static <T extends Annotation> Stream<Evaluator<T>> streamMethodEvaluators(
+ private static Stream<Evaluator> streamMethodEvaluators(
final Class<?> cls,
- final Class<T> annotationType,
+ final Predicate<AnnotatedElement> memberFilter,
final _ClassCache classCache) {
return classCache
@@ -83,30 +87,28 @@ public final class Evaluators {
.filter(MethodUtil::isNotStatic)
.filter(MethodUtil::isNoArg)
.filter(MethodUtil::isNotVoid)
- .map(method->MethodEvaluator.create(method, annotationType))
- .flatMap(Optional::stream);
+ .map(method->memberFilter.test(method) ? new MethodEvaluator(cls, method) : null)
+ .filter(_NullSafe::isPresent)
+ .map(Evaluator.class::cast);
}
- private static <T extends Annotation> Stream<Evaluator<T>> streamFieldEvaluators(
+ private static <T extends Annotation> Stream<Evaluator> streamFieldEvaluators(
final Class<?> cls,
- final Class<T> annotationType,
+ final Predicate<AnnotatedElement> memberFilter,
final _ClassCache classCache) {
return classCache
.streamDeclaredFields(cls)
- .map(field->FieldEvaluator.create(field, annotationType))
- .flatMap(Optional::stream);
+ .map(field->memberFilter.test(field) ? new FieldEvaluator(cls, field) : null)
+ .filter(_NullSafe::isPresent)
+ .map(Evaluator.class::cast);
}
// -- EVALUATOR
- public static abstract class Evaluator<T extends Annotation> {
- @Getter private final T annotation;
- private MethodHandle mh;
+ public static abstract class Evaluator {
- protected Evaluator(final T annotation) {
- this.annotation = annotation;
- }
+ private MethodHandle mh;
protected abstract MethodHandle createMethodHandle() throws IllegalAccessException;
public abstract String name();
@@ -129,23 +131,12 @@ public final class Evaluators {
}
}
- public static class MethodEvaluator<T extends Annotation> extends Evaluator<T> {
-
- static <T extends Annotation> Optional<MethodEvaluator<T>> create(
- final Method method,
- final Class<T> annotationType) {
-
- return Optional.ofNullable(method.getAnnotation(annotationType))
- .map(annot->new MethodEvaluator<>(method, annot));
- }
+ @RequiredArgsConstructor
+ public static class MethodEvaluator extends Evaluator {
+ @Getter private final Class<?> correspondingClass;
@Getter private final Method method;
- MethodEvaluator(final Method method, final T annotation) {
- super(annotation);
- this.method = method;
- }
-
@Override
public String name() {
return method.getName() + "()";
@@ -157,23 +148,12 @@ public final class Evaluators {
}
}
- public static class FieldEvaluator<T extends Annotation> extends Evaluator<T> {
-
- static <T extends Annotation> Optional<FieldEvaluator<T>> create(
- final Field field,
- final Class<T> annotationType) {
-
- return Optional.ofNullable(field.getAnnotation(annotationType))
- .map(annot->new FieldEvaluator<>(field, annot));
- }
+ @RequiredArgsConstructor
+ public static class FieldEvaluator extends Evaluator {
+ @Getter private final Class<?> correspondingClass;
@Getter private final Field field;
- FieldEvaluator(final Field field, final T annotation) {
- super(annotation);
- this.field = field;
- }
-
@Override
public String name() {
return field.getName();
@@ -184,13 +164,13 @@ public final class Evaluators {
return _Reflect.handleOfGetterOn(field);
}
- public Optional<Method> getGetter(final Class<?> originatingClass) {
+ public Optional<Method> lookupGetter() {
try {
return Optional.ofNullable(
- _Reflect.getGetter(originatingClass, field.getName()) );
+ _Reflect.getGetter(correspondingClass, field.getName()) );
} catch (IntrospectionException e) {
log.warn("failed reflective introspection on {} field {}",
- originatingClass, field.getName(), e);
+ correspondingClass, field.getName(), e);
}
return Optional.empty();
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java
index e3c789f..c7f1c00 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java
@@ -19,6 +19,7 @@
package org.apache.isis.core.metamodel.facets.object.navparent.annotation;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Optional;
@@ -26,6 +27,7 @@ import javax.inject.Inject;
import org.apache.isis.applib.annotation.PropertyLayout;
import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.reflection._Annotations;
import org.apache.isis.commons.internal.reflection._Reflect.InterfacePolicy;
import org.apache.isis.commons.internal.reflection._Reflect.TypeHierarchyPolicy;
import org.apache.isis.core.metamodel.context.MetaModelContext;
@@ -70,29 +72,28 @@ implements MetaModelRefiner {
// That's the one we use to
// resolve the current domain-object's navigable parent.
- final Optional<Evaluators.Evaluator<PropertyLayout>> evaluators =
+ final Optional<Evaluators.Evaluator> evaluators =
Evaluators.streamEvaluators(cls,
- PropertyLayout.class,
+ NavigableParentAnnotationFacetFactory::isNavigableParentFlagSet,
TypeHierarchyPolicy.INCLUDE,
InterfacePolicy.EXCLUDE)
- .filter(NavigableParentAnnotationFacetFactory::isNavigableParentFlagSet)
.findFirst();
if (evaluators.isEmpty()) {
return; // no parent resolvable
}
- final Evaluators.Evaluator<PropertyLayout> parentEvaluator = evaluators.get();
+ final Evaluators.Evaluator parentEvaluator = evaluators.get();
final Method method;
// find method that provides the parent ...
if(parentEvaluator instanceof Evaluators.MethodEvaluator) {
- // we have a @Parent annotated method
- method = ((Evaluators.MethodEvaluator<PropertyLayout>) parentEvaluator).getMethod();
+ // we have a 'parent' annotated method
+ method = ((Evaluators.MethodEvaluator) parentEvaluator).getMethod();
} else if(parentEvaluator instanceof Evaluators.FieldEvaluator) {
- // we have a @Parent annotated field (useful if one uses lombok's @Getter on a field)
- method = ((Evaluators.FieldEvaluator<PropertyLayout>) parentEvaluator).getGetter(cls).orElse(null);
+ // we have a 'parent' annotated field (useful if one uses lombok's @Getter on a field)
+ method = ((Evaluators.FieldEvaluator) parentEvaluator).lookupGetter().orElse(null);
if(method==null)
return; // code should not be reached, since case should be handled by meta-data validation
@@ -108,11 +109,13 @@ implements MetaModelRefiner {
}
}
- private static boolean isNavigableParentFlagSet(final Evaluators.Evaluator<PropertyLayout> evaluator){
- return evaluator.getAnnotation().navigable().isParent();
+ private static boolean isNavigableParentFlagSet(final AnnotatedElement annotatedElement){
+ return _Annotations
+ .synthesizeInherited(annotatedElement, PropertyLayout.class)
+ .map(propertyLayout->propertyLayout.navigable().isParent())
+ .orElse(false);
}
-
/**
* For detailed behavior see
* <a href="https://issues.apache.org/jira/browse/ISIS-1816">ISIS-1816</a>.
@@ -130,10 +133,9 @@ implements MetaModelRefiner {
val evaluators =
Evaluators.streamEvaluators(cls,
- PropertyLayout.class,
+ NavigableParentAnnotationFacetFactory::isNavigableParentFlagSet,
TypeHierarchyPolicy.EXCLUDE,
InterfacePolicy.INCLUDE)
- .filter(NavigableParentAnnotationFacetFactory::isNavigableParentFlagSet)
.collect(Can.toCan());
if (evaluators.isEmpty()) {
@@ -155,15 +157,15 @@ implements MetaModelRefiner {
return; // continue validation processing
}
- final Evaluators.Evaluator<PropertyLayout> parentEvaluator = evaluators.getSingletonOrFail();
+ final Evaluators.Evaluator parentEvaluator = evaluators.getSingletonOrFail();
if(parentEvaluator instanceof Evaluators.FieldEvaluator) {
// we have a @Parent annotated field (useful if one uses lombok's @Getter on a field)
- final Evaluators.FieldEvaluator<PropertyLayout> fieldEvaluator =
- (Evaluators.FieldEvaluator<PropertyLayout>) parentEvaluator;
+ final Evaluators.FieldEvaluator fieldEvaluator =
+ (Evaluators.FieldEvaluator) parentEvaluator;
- if(!fieldEvaluator.getGetter(cls).isPresent()) {
+ if(!fieldEvaluator.lookupGetter().isPresent()) {
ValidationFailure.raiseFormatted(
spec,
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java
index aa192e8..cc6da87 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java
@@ -19,18 +19,22 @@
package org.apache.isis.core.metamodel.facets.object.title.annotation;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import org.apache.isis.applib.annotation.Title;
import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.base._Refs;
import org.apache.isis.commons.internal.base._Strings;
import org.apache.isis.commons.internal.collections._Lists;
import org.apache.isis.commons.internal.compare._Comparators;
import org.apache.isis.commons.internal.functions._Predicates;
+import org.apache.isis.commons.internal.reflection._Annotations;
import org.apache.isis.commons.internal.reflection._Reflect.InterfacePolicy;
import org.apache.isis.commons.internal.reflection._Reflect.TypeHierarchyPolicy;
import org.apache.isis.core.metamodel.facetapi.FacetHolder;
@@ -41,8 +45,10 @@ import org.apache.isis.core.metamodel.facets.object.title.TitleFacet;
import org.apache.isis.core.metamodel.facets.object.title.TitleFacetAbstract;
import org.apache.isis.core.metamodel.spec.ManagedObject;
+import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
import lombok.val;
import lombok.extern.log4j.Log4j2;
@@ -55,14 +61,15 @@ implements ImperativeFacet {
final @NonNull Class<?> cls,
final @NonNull FacetHolder holder){
- val titleComponents = Evaluators.streamEvaluators(cls,
- Title.class,
- TypeHierarchyPolicy.EXCLUDE,
- InterfacePolicy.INCLUDE)
- .sorted((eval1, eval2) -> _Comparators.deweyOrderCompare(
- eval1.getAnnotation().sequence(),
- eval2.getAnnotation().sequence()))
- .map(TitleFacetViaTitleAnnotation.TitleComponent::of)
+ val titleRef = _Refs.<Title>objectRef(null);
+
+ val titleComponents = Evaluators
+ .streamEvaluators(cls,
+ annotatedElement->isTitleComponent(annotatedElement, titleRef::set),
+ TypeHierarchyPolicy.EXCLUDE,
+ InterfacePolicy.INCLUDE)
+ .map(evaluator->TitleComponent.of(evaluator, titleRef.getValueElseFail()))
+ .sorted()
.collect(Can.toCan());
if (titleComponents.isEmpty()) {
@@ -175,33 +182,36 @@ implements ImperativeFacet {
return str.length() < maxLength ? str : str.substring(0, maxLength - 3) + "...";
}
- public static class TitleComponent {
+ private static boolean isTitleComponent(
+ final AnnotatedElement annotatedElement,
+ final Consumer<Title> onTitleFound){
+ return _Annotations
+ .synthesizeInherited(annotatedElement, Title.class)
+ .map(title->{onTitleFound.accept(title); return true;})
+ .orElse(false);
+ }
+
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ public static class TitleComponent
+ implements Comparable<TitleComponent> {
- public static TitleComponent of(final Evaluators.Evaluator<Title> titleEvaluator) {
- final Title annotation = titleEvaluator.getAnnotation();
+ public static TitleComponent of(
+ final Evaluators.Evaluator titleEvaluator,
+ final Title annotation) {
+
+ final String deweyOrdinal = annotation != null ? annotation.sequence() : "1";
final String prepend = annotation != null ? annotation.prepend() : " ";
final String append = annotation != null ? annotation.append() : "";
final int abbreviateTo = annotation != null ? annotation.abbreviatedTo() : Integer.MAX_VALUE;
- return new TitleComponent(prepend, append, titleEvaluator, abbreviateTo);
+ return new TitleComponent(titleEvaluator, deweyOrdinal, prepend, append, abbreviateTo);
}
+ @Getter private final Evaluators.Evaluator titleEvaluator;
+ @Getter private final String deweyOrdinal;
@Getter private final String prepend;
@Getter private final String append;
- @Getter private final Evaluators.Evaluator<Title> titleEvaluator;
private final int abbreviateTo;
- private TitleComponent(
- final String prepend,
- final String append,
- final Evaluators.Evaluator<Title> titleEvaluator,
- final int abbreviateTo) {
- super();
- this.prepend = prepend;
- this.append = append;
- this.titleEvaluator = titleEvaluator;
- this.abbreviateTo = abbreviateTo;
- }
-
@Override
public String toString() {
final List<String> parts = _Lists.newArrayList();
@@ -216,5 +226,10 @@ implements ImperativeFacet {
}
return String.join(";", parts);
}
+
+ @Override
+ public int compareTo(final TitleComponent other) {
+ return _Comparators.deweyOrderCompare(this.getDeweyOrdinal(), other.getDeweyOrdinal());
+ }
}
}
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/annotation/TitleAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/annotation/TitleAnnotationFacetFactoryTest.java
index 905dab7..0aacdc4 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/annotation/TitleAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/annotation/TitleAnnotationFacetFactoryTest.java
@@ -31,9 +31,6 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
import org.apache.isis.applib.annotation.Title;
import org.apache.isis.core.metamodel.facetapi.Facet;
import org.apache.isis.core.metamodel.facets.AbstractFacetFactoryJUnit4TestCase;
@@ -45,6 +42,9 @@ import org.apache.isis.core.metamodel.facets.object.title.annotation.TitleFacetV
import org.apache.isis.core.metamodel.spec.ManagedObject;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
public class TitleAnnotationFacetFactoryTest
extends AbstractFacetFactoryJUnit4TestCase {
@@ -90,8 +90,8 @@ extends AbstractFacetFactoryJUnit4TestCase {
final List<Method> titleMethods = Arrays.asList(Customer.class.getMethod("someTitle"));
for (int i = 0; i < titleMethods.size(); i++) {
- final Evaluators.MethodEvaluator<Title> titleEvaluator =
- (Evaluators.MethodEvaluator<Title>) titleFacetViaTitleAnnotation.getComponents().getElseFail(i)
+ final Evaluators.MethodEvaluator titleEvaluator =
+ (Evaluators.MethodEvaluator) titleFacetViaTitleAnnotation.getComponents().getElseFail(i)
.getTitleEvaluator();
Assert.assertEquals(titleMethods.get(i),
@@ -133,8 +133,8 @@ extends AbstractFacetFactoryJUnit4TestCase {
//final List<TitleComponent> components = titleFacetViaTitleAnnotation.getComponents();
for (int i = 0; i < titleMethods.size(); i++) {
- final Evaluators.MethodEvaluator<Title> titleEvaluator =
- (Evaluators.MethodEvaluator<Title>) titleFacetViaTitleAnnotation.getComponents().getElseFail(i)
+ final Evaluators.MethodEvaluator titleEvaluator =
+ (Evaluators.MethodEvaluator) titleFacetViaTitleAnnotation.getComponents().getElseFail(i)
.getTitleEvaluator();
Assert.assertEquals(titleMethods.get(i),