Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package com.g2forge.habitat.metadata.access.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.g2forge.alexandria.java.core.helpers.HCollection;
import com.g2forge.alexandria.java.core.helpers.HTree;
import com.g2forge.alexandria.java.reflect.annotations.IJavaAnnotations;
import com.g2forge.habitat.metadata.annotations.ContainerAnnotationReflection;
import com.g2forge.habitat.metadata.type.predicate.IAnnotationPredicateType;
Expand Down Expand Up @@ -30,25 +37,67 @@ public class AnnotationPredicate<T extends Annotation> implements IPredicate<T>
@Override
public T get0() {
final Class<T> annotationType = getType().getAnnotationType();
final IJavaAnnotations annotations = getSubject().getAnnotations();
final T retVal = annotations.getAnnotation(annotationType);
if (retVal != null) return retVal;

final ContainerAnnotationReflection<T, Annotation> containerAnnotationReflection = getContainerAnnotationReflection();
if (containerAnnotationReflection == null) return null;
final Annotation repeatable = annotations.getAnnotation(containerAnnotationReflection.getRepeatable());
if (repeatable == null) return null;
return containerAnnotationReflection.getCollectionStrategy().builder().add(repeatable).get();
if (annotationType.isAnnotationPresent(Inherited.class)) {
// Inherited annotations
final ContainerAnnotationReflection<T, Annotation> containerAnnotationReflection = getContainerAnnotationReflection();
if (containerAnnotationReflection == null) {
final Optional<IElementSubject> subject = HTree.find(getSubject(), IElementSubject::getParents, s -> s.getAnnotations().isAnnotated(annotationType));
if (!subject.isPresent()) return null;
final IJavaAnnotations annotations = subject.get().getAnnotations();
return annotations.getAnnotation(annotationType);
} else {
final Class<Annotation> repeatableAnnotationType = containerAnnotationReflection.getRepeatable();
final List<Annotation> repeatableAnnotationValues = HTree.dfs(getSubject(), IElementSubject::getParents, false).flatMap(s -> {
final IJavaAnnotations annotations = s.getAnnotations();
final List<Annotation> retVal = new ArrayList<>();

final T annotation = annotations.getAnnotation(annotationType);
if (annotation != null) retVal.addAll(HCollection.asListIterable(containerAnnotationReflection.getCollectionStrategy().iterable(annotation)));

final Annotation repeatable = annotations.getAnnotation(repeatableAnnotationType);
if (repeatable != null) retVal.add(repeatable);

return retVal.stream();
}).collect(Collectors.toList());
return containerAnnotationReflection.getCollectionStrategy().builder().add(repeatableAnnotationValues).get();
}
} else {
// Non-inherited annotations present on the subject
final IJavaAnnotations annotations = getSubject().getAnnotations();
final T retVal = annotations.getAnnotation(annotationType);
if (retVal != null) return retVal;

// Non-inherited annotations which are repeated, and whose repeatable child is on the subject
final ContainerAnnotationReflection<T, Annotation> containerAnnotationReflection = getContainerAnnotationReflection();
if (containerAnnotationReflection == null) return null;
final Annotation repeatable = annotations.getAnnotation(containerAnnotationReflection.getRepeatable());
if (repeatable == null) return null;
return containerAnnotationReflection.getCollectionStrategy().builder().add(repeatable).get();
}
}

@Override
public boolean isPresent() {
final IJavaAnnotations annotations = getSubject().getAnnotations();
final boolean retVal = annotations.isAnnotated(getType().getAnnotationType());
if (retVal) return true;
final Class<T> annotationType = getType().getAnnotationType();
if (annotationType.isAnnotationPresent(Inherited.class)) {
// Inherited annotations
final ContainerAnnotationReflection<T, Annotation> containerAnnotationReflection = getContainerAnnotationReflection();
if (containerAnnotationReflection == null) return HTree.find(getSubject(), IElementSubject::getParents, s -> s.getAnnotations().isAnnotated(annotationType)).isPresent();
else {
final Class<Annotation> repeatableAnnotationType = containerAnnotationReflection.getRepeatable();
return HTree.find(getSubject(), IElementSubject::getParents, s -> {
final IJavaAnnotations annotations = s.getAnnotations();
return annotations.isAnnotated(annotationType) || annotations.isAnnotated(repeatableAnnotationType);
}).isPresent();
}
} else {
final IJavaAnnotations annotations = getSubject().getAnnotations();
final boolean retVal = annotations.isAnnotated(annotationType);
if (retVal) return true;

final ContainerAnnotationReflection<T, Annotation> containerAnnotationReflection = getContainerAnnotationReflection();
if (containerAnnotationReflection == null) return false;
return annotations.isAnnotated(containerAnnotationReflection.getRepeatable());
final ContainerAnnotationReflection<T, Annotation> containerAnnotationReflection = getContainerAnnotationReflection();
if (containerAnnotationReflection == null) return false;
return annotations.isAnnotated(containerAnnotationReflection.getRepeatable());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.g2forge.habitat.metadata.value.implementations;

import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.List;

import com.g2forge.alexandria.java.core.helpers.HCollection;
import com.g2forge.alexandria.java.reflect.annotations.ElementJavaAnnotations;
import com.g2forge.alexandria.java.reflect.annotations.IJavaAnnotations;
import com.g2forge.habitat.metadata.type.subject.ISubjectType;
Expand All @@ -19,6 +22,21 @@
@Builder(toBuilder = true)
@RequiredArgsConstructor
class ElementSubject implements IElementSubject {
protected static List<IElementSubject> getParents(IElementSubject _this, AnnotatedElement element) {
if (element instanceof Class) {
@SuppressWarnings("rawtypes")
final Class<?> cast = (Class) element;

final List<IElementSubject> parents = new ArrayList<>();
if (cast.getSuperclass() != null) parents.add(new ElementSubject(_this.getContext(), cast.getSuperclass()));
for (Class<?> parent : cast.getInterfaces()) {
parents.add(new ElementSubject(_this.getContext(), parent));
}
return parents;
}
return HCollection.emptyList();
}

@ToString.Exclude
protected final IMetadataValueContext context;

Expand All @@ -33,4 +51,9 @@ class ElementSubject implements IElementSubject {
@EqualsAndHashCode.Exclude
@ToString.Exclude
private final IJavaAnnotations annotations = new ElementJavaAnnotations(getElement());

@Override
public List<IElementSubject> getParents() {
return getParents(this, getElement());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.List;

import com.g2forge.alexandria.java.reflect.annotations.ElementJavaAnnotations;
import com.g2forge.alexandria.java.reflect.annotations.IJavaAnnotations;
import com.g2forge.habitat.metadata.type.subject.IValueSubjectType;
import com.g2forge.habitat.metadata.value.IMetadataValueContext;
import com.g2forge.habitat.metadata.value.subject.IElementSubject;
import com.g2forge.habitat.metadata.value.subject.IValueSubject;

import lombok.Builder;
Expand All @@ -19,7 +21,7 @@
@Data
@Builder(toBuilder = true)
@RequiredArgsConstructor
public class ElementValueSubject implements IValueSubject {
class ElementValueSubject implements IValueSubject {
@ToString.Exclude
protected final IMetadataValueContext context;

Expand All @@ -46,4 +48,9 @@ protected IValueSubjectType computeType() {

return (IValueSubjectType) getContext().getTypeContext().subject(elementType, valueType);
}

@Override
public List<IElementSubject> getParents() {
return ElementSubject.getParents(this, getElement());
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.g2forge.habitat.metadata.value.subject;

import java.lang.reflect.AnnotatedElement;
import java.util.List;

import com.g2forge.alexandria.java.reflect.annotations.IJavaAnnotated;
import com.g2forge.habitat.metadata.type.subject.IElementSubjectType;

@SubjectType(IElementSubjectType.class)
public interface IElementSubject extends ISubject, IJavaAnnotated {
public AnnotatedElement getElement();

public List<IElementSubject> getParents();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.g2forge.habitat.metadata;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

import com.g2forge.alexandria.java.core.helpers.HCollection;
import com.g2forge.alexandria.test.HAssert;
import com.g2forge.habitat.metadata.value.predicate.IPredicate;

/**
* This is similar to {@link TestRepeatableAnnotationMetadata} except that {@link TestInheritedAnnotationMetadata.Contained} is {@link Inherited}.
*/
public class TestInheritedAnnotationMetadata {
@Contained("AC")
@NonRepeatable("ANR")
public interface A {}

@Contained("BC")
@NonRepeatable("BNR")
public interface B extends A {}

public interface C extends A {}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(Container.class)
@Inherited
public @interface Contained {
public String value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface Container {
public Contained[] value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface NonRepeatable {
public String value();
}

@Test
public void aContained() {
final IPredicate<Contained> contained = Metadata.getStandard().of(A.class).bind(Contained.class);
HAssert.assertTrue(contained.isPresent());
HAssert.assertEquals("AC", contained.get0().value());
}

@Test
public void aContainer() {
final IPredicate<Container> container = Metadata.getStandard().of(A.class).bind(Container.class);
HAssert.assertTrue(container.isPresent());
HAssert.assertEquals(HCollection.asList("AC"), Stream.of(container.get0().value()).map(Contained::value).collect(Collectors.toList()));
}

@Test
public void aNonRepeatable() {
final IPredicate<NonRepeatable> nonRepeatable = Metadata.getStandard().of(A.class).bind(NonRepeatable.class);
HAssert.assertTrue(nonRepeatable.isPresent());
HAssert.assertEquals("ANR", nonRepeatable.get0().value());
}

@Test
public void bContained() {
final IPredicate<Contained> contained = Metadata.getStandard().of(B.class).bind(Contained.class);
HAssert.assertTrue(contained.isPresent());
HAssert.assertEquals("BC", contained.get0().value());
}

@Test
public void bContainer() {
final IPredicate<Container> container = Metadata.getStandard().of(B.class).bind(Container.class);
HAssert.assertTrue(container.isPresent());
HAssert.assertEquals(HCollection.asList("BC", "AC"), Stream.of(container.get0().value()).map(Contained::value).collect(Collectors.toList()));
}

@Test
public void bNonRepeatable() {
final IPredicate<NonRepeatable> nonRepeatable = Metadata.getStandard().of(B.class).bind(NonRepeatable.class);
HAssert.assertTrue(nonRepeatable.isPresent());
HAssert.assertEquals("BNR", nonRepeatable.get0().value());
}

@Test
public void cContained() {
final IPredicate<Contained> contained = Metadata.getStandard().of(C.class).bind(Contained.class);
HAssert.assertTrue(contained.isPresent());
HAssert.assertEquals("AC", contained.get0().value());
}

@Test
public void cContainer() {
final IPredicate<Container> container = Metadata.getStandard().of(C.class).bind(Container.class);
HAssert.assertTrue(container.isPresent());
HAssert.assertEquals(HCollection.asList("AC"), Stream.of(container.get0().value()).map(Contained::value).collect(Collectors.toList()));
}

@Test
public void cNonRepeatable() {
final IPredicate<NonRepeatable> nonRepeatable = Metadata.getStandard().of(C.class).bind(NonRepeatable.class);
HAssert.assertTrue(nonRepeatable.isPresent());
HAssert.assertEquals("ANR", nonRepeatable.get0().value());
}
}